Skip to content
Merged
1 change: 1 addition & 0 deletions .test_patterns.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ tests:

# boxes
- regex: "boxes/scripts/run_test.sh vite"
error_regex: "Test timeout of 90000ms exceeded."
owners:
- *grego

Expand Down
38 changes: 24 additions & 14 deletions yarn-project/aztec.js/src/account_manager/deploy_account_method.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { AuthWitnessProvider } from '@aztec/entrypoints/interfaces';
import {
EncodedAppEntrypointPayload,
EncodedFeeEntrypointPayload,
ExecutionPayload,
EncodedAppEntrypointCalls,
EncodedCallsForEntrypoint,
computeCombinedPayloadHash,
} from '@aztec/entrypoints/payload';
import { mergeExecutionPayloads } from '@aztec/entrypoints/utils';
} from '@aztec/entrypoints/encoding';
import type { AuthWitnessProvider } from '@aztec/entrypoints/interfaces';
import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/entrypoints/payload';
import { type ContractArtifact, type FunctionArtifact, getFunctionArtifactByName } from '@aztec/stdlib/abi';
import type { PublicKeys } from '@aztec/stdlib/keys';

Expand Down Expand Up @@ -51,22 +50,33 @@ export class DeployAccountMethod extends DeployMethod {

if (options.fee && this.#feePaymentArtifact) {
const { address } = await this.getInstance();
const emptyAppPayload = await EncodedAppEntrypointPayload.fromAppExecution([]);
const emptyAppCalls = await EncodedAppEntrypointCalls.fromAppExecution([]);
const fee = await this.getDefaultFeeOptions(options.fee);
const feePayload = await EncodedFeeEntrypointPayload.fromFeeOptions(address, fee);
const args = [emptyAppPayload, feePayload, false];
// Get the execution payload for the fee, it includes the calls and potentially authWitnesses
const { calls: feeCalls, authWitnesses: feeAuthwitnesses } = await fee.paymentMethod.getExecutionPayload(
fee.gasSettings,
);
// Encode the calls for the fee
const feePayer = await fee.paymentMethod.getFeePayer(fee.gasSettings);
const isFeePayer = feePayer.equals(address);
const feeEncodedCalls = await EncodedCallsForEntrypoint.fromFeeCalls(feeCalls, isFeePayer);

// Get the entrypoint args
const args = [emptyAppCalls, feeEncodedCalls, false];

// Compute the authwitness required to verify the combined payload
const combinedPayloadAuthWitness = await this.#authWitnessProvider.createAuthWit(
await computeCombinedPayloadHash(emptyAppCalls, feeEncodedCalls),
);

const call = new ContractFunctionInteraction(
this.wallet,
address,
this.#feePaymentArtifact,
args,
[
await this.#authWitnessProvider.createAuthWit(await computeCombinedPayloadHash(emptyAppPayload, feePayload)),
...feePayload.authWitnesses,
],
[combinedPayloadAuthWitness, ...feeAuthwitnesses],
[],
[...emptyAppPayload.hashedArguments, ...feePayload.hashedArguments],
[...emptyAppCalls.hashedArguments, ...feeEncodedCalls.hashedArguments],
);

exec = mergeExecutionPayloads([exec, await call.request()]);
Expand Down
50 changes: 17 additions & 33 deletions yarn-project/aztec.js/src/contract/base_contract_interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@ import type { Fr } from '@aztec/foundation/fields';
import { createLogger } from '@aztec/foundation/log';
import type { AuthWitness } from '@aztec/stdlib/auth-witness';
import { GasSettings } from '@aztec/stdlib/gas';
import type { Capsule, HashedValues, TxExecutionRequest, TxProvingResult } from '@aztec/stdlib/tx';
import type { Capsule, TxExecutionRequest, TxProvingResult } from '@aztec/stdlib/tx';

import { FeeJuicePaymentMethod } from '../fee/fee_juice_payment_method.js';
import type { Wallet } from '../wallet/wallet.js';
import { getGasLimits } from './get_gas_limits.js';
import { ProvenTx } from './proven_tx.js';
import { SentTx } from './sent_tx.js';

/**
* Represents the options to configure a request from a contract interaction.
* Allows specifying additional auth witnesses and capsules to use during execution
*/
export type RequestMethodOptions = {
/** Extra authwits to use during execution */
authWitnesses?: AuthWitness[];
/** Extra capsules to use during execution */
capsules?: Capsule[];
};

/**
* Represents options for calling a (constrained) function in a contract.
* Allows the user to specify the sender address and nonce for a transaction.
*/
export type SendMethodOptions = {
export type SendMethodOptions = RequestMethodOptions & {
/** Wether to skip the simulation of the public part of the transaction. */
skipPublicSimulation?: boolean;
/** The fee options for the transaction. */
Expand All @@ -25,10 +35,6 @@ export type SendMethodOptions = {
nonce?: Fr;
/** Whether the transaction can be cancelled. If true, an extra nullifier will be emitted: H(nonce, GENERATOR_INDEX__TX_NULLIFIER) */
cancellable?: boolean;
/** Authwits to use in the simulation */
authWitnesses?: AuthWitness[];
/** Capsules to use in the simulation */
capsules?: Capsule[];
};

/**
Expand All @@ -42,7 +48,6 @@ export abstract class BaseContractInteraction {
protected wallet: Wallet,
protected authWitnesses: AuthWitness[] = [],
protected capsules: Capsule[] = [],
protected extraHashedValues: HashedValues[] = [],
) {}

/**
Expand All @@ -58,7 +63,7 @@ export abstract class BaseContractInteraction {
* @param options - An optional object containing additional configuration for the transaction.
* @returns An execution request wrapped in promise.
*/
public abstract request(options?: SendMethodOptions): Promise<ExecutionPayload>;
public abstract request(options?: RequestMethodOptions): Promise<ExecutionPayload>;

/**
* Creates a transaction execution request, simulates and proves it. Differs from .prove in
Expand Down Expand Up @@ -153,8 +158,8 @@ export abstract class BaseContractInteraction {
*/
protected async getFeeOptions(
executionPayload: ExecutionPayload,
fee?: UserFeeOptions,
options?: TxExecutionOptions,
fee: UserFeeOptions = {},
options: TxExecutionOptions,
): Promise<FeeOptions> {
// docs:end:getFeeOptions
const defaultFeeOptions = await this.getDefaultFeeOptions(fee);
Expand All @@ -165,7 +170,7 @@ export abstract class BaseContractInteraction {
let gasSettings = defaultFeeOptions.gasSettings;
if (fee?.estimateGas) {
const feeForEstimation: FeeOptions = { paymentMethod, gasSettings };
const txRequest = await this.wallet.createTxExecutionRequest(executionPayload, feeForEstimation, options ?? {});
const txRequest = await this.wallet.createTxExecutionRequest(executionPayload, feeForEstimation, options);
const simulationResult = await this.wallet.simulateTx(
txRequest,
true /*simulatePublic*/,
Expand All @@ -185,25 +190,4 @@ export abstract class BaseContractInteraction {

return { gasSettings, paymentMethod };
}

/**
* Return all authWitnesses added for this interaction.
*/
public getAuthWitnesses() {
return this.authWitnesses;
}

/**
* Return all capsules added for this contract interaction.
*/
public getCapsules() {
return this.capsules;
}

/**
* Return all extra hashed values added for this contract interaction.
*/
public getExtraHashedValues() {
return this.extraHashedValues;
}
}
42 changes: 16 additions & 26 deletions yarn-project/aztec.js/src/contract/batch_call.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { ExecutionPayload } from '@aztec/entrypoints/payload';
import { mergeExecutionPayloads } from '@aztec/entrypoints/utils';
import { mergeExecutionPayloads } from '@aztec/entrypoints/payload';
import { type FunctionCall, FunctionType, decodeFromAbi } from '@aztec/stdlib/abi';
import type { TxExecutionRequest } from '@aztec/stdlib/tx';

import type { Wallet } from '../wallet/wallet.js';
import { BaseContractInteraction, type SendMethodOptions } from './base_contract_interaction.js';
import {
BaseContractInteraction,
type RequestMethodOptions,
type SendMethodOptions,
} from './base_contract_interaction.js';
import type { SimulateMethodOptions } from './contract_function_interaction.js';

/** A batch of function calls to be sent as a single transaction through a wallet. */
Expand All @@ -30,11 +34,11 @@ export class BatchCall extends BaseContractInteraction {

/**
* Returns an execution request that represents this operation.
* @param _options - (ignored) An optional object containing additional configuration for the transaction.
* @returns An execution request wrapped in promise.
* @param options - An optional object containing additional configuration for the request generation.
* @returns An execution payload wrapped in promise.
*/
public async request(_options: SendMethodOptions = {}): Promise<ExecutionPayload> {
const requests = await this.getRequests();
public async request(options: RequestMethodOptions = {}): Promise<ExecutionPayload> {
const requests = await this.getRequests(options);
return mergeExecutionPayloads(requests);
}

Expand All @@ -48,7 +52,7 @@ export class BatchCall extends BaseContractInteraction {
* @returns The result of the transaction as returned by the contract function.
*/
public async simulate(options: SimulateMethodOptions = {}): Promise<any> {
const { indexedExecutionPayloads, unconstrained } = (await this.getRequests()).reduce<{
const { indexedExecutionPayloads, unconstrained } = (await this.getRequests(options)).reduce<{
/** Keep track of the number of private calls to retrieve the return values */
privateIndex: 0;
/** Keep track of the number of public calls to retrieve the return values */
Expand Down Expand Up @@ -76,9 +80,9 @@ export class BatchCall extends BaseContractInteraction {

const payloads = indexedExecutionPayloads.map(([request]) => request);
const requestWithoutFee = mergeExecutionPayloads(payloads);
const { fee: userFee } = options;
const fee = await this.getFeeOptions(requestWithoutFee, userFee);
const txRequest = await this.wallet.createTxExecutionRequest(requestWithoutFee, fee, {});
const { fee: userFee, nonce, cancellable } = options;
const fee = await this.getFeeOptions(requestWithoutFee, userFee, {});
const txRequest = await this.wallet.createTxExecutionRequest(requestWithoutFee, fee, { nonce, cancellable });

const unconstrainedCalls = unconstrained.map(
async ([call, index]) =>
Expand Down Expand Up @@ -113,21 +117,7 @@ export class BatchCall extends BaseContractInteraction {
return results;
}

/**
* Return all authWitnesses added for this interaction.
*/
public override getAuthWitnesses() {
return [this.authWitnesses, ...this.calls.map(c => c.getAuthWitnesses())].flat();
}

/**
* Return all capsules added for this interaction.
*/
public override getCapsules() {
return [this.capsules, ...this.calls.map(c => c.getCapsules())].flat();
}

private async getRequests() {
return await Promise.all(this.calls.map(c => c.request()));
private async getRequests(options: RequestMethodOptions = {}) {
return await Promise.all(this.calls.map(c => c.request(options)));
}
}
73 changes: 51 additions & 22 deletions yarn-project/aztec.js/src/contract/contract_function_interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import type { AuthWitness } from '@aztec/stdlib/auth-witness';
import { AztecAddress } from '@aztec/stdlib/aztec-address';
import type { Capsule, HashedValues, TxExecutionRequest, TxProfileResult } from '@aztec/stdlib/tx';

import { FeeJuicePaymentMethod } from '../fee/fee_juice_payment_method.js';
import type { Wallet } from '../wallet/wallet.js';
import { BaseContractInteraction, type SendMethodOptions } from './base_contract_interaction.js';
import {
BaseContractInteraction,
type RequestMethodOptions,
type SendMethodOptions,
} from './base_contract_interaction.js';

export type { SendMethodOptions };

Expand All @@ -15,33 +18,31 @@ export type { SendMethodOptions };
* Allows specifying the address from which the view method should be called.
* Disregarded for simulation of public functions
*/
export type ProfileMethodOptions = Pick<SendMethodOptions, 'fee'> & {
export type ProfileMethodOptions = Pick<
SendMethodOptions,
'authWitnesses' | 'capsules' | 'fee' | 'nonce' | 'cancellable'
> & {
/** Whether to return gates information or the bytecode/witnesses. */
profileMode: 'gates' | 'execution-steps' | 'full';
/** The sender's Aztec address. */
from?: AztecAddress;
/** Authwits to use in the simulation */
authWitnesses?: AuthWitness[];
/** Capsules to use in the simulation */
capsules?: Capsule[];
};

/**
* Represents the options for simulating a contract function interaction.
* Allows specifying the address from which the view method should be called.
* Allows specifying the address from which the method should be called.
* Disregarded for simulation of public functions
*/
export type SimulateMethodOptions = Pick<SendMethodOptions, 'fee'> & {
export type SimulateMethodOptions = Pick<
SendMethodOptions,
'authWitnesses' | 'capsules' | 'fee' | 'nonce' | 'cancellable'
> & {
/** The sender's Aztec address. */
from?: AztecAddress;
/** Simulate without checking for the validity of the resulting transaction, e.g. whether it emits any existing nullifiers. */
skipTxValidation?: boolean;
/** Whether to ensure the fee payer is not empty and has enough balance to pay for the fee. */
skipFeeEnforcement?: boolean;
/** Authwits to use in the simulation */
authWitnesses?: AuthWitness[];
/** Capsules to use in the simulation */
capsules?: Capsule[];
};

/**
Expand All @@ -56,9 +57,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
protected args: any[],
authWitnesses: AuthWitness[] = [],
capsules: Capsule[] = [],
extraHashedValues: HashedValues[] = [],
private extraHashedArgs: HashedValues[] = [],
) {
super(wallet, authWitnesses, capsules, extraHashedValues);
super(wallet, authWitnesses, capsules);
if (args.some(arg => arg === undefined || arg === null)) {
throw new Error('All function interaction arguments must be defined and not null. Received: ' + args);
}
Expand Down Expand Up @@ -88,10 +89,10 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
/**
* Returns an execution request that represents this operation.
* Can be used as a building block for constructing batch requests.
* @param options - An optional object containing additional configuration for the transaction.
* @returns An execution request wrapped in promise.
* @param options - An optional object containing additional configuration for the request generation.
* @returns An execution payload wrapped in promise.
*/
public async request(options: SendMethodOptions = {}): Promise<ExecutionPayload> {
public async request(options: RequestMethodOptions = {}): Promise<ExecutionPayload> {
// docs:end:request
const args = encodeArguments(this.functionDao, this.args);
const calls = [
Expand All @@ -110,7 +111,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
calls,
this.authWitnesses.concat(authWitnesses ?? []),
this.capsules.concat(capsules ?? []),
this.extraHashedValues,
this.extraHashedArgs,
);
}

Expand All @@ -136,9 +137,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
);
}

const fee = options.fee ?? { paymentMethod: new FeeJuicePaymentMethod(AztecAddress.ZERO) };
const { authWitnesses, capsules } = options;
const txRequest = await this.create({ fee, authWitnesses, capsules });
const txRequest = await this.create(options);
const simulatedTx = await this.wallet.simulateTx(
txRequest,
true /* simulatePublic */,
Expand Down Expand Up @@ -180,4 +179,34 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
const txRequest = await this.create({ fee, authWitnesses, capsules });
return await this.wallet.profileTx(txRequest, options.profileMode, options?.from);
}

/**
* Augments this ContractFunctionInteraction with additional metadata, such as authWitnesses, capsules, and extraHashedArgs.
* This is useful when creating a "batteries included" interaction, such as registering a contract class with its associated
* capsule instead of having the user provide them externally.
* @param options - An object containing the metadata to add to the interaction
* @returns A new ContractFunctionInteraction with the added metadata, but calling the same original function in the same manner
*/
public with({
authWitnesses = [],
capsules = [],
extraHashedArgs = [],
}: {
/** The authWitnesses to add to the interaction */
authWitnesses?: AuthWitness[];
/** The capsules to add to the interaction */
capsules?: Capsule[];
/** The extra hashed args to add to the interaction */
extraHashedArgs?: HashedValues[];
}): ContractFunctionInteraction {
return new ContractFunctionInteraction(
this.wallet,
this.contractAddress,
this.functionDao,
this.args,
this.authWitnesses.concat(authWitnesses),
this.capsules.concat(capsules),
this.extraHashedArgs.concat(extraHashedArgs),
);
}
}
Loading