Skip to content

Commit

Permalink
feat: add batched signerless contract calls (#5313)
Browse files Browse the repository at this point in the history
Adds a dedicated entrypoint for calls into contracts done from outside
the context of an account contract.

Previously we had just the `SignerlessWallet` that provided this
functionality but it was limited to only one private function call per
tx. This PR adds a dedicated contract to act as an entrypoint that takes
the same payload as normal account contracts and calls external
functions without doing any auth checks.
  • Loading branch information
alexghr authored Mar 22, 2024
1 parent 41107e3 commit be60eb3
Show file tree
Hide file tree
Showing 33 changed files with 260 additions and 69 deletions.
1 change: 1 addition & 0 deletions noir-projects/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ members = [
"contracts/token_bridge_contract",
"contracts/uniswap_contract",
"contracts/reader_contract",
"contracts/multi_call_entrypoint_contract",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "multi_call_entrypoint_contract"
authors = [""]
compiler_version = ">=0.18.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
authwit = { path = "../../../aztec-nr/authwit" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// An entrypoint contract that allows everything to go through. Only used for testing
// Pair this with SignerlessWallet to perform multiple actions before any account contracts are deployed (and without authentication)
contract MultiCallEntrypoint {
use dep::std;

use dep::aztec::prelude::AztecAddress;
use dep::authwit::entrypoint::app::AppPayload;

#[aztec(private)]
fn entrypoint(app_payload: pub AppPayload) {
app_payload.execute_calls(&mut context);
}
}
3 changes: 2 additions & 1 deletion yarn-project/accounts/src/defaults/account_interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AccountInterface, AuthWitnessProvider, EntrypointInterface, FeeOptions } from '@aztec/aztec.js/account';
import { AccountInterface, AuthWitnessProvider } from '@aztec/aztec.js/account';
import { EntrypointInterface, FeeOptions } from '@aztec/aztec.js/entrypoint';
import { AuthWitness, FunctionCall, TxExecutionRequest } from '@aztec/circuit-types';
import { AztecAddress, CompleteAddress, Fr } from '@aztec/circuits.js';
import { DefaultAccountEntrypoint } from '@aztec/entrypoints/account';
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"./account": "./dest/api/account.js",
"./aztec_address": "./dest/api/aztec_address.js",
"./deployment": "./dest/api/deployment.js",
"./entrypoint": "./dest/api/entrypoint.js",
"./eth_address": "./dest/api/eth_address.js",
"./ethereum": "./dest/api/ethereum.js",
"./fee": "./dest/api/fee.js",
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Fr } from '@aztec/circuits.js';

export { AccountContract } from './contract.js';
export { AccountInterface, AuthWitnessProvider, EntrypointInterface, FeeOptions } from './interface.js';
export { AccountInterface, AuthWitnessProvider } from './interface.js';
export * from './wallet.js';

/** A contract deployment salt. */
Expand Down
25 changes: 2 additions & 23 deletions yarn-project/aztec.js/src/account/interface.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { AuthWitness, CompleteAddress, FunctionCall, TxExecutionRequest } from '@aztec/circuit-types';
import { AuthWitness, CompleteAddress, FunctionCall } from '@aztec/circuit-types';
import { AztecAddress } from '@aztec/circuits.js';
import { Fr } from '@aztec/foundation/fields';

import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
import { FeePaymentMethod } from '../fee/fee_payment_method.js';

/**
* Fee payment options for a transaction.
*/
export type FeeOptions = {
/** The fee payment method to use */
paymentMethod: FeePaymentMethod;
/** The fee limit to pay */
maxFee: bigint | number | Fr;
};
import { EntrypointInterface } from '../entrypoint/entrypoint.js';

// docs:start:account-interface
/** Creates authorization witnesses. */
Expand Down Expand Up @@ -44,17 +34,6 @@ export interface AuthWitnessProvider {
): Promise<AuthWitness>;
}

/** Creates transaction execution requests out of a set of function calls. */
export interface EntrypointInterface {
/**
* Generates an authenticated request out of set of function calls.
* @param executions - The execution intents to be run.
* @param feeOpts - The fee to be paid for the transaction.
* @returns The authenticated transaction execution request.
*/
createTxExecutionRequest(executions: FunctionCall[], feeOpts?: FeeOptions): Promise<TxExecutionRequest>;
}

/**
* Handler for interfacing with an account. Knows how to create transaction execution
* requests and authorize actions for its corresponding account.
Expand Down
10 changes: 1 addition & 9 deletions yarn-project/aztec.js/src/api/account.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
export {
AccountContract,
AccountInterface,
AuthWitnessProvider,
EntrypointInterface,
Salt,
Wallet,
FeeOptions,
} from '../account/index.js';
export { AccountContract, AccountInterface, AuthWitnessProvider, Salt, Wallet } from '../account/index.js';

export { AccountManager } from '../account_manager/index.js';

Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec.js/src/api/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '../entrypoint/entrypoint.js';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PXE, Tx, TxExecutionRequest } from '@aztec/circuit-types';

import { FeeOptions } from '../account/interface.js';
import { FeeOptions } from '../entrypoint/entrypoint.js';
import { SentTx } from './sent_tx.js';

/**
Expand Down
27 changes: 27 additions & 0 deletions yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types';
import { TxContext } from '@aztec/circuits.js';

import { EntrypointInterface } from './entrypoint.js';

/**
* Default implementation of the entrypoint interface. It calls a function on a contract directly
*/
export class DefaultEntrypoint implements EntrypointInterface {
constructor(private chainId: number, private protocolVersion: number) {}

createTxExecutionRequest(executions: FunctionCall[]): Promise<TxExecutionRequest> {
const [execution] = executions;
const packedArguments = PackedArguments.fromArgs(execution.args);
const txContext = TxContext.empty(this.chainId, this.protocolVersion);
return Promise.resolve(
new TxExecutionRequest(
execution.to,
execution.functionData,
packedArguments.hash,
txContext,
[packedArguments],
[],
),
);
}
}
25 changes: 25 additions & 0 deletions yarn-project/aztec.js/src/entrypoint/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FunctionCall, TxExecutionRequest } from '@aztec/circuit-types';
import { Fr } from '@aztec/foundation/fields';

import { FeePaymentMethod } from '../fee/fee_payment_method.js';

/**
* Fee payment options for a transaction.
*/
export type FeeOptions = {
/** The fee payment method to use */
paymentMethod: FeePaymentMethod;
/** The fee limit to pay */
maxFee: bigint | number | Fr;
};

/** Creates transaction execution requests out of a set of function calls. */
export interface EntrypointInterface {
/**
* Generates an execution request out of set of function calls.
* @param executions - The execution intents to be run.
* @param feeOpts - The fee to be paid for the transaction.
* @returns The authenticated transaction execution request.
*/
createTxExecutionRequest(executions: FunctionCall[], feeOpts?: FeeOptions): Promise<TxExecutionRequest>;
}
3 changes: 2 additions & 1 deletion yarn-project/aztec.js/src/wallet/account_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { AuthWitness, FunctionCall, PXE, TxExecutionRequest } from '@aztec/circu
import { AztecAddress, Fr } from '@aztec/circuits.js';
import { ABIParameterVisibility, FunctionAbi, FunctionType } from '@aztec/foundation/abi';

import { AccountInterface, FeeOptions } from '../account/interface.js';
import { AccountInterface } from '../account/interface.js';
import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
import { FeeOptions } from '../entrypoint/entrypoint.js';
import { computeAuthWitMessageHash } from '../utils/authwit.js';
import { BaseWallet } from './base_wallet.js';

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { ContractArtifact } from '@aztec/foundation/abi';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';
import { NodeInfo } from '@aztec/types/interfaces';

import { FeeOptions } from '../account/interface.js';
import { Wallet } from '../account/wallet.js';
import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
import { FeeOptions } from '../entrypoint/entrypoint.js';

/**
* A base class for Wallet implementations
Expand Down
32 changes: 14 additions & 18 deletions yarn-project/aztec.js/src/wallet/signerless_wallet.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { AuthWitness, FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types';
import { CompleteAddress, Fr, TxContext } from '@aztec/circuits.js';
import { AuthWitness, FunctionCall, PXE, TxExecutionRequest } from '@aztec/circuit-types';
import { CompleteAddress, Fr } from '@aztec/circuits.js';

import { DefaultEntrypoint } from '../entrypoint/default_entrypoint.js';
import { EntrypointInterface } from '../entrypoint/entrypoint.js';
import { BaseWallet } from './base_wallet.js';

/**
* Wallet implementation which creates a transaction request directly to the requested contract without any signing.
*/
export class SignerlessWallet extends BaseWallet {
constructor(pxe: PXE, private entrypoint?: EntrypointInterface) {
super(pxe);
}

async createTxExecutionRequest(executions: FunctionCall[]): Promise<TxExecutionRequest> {
if (executions.length !== 1) {
throw new Error(`Unexpected number of executions. Expected 1 but received ${executions.length}.`);
let entrypoint = this.entrypoint;
if (!entrypoint) {
const { chainId, protocolVersion } = await this.pxe.getNodeInfo();
entrypoint = new DefaultEntrypoint(chainId, protocolVersion);
}
const [execution] = executions;
const packedArguments = PackedArguments.fromArgs(execution.args);
const { chainId, protocolVersion } = await this.pxe.getNodeInfo();
const txContext = TxContext.empty(chainId, protocolVersion);
return Promise.resolve(
new TxExecutionRequest(
execution.to,
execution.functionData,
packedArguments.hash,
txContext,
[packedArguments],
[],
),
);

return entrypoint.createTxExecutionRequest(executions);
}

getChainId(): Fr {
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@aztec/aztec.js": "workspace:^",
"@aztec/circuit-types": "workspace:^",
"@aztec/circuits.js": "workspace:^",
"@aztec/entrypoints": "workspace:^",
"@aztec/ethereum": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/kv-store": "workspace:^",
Expand Down
17 changes: 12 additions & 5 deletions yarn-project/aztec/src/sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/usr/bin/env -S node --no-warnings
import { AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node';
import { AztecAddress, SignerlessWallet, Wallet } from '@aztec/aztec.js';
import { AztecAddress, BatchCall, SignerlessWallet, Wallet } from '@aztec/aztec.js';
import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment';
import { AztecNode } from '@aztec/circuit-types';
import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multi-call';
import {
DeployL1Contracts,
L1ContractAddresses,
Expand Down Expand Up @@ -165,8 +166,12 @@ async function deployCanonicalL2GasToken(deployer: Wallet, l1ContractAddresses:
return;
}

await (await registerContractClass(deployer, canonicalGasToken.artifact)).send().wait();
await deployInstance(deployer, canonicalGasToken.instance).send().wait();
const batch = new BatchCall(deployer, [
(await registerContractClass(deployer, canonicalGasToken.artifact)).request(),
deployInstance(deployer, canonicalGasToken.instance).request(),
]);

await batch.send().wait();

logger(`Deployed Gas Token on L2 at ${canonicalGasToken.address}`);
}
Expand Down Expand Up @@ -200,8 +205,10 @@ export async function createSandbox(config: Partial<SandboxConfig> = {}) {
const pxe = await createAztecPXE(node);

if (config.enableGas) {
const deployer = new SignerlessWallet(pxe);
await deployCanonicalL2GasToken(deployer, aztecNodeConfig.l1Contracts);
await deployCanonicalL2GasToken(
new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint()),
aztecNodeConfig.l1Contracts,
);
}

const stop = async () => {
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/aztec/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
{
"path": "../circuits.js"
},
{
"path": "../entrypoints"
},
{
"path": "../ethereum"
},
Expand Down
1 change: 1 addition & 0 deletions yarn-project/end-to-end/src/cli_docs_sandbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ GasTokenContractArtifact
ImportTestContractArtifact
InclusionProofsContractArtifact
LendingContractArtifact
MultiCallEntrypointContractArtifact
ParentContractArtifact
PendingNoteHashesContractArtifact
PriceFeedContractArtifact
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
LogType,
PXE,
SentTx,
SignerlessWallet,
Wallet,
createAztecNodeClient,
createDebugLogger,
Expand All @@ -27,6 +28,7 @@ import {
waitForPXE,
} from '@aztec/aztec.js';
import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment';
import { DefaultMultiCallEntrypoint } from '@aztec/entrypoints/multi-call';
import { randomBytes } from '@aztec/foundation/crypto';
import {
AvailabilityOracleAbi,
Expand Down Expand Up @@ -264,7 +266,7 @@ async function setupWithRemoteEnvironment(
if (['1', 'true'].includes(ENABLE_GAS)) {
// this contract might already have been deployed
// the following function is idempotent
await deployCanonicalGasToken(wallets[0]);
await deployCanonicalGasToken(new SignerlessWallet(pxeClient, new DefaultMultiCallEntrypoint()));
}

return {
Expand Down Expand Up @@ -370,9 +372,7 @@ export async function setup(
const { pxe, accounts, wallets } = await setupPXEService(numberOfAccounts, aztecNode!, pxeOpts, logger);

if (['1', 'true'].includes(ENABLE_GAS)) {
// this should be a neutral wallet, but the SignerlessWallet only accepts a single function call
// and this needs two: one to register the class and another to deploy the instance
await deployCanonicalGasToken(wallets[0]);
await deployCanonicalGasToken(new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint()));
}

const cheatCodes = CheatCodes.create(config.rpcUrl, pxe!);
Expand Down
4 changes: 3 additions & 1 deletion yarn-project/entrypoints/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"type": "module",
"exports": {
"./dapp": "./dest/dapp_entrypoint.js",
"./account": "./dest/account_entrypoint.js"
"./account": "./dest/account_entrypoint.js",
"./multi-call": "./dest/multi_call_entrypoint.js"
},
"typedocOptions": {
"entryPoints": [
Expand Down Expand Up @@ -40,6 +41,7 @@
"@aztec/circuit-types": "workspace:^",
"@aztec/circuits.js": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/protocol-contracts": "workspace:^",
"tslib": "^2.4.0"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/entrypoints/src/account_entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AuthWitnessProvider, EntrypointInterface, FeeOptions } from '@aztec/aztec.js/account';
import { AuthWitnessProvider } from '@aztec/aztec.js/account';
import { EntrypointInterface, FeeOptions } from '@aztec/aztec.js/entrypoint';
import { FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types';
import { AztecAddress, FunctionData, GeneratorIndex, TxContext } from '@aztec/circuits.js';
import { FunctionAbi, encodeArguments } from '@aztec/foundation/abi';
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/entrypoints/src/dapp_entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { computeInnerAuthWitHash, computeOuterAuthWitHash } from '@aztec/aztec.js';
import { AuthWitnessProvider, EntrypointInterface } from '@aztec/aztec.js/account';
import { AuthWitnessProvider } from '@aztec/aztec.js/account';
import { EntrypointInterface } from '@aztec/aztec.js/entrypoint';
import { FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types';
import { AztecAddress, Fr, FunctionData, TxContext } from '@aztec/circuits.js';
import { FunctionAbi, encodeArguments } from '@aztec/foundation/abi';
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/entrypoints/src/entrypoint_payload.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FeeOptions } from '@aztec/aztec.js/account';
import { FeeOptions } from '@aztec/aztec.js/entrypoint';
import { Fr } from '@aztec/aztec.js/fields';
import { FunctionCall, PackedArguments, emptyFunctionCall } from '@aztec/circuit-types';
import { AztecAddress } from '@aztec/circuits.js';
Expand Down
Loading

0 comments on commit be60eb3

Please sign in to comment.