From dc63a8a650e7a9137b536c15ef559483400293ac Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 11:20:55 +0000 Subject: [PATCH 001/119] merge with merge-train --- .../src/contract/base_contract_interaction.ts | 4 +- .../aztec.js/src/contract/batch_call.ts | 10 ++- .../aztec.js/src/contract/contract.test.ts | 6 +- .../contract/contract_function_interaction.ts | 28 +++---- .../aztec.js/src/contract/deploy_method.ts | 52 +++++++++---- .../src/contract/interaction_options.ts | 64 ++++++++++++--- yarn-project/aztec.js/src/utils/authwit.ts | 46 ++++++----- .../aztec.js/src/wallet/wallet.test.ts | 17 ++-- yarn-project/aztec.js/src/wallet/wallet.ts | 12 ++- yarn-project/aztec/src/examples/token.ts | 10 ++- .../aztec/src/local-network/banana_fpc.ts | 2 +- yarn-project/bot/src/amm_bot.ts | 35 +++++---- yarn-project/bot/src/bot.ts | 3 +- yarn-project/bot/src/cross_chain_bot.ts | 3 +- yarn-project/bot/src/factory.ts | 29 ++++--- yarn-project/bot/src/utils.ts | 6 +- .../cli-wallet/src/cmds/create_account.ts | 18 +++-- yarn-project/cli-wallet/src/cmds/deploy.ts | 4 +- .../cli-wallet/src/cmds/deploy_account.ts | 18 +++-- yarn-project/cli-wallet/src/cmds/send.ts | 4 +- yarn-project/cli-wallet/src/cmds/simulate.ts | 2 +- .../client_flows/account_deployments.test.ts | 4 +- .../src/bench/client_flows/amm.test.ts | 2 +- .../src/bench/client_flows/bridging.test.ts | 2 +- .../client_flows/client_flows_benchmark.ts | 62 ++++++++------- .../bench/client_flows/deployments.test.ts | 4 +- .../bench/client_flows/storage_proof.test.ts | 4 +- .../src/bench/client_flows/transfers.test.ts | 4 +- .../src/bench/node_rpc_perf.test.ts | 4 +- .../src/bench/tx_stats_bench.test.ts | 4 +- yarn-project/end-to-end/src/bench/utils.ts | 9 ++- .../src/composed/docs_examples.test.ts | 6 +- .../e2e_local_network_example.test.ts | 24 +++--- .../src/composed/e2e_persistence.test.ts | 73 +++++++++++------ .../e2e_token_bridge_tutorial_test.test.ts | 16 ++-- .../src/composed/ha/e2e_ha_full.test.ts | 6 +- ...e2e_multi_validator_node_key_store.test.ts | 5 +- .../end-to-end/src/devnet/e2e_smoke.test.ts | 8 +- .../end-to-end/src/e2e_2_pxes.test.ts | 4 +- .../src/e2e_account_contracts.test.ts | 2 +- yarn-project/end-to-end/src/e2e_amm.test.ts | 69 +++++++++------- .../end-to-end/src/e2e_authwit.test.ts | 4 +- .../end-to-end/src/e2e_avm_simulator.test.ts | 32 +++++--- .../blacklist_token_contract_test.ts | 32 ++++---- .../e2e_blacklist_token_contract/burn.test.ts | 70 +++++++++++++---- .../minting.test.ts | 10 ++- .../shielding.test.ts | 39 +++++++--- .../transfer_private.test.ts | 66 +++++++++++++--- .../transfer_public.test.ts | 78 +++++++++++++++---- .../unshielding.test.ts | 30 +++++-- .../end-to-end/src/e2e_block_building.test.ts | 45 ++++++----- .../end-to-end/src/e2e_card_game.test.ts | 34 ++++---- .../src/e2e_contract_updates.test.ts | 4 +- .../l1_to_l2.test.ts | 4 +- .../l2_to_l1.test.ts | 10 +-- .../src/e2e_crowdfunding_and_claim.test.ts | 30 +++---- .../contract_class_registration.test.ts | 12 +-- .../e2e_deploy_contract/deploy_method.test.ts | 19 +++-- .../src/e2e_deploy_contract/legacy.test.ts | 8 +- .../private_initialization.test.ts | 2 +- .../end-to-end/src/e2e_double_spend.test.ts | 2 +- .../epochs_invalidate_block.parallel.test.ts | 2 +- .../e2e_epochs/epochs_mbps.parallel.test.ts | 4 +- .../epochs_proof_public_cross_chain.test.ts | 6 +- .../src/e2e_escrow_contract.test.ts | 6 +- .../end-to-end/src/e2e_event_logs.test.ts | 23 +++--- .../end-to-end/src/e2e_event_only.test.ts | 6 +- .../src/e2e_expiration_timestamp.test.ts | 2 +- .../src/e2e_fees/account_init.test.ts | 10 +-- .../end-to-end/src/e2e_fees/failures.test.ts | 6 +- .../src/e2e_fees/fee_juice_payments.test.ts | 28 ++++--- .../src/e2e_fees/fee_settings.test.ts | 2 +- .../end-to-end/src/e2e_fees/fees_test.ts | 12 ++- .../src/e2e_fees/gas_estimation.test.ts | 14 ++-- .../src/e2e_fees/private_payments.test.ts | 20 ++--- .../src/e2e_fees/public_payments.test.ts | 14 ++-- .../src/e2e_fees/sponsored_payments.test.ts | 16 ++-- .../src/e2e_kernelless_simulation.test.ts | 22 +++--- yarn-project/end-to-end/src/e2e_keys.test.ts | 4 +- .../src/e2e_lending_contract.test.ts | 16 +++- .../end-to-end/src/e2e_mempool_limit.test.ts | 4 +- .../e2e_multi_validator_node.test.ts | 4 +- .../end-to-end/src/e2e_multiple_blobs.test.ts | 6 +- .../src/e2e_nested_contract/importer.test.ts | 4 +- .../manual_private_enqueue.test.ts | 4 +- .../e2e_nested_contract/manual_public.test.ts | 2 +- .../nested_contract_test.ts | 10 ++- yarn-project/end-to-end/src/e2e_nft.test.ts | 20 +++-- .../end-to-end/src/e2e_note_getter.test.ts | 21 +++-- .../src/e2e_offchain_effect.test.ts | 8 +- .../end-to-end/src/e2e_orderbook.test.ts | 34 +++++--- .../end-to-end/src/e2e_ordering.test.ts | 4 +- .../end-to-end/src/e2e_p2p/add_rollup.test.ts | 4 +- .../end-to-end/src/e2e_p2p/p2p_network.ts | 5 +- yarn-project/end-to-end/src/e2e_p2p/shared.ts | 2 +- .../e2e_pending_note_hashes_contract.test.ts | 2 +- .../end-to-end/src/e2e_phase_check.test.ts | 2 +- .../src/e2e_private_voting_contract.test.ts | 2 +- .../end-to-end/src/e2e_prover/client.test.ts | 6 +- .../end-to-end/src/e2e_prover/full.test.ts | 10 ++- .../end-to-end/src/e2e_pruned_blocks.test.ts | 6 +- .../e2e_public_testnet_transfer.test.ts | 10 ++- .../src/e2e_scope_isolation.test.ts | 2 +- .../gov_proposal.parallel.test.ts | 11 +-- .../end-to-end/src/e2e_simple.test.ts | 2 +- .../end-to-end/src/e2e_state_vars.test.ts | 48 ++++++------ .../end-to-end/src/e2e_static_calls.test.ts | 4 +- .../e2e_storage_proof.test.ts | 4 +- .../end-to-end/src/e2e_synching.test.ts | 40 ++++++---- .../src/e2e_token_contract/burn.test.ts | 52 +++++++++---- .../private_transfer_recursion.test.ts | 10 ++- .../reading_constants.test.ts | 14 +++- .../e2e_token_contract/token_contract_test.ts | 22 ++++-- .../src/e2e_token_contract/transfer.test.ts | 12 +-- .../transfer_in_private.test.ts | 26 +++++-- .../transfer_in_public.test.ts | 34 ++++---- .../transfer_to_private.test.ts | 8 +- .../transfer_to_public.test.ts | 24 ++++-- .../src/fixtures/e2e_prover_test.ts | 16 ++-- yarn-project/end-to-end/src/fixtures/setup.ts | 6 +- .../end-to-end/src/fixtures/token_utils.ts | 6 +- .../writing_an_account_contract.test.ts | 4 +- .../src/shared/cross_chain_test_harness.ts | 22 +++--- .../src/shared/submit-transactions.ts | 2 +- .../end-to-end/src/shared/uniswap_l1_l2.ts | 10 ++- .../src/simulators/lending_simulator.ts | 6 +- .../end-to-end/src/spartan/1tps.test.ts | 21 +++-- .../end-to-end/src/spartan/4epochs.test.ts | 35 +++++---- .../end-to-end/src/spartan/n_tps.test.ts | 4 +- .../src/spartan/n_tps_prove.test.ts | 4 +- .../src/spartan/reqresp_effectiveness.test.ts | 3 +- .../src/spartan/setup_test_wallets.ts | 10 +-- .../end-to-end/src/spartan/transfer.test.ts | 29 ++++--- yarn-project/stdlib/src/tx/simulated_tx.ts | 9 ++- .../wallet-sdk/src/base-wallet/base_wallet.ts | 6 +- 135 files changed, 1358 insertions(+), 747 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index 8e47f1eb7cfe..bfa319bc77df 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -41,8 +41,8 @@ export abstract class BaseContractInteraction { * the AztecAddress of the sender, optional fee configuration, and optional wait settings * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ - // Overload for when wait is not specified at all - returns TReturn - public send(options: SendInteractionOptionsWithoutWait): Promise; + // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } + public send(options: SendInteractionOptionsWithoutWait): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index cd112be6f59b..1ec44f3f3d25 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -108,7 +108,10 @@ export class BatchCall extends BaseContractInteraction { const wrappedResult = batchResults[i]; if (wrappedResult.name === 'executeUtility') { const rawReturnValues = (wrappedResult.result as UtilityExecutionResult).result; - results[resultIndex] = rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : []; + results[resultIndex] = { + result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], + offchainEffects: [], + }; } } @@ -127,7 +130,10 @@ export class BatchCall extends BaseContractInteraction { ? simulatedTx.getPrivateReturnValues()?.nested?.[resultIndex].values : simulatedTx.getPublicReturnValues()?.[resultIndex].values; - results[callIndex] = rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : []; + results[callIndex] = { + result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], + offchainEffects: simulatedTx.offchainEffects, + }; }); } } diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 28005f6ecbfc..7f815be7c056 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -136,7 +136,7 @@ describe('Contract Class', () => { wallet.simulateTx.mockResolvedValue(mockTxSimulationResult); account.createTxExecutionRequest.mockResolvedValue(mockTxRequest); wallet.registerContract.mockResolvedValue(contractInstance); - wallet.sendTx.mockResolvedValue(mockTxReceipt); + wallet.sendTx.mockResolvedValue({ receipt: mockTxReceipt, offchainEffects: [] }); wallet.executeUtility.mockResolvedValue(mockUtilityResultValue); }); @@ -144,7 +144,7 @@ describe('Contract Class', () => { const fooContract = Contract.at(contractAddress, defaultArtifact, wallet); const param0 = 12; const param1 = 345n; - const receipt = await fooContract.methods.bar(param0, param1).send({ from: account.getAddress() }); + const { receipt } = await fooContract.methods.bar(param0, param1).send({ from: account.getAddress() }); expect(receipt).toBe(mockTxReceipt); expect(wallet.sendTx).toHaveBeenCalledTimes(1); @@ -152,7 +152,7 @@ describe('Contract Class', () => { it('should call view on a utility function', async () => { const fooContract = Contract.at(contractAddress, defaultArtifact, wallet); - const result = await fooContract.methods.qux(123n).simulate({ from: account.getAddress() }); + const { result } = await fooContract.methods.qux(123n).simulate({ from: account.getAddress() }); expect(wallet.executeUtility).toHaveBeenCalledTimes(1); expect(wallet.executeUtility).toHaveBeenCalledWith( expect.objectContaining({ name: 'qux', to: contractAddress }), diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index a087d85f0525..909d599891e3 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -8,7 +8,7 @@ import { } from '@aztec/stdlib/abi'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { type Capsule, type HashedValues, type TxProfileResult, collectOffchainEffects } from '@aztec/stdlib/tx'; +import type { Capsule, HashedValues, TxProfileResult } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; @@ -18,6 +18,8 @@ import { type ProfileInteractionOptions, type RequestInteractionOptions, type SimulateInteractionOptions, + type SimulateWithGasEstimationOptions, + type SimulateWithMetadataOptions, type SimulationReturn, toProfileOptions, toSimulateOptions, @@ -97,17 +99,15 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * function or a rich object containing extra metadata, such as estimated gas costs (if requested via options), * execution statistics and emitted offchain effects */ - public async simulate( - options: T, - ): Promise['estimateGas']>>; + public async simulate(options: SimulateWithMetadataOptions): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate( - options: T, - ): Promise>; + public async simulate(options: SimulateWithGasEstimationOptions): Promise>; + // eslint-disable-next-line jsdoc/require-jsdoc + public async simulate(options?: SimulateInteractionOptions): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc public async simulate( - options: SimulateInteractionOptions, - ): Promise> { + options: SimulateInteractionOptions = {} as SimulateInteractionOptions, + ): Promise> { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const call = await this.getFunctionCall(); @@ -122,11 +122,11 @@ export class ContractFunctionInteraction extends BaseContractInteraction { if (options.includeMetadata) { return { stats: utilityResult.stats, + offchainEffects: [], result: returnValue, }; - } else { - return returnValue; } + return { result: returnValue, offchainEffects: [] }; } const executionPayload = await this.request(options); @@ -148,6 +148,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } const returnValue = rawReturnValues ? decodeFromAbi(this.functionDao.returnTypes, rawReturnValues) : []; + const offchainEffects = simulatedTx.offchainEffects; if (options.includeMetadata || options.fee?.estimateGas) { const { gasLimits, teardownGasLimits } = getGasLimits(simulatedTx, options.fee?.estimatedGasPadding); @@ -156,13 +157,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction { ); return { stats: simulatedTx.stats, - offchainEffects: collectOffchainEffects(simulatedTx.privateExecutionResult), + offchainEffects, result: returnValue, estimatedGas: { gasLimits, teardownGasLimits }, }; - } else { - return returnValue; } + return { result: returnValue, offchainEffects }; } /** diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 5c4d9544aa2e..f693ded613ed 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -9,7 +9,7 @@ import { getContractInstanceFromInstantiationParams, } from '@aztec/stdlib/contract'; import type { PublicKeys } from '@aztec/stdlib/keys'; -import { type Capsule, TxHash, type TxProfileResult, type TxReceipt, collectOffchainEffects } from '@aztec/stdlib/tx'; +import type { Capsule, OffchainEffect, TxProfileResult, TxReceipt } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import { publishContractClass } from '../deployment/publish_class.js'; @@ -27,6 +27,7 @@ import { type SendInteractionOptionsWithoutWait, type SimulationInteractionFeeOptions, type SimulationReturn, + type TxSendResultImmediate, toProfileOptions, toSendOptions, toSimulateOptions, @@ -123,6 +124,12 @@ export type DeployTxReceipt = TxR instance: ContractInstanceWithAddress; }; +/** Wait options that request a full receipt instead of just the contract instance. */ +type WaitWithReturnReceipt = { + /** Request the full receipt instead of just the contract instance. */ + returnReceipt: true; +}; + /** * Represents the result type of deploying a contract. * - If wait is NO_WAIT, returns TxHash immediately. @@ -130,13 +137,20 @@ export type DeployTxReceipt = TxR * - Otherwise (undefined or DeployWaitOptions without returnReceipt), returns TContract after waiting. */ export type DeployReturn = W extends NoWait - ? TxHash - : W extends { - // eslint-disable-next-line jsdoc/require-jsdoc - returnReceipt: true; + ? TxSendResultImmediate + : W extends WaitWithReturnReceipt + ? { + /** The deploy transaction receipt. */ + receipt: DeployTxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; } - ? DeployTxReceipt - : TContract; + : { + /** The deployed contract instance. */ + contract: TContract; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; + }; /** * Contract interaction for deployment. @@ -308,24 +322,30 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send(options: DeployOptionsWithoutWait): Promise; - // Generic overload for explicit wait values + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class + public override send(options: DeployOptionsWithoutWait): Promise>; + + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, ): Promise>; + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override async send(options: DeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); const sendOptions = this.convertDeployOptionsToSendOptions(options); if (options.wait === NO_WAIT) { - const txHash = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); - this.log.debug(`Sent deployment tx ${txHash.hash} of ${this.artifact.name} contract`); - return txHash; + const result = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); + this.log.debug(`Sent deployment tx ${result.txHash.hash} of ${this.artifact.name} contract`); + return result; } - const receipt = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); + const { receipt, offchainEffects } = await this.wallet.sendTx( + executionPayload, + sendOptions as SendOptions, + ); this.log.debug(`Deployed ${this.artifact.name} contract in tx ${receipt.txHash}`); // Attach contract instance @@ -334,10 +354,10 @@ export class DeployMethod extends // Return full receipt if requested, otherwise just the contract if (options.wait && typeof options.wait === 'object' && options.wait.returnReceipt) { - return { ...receipt, contract, instance }; + return { receipt: { ...receipt, contract, instance }, offchainEffects }; } - return contract; + return { contract, offchainEffects }; } /** @@ -376,7 +396,7 @@ export class DeployMethod extends ); return { stats: simulatedTx.stats!, - offchainEffects: collectOffchainEffects(simulatedTx.privateExecutionResult), + offchainEffects: simulatedTx.offchainEffects, result: undefined, estimatedGas: { gasLimits, teardownGasLimits }, }; diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 2651c9f5f49d..3f870dad4bd4 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -109,11 +109,26 @@ export type SimulateInteractionOptions = Omit & { skipTxValidation?: boolean; /** Whether to ensure the fee payer is not empty and has enough balance to pay for the fee. */ skipFeeEnforcement?: boolean; - /** Whether to include metadata such as offchain effects and performance statistics (e.g. timing information of the different circuits and oracles) in - * the simulation result, instead of just the return value of the function */ + /** Whether to include metadata such as performance statistics (e.g. timing information of the different circuits and oracles) and gas estimation + * in the simulation result, in addition to the return value and offchain effects */ includeMetadata?: boolean; }; +/** Simulation options that request full metadata (stats, gas estimation) in the result. */ +export type SimulateWithMetadataOptions = SimulateInteractionOptions & { + /** Request metadata in the simulation result. */ + includeMetadata: true; +}; + +/** Simulation options that request gas estimation in the result. */ +export type SimulateWithGasEstimationOptions = SimulateInteractionOptions & { + /** Fee options with gas estimation enabled. */ + fee: { + /** Request gas estimation in the simulation result. */ + estimateGas: true; + }; +}; + /** * Represents the options for profiling an interaction. */ @@ -126,9 +141,8 @@ export type ProfileInteractionOptions = SimulateInteractionOptions & { /** * Represents the result type of a simulation. - * By default, it will just be the return value of the simulated function - * If `includeMetadata` is set to true in `SimulateInteractionOptions` on the input of `simulate(...)`, - * it will provide extra information. + * Always includes the return value and offchain effects. + * When `includeMetadata` is set to true, also includes stats and gas estimation. */ export type SimulationReturn = T extends true ? { @@ -136,19 +150,47 @@ export type SimulationReturn = T extends true stats: SimulationStats; /** Offchain effects generated during the simulation */ offchainEffects: OffchainEffect[]; - /** Return value of the function */ + /** Return value of the function */ result: any; /** Gas estimation results */ estimatedGas: Pick; } - : any; + : { + /** Return value of the function */ + result: any; + /** Offchain effects generated during the simulation */ + offchainEffects: OffchainEffect[]; + }; + +/** Result of sendTx when not waiting for mining. */ +export type TxSendResultImmediate = { + /** The hash of the sent transaction. */ + txHash: TxHash; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; + +/** Result of sendTx when waiting for mining. */ +export type TxSendResultMined = { + /** The transaction receipt. */ + receipt: TxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; /** * Represents the result type of sending a transaction. - * If `wait` is NO_WAIT, returns TxHash immediately without waiting. - * If `wait` is undefined or WaitOpts, returns TReturn (defaults to TxReceipt) after waiting. - */ -export type SendReturn = T extends NoWait ? TxHash : TReturn; + * If `wait` is NO_WAIT, returns TxSendResultImmediate. + * Otherwise returns TxSendResultMined. + */ +export type SendReturn = T extends NoWait + ? TxSendResultImmediate + : { + /** The transaction receipt. */ + receipt: TReturn; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; + }; /** * Transforms and cleans up the higher level SendInteractionOptions defined by the interaction into diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index d0d8b9f1c088..c1ff57e9f454 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -5,7 +5,7 @@ import { type ABIParameterVisibility, type FunctionAbi, type FunctionCall, Funct import { AuthWitness, computeInnerAuthWitHash, computeOuterAuthWitHash } from '@aztec/stdlib/auth-witness'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { computeVarArgsHash } from '@aztec/stdlib/hash'; -import type { TxHash, TxProfileResult, TxReceipt } from '@aztec/stdlib/tx'; +import type { TxProfileResult } from '@aztec/stdlib/tx'; import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; import type { @@ -15,6 +15,7 @@ import type { SendInteractionOptionsWithoutWait, SendReturn, SimulateInteractionOptions, + SimulateWithMetadataOptions, SimulationReturn, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -189,10 +190,12 @@ export async function lookupValidity( errorTypes: {}, } as FunctionAbi; try { - results.isValidInPrivate = (await new ContractFunctionInteraction(wallet, onBehalfOf, lookupValidityAbi, [ - consumer, - innerHash, - ]).simulate({ from: onBehalfOf, authWitnesses: [witness] })) as boolean; + results.isValidInPrivate = ( + await new ContractFunctionInteraction(wallet, onBehalfOf, lookupValidityAbi, [consumer, innerHash]).simulate({ + from: onBehalfOf, + authWitnesses: [witness], + }) + ).result as boolean; // TODO: Narrow down the error to make sure simulation failed due to an invalid authwit // eslint-disable-next-line no-empty } catch {} @@ -219,12 +222,12 @@ export async function lookupValidity( returnTypes: [{ kind: 'boolean' }], errorTypes: {}, } as FunctionAbi; - results.isValidInPublic = (await new ContractFunctionInteraction( - wallet, - ProtocolContractAddress.AuthRegistry, - isConsumableAbi, - [onBehalfOf, messageHash], - ).simulate({ from: onBehalfOf })) as boolean; + results.isValidInPublic = ( + await new ContractFunctionInteraction(wallet, ProtocolContractAddress.AuthRegistry, isConsumableAbi, [ + onBehalfOf, + messageHash, + ]).simulate({ from: onBehalfOf }) + ).result as boolean; return results; } @@ -262,14 +265,17 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing additional configuration for the transaction. * @returns The result of the transaction as returned by the contract function. */ - public override simulate( - options: Omit, - ): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) + public override simulate(options: Omit): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) + // eslint-disable-next-line jsdoc/require-jsdoc + public override simulate(options?: Omit): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) // eslint-disable-next-line jsdoc/require-jsdoc public override simulate( - options: Omit = {}, - ): Promise> { - return super.simulate({ ...options, from: this.from }); + options: Omit = {} as Omit, + ): Promise> { + return super.simulate({ ...options, from: this.from } as SimulateInteractionOptions); } /** @@ -290,8 +296,8 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing 'fee' options information * @returns A TxReceipt (if wait is true/undefined) or TxHash (if wait is false) */ - // Overload for when wait is not specified at all - returns TxReceipt - public override send(options?: Omit): Promise; + // Overload for when wait is not specified at all + public override send(options?: Omit): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public override send( @@ -300,7 +306,7 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac // eslint-disable-next-line jsdoc/require-jsdoc public override send( options?: Omit, 'from'>, - ): Promise { + ): Promise> { return super.send({ ...options, from: this.from }); } diff --git a/yarn-project/aztec.js/src/wallet/wallet.test.ts b/yarn-project/aztec.js/src/wallet/wallet.test.ts index bcee66440e18..afc503bbc51d 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.test.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.test.ts @@ -11,6 +11,7 @@ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import { PublicKeys } from '@aztec/stdlib/keys'; import { ExecutionPayload, + type OffchainEffect, TxHash, TxProfileResult, TxReceipt, @@ -209,12 +210,14 @@ describe('WalletSchema', () => { const resultWithWait = await context.client.sendTx(exec, { from: await AztecAddress.random(), }); - expect(resultWithWait).toBeInstanceOf(TxReceipt); + expect(resultWithWait.receipt).toBeInstanceOf(TxReceipt); + expect(resultWithWait.offchainEffects).toEqual([]); const resultWithoutWait = await context.client.sendTx(exec, { from: await AztecAddress.random(), wait: NO_WAIT, }); - expect(resultWithoutWait).toBeInstanceOf(TxHash); + expect(resultWithoutWait.txHash).toBeInstanceOf(TxHash); + expect(resultWithoutWait.offchainEffects).toEqual([]); }); it('createAuthWit', async () => { @@ -353,7 +356,7 @@ describe('WalletSchema', () => { expect(results[8]).toEqual({ name: 'simulateTx', result: expect.any(TxSimulationResult) }); expect(results[9]).toEqual({ name: 'executeUtility', result: expect.any(UtilityExecutionResult) }); expect(results[10]).toEqual({ name: 'profileTx', result: expect.any(TxProfileResult) }); - expect(results[11]).toEqual({ name: 'sendTx', result: expect.any(TxReceipt) }); + expect(results[11]).toEqual({ name: 'sendTx', result: { receipt: expect.any(TxReceipt), offchainEffects: [] } }); expect(results[12]).toEqual({ name: 'createAuthWit', result: expect.any(AuthWitness) }); }); }); @@ -446,9 +449,13 @@ class MockWallet implements Wallet { opts: SendOptions, ): Promise> { if (opts.wait === NO_WAIT) { - return Promise.resolve(TxHash.random()) as Promise>; + return Promise.resolve({ txHash: TxHash.random(), offchainEffects: [] as OffchainEffect[] }) as Promise< + SendReturn + >; } - return Promise.resolve(TxReceipt.empty()) as Promise>; + return Promise.resolve({ receipt: TxReceipt.empty(), offchainEffects: [] as OffchainEffect[] }) as Promise< + SendReturn + >; } createAuthWit(_from: AztecAddress, _messageHashOrIntent: any): Promise { diff --git a/yarn-project/aztec.js/src/wallet/wallet.ts b/yarn-project/aztec.js/src/wallet/wallet.ts index 9918542d297e..737184dd4975 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.ts @@ -489,6 +489,11 @@ export const WalletCapabilitiesSchema = z.object({ expiresAt: optional(z.number()), }); +const OffchainEffectSchema = z.object({ + data: z.array(schemas.Fr), + contractAddress: schemas.AztecAddress, +}); + /** * Record of all wallet method schemas (excluding batch). * This is the single source of truth for method schemas - batch schemas are derived from this. @@ -532,7 +537,12 @@ const WalletMethodSchemas = { sendTx: z .function() .args(ExecutionPayloadSchema, SendOptionsSchema) - .returns(z.union([TxHash.schema, TxReceipt.schema])), + .returns( + z.union([ + z.object({ txHash: TxHash.schema, offchainEffects: z.array(OffchainEffectSchema) }), + z.object({ receipt: TxReceipt.schema, offchainEffects: z.array(OffchainEffectSchema) }), + ]), + ), createAuthWit: z.function().args(schemas.AztecAddress, MessageHashOrIntentSchema).returns(AuthWitness.schema), requestCapabilities: z.function().args(AppCapabilitiesSchema).returns(WalletCapabilitiesSchema), }; diff --git a/yarn-project/aztec/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts index 2a99d350c314..1e7e2077910d 100644 --- a/yarn-project/aztec/src/examples/token.ts +++ b/yarn-project/aztec/src/examples/token.ts @@ -32,7 +32,9 @@ async function main() { logger.info(`Fetched Alice and Bob accounts: ${alice.toString()}, ${bob.toString()}`); logger.info('Deploying Token...'); - const token = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({ from: alice }); + const { contract: token } = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({ + from: alice, + }); logger.info('Token deployed'); // Mint tokens to Alice @@ -41,7 +43,7 @@ async function main() { logger.info(`${ALICE_MINT_BALANCE} tokens were successfully minted by Alice and transferred to private`); - const balanceAfterMint = await token.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: balanceAfterMint } = await token.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Tokens successfully minted. New Alice's balance: ${balanceAfterMint}`); // We will now transfer tokens from Alice to Bob @@ -49,10 +51,10 @@ async function main() { await token.methods.transfer(bob, TRANSFER_AMOUNT).send({ from: alice }); // Check the new balances - const aliceBalance = await token.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceBalance } = await token.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance ${aliceBalance}`); - const bobBalance = await token.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobBalance } = await token.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance ${bobBalance}`); } diff --git a/yarn-project/aztec/src/local-network/banana_fpc.ts b/yarn-project/aztec/src/local-network/banana_fpc.ts index 5ccb73d8746f..e5363e0eda73 100644 --- a/yarn-project/aztec/src/local-network/banana_fpc.ts +++ b/yarn-project/aztec/src/local-network/banana_fpc.ts @@ -48,7 +48,7 @@ export async function getBananaFPCAddress(initialAccounts: InitialAccountData[]) export async function setupBananaFPC(initialAccounts: InitialAccountData[], wallet: Wallet, log: LogFn) { const bananaCoinAddress = await getBananaCoinAddress(initialAccounts); const admin = getBananaAdmin(initialAccounts); - const [bananaCoin, fpc] = await Promise.all([ + const [{ contract: bananaCoin }, { contract: fpc }] = await Promise.all([ TokenContract.deploy(wallet, admin, bananaCoinArgs.name, bananaCoinArgs.symbol, bananaCoinArgs.decimal).send({ from: admin, contractAddressSalt: BANANA_COIN_SALT, diff --git a/yarn-project/bot/src/amm_bot.ts b/yarn-project/bot/src/amm_bot.ts index 362470479518..21461c775d90 100644 --- a/yarn-project/bot/src/amm_bot.ts +++ b/yarn-project/bot/src/amm_bot.ts @@ -71,12 +71,14 @@ export class AmmBot extends BaseBot { .getFunctionCall(), }); - const amountOutMin = await amm.methods - .get_amount_out_for_exact_in( - await tokenIn.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }), - await tokenOut.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }), - amountIn, - ) + const { result: tokenInBalance } = await tokenIn.methods + .balance_of_public(amm.address) + .simulate({ from: this.defaultAccountAddress }); + const { result: tokenOutBalance } = await tokenOut.methods + .balance_of_public(amm.address) + .simulate({ from: this.defaultAccountAddress }); + const { result: amountOutMin } = await amm.methods + .get_amount_out_for_exact_in(tokenInBalance, tokenOutBalance, amountIn) .simulate({ from: this.defaultAccountAddress }); const swapExactTokensInteraction = amm.methods @@ -89,7 +91,8 @@ export class AmmBot extends BaseBot { this.log.verbose(`Sending transaction`, logCtx); this.log.info(`Tx. Balances: ${jsonStringify(balances)}`, { ...logCtx, balances }); - return swapExactTokensInteraction.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await swapExactTokensInteraction.send({ ...opts, wait: NO_WAIT }); + return txHash; } protected override async onTxMined(receipt: TxReceipt, logCtx: object): Promise { @@ -110,15 +113,17 @@ export class AmmBot extends BaseBot { } private async getPublicBalanceFor(address: AztecAddress, from?: AztecAddress): Promise { - return { - token0: await this.token0.methods.balance_of_public(address).simulate({ from: from ?? address }), - token1: await this.token1.methods.balance_of_public(address).simulate({ from: from ?? address }), - }; + const { result: token0 } = await this.token0.methods.balance_of_public(address).simulate({ from: from ?? address }); + const { result: token1 } = await this.token1.methods.balance_of_public(address).simulate({ from: from ?? address }); + return { token0, token1 }; } private async getPrivateBalanceFor(address: AztecAddress, from?: AztecAddress): Promise { - return { - token0: await this.token0.methods.balance_of_private(address).simulate({ from: from ?? address }), - token1: await this.token1.methods.balance_of_private(address).simulate({ from: from ?? address }), - }; + const { result: token0 } = await this.token0.methods + .balance_of_private(address) + .simulate({ from: from ?? address }); + const { result: token1 } = await this.token1.methods + .balance_of_private(address) + .simulate({ from: from ?? address }); + return { token0, token1 }; } } diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index dce40d2194ad..c2128b2a219e 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -76,7 +76,8 @@ export class Bot extends BaseBot { await batch.simulate({ from: this.defaultAccountAddress }); this.log.verbose(`Sending transaction`, logCtx); - return batch.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await batch.send({ ...opts, wait: NO_WAIT }); + return txHash; } public async getBalances() { diff --git a/yarn-project/bot/src/cross_chain_bot.ts b/yarn-project/bot/src/cross_chain_bot.ts index 0165b5a778a8..ea4a6c365c29 100644 --- a/yarn-project/bot/src/cross_chain_bot.ts +++ b/yarn-project/bot/src/cross_chain_bot.ts @@ -140,7 +140,8 @@ export class CrossChainBot extends BaseBot { const opts = await this.getSendMethodOpts(batch); this.log.verbose(`Sending cross-chain batch with ${calls.length} calls`, logCtx); - return batch.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await batch.send({ ...opts, wait: NO_WAIT }); + return txHash; } protected override async onTxMined(receipt: TxReceipt, logCtx: object): Promise { diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 77b474c559b0..56f671840ad0 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -227,7 +227,7 @@ export class BotFactory { const gasSettings = GasSettings.default({ maxFeesPerGas }); await this.withNoMinTxsPerBlock(async () => { - const txHash = await deployMethod.send({ + const { txHash } = await deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod }, wait: NO_WAIT, @@ -296,7 +296,7 @@ export class BotFactory { await deploy.register(); } else { this.log.info(`Deploying token contract at ${address.toString()}`); - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`); await this.withNoMinTxsPerBlock(async () => { await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); @@ -337,7 +337,7 @@ export class BotFactory { const amm = AMMContract.at(instance.address, this.wallet); this.log.info(`AMM deployed at ${amm.address}`); - const minterReceipt = await lpToken.methods + const { receipt: minterReceipt } = await lpToken.methods .set_minter(amm.address, true) .send({ from: deployer, wait: { timeout: this.config.txMinedWaitSeconds } }); this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`); @@ -356,9 +356,18 @@ export class BotFactory { ): Promise { const getPrivateBalances = () => Promise.all([ - token0.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), - token1.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), - lpToken.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), + token0.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), + token1.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), + lpToken.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), ]); const authwitNonce = Fr.random(); @@ -399,14 +408,14 @@ export class BotFactory { .getFunctionCall(), }); - const mintReceipt = await new BatchCall(this.wallet, [ + const { receipt: mintReceipt } = await new BatchCall(this.wallet, [ token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE), token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE), ]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } }); this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`); - const addLiquidityReceipt = await amm.methods + const { receipt: addLiquidityReceipt } = await amm.methods .add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce) .send({ from: liquidityProvider, @@ -437,7 +446,7 @@ export class BotFactory { } else { this.log.info(`Deploying contract ${name} at ${address.toString()}`); await this.withNoMinTxsPerBlock(async () => { - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`); return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); }); @@ -480,7 +489,7 @@ export class BotFactory { } await this.withNoMinTxsPerBlock(async () => { - const txHash = await new BatchCall(token.wallet, calls).send({ from: minter, wait: NO_WAIT }); + const { txHash } = await new BatchCall(token.wallet, calls).send({ from: minter, wait: NO_WAIT }); this.log.info(`Sent token mint tx with hash ${txHash.toString()}`); return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); }); diff --git a/yarn-project/bot/src/utils.ts b/yarn-project/bot/src/utils.ts index 1bd0b7f6b743..807d34be95ab 100644 --- a/yarn-project/bot/src/utils.ts +++ b/yarn-project/bot/src/utils.ts @@ -15,8 +15,8 @@ export async function getBalances( who: AztecAddress, from?: AztecAddress, ): Promise<{ privateBalance: bigint; publicBalance: bigint }> { - const privateBalance = await token.methods.balance_of_private(who).simulate({ from: from ?? who }); - const publicBalance = await token.methods.balance_of_public(who).simulate({ from: from ?? who }); + const { result: privateBalance } = await token.methods.balance_of_private(who).simulate({ from: from ?? who }); + const { result: publicBalance } = await token.methods.balance_of_public(who).simulate({ from: from ?? who }); return { privateBalance, publicBalance }; } @@ -25,7 +25,7 @@ export async function getPrivateBalance( who: AztecAddress, from?: AztecAddress, ): Promise { - const privateBalance = await token.methods.get_balance(who).simulate({ from: from ?? who }); + const { result: privateBalance } = await token.methods.get_balance(who).simulate({ from: from ?? who }); return privateBalance; } diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index 64b730b95aa1..1279cf8b0f54 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -109,7 +109,7 @@ export async function createAccount( if (!json) { log(`\nWaiting for account contract deployment...`); } - const result = await deployMethod.send({ + const sendOpts = { ...deployAccountOpts, fee: deployAccountOpts.fee ? { @@ -117,18 +117,20 @@ export async function createAccount( gasSettings: estimatedGas, } : undefined, - wait: wait ? { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true } : NO_WAIT, - }); - const isReceipt = (data: TxReceipt | TxHash): data is TxReceipt => 'txHash' in data; - if (isReceipt(result)) { - txReceipt = result; - txHash = result.txHash; + }; + if (wait) { + const { receipt } = await deployMethod.send({ + ...sendOpts, + wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true }, + }); + txReceipt = receipt; + txHash = receipt.txHash; out.txReceipt = { status: txReceipt.status, transactionFee: txReceipt.transactionFee, }; } else { - txHash = result; + ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT })); } debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`); out.txHash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/deploy.ts b/yarn-project/cli-wallet/src/cmds/deploy.ts index 0de9615aa276..d4ddc6a3c968 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy.ts @@ -98,7 +98,7 @@ export async function deploy( const instance = await deploy.getInstance(); if (wait) { - const receipt = await deploy.send({ ...deployOpts, wait: { timeout, returnReceipt: true } }); + const { receipt } = await deploy.send({ ...deployOpts, wait: { timeout, returnReceipt: true } }); const txHash = receipt.txHash; debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`); out.hash = txHash; @@ -121,7 +121,7 @@ export async function deploy( }; } } else { - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`); out.hash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/deploy_account.ts b/yarn-project/cli-wallet/src/cmds/deploy_account.ts index f18037e7cbb6..7c3c6e020023 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy_account.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy_account.ts @@ -89,7 +89,7 @@ export async function deployAccount( if (!json) { log(`\nWaiting for account contract deployment...`); } - const result = await deployMethod.send({ + const sendOpts = { ...deployAccountOpts, fee: deployAccountOpts.fee ? { @@ -97,18 +97,20 @@ export async function deployAccount( gasSettings: estimatedGas, } : undefined, - wait: wait ? { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true } : NO_WAIT, - }); - const isReceipt = (data: TxReceipt | TxHash): data is TxReceipt => 'txHash' in data; - if (isReceipt(result)) { - txReceipt = result; - txHash = result.txHash; + }; + if (wait) { + const { receipt } = await deployMethod.send({ + ...sendOpts, + wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true }, + }); + txReceipt = receipt; + txHash = receipt.txHash; out.txReceipt = { status: txReceipt.status, transactionFee: txReceipt.transactionFee, }; } else { - txHash = result; + ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT })); } debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`); out.txHash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/send.ts b/yarn-project/cli-wallet/src/cmds/send.ts index 4059db13a93b..30129ef78e0d 100644 --- a/yarn-project/cli-wallet/src/cmds/send.ts +++ b/yarn-project/cli-wallet/src/cmds/send.ts @@ -52,7 +52,7 @@ export async function send( if (wait) { try { - const receipt = await call.send({ + const { receipt } = await call.send({ ...sendOptions, fee: { ...sendOptions.fee, gasSettings: estimatedGas }, wait: { timeout: DEFAULT_TX_TIMEOUT_S }, @@ -74,7 +74,7 @@ export async function send( throw err; } } else { - const txHash = await call.send({ + const { txHash } = await call.send({ ...sendOptions, fee: { ...sendOptions.fee, gasSettings: estimatedGas }, wait: NO_WAIT, diff --git a/yarn-project/cli-wallet/src/cmds/simulate.ts b/yarn-project/cli-wallet/src/cmds/simulate.ts index 2443428df708..aec161b4ab33 100644 --- a/yarn-project/cli-wallet/src/cmds/simulate.ts +++ b/yarn-project/cli-wallet/src/cmds/simulate.ts @@ -38,7 +38,7 @@ export async function simulate( }); if (verbose) { await printAuthorizations( - simulationResult.offchainEffects!, + simulationResult.offchainEffects, async (address: AztecAddress) => { const metadata = await wallet.getContractMetadata(address); if (!metadata.instance) { diff --git a/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts b/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts index db57a7b52d9b..e38eb09f7c9f 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts @@ -91,8 +91,8 @@ describe('Deployment benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); - expect(tx.transactionFee!).toBeGreaterThan(0n); + const { receipt } = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); + expect(receipt.transactionFee!).toBeGreaterThan(0n); } }); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts index 22ca27be3eee..44a5652b484f 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts @@ -199,7 +199,7 @@ describe('AMM benchmark', () => { ); if (process.env.SANITY_CHECKS) { - const tx = await addLiquidityInteraction.send({ from: benchysAddress }); + const { receipt: tx } = await addLiquidityInteraction.send({ from: benchysAddress }); expect(tx.transactionFee!).toBeGreaterThan(0n); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts b/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts index 0800c709ea82..27aa73ef8e39 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts @@ -106,7 +106,7 @@ describe('Bridging benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await claimInteraction.send(options); + const { receipt: tx } = await claimInteraction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); // 4. Check the balance diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index 6281e6f7f4cd..a9b9e57e606b 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -161,11 +161,15 @@ export class ClientFlowsBenchmark { /** Admin mints bananaCoin tokens privately to the target address and redeems them. */ async mintPrivateBananas(amount: bigint, address: AztecAddress) { - const balanceBefore = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceBefore } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); await mintTokensToPrivate(this.bananaCoin, this.adminAddress, address, amount); - const balanceAfter = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceAfter } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); expect(balanceAfter).toEqual(balanceBefore + amount); } @@ -241,13 +245,12 @@ export class ClientFlowsBenchmark { async applyDeployBananaToken() { this.logger.info('Applying banana token deployment'); - const { contract: bananaCoin, instance: bananaCoinInstance } = await BananaCoin.deploy( - this.adminWallet, - this.adminAddress, - 'BC', - 'BC', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: bananaCoin, instance: bananaCoinInstance }, + } = await BananaCoin.deploy(this.adminWallet, this.adminAddress, 'BC', 'BC', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`); this.bananaCoin = bananaCoin; this.bananaCoinInstance = bananaCoinInstance; @@ -255,13 +258,12 @@ export class ClientFlowsBenchmark { async applyDeployCandyBarToken() { this.logger.info('Applying candy bar token deployment'); - const { contract: candyBarCoin, instance: candyBarCoinInstance } = await TokenContract.deploy( - this.adminWallet, - this.adminAddress, - 'CBC', - 'CBC', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: candyBarCoin, instance: candyBarCoinInstance }, + } = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'CBC', 'CBC', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`CandyBarCoin deployed at ${candyBarCoin.address}`); this.candyBarCoin = candyBarCoin; this.candyBarCoinInstance = candyBarCoinInstance; @@ -273,11 +275,12 @@ export class ClientFlowsBenchmark { expect((await this.context.wallet.getContractMetadata(feeJuiceContract.address)).isContractPublished).toBe(true); const bananaCoin = this.bananaCoin; - const { contract: bananaFPC, instance: bananaFPCInstance } = await FPCContract.deploy( - this.adminWallet, - bananaCoin.address, - this.adminAddress, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: bananaFPC, instance: bananaFPCInstance }, + } = await FPCContract.deploy(this.adminWallet, bananaCoin.address, this.adminAddress).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`BananaPay deployed at ${bananaFPC.address}`); @@ -340,14 +343,15 @@ export class ClientFlowsBenchmark { public async applyDeployAmm() { this.logger.info('Applying AMM deployment'); - const { contract: liquidityToken, instance: liquidityTokenInstance } = await TokenContract.deploy( - this.adminWallet, - this.adminAddress, - 'LPT', - 'LPT', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); - const { contract: amm, instance: ammInstance } = await AMMContract.deploy( + const { + receipt: { contract: liquidityToken, instance: liquidityTokenInstance }, + } = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'LPT', 'LPT', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); + const { + receipt: { contract: amm, instance: ammInstance }, + } = await AMMContract.deploy( this.adminWallet, this.bananaCoin.address, this.candyBarCoin.address, diff --git a/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts b/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts index 124e0b0cbbae..f75f4d011751 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts @@ -86,8 +86,8 @@ describe('Deployment benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); - expect(tx.transactionFee!).toBeGreaterThan(0n); + const { receipt } = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); + expect(receipt.transactionFee!).toBeGreaterThan(0n); } }); }); diff --git a/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts b/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts index 00d431a4bc40..c46f17ccf444 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts @@ -34,7 +34,7 @@ describe('Storage proof benchmark', () => { await t.applyFPCSetup(); await t.applyDeploySponsoredFPC(); - const deployed = await StorageProofTestContract.deploy(t.adminWallet).send({ + const { receipt: deployed } = await StorageProofTestContract.deploy(t.adminWallet).send({ from: t.adminAddress, wait: { returnReceipt: true }, }); @@ -106,7 +106,7 @@ describe('Storage proof benchmark', () => { ); if (process.env.SANITY_CHECKS) { - const tx = await interaction.send(options); + const { receipt: tx } = await interaction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); expect(tx.hasExecutionSucceeded()).toBe(true); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts b/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts index 77c8149072b7..df7222e067db 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts @@ -151,7 +151,7 @@ describe('Transfer benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await transferInteraction.send(options); + const { receipt: tx } = await transferInteraction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); // Sanity checks @@ -179,7 +179,7 @@ describe('Transfer benchmark', () => { */ expect(txEffects!.data.noteHashes.length).toBe(2 + (benchmarkingPaymentMethod === 'private_fpc' ? 2 : 0)); - const senderBalance = await asset.methods + const { result: senderBalance } = await asset.methods .balance_of_private(benchysAddress) .simulate({ from: benchysAddress }); expect(senderBalance).toEqual(expectedChange); diff --git a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts index a0362bf60e56..7f7cac22f729 100644 --- a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts +++ b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts @@ -152,9 +152,9 @@ describe('e2e_node_rpc_perf', () => { })); logger.info('Deploying token contract...'); - tokenContract = await TokenContract.deploy(wallet, ownerAddress, 'TestToken', 'TST', 18n).send({ + ({ contract: tokenContract } = await TokenContract.deploy(wallet, ownerAddress, 'TestToken', 'TST', 18n).send({ from: ownerAddress, - }); + })); contractAddress = tokenContract.address; logger.info(`Token contract deployed at ${contractAddress}`); diff --git a/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts b/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts index 54d51c3ce6da..e69ffed1efc2 100644 --- a/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts +++ b/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts @@ -65,12 +65,12 @@ describe('transaction benchmarks', () => { } = t); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/bench/utils.ts b/yarn-project/end-to-end/src/bench/utils.ts index 370baefc8b6c..7daa924782df 100644 --- a/yarn-project/end-to-end/src/bench/utils.ts +++ b/yarn-project/end-to-end/src/bench/utils.ts @@ -25,7 +25,7 @@ export async function benchmarkSetup( ) { const context = await setup(1, { ...opts, telemetryConfig: { benchmark: true } }); const defaultAccountAddress = context.accounts[0]; - const contract = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress }); + const { contract } = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress }); context.logger.info(`Deployed benchmarking contract at ${contract.address}`); const sequencer = (context.aztecNode as AztecNodeService).getSequencer()!; const telemetry = context.telemetryClient as BenchmarkTelemetryClient; @@ -149,7 +149,12 @@ export async function sendTxs( context.logger.info(`Creating ${txCount} txs`); const [from] = context.accounts; context.logger.info(`Sending ${txCount} txs`); - return Promise.all(calls.map(call => call.send({ from, wait: NO_WAIT }))); + return Promise.all( + calls.map(async call => { + const { txHash } = await call.send({ from, wait: NO_WAIT }); + return txHash; + }), + ); } export async function waitTxs(txs: TxHash[], context: EndToEndContext, txWaitOpts?: WaitOpts) { diff --git a/yarn-project/end-to-end/src/composed/docs_examples.test.ts b/yarn-project/end-to-end/src/composed/docs_examples.test.ts index fe843da8ff56..45611db7c898 100644 --- a/yarn-project/end-to-end/src/composed/docs_examples.test.ts +++ b/yarn-project/end-to-end/src/composed/docs_examples.test.ts @@ -34,7 +34,7 @@ describe('docs_examples', () => { const newAccountAddress = newAccountManager.address; const defaultAccountAddress = prefundedAccount.address; - const deployedContract = await TokenContract.deploy( + const { contract: deployedContract } = await TokenContract.deploy( wallet, // wallet instance defaultAccountAddress, // account 'TokenName', // constructor arg1 @@ -47,7 +47,9 @@ describe('docs_examples', () => { await contract.methods.mint_to_public(newAccountAddress, 1).send({ from: defaultAccountAddress }); // docs:start:simulate_function - const balance = await contract.methods.balance_of_public(newAccountAddress).simulate({ from: newAccountAddress }); + const { result: balance } = await contract.methods + .balance_of_public(newAccountAddress) + .simulate({ from: newAccountAddress }); expect(balance).toEqual(1n); // docs:end:simulate_function }); diff --git a/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts b/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts index 951c3e0152c4..7bd8d64e0fad 100644 --- a/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts @@ -71,10 +71,10 @@ describe('e2e_local_network_example', () => { ////////////// QUERYING THE TOKEN BALANCE FOR EACH ACCOUNT ////////////// - let aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + let { result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance ${aliceBalance}`); - let bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + let { result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply); @@ -88,10 +88,10 @@ describe('e2e_local_network_example', () => { await tokenContract.methods.transfer(bob, transferQuantity).send({ from: alice }); // Check the new balances - aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + ({ result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice })); logger.info(`Alice's balance ${aliceBalance}`); - bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + ({ result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob })); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply - transferQuantity); @@ -108,10 +108,10 @@ describe('e2e_local_network_example', () => { await mintTokensToPrivate(tokenContract, bob, bob, mintQuantity); // Check the new balances - aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + ({ result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice })); logger.info(`Alice's balance ${aliceBalance}`); - bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + ({ result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob })); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply - transferQuantity); @@ -188,7 +188,7 @@ describe('e2e_local_network_example', () => { const maxFeesPerGas = (await node.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PrivateFeePaymentMethod(bananaFPCAddress, alice, wallet, gasSettings); - const receiptForAlice = await bananaCoin.methods + const { receipt: receiptForAlice } = await bananaCoin.methods .transfer(bob, amountTransferToBob) .send({ from: alice, fee: { paymentMethod } }); // docs:end:private_fpc_payment @@ -196,11 +196,11 @@ describe('e2e_local_network_example', () => { logger.info(`Transaction fee: ${transactionFee}`); // Check the balances - const aliceBalance = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceBalance } = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance: ${aliceBalance}`); expect(aliceBalance).toEqual(mintAmount - transactionFee - amountTransferToBob); - const bobBalance = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobBalance } = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance: ${bobBalance}`); expect(bobBalance).toEqual(amountTransferToBob); @@ -214,16 +214,16 @@ describe('e2e_local_network_example', () => { // const sponsoredPaymentMethod = await SponsoredFeePaymentMethod.new(pxe); const initialFPCFeeJuice = await getFeeJuiceBalance(sponsoredFPC, node); - const receiptForBob = await bananaCoin.methods + const { receipt: receiptForBob } = await bananaCoin.methods .transfer(alice, amountTransferToAlice) .send({ from: bob, fee: { paymentMethod: sponsoredPaymentMethod } }); // docs:end:sponsored_fpc_payment // Check the balances - const aliceNewBalance = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceNewBalance } = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's new balance: ${aliceNewBalance}`); expect(aliceNewBalance).toEqual(aliceBalance + amountTransferToAlice); - const bobNewBalance = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobNewBalance } = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's new balance: ${bobNewBalance}`); expect(bobNewBalance).toEqual(bobBalance - amountTransferToAlice); diff --git a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts index 40a53ad5f254..3f60809d3f8f 100644 --- a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts @@ -68,7 +68,9 @@ describe('Aztec persistence', () => { owner = initialFundedAccounts[0]; ownerAddress = owner.address; - const { contract, instance } = await TokenBlacklistContract.deploy(wallet, ownerAddress).send({ + const { + receipt: { contract, instance }, + } = await TokenBlacklistContract.deploy(wallet, ownerAddress).send({ from: ownerAddress, wait: { returnReceipt: true }, }); @@ -84,7 +86,7 @@ describe('Aztec persistence', () => { const secret = Fr.random(); - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(1000n, await computeSecretHash(secret)) .send({ from: ownerAddress }); @@ -139,19 +141,29 @@ describe('Aztec persistence', () => { it('correctly restores private notes', async () => { // test for >0 instead of exact value so test isn't dependent on run order await expect( - contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), ).resolves.toBeGreaterThan(0n); }); it('correctly restores public storage', async () => { - await expect(contract.methods.total_supply().simulate({ from: ownerAddress })).resolves.toBeGreaterThan(0n); + await expect( + contract.methods + .total_supply() + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toBeGreaterThan(0n); }); it('tracks new notes for the owner', async () => { - const balance = await contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }); + const { result: balance } = await contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }); const secret = Fr.random(); - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(1000n, await computeSecretHash(secret)) .send({ from: ownerAddress }); await addPendingShieldNoteToPXE( @@ -165,9 +177,12 @@ describe('Aztec persistence', () => { await contract.methods.redeem_shield(ownerAddress, 1000n, secret).send({ from: ownerAddress }); - await expect(contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - balance + 1000n, - ); + await expect( + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(balance + 1000n); }); it('allows spending of private notes', async () => { @@ -175,13 +190,13 @@ describe('Aztec persistence', () => { const otherAccount = await context.wallet.createSchnorrAccount(account.secret, account.salt); const otherAddress = otherAccount.address; - const initialOwnerBalance = await contract.methods + const { result: initialOwnerBalance } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); await contract.methods.transfer(ownerAddress, otherAddress, 500n, 0).send({ from: ownerAddress }); - const [ownerBalance, targetBalance] = await Promise.all([ + const [{ result: ownerBalance }, { result: targetBalance }] = await Promise.all([ contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), contract.methods.balance_of_private(otherAddress).simulate({ from: otherAddress }), ]); @@ -224,16 +239,24 @@ describe('Aztec persistence', () => { it("pxe does not have owner's private notes", async () => { await context.wallet.registerContract(contractInstance, TokenBlacklistContract.artifact); const contract = TokenBlacklistContract.at(contractAddress, wallet); - await expect(contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - 0n, - ); + await expect( + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(0n); }); it('has access to public storage', async () => { await context.wallet.registerContract(contractInstance, TokenBlacklistContract.artifact); const contract = TokenBlacklistContract.at(contractAddress, wallet); - await expect(contract.methods.total_supply().simulate({ from: ownerAddress })).resolves.toBeGreaterThan(0n); + await expect( + contract.methods + .total_supply() + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toBeGreaterThan(0n); }); it('pxe restores notes after registering the owner', async () => { @@ -245,7 +268,10 @@ describe('Aztec persistence', () => { // check that notes total more than 0 so that this test isn't dependent on run order await expect( - contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), ).resolves.toBeGreaterThan(0n); }); }); @@ -274,7 +300,7 @@ describe('Aztec persistence', () => { // mint some tokens with a secret we know and redeem later on a separate PXE secret = Fr.random(); mintAmount = 1000n; - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(mintAmount, await computeSecretHash(secret)) .send({ from: ownerAddress }); mintTxHash = mintTxReceipt.txHash; @@ -301,9 +327,12 @@ describe('Aztec persistence', () => { }); it("restores owner's public balance", async () => { - await expect(contract.methods.balance_of_public(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - revealedAmount, - ); + await expect( + contract.methods + .balance_of_public(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(revealedAmount); }); it('allows consuming transparent note created on another PXE', async () => { @@ -317,12 +346,12 @@ describe('Aztec persistence', () => { aztecNode, ); - const balanceBeforeRedeem = await contract.methods + const { result: balanceBeforeRedeem } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); await contract.methods.redeem_shield(ownerAddress, mintAmount, secret).send({ from: ownerAddress }); - const balanceAfterRedeem = await contract.methods + const { result: balanceAfterRedeem } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); diff --git a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts index 923b14ea06f8..3a4b9932e270 100644 --- a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts @@ -100,7 +100,13 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { // Deploy L2 token contract // docs:start:deploy-l2-token - const l2TokenContract = await TokenContract.deploy(wallet, ownerAztecAddress, 'L2 Token', 'L2', 18).send({ + const { contract: l2TokenContract } = await TokenContract.deploy( + wallet, + ownerAztecAddress, + 'L2 Token', + 'L2', + 18, + ).send({ from: ownerAztecAddress, }); logger.info(`L2 token contract deployed at ${l2TokenContract.address}`); @@ -130,7 +136,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { // docs:end:deploy-portal // Deploy L2 bridge contract // docs:start:deploy-l2-bridge - const l2BridgeContract = await TokenBridgeContract.deploy( + const { contract: l2BridgeContract } = await TokenBridgeContract.deploy( wallet, l2TokenContract.address, l1PortalContractAddress, @@ -175,7 +181,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { await l2BridgeContract.methods .claim_public(ownerAztecAddress, MINT_AMOUNT, claim.claimSecret, claim.messageLeafIndex) .send({ from: ownerAztecAddress }); - const balance = await l2TokenContract.methods + const { result: balance } = await l2TokenContract.methods .balance_of_public(ownerAztecAddress) .simulate({ from: ownerAztecAddress }); logger.info(`Public L2 balance of ${ownerAztecAddress} is ${balance}`); @@ -206,12 +212,12 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { l2BridgeContract.address, EthAddress.ZERO, ); - const l2TxReceipt = await l2BridgeContract.methods + const { receipt: l2TxReceipt } = await l2BridgeContract.methods .exit_to_l1_public(EthAddress.fromString(ownerEthAddress), withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ from: ownerAztecAddress }); await waitForProven(node, l2TxReceipt, { provenTimeout: 500 }); - const newL2Balance = await l2TokenContract.methods + const { result: newL2Balance } = await l2TokenContract.methods .balance_of_public(ownerAztecAddress) .simulate({ from: ownerAztecAddress }); logger.info(`New L2 balance of ${ownerAztecAddress} is ${newL2Balance}`); diff --git a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts index 1ce73ebffd1c..2606a60654de 100644 --- a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts +++ b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts @@ -279,7 +279,7 @@ describe('HA Full Setup', () => { const sender = ownerAddress; logger.info(`Deploying contract from ${sender}`); - const receipt = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, @@ -396,7 +396,7 @@ describe('HA Full Setup', () => { // Send a transaction to trigger block building which will also trigger voting logger.info('Sending transaction to trigger block building...'); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const receipt = await deployer.deploy(ownerAddress, ownerAddress, 42).send({ + const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, 42).send({ from: ownerAddress, contractAddressSalt: Fr.random(), wait: { returnReceipt: true }, @@ -512,7 +512,7 @@ describe('HA Full Setup', () => { logger.info(`Active nodes: ${haNodeServices.length - killedNodes.length}/${NODE_COUNT}`); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const receipt = await deployer.deploy(ownerAddress, ownerAddress, i + 100).send({ + const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, i + 100).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(i + 100)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts index ce44c4ecd9c4..ff0bf8f94e9b 100644 --- a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts +++ b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts @@ -400,7 +400,10 @@ describe('e2e_multi_validator_node', () => { }); const settledTransactions = await Promise.all( - sentTransactionPromises.map(async sentTransactionPromise => waitForTx(aztecNode, await sentTransactionPromise)), + sentTransactionPromises.map(async sentTransactionPromise => { + const { txHash } = await sentTransactionPromise; + return waitForTx(aztecNode, txHash); + }), ); await Promise.all( diff --git a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts index 2ce5cf1fef9d..6b985b3a0242 100644 --- a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -138,7 +138,7 @@ describe('End-to-end tests for devnet', () => { const l2AccountDeployMethod = await l2AccountManager.getDeployMethod(); - const txReceipt = await l2AccountDeployMethod.send({ + const { receipt: txReceipt } = await l2AccountDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod: new FeeJuicePaymentMethodWithClaim(l2AccountAddress, { @@ -172,7 +172,9 @@ describe('End-to-end tests for devnet', () => { expect(txReceipt.isMined() && txReceipt.hasExecutionSucceeded()).toBe(true); const feeJuice = FeeJuiceContract.at((await node.getNodeInfo()).protocolContractAddresses.feeJuice, wallet); - const balance = await feeJuice.methods.balance_of_public(l2AccountAddress).simulate({ from: l2AccountAddress }); + const { result: balance } = await feeJuice.methods + .balance_of_public(l2AccountAddress) + .simulate({ from: l2AccountAddress }); expect(balance).toEqual(amount - txReceipt.transactionFee!); }); @@ -253,7 +255,7 @@ describe('End-to-end tests for devnet', () => { async function advanceChainWithEmptyBlocks(wallet: TestWallet) { const [fundedAccountAddress] = await registerInitialLocalNetworkAccountsInWallet(wallet); - const test = await TestContract.deploy(wallet).send({ + const { contract: test } = await TestContract.deploy(wallet).send({ from: fundedAccountAddress, universalDeploy: true, skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index e7c6b83ebe8b..fb18c816774d 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -97,7 +97,9 @@ describe('e2e_2_pxes', () => { const deployChildContractViaServerA = async () => { logger.info(`Deploying Child contract...`); - const { instance } = await ChildContract.deploy(walletA).send({ + const { + receipt: { instance }, + } = await ChildContract.deploy(walletA).send({ from: accountAAddress, wait: { returnReceipt: true }, }); diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index 85285d0a4fb1..a4e0338f4cfa 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -65,7 +65,7 @@ const itShouldBehaveLikeAnAccountContract = ( await deployMethod.send({ from: AztecAddress.ZERO }); } - child = await ChildContract.deploy(wallet).send({ from: address }); + ({ contract: child } = await ChildContract.deploy(wallet).send({ from: address })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_amm.test.ts b/yarn-project/end-to-end/src/e2e_amm.test.ts index 79f640236665..b92c5e9dc683 100644 --- a/yarn-project/end-to-end/src/e2e_amm.test.ts +++ b/yarn-project/end-to-end/src/e2e_amm.test.ts @@ -49,9 +49,9 @@ describe('AMM', () => { ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: liquidityToken } = await deployToken(wallet, adminAddress, 0n, logger)); - amm = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ + ({ contract: amm } = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ from: adminAddress, - }); + })); // TODO(#9480): consider deploying the token by some factory when the AMM is deployed, and making the AMM be the // minter there. @@ -81,15 +81,15 @@ describe('AMM', () => { async function getAmmBalances(): Promise { return { - token0: await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), - token1: await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), + token0: (await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, + token1: (await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, }; } async function getWalletBalances(lp: AztecAddress): Promise { return { - token0: await token0.methods.balance_of_private(lp).simulate({ from: lp }), - token1: await token1.methods.balance_of_private(lp).simulate({ from: lp }), + token0: (await token0.methods.balance_of_private(lp).simulate({ from: lp })).result, + token1: (await token1.methods.balance_of_private(lp).simulate({ from: lp })).result, }; } @@ -146,11 +146,13 @@ describe('AMM', () => { // Liquidity tokens should also be minted for the liquidity provider, as well as locked at the zero address. const expectedLiquidityTokens = (INITIAL_AMM_TOTAL_SUPPLY * 99n) / 100n; expect( - await liquidityToken.methods - .balance_of_private(liquidityProviderAddress) - .simulate({ from: liquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(liquidityProviderAddress) + .simulate({ from: liquidityProviderAddress }) + ).result, ).toEqual(expectedLiquidityTokens); - expect(await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).toEqual( + expect((await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( INITIAL_AMM_TOTAL_SUPPLY, ); }); @@ -162,7 +164,8 @@ describe('AMM', () => { const ammBalancesBefore = await getAmmBalances(); const lpBalancesBefore = await getWalletBalances(otherLiquidityProviderAddress); - const liquidityTokenSupplyBefore = await liquidityToken.methods.total_supply().simulate({ from: adminAddress }); + const liquidityTokenSupplyBefore = (await liquidityToken.methods.total_supply().simulate({ from: adminAddress })) + .result; // The pool currently has the same number of tokens for token0 and token1, since that is the ratio the first // liquidity provider used. Our maximum values have a different ratio (6:5 instead of 1:1), so we will end up @@ -214,11 +217,15 @@ describe('AMM', () => { (liquidityTokenSupplyBefore * (ammBalancesBefore.token0 + expectedAmount0)) / ammBalancesBefore.token0; const expectedLiquidityTokens = expectedTotalSupply - INITIAL_AMM_TOTAL_SUPPLY; - expect(await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).toEqual(expectedTotalSupply); + expect((await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + expectedTotalSupply, + ); expect( - await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result, ).toEqual(expectedLiquidityTokens); }); @@ -239,9 +246,11 @@ describe('AMM', () => { // We compute the expected amount out and set it as the minimum. In a real-life scenario we'd choose a slightly // lower value to account for slippage, but since we're the only actor interacting with the AMM we can afford to // just pass the exact value. Of course any lower value would also suffice. - const amountOutMin = await amm.methods - .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) - .simulate({ from: swapperAddress }); + const amountOutMin = ( + await amm.methods + .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) + .simulate({ from: swapperAddress }) + ).result; const swapExactTokensInteraction = amm.methods .swap_exact_tokens_for_tokens(token0.address, token1.address, amountIn, amountOutMin, nonceForAuthwits) @@ -264,9 +273,11 @@ describe('AMM', () => { // query the contract for how much token0 we'd get if we sent our entire token1 balance, and then request exactly // that amount. This would fail in a real-life scenario since we'd need to account for slippage, but we can do it // in this test environment since there's nobody else interacting with the AMM. - const amountOut = await amm.methods - .get_amount_out_for_exact_in(ammBalancesBefore.token1, ammBalancesBefore.token0, swapperBalancesBefore.token1) - .simulate({ from: swapperAddress }); + const amountOut = ( + await amm.methods + .get_amount_out_for_exact_in(ammBalancesBefore.token1, ammBalancesBefore.token0, swapperBalancesBefore.token1) + .simulate({ from: swapperAddress }) + ).result; const amountInMax = swapperBalancesBefore.token1; // Swaps also transfer tokens into the AMM, so we provide an authwit for the full amount in (any change will be @@ -299,9 +310,11 @@ describe('AMM', () => { it('remove liquidity', async () => { // We now withdraw all of the tokens of one of the liquidity providers by burning their entire liquidity token // balance. - const liquidityTokenBalance = await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }); + const liquidityTokenBalance = ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result; // Because private burning requires first transferring the tokens into the AMM, we again need to provide an // authwit. @@ -328,9 +341,11 @@ describe('AMM', () => { // The liquidity provider should have no remaining liquidity tokens, and should have recovered the value they // originally deposited. expect( - await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result, ).toEqual(0n); // We now assert that the liquidity provider ended up with more tokens than they began with. These extra tokens diff --git a/yarn-project/end-to-end/src/e2e_authwit.test.ts b/yarn-project/end-to-end/src/e2e_authwit.test.ts index adccc438291a..e978ab49e233 100644 --- a/yarn-project/end-to-end/src/e2e_authwit.test.ts +++ b/yarn-project/end-to-end/src/e2e_authwit.test.ts @@ -32,8 +32,8 @@ describe('e2e_authwit_tests', () => { } = await setup(2)); await ensureAccountContractsPublished(wallet, [account1Address, account2Address]); - auth = await AuthWitTestContract.deploy(wallet).send({ from: account1Address }); - authwitProxy = await GenericProxyContract.deploy(wallet).send({ from: account1Address }); + ({ contract: auth } = await AuthWitTestContract.deploy(wallet).send({ from: account1Address })); + ({ contract: authwitProxy } = await GenericProxyContract.deploy(wallet).send({ from: account1Address })); }); describe('Private', () => { diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 07782ca8dd71..1cf55068255e 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -36,11 +36,13 @@ describe('e2e_avm_simulator', () => { let secondAvmContract: AvmTestContract; beforeEach(async () => { - ({ contract: avmContract, instance: avmContractInstance } = await AvmTestContract.deploy(wallet).send({ + ({ + receipt: { contract: avmContract, instance: avmContractInstance }, + } = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress, wait: { returnReceipt: true }, })); - secondAvmContract = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: secondAvmContract } = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); describe('Assertions & error enriching', () => { @@ -145,7 +147,7 @@ describe('e2e_avm_simulator', () => { describe('Contract instance', () => { it('Works', async () => { - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .test_get_contract_instance_matches( avmContract.address, avmContractInstance.deployer, @@ -160,17 +162,21 @@ describe('e2e_avm_simulator', () => { describe('Nullifiers', () => { // Nullifier will not yet be siloed by the kernel. it('Emit and check in the same tx', async () => { - const tx = await avmContract.methods.emit_nullifier_and_check(123456).send({ from: defaultAccountAddress }); + const { receipt: tx } = await avmContract.methods + .emit_nullifier_and_check(123456) + .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); // Nullifier will have been siloed by the kernel, but we check against the unsiloed one. it('Emit and check in separate tx', async () => { const nullifier = new Fr(123456); - let tx = await avmContract.methods.new_nullifier(nullifier).send({ from: defaultAccountAddress }); + let { receipt: tx } = await avmContract.methods.new_nullifier(nullifier).send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); - tx = await avmContract.methods.assert_nullifier_exists(nullifier).send({ from: defaultAccountAddress }); + ({ receipt: tx } = await avmContract.methods + .assert_nullifier_exists(nullifier) + .send({ from: defaultAccountAddress })); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); @@ -204,7 +210,9 @@ describe('e2e_avm_simulator', () => { it('Nested CALL instruction to non-existent contract returns failure, but caller can recover', async () => { // The nested call reverts (returns failure), but the caller doesn't HAVE to rethrow. - const tx = await avmContract.methods.nested_call_to_nothing_recovers().send({ from: defaultAccountAddress }); + const { receipt: tx } = await avmContract.methods + .nested_call_to_nothing_recovers() + .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); it('Should NOT be able to emit the same unsiloed nullifier from the same contract', async () => { @@ -218,7 +226,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit different unsiloed nullifiers from the same contract', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_different_nullifier_in_nested_call(avmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -226,7 +234,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit the same unsiloed nullifier from two different contracts', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_same_nullifier_in_nested_call(secondAvmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -234,7 +242,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit different unsiloed nullifiers from two different contracts', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_different_nullifier_in_nested_call(secondAvmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -246,7 +254,9 @@ describe('e2e_avm_simulator', () => { let avmContract: AvmInitializerTestContract; beforeEach(async () => { - avmContract = await AvmInitializerTestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: avmContract } = await AvmInitializerTestContract.deploy(wallet).send({ + from: defaultAccountAddress, + })); }); describe('Storage', () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index 22cad7213ca5..5fc0640a8079 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -107,20 +107,24 @@ export class BlacklistTokenContractTest { await publicDeployAccounts(this.wallet, [this.adminAddress, this.otherAddress, this.blacklistedAddress]); this.logger.verbose(`Deploying TokenContract...`); - this.asset = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({ + ({ contract: this.asset } = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({ from: this.adminAddress, - }); + })); this.logger.verbose(`Token deployed to ${this.asset.address}`); this.logger.verbose(`Deploying bad account...`); - this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.badAccount.address}.`); // Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender // (so their notes are in scope), but msg_sender in the target must differ from the note owner // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender. this.logger.verbose(`Deploying generic proxy...`); - this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`); await this.crossTimestampOfChange(); @@ -133,9 +137,9 @@ export class BlacklistTokenContractTest { [this.adminAddress, this.otherAddress, this.blacklistedAddress], ); - expect(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).toEqual( - new Role().withAdmin().toNoirStruct(), - ); + expect( + (await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result, + ).toEqual(new Role().withAdmin().toNoirStruct()); } async setup() { @@ -189,9 +193,9 @@ export class BlacklistTokenContractTest { await this.crossTimestampOfChange(); - expect(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).toEqual( - adminMinterRole.toNoirStruct(), - ); + expect( + (await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result, + ).toEqual(adminMinterRole.toNoirStruct()); this.logger.verbose(`Minting ${amount} publicly...`); await asset.methods.mint_public(this.adminAddress, amount).send({ from: this.adminAddress }); @@ -199,7 +203,7 @@ export class BlacklistTokenContractTest { this.logger.verbose(`Minting ${amount} privately...`); const secret = Fr.random(); const secretHash = await computeSecretHash(secret); - const receipt = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress }); + const { receipt } = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress }); await this.addPendingShieldNoteToPXE(asset, this.adminAddress, amount, secretHash, receipt.txHash); await asset.methods.redeem_shield(this.adminAddress, amount, secret).send({ from: this.adminAddress }); @@ -207,20 +211,20 @@ export class BlacklistTokenContractTest { tokenSim.mintPublic(this.adminAddress, amount); - const publicBalance = await asset.methods + const { result: publicBalance } = await asset.methods .balance_of_public(this.adminAddress) .simulate({ from: this.adminAddress }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(this.adminAddress)); tokenSim.mintPrivate(this.adminAddress, amount); - const privateBalance = await asset.methods + const { result: privateBalance } = await asset.methods .balance_of_private(this.adminAddress) .simulate({ from: this.adminAddress }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(this.adminAddress)); - const totalSupply = await asset.methods.total_supply().simulate({ from: this.adminAddress }); + const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: this.adminAddress }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); } diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts index 6028b6173922..9b8b28204e15 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts @@ -27,7 +27,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('public', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_public(adminAddress, amount, 0).send({ from: adminAddress }); @@ -36,7 +39,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -61,7 +67,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -70,7 +79,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); const authwitNonce = 1; @@ -82,7 +94,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -91,7 +106,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -109,7 +127,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -138,7 +159,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('private', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn(adminAddress, amount, 0).send({ from: adminAddress }); @@ -146,7 +170,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -166,7 +193,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.burn(adminAddress, amount, 0).simulate({ from: adminAddress })).rejects.toThrow( @@ -175,7 +205,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.burn(adminAddress, amount, 1).simulate({ from: adminAddress })).rejects.toThrow( @@ -184,7 +217,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -199,7 +235,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -218,7 +257,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts index f1386cfa812a..3e704d342355 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts @@ -86,9 +86,11 @@ describe('e2e_blacklist_token_contract mint', () => { describe('Mint flow', () => { it('mint_private as minter and redeem as recipient', async () => { - const balanceBefore = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balanceBefore } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); - const receipt = await asset.methods.mint_private(amount, secretHash).send({ from: adminAddress }); + const { receipt } = await asset.methods.mint_private(amount, secretHash).send({ from: adminAddress }); txHash = receipt.txHash; await t.addPendingShieldNoteToPXE(asset, adminAddress, amount, secretHash, txHash); @@ -96,7 +98,9 @@ describe('e2e_blacklist_token_contract mint', () => { await asset.methods.redeem_shield(adminAddress, amount, secret).send({ from: adminAddress }); tokenSim.mintPrivate(adminAddress, amount); - const balanceAfter = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balanceAfter } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); expect(balanceAfter).toBe(balanceBefore + amount); }); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts index b09cb1d9b9c2..7ac49c276345 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts @@ -31,11 +31,14 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of self', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); - const receipt = await asset.methods.shield(adminAddress, amount, secretHash, 0).send({ from: adminAddress }); + const { receipt } = await asset.methods.shield(adminAddress, amount, secretHash, 0).send({ from: adminAddress }); // Redeem it await t.addPendingShieldNoteToPXE(asset, adminAddress, amount, secretHash, receipt.txHash); @@ -47,7 +50,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -61,7 +67,7 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { ); await validateActionInteraction.send(); - const receipt = await action.send({ from: otherAddress }); + const { receipt } = await action.send({ from: otherAddress }); // Check that replaying the shield should fail! await expect( @@ -79,7 +85,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); @@ -89,7 +98,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); @@ -101,7 +113,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -119,7 +134,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (wrong designated caller)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -137,7 +155,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (without approval)', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts index 87fc616cd1e3..88bbbf428fd5 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts @@ -26,7 +26,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const tokenTransferInteraction = asset.methods.transfer(adminAddress, otherAddress, amount, 0); @@ -35,7 +38,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer to self', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); @@ -44,7 +50,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -64,7 +73,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); @@ -74,7 +86,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); @@ -86,8 +101,14 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_private(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_private(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -99,8 +120,18 @@ describe('e2e_blacklist_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow('Assertion failed: Balance too low'); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_private(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_private(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { @@ -111,7 +142,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -130,7 +164,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -148,7 +185,12 @@ describe('e2e_blacklist_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); + expect( + await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); }); it('transfer from a blacklisted account', async () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts index c5d23f421bbd..2862a3c735e7 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts @@ -24,7 +24,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_public(adminAddress, otherAddress, amount, 0).send({ from: adminAddress }); @@ -33,7 +36,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer to self', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_public(adminAddress, adminAddress, amount, 0).send({ from: adminAddress }); @@ -42,7 +48,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -68,7 +77,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -79,7 +91,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; const authwitNonce = 1; await expect( @@ -92,7 +107,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -103,8 +121,14 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -121,13 +145,29 @@ describe('e2e_blacklist_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: otherAddress })).rejects.toThrow(U128_UNDERFLOW_ERROR); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -145,8 +185,18 @@ describe('e2e_blacklist_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: otherAddress })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts index ae10ada9661d..9547b5b992dd 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts @@ -26,7 +26,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of self', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv / 2n; expect(amount).toBeGreaterThan(0n); @@ -36,7 +39,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -56,7 +62,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -66,7 +75,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -78,7 +90,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -93,7 +108,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 21aa382720ac..304e34075a58 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -84,7 +84,7 @@ describe('e2e_block_building', () => { // so many so that we don't end up hitting a reorg or timing out the tx wait(). const TX_COUNT = 16; - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); logger.info(`Deployed stateful test contract at ${contract.address}`); // We add a delay to every public tx processing @@ -165,7 +165,7 @@ describe('e2e_block_building', () => { it('assembles a block with multiple txs with public fns', async () => { // First deploy the contract - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); // Assemble N contract deployment txs // We need to create them sequentially since we cannot have parallel calls to a circuit @@ -194,8 +194,8 @@ describe('e2e_block_building', () => { // Skipped since we only use it to manually test number of invocations to world-state. it.skip('builds blocks with multiple public fns after multiple nullifier insertions', async () => { // First deploy the contracts - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); - const another = await TestContract.deploy(wallet).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract: another } = await TestContract.deploy(wallet).send({ from: ownerAddress }); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 16, maxTxsPerBlock: 16 }); @@ -243,12 +243,12 @@ describe('e2e_block_building', () => { [minterAddress, true], ); - const [deployTxReceipt, callTxReceipt] = await Promise.all([ + const [deployResult, callResult] = await Promise.all([ deployMethod.send({ from: ownerAddress, wait: { returnReceipt: true } }), callInteraction.send({ from: ownerAddress }), ]); - expect(deployTxReceipt.blockNumber).toEqual(callTxReceipt.blockNumber); + expect(deployResult.receipt.blockNumber).toEqual(callResult.receipt.blockNumber); }); }); @@ -263,7 +263,7 @@ describe('e2e_block_building', () => { wallet, accounts: [ownerAddress], } = await setup(1)); - contract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: ownerAddress })); logger.info(`Test contract deployed at ${contract.address}`); }); @@ -391,7 +391,7 @@ describe('e2e_block_building', () => { } = await setup(1)); logger.info(`Deploying test contract`); - testContract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: ownerAddress })); }, 60_000); afterAll(() => teardown()); @@ -495,9 +495,11 @@ describe('e2e_block_building', () => { })); logger.info('Deploying token contract'); - const token = await TokenContract.deploy(wallet, ownerAddress, 'TokenName', 'TokenSymbol', 18).send({ - from: ownerAddress, - }); + const { contract: token } = await TokenContract.deploy(wallet, ownerAddress, 'TokenName', 'TokenSymbol', 18).send( + { + from: ownerAddress, + }, + ); logger.info('Updating txs per block to 4'); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 4, maxTxsPerBlock: 4 }); @@ -530,7 +532,7 @@ describe('e2e_block_building', () => { accounts: [ownerAddress], } = context); - const testContract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + const { contract: testContract } = await TestContract.deploy(wallet).send({ from: ownerAddress }); logger.warn(`Test contract deployed at ${testContract.address}`); // We want the sequencer to wait until both txs have arrived (so minTxsPerBlock=2), but agree to build @@ -549,7 +551,8 @@ describe('e2e_block_building', () => { ]); const batches = times(2, makeBatch); - const txHashes = await Promise.all(batches.map(batch => batch.send({ from: ownerAddress, wait: NO_WAIT }))); + const txHashResults = await Promise.all(batches.map(batch => batch.send({ from: ownerAddress, wait: NO_WAIT }))); + const txHashes = txHashResults.map(({ txHash }) => txHash); logger.warn(`Sent two txs to test contract`, { txs: txHashes.map(hash => hash.toString()) }); await Promise.race(txHashes.map(txHash => waitForTx(aztecNode, txHash, { timeout: 60 }))); @@ -581,7 +584,7 @@ describe('e2e_block_building', () => { accounts: [ownerAddress], } = await setup(1)); - contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + ({ contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress })); initialBlockNumber = await aztecNode.getBlockNumber(); logger.info(`Stateful test contract deployed at ${contract.address}`); @@ -605,15 +608,15 @@ describe('e2e_block_building', () => { // Send a tx to the contract that creates a note. This tx will be reorgd but re-included, // since it is being built against a proven block number. logger.info('Sending initial tx'); - const tx1 = await contract.methods.create_note(ownerAddress, 20).send({ from: ownerAddress }); + const { receipt: tx1 } = await contract.methods.create_note(ownerAddress, 20).send({ from: ownerAddress }); expect(tx1.blockNumber).toEqual(initialBlockNumber + 1); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(21n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(21n); // And send a second one, which won't be re-included. logger.info('Sending second tx'); - const tx2 = await contract.methods.create_note(ownerAddress, 30).send({ from: ownerAddress }); + const { receipt: tx2 } = await contract.methods.create_note(ownerAddress, 30).send({ from: ownerAddress }); expect(tx2.blockNumber).toEqual(initialBlockNumber + 2); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(51n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(51n); logger.info('Advancing past the proof submission window'); @@ -648,12 +651,12 @@ describe('e2e_block_building', () => { expect(newTx1Receipt.blockHash).not.toEqual(tx1.blockHash); // PXE should have cleared out the 30-note from tx2, but reapplied the 20-note from tx1 - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(21n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(21n); // And we should be able to send a new tx on the new chain logger.info('Sending new tx on reorgd chain'); - const tx3 = await contract.methods.create_note(ownerAddress, 10).send({ from: ownerAddress }); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(31n); + const { receipt: tx3 } = await contract.methods.create_note(ownerAddress, 10).send({ from: ownerAddress }); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(31n); expect(tx3.blockNumber).toBeGreaterThanOrEqual(newTx1Receipt.blockNumber! + 1); }); }); diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 56e05017370b..6f387d27e51c 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -104,7 +104,7 @@ describe('e2e_card_game', () => { const deployContract = async () => { logger.debug(`Deploying L2 contract...`); - contract = await CardGameContract.deploy(wallet).send({ from: firstPlayer }); + ({ contract } = await CardGameContract.deploy(wallet).send({ from: firstPlayer })); logger.info(`L2 contract deployed at ${contract.address}`); }; @@ -113,7 +113,9 @@ describe('e2e_card_game', () => { // docs:start:send_tx await contract.methods.buy_pack(seed).send({ from: firstPlayer }); // docs:end:send_tx - const collection = await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }); + const { result: collection } = await contract.methods + .view_collection_cards(firstPlayer, 0) + .simulate({ from: firstPlayer }); const expected = await getPackedCards(0, seed); expect(boundedVecToArray(collection)).toMatchObject(expected); }); @@ -128,7 +130,7 @@ describe('e2e_card_game', () => { contract.methods.buy_pack(seed).send({ from: secondPlayer }), ]); firstPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }), + (await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer })).result, ); }); @@ -143,11 +145,13 @@ describe('e2e_card_game', () => { .send({ from: secondPlayer }), ).rejects.toThrow(`Not all cards were removed`); - const collection = await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }); + const { result: collection } = await contract.methods + .view_collection_cards(firstPlayer, 0) + .simulate({ from: firstPlayer }); expect(boundedVecToArray(collection)).toHaveLength(1); expect(boundedVecToArray(collection)).toMatchObject([firstPlayerCollection[1]]); - expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })) as Game).toMatchObject({ + expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })).result as Game).toMatchObject({ players: [ { address: firstPlayer, @@ -169,9 +173,8 @@ describe('e2e_card_game', () => { it('should start games', async () => { const secondPlayerCollection = boundedVecToArray( - (await contract.methods - .view_collection_cards(secondPlayer, 0) - .simulate({ from: secondPlayer })) as NoirBoundedVec, + (await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer })) + .result as NoirBoundedVec, ); await Promise.all([ @@ -185,7 +188,7 @@ describe('e2e_card_game', () => { await contract.methods.start_game(GAME_ID).send({ from: firstPlayer }); - expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })) as Game).toMatchObject({ + expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })).result as Game).toMatchObject({ players: expect.arrayContaining([ { address: firstPlayer, @@ -220,15 +223,15 @@ describe('e2e_card_game', () => { ]); firstPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }), + (await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer })).result, ); secondPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer }), + (await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer })).result, ); thirdPlayerCOllection = boundedVecToArray( - await contract.methods.view_collection_cards(thirdPlayer, 0).simulate({ from: thirdPlayer }), + (await contract.methods.view_collection_cards(thirdPlayer, 0).simulate({ from: thirdPlayer })).result, ); }); @@ -240,7 +243,7 @@ describe('e2e_card_game', () => { } async function playGame(playerDecks: { address: AztecAddress; deck: Card[] }[], id = GAME_ID) { - const initialGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })) as Game; + const initialGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })).result as Game; const players = initialGameState.players.map(player => player.address); const cards = players.map( player => playerDecks.find(playerDeckEntry => playerDeckEntry.address.equals(player))!.deck, @@ -254,7 +257,7 @@ describe('e2e_card_game', () => { } } - const finalGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })) as Game; + const finalGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })).result as Game; expect(finalGameState.finished).toBe(true); return finalGameState; @@ -285,7 +288,8 @@ describe('e2e_card_game', () => { await contract.methods.claim_cards(GAME_ID, game.rounds_cards.map(cardToField)).send({ from: winner }); const winnerCollection = boundedVecToArray( - (await contract.methods.view_collection_cards(winner, 0).simulate({ from: winner })) as NoirBoundedVec, + (await contract.methods.view_collection_cards(winner, 0).simulate({ from: winner })) + .result as NoirBoundedVec, ); const winnerGameDeck = [winnerCollection[0], winnerCollection[3]]; diff --git a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts index 8c8fd078c7de..699cb2bb459d 100644 --- a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts +++ b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts @@ -110,7 +110,9 @@ describe('e2e_contract_updates', () => { } sequencer = maybeSequencer; - ({ contract, instance } = await UpdatableContract.deploy(wallet, constructorArgs[0]).send({ + ({ + receipt: { contract, instance }, + } = await UpdatableContract.deploy(wallet, constructorArgs[0]).send({ from: defaultAccountAddress, contractAddressSalt: salt, wait: { returnReceipt: true }, diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index e83b195c5f84..e074e5a9239c 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -34,7 +34,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { await t.setup(); ({ logger: log, crossChainTestHarness, wallet, user1Address, aztecNode } = t); - testContract = await TestContract.deploy(wallet).send({ from: user1Address }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: user1Address })); }, 300_000); afterEach(async () => { @@ -219,7 +219,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // On public, we actually send the tx and check that it reverts due to the missing message. // This advances the block too as a side-effect. Note that we do not rely on a simulation since the cross chain messages // do not get added at the beginning of the block during node_simulatePublicCalls (maybe they should?). - const receipt = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); + const { receipt } = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); await t.context.watcher.markAsProven(); } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index 7432bd33d4f3..d747d3ef6666 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -43,7 +43,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { version = BigInt(await rollup.getVersion()); - contract = await TestContract.deploy(wallet).send({ from: user1Address }); + ({ contract } = await TestContract.deploy(wallet).send({ from: user1Address })); }); afterAll(async () => { @@ -60,7 +60,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { // Configure the node to be able to rollup only 1 tx. await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); - const txReceipt = await new BatchCall(wallet, [ + const { receipt: txReceipt } = await new BatchCall(wallet, [ contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(contents[0], recipient), contract.methods.create_l2_to_l1_message_arbitrary_recipient_public(contents[1], recipient), ]).send({ from: user1Address }); @@ -92,7 +92,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { await aztecNodeAdmin.setConfig({ minTxsPerBlock: 2 }); // Send the 2 txs. - const [noMessageReceipt, withMessageReceipt] = await Promise.all([ + const [{ receipt: noMessageReceipt }, { receipt: withMessageReceipt }] = await Promise.all([ contract.methods.emit_nullifier(Fr.random()).send({ from: user1Address }), contract.methods .create_l2_to_l1_message_arbitrary_recipient_private(content, recipient) @@ -119,7 +119,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { const call0 = createBatchCall(wallet, tx0.recipients, tx0.contents); const call1 = createBatchCall(wallet, tx1.recipients, tx1.contents); - const [l2TxReceipt0, l2TxReceipt1] = await Promise.all([ + const [{ receipt: l2TxReceipt0 }, { receipt: l2TxReceipt1 }] = await Promise.all([ call0.send({ from: user1Address }), call1.send({ from: user1Address }), ]); @@ -173,7 +173,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { const call1 = createBatchCall(wallet, tx1.recipients, tx1.contents); const call2 = createBatchCall(wallet, tx2.recipients, tx2.contents); - const [l2TxReceipt0, l2TxReceipt1, l2TxReceipt2] = await Promise.all([ + const [{ receipt: l2TxReceipt0 }, { receipt: l2TxReceipt1 }, { receipt: l2TxReceipt2 }] = await Promise.all([ call0.send({ from: user1Address }), call1.send({ from: user1Address }), call2.send({ from: user1Address }), diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index e82734a14b15..dc6b292382e8 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -62,22 +62,22 @@ describe('e2e_crowdfunding_and_claim', () => { // We set the deadline to a week from now deadline = (await cheatCodes.eth.timestamp()) + 7 * 24 * 60 * 60; - donationToken = await TokenContract.deploy( + ({ contract: donationToken } = await TokenContract.deploy( wallet, operatorAddress, donationTokenMetadata.name, donationTokenMetadata.symbol, donationTokenMetadata.decimals, - ).send({ from: operatorAddress }); + ).send({ from: operatorAddress })); logger.info(`Donation Token deployed to ${donationToken.address}`); - rewardToken = await TokenContract.deploy( + ({ contract: rewardToken } = await TokenContract.deploy( wallet, operatorAddress, rewardTokenMetadata.name, rewardTokenMetadata.symbol, rewardTokenMetadata.decimals, - ).send({ from: operatorAddress }); + ).send({ from: operatorAddress })); logger.info(`Reward Token deployed to ${rewardToken.address}`); // We deploy the Crowdfunding contract as an escrow contract (i.e. with populated public keys that make it @@ -94,12 +94,16 @@ describe('e2e_crowdfunding_and_claim', () => { ); const crowdfundingInstance = await crowdfundingDeployment.getInstance(); await wallet.registerContract(crowdfundingInstance, CrowdfundingContract.artifact, crowdfundingSecretKey); - crowdfundingContract = await crowdfundingDeployment.send({ from: operatorAddress }); + ({ contract: crowdfundingContract } = await crowdfundingDeployment.send({ from: operatorAddress })); logger.info(`Crowdfunding contract deployed at ${crowdfundingContract.address}`); - claimContract = await ClaimContract.deploy(wallet, crowdfundingContract.address, rewardToken.address).send({ + ({ contract: claimContract } = await ClaimContract.deploy( + wallet, + crowdfundingContract.address, + rewardToken.address, + ).send({ from: operatorAddress, - }); + })); logger.info(`Claim contract deployed at ${claimContract.address}`); await rewardToken.methods.set_minter(claimContract.address, true).send({ from: operatorAddress }); @@ -129,7 +133,7 @@ describe('e2e_crowdfunding_and_claim', () => { // The donor should have exactly one note const pageIndex = 0; - const notes = await crowdfundingContract.methods + const { result: notes } = await crowdfundingContract.methods .get_donation_notes(donor1Address, pageIndex) .simulate({ from: donor1Address }); expect(notes.len).toEqual(1n); @@ -184,7 +188,7 @@ describe('e2e_crowdfunding_and_claim', () => { // The donor should have exactly one note const pageIndex = 0; - const notes = await crowdfundingContract.methods + const { result: notes } = await crowdfundingContract.methods .get_donation_notes(donorAddress, pageIndex) .simulate({ from: donorAddress }); expect(notes.len).toEqual(1n); @@ -221,7 +225,7 @@ describe('e2e_crowdfunding_and_claim', () => { deadline, ); - otherCrowdfundingContract = await otherCrowdfundingDeployment.send({ from: operatorAddress }); + ({ contract: otherCrowdfundingContract } = await otherCrowdfundingDeployment.send({ from: operatorAddress })); logger.info(`Crowdfunding contract deployed at ${otherCrowdfundingContract.address}`); } @@ -241,11 +245,11 @@ describe('e2e_crowdfunding_and_claim', () => { // 3) Get the donation note const pageIndex = 0; - const notes = await otherCrowdfundingContract.methods + const { result: notes2 } = await otherCrowdfundingContract.methods .get_donation_notes(donor1Address, pageIndex) .simulate({ from: donor1Address }); - expect(notes.len).toEqual(1n); - const otherContractNote = notes.storage[0]; + expect(notes2.len).toEqual(1n); + const otherContractNote = notes2.storage[0]; // 4) Try to claim rewards using note from other contract await expect( diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index fc5a57f84fe6..d027cd6c4b39 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -37,7 +37,7 @@ describe('e2e_deploy_contract contract class registration', () => { ({ logger, wallet, aztecNode, defaultAccountAddress } = await t.setup()); artifact = StatefulTestContract.artifact; publicationTxReceipt = await publishContractClass(wallet, artifact).then(c => - c.send({ from: defaultAccountAddress }), + c.send({ from: defaultAccountAddress }).then(({ receipt }) => receipt), ); contractClass = await getContractClassFromArtifact(artifact); expect(await aztecNode.getContractClass(contractClass.id)).toBeDefined(); @@ -47,7 +47,7 @@ describe('e2e_deploy_contract contract class registration', () => { describe('publishing a contract class', () => { it('emits public bytecode', async () => { - const publicationTxReceipt = await publishContractClass(wallet, TestContract.artifact).then(c => + const { receipt: publicationTxReceipt } = await publishContractClass(wallet, TestContract.artifact).then(c => c.send({ from: defaultAccountAddress }), ); const logs = await aztecNode.getContractClassLogs({ txHash: publicationTxReceipt.txHash }); @@ -155,7 +155,7 @@ describe('e2e_deploy_contract contract class registration', () => { it('refuses to call a public function with init check if the instance is not initialized', async () => { const whom = await AztecAddress.random(); - const receipt = await contract.methods + const { receipt } = await contract.methods .increment_public_value(whom, 10) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); @@ -195,7 +195,7 @@ describe('e2e_deploy_contract contract class registration', () => { it('refuses to initialize the instance with wrong args via a public function', async () => { const whom = await AztecAddress.random(); - const receipt = await contract.methods + const { receipt } = await contract.methods .public_constructor(whom, 43) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); @@ -228,7 +228,7 @@ describe('e2e_deploy_contract contract class registration', () => { // Register the instance to be deployed in the pxe await wallet.registerContract(instance, artifact); // Set up the contract that calls the deployer (which happens to be the TestContract) and call it - const deployer = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: deployer } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); await deployer.methods.publish_contract_instance(instance.address).send({ from: defaultAccountAddress }); }); @@ -243,7 +243,7 @@ describe('e2e_deploy_contract contract class registration', () => { ).rejects.toThrow(/not deployed/); // This time, don't throw on revert and confirm that the tx is included // despite reverting in app logic because of the call to a non-existent contract - const tx = await instance.methods + const { receipt: tx } = await instance.methods .increment_public_value_no_init_check(whom, 10) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(tx.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index 3d81fda867be..9ce2b7366aea 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -40,7 +40,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; logger.debug(`Deploying stateful test contract`); // docs:start:deploy_basic - const contract = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); // docs:end:deploy_basic expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); logger.debug(`Calling public method on stateful test contract at ${contract.address.toString()}`); @@ -58,7 +58,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; // docs:start:deploy_universal const opts = { universalDeploy: true, from: defaultAccountAddress }; - const contract = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); + const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); // docs:end:deploy_universal expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); @@ -68,7 +68,7 @@ describe('e2e_deploy_contract deploy method', () => { it('publicly deploys and calls a public function from the constructor', async () => { const owner = defaultAccountAddress; // docs:start:deploy_token - const token = await TokenContract.deploy(wallet, owner, 'TOKEN', 'TKN', 18).send({ + const { contract: token } = await TokenContract.deploy(wallet, owner, 'TOKEN', 'TKN', 18).send({ from: defaultAccountAddress, }); // docs:end:deploy_token @@ -79,7 +79,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; logger.debug(`Deploying contract via a public constructor`); // docs:start:deploy_with_opts - const contract = await StatefulTestContract.deployWithOpts( + const { contract } = await StatefulTestContract.deployWithOpts( { wallet, method: 'public_constructor' }, owner, 42, @@ -96,7 +96,7 @@ describe('e2e_deploy_contract deploy method', () => { it('deploys a contract with a default initializer not named constructor', async () => { logger.debug(`Deploying contract with a default initializer named initialize`); const opts = { skipClassPublication: true, skipInstancePublication: true, from: defaultAccountAddress }; - const contract = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); + const { contract } = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); logger.debug(`Calling a function to ensure the contract was properly initialized`); await contract.methods.increment_twice(defaultAccountAddress).send({ from: defaultAccountAddress }); expect(await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).toEqual( @@ -106,10 +106,10 @@ describe('e2e_deploy_contract deploy method', () => { it('publicly deploys a contract with no constructor', async () => { logger.debug(`Deploying contract with no constructor`); - const contract = await NoConstructorContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract } = await NoConstructorContract.deploy(wallet).send({ from: defaultAccountAddress }); const arbitraryValue = 42; logger.debug(`Call a public function to check that it was publicly deployed`); - const receipt = await contract.methods.emit_public(arbitraryValue).send({ from: defaultAccountAddress }); + const { receipt } = await contract.methods.emit_public(arbitraryValue).send({ from: defaultAccountAddress }); const logs = await aztecNode.getPublicLogs({ txHash: receipt.txHash }); expect(logs.logs[0].log.getEmittedFields()).toEqual([new Fr(arbitraryValue)]); }); @@ -162,7 +162,10 @@ describe('e2e_deploy_contract deploy method', () => { const publicCallTxPromise = publicCall.send({ from: defaultAccountAddress, wait: { timeout: 600 } }); logger.debug('Deploying a contract and calling a public function in the same block'); - const [deployTxReceipt, publicCallTxReceipt] = await Promise.all([deployTxPromise, publicCallTxPromise]); + const [{ receipt: deployTxReceipt }, { receipt: publicCallTxReceipt }] = await Promise.all([ + deployTxPromise, + publicCallTxPromise, + ]); expect(deployTxReceipt.blockNumber).toEqual(publicCallTxReceipt.blockNumber); }, 300_000); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts index 55f638be25f6..9d16aeb08bb0 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts @@ -36,7 +36,7 @@ describe('e2e_deploy_contract legacy', () => { deployer: defaultAccountAddress, }); const deployer = new ContractDeployer(TestContractArtifact, wallet); - const receipt = await deployer + const { receipt } = await deployer .deploy() .send({ from: defaultAccountAddress, contractAddressSalt: salt, wait: { returnReceipt: true } }); expect(receipt.contract.address).toEqual(deploymentData.address); @@ -65,7 +65,7 @@ describe('e2e_deploy_contract legacy', () => { for (let index = 0; index < 2; index++) { logger.info(`Deploying contract ${index + 1}...`); - const receipt = await deployer + const { receipt } = await deployer .deploy() .send({ from: defaultAccountAddress, contractAddressSalt: Fr.random(), wait: { returnReceipt: true } }); logger.info(`Sending TX to contract ${index + 1}...`); @@ -113,8 +113,8 @@ describe('e2e_deploy_contract legacy', () => { expect(goodTxPromiseResult.status).toBe('fulfilled'); expect(badTxReceiptResult.status).toBe('fulfilled'); // but reverted - const goodTxReceipt = goodTxPromiseResult.status === 'fulfilled' ? goodTxPromiseResult.value : null; - const badTxReceipt = badTxReceiptResult.status === 'fulfilled' ? badTxReceiptResult.value : null; + const goodTxReceipt = goodTxPromiseResult.status === 'fulfilled' ? goodTxPromiseResult.value.receipt : null; + const badTxReceipt = badTxReceiptResult.status === 'fulfilled' ? badTxReceiptResult.value.receipt : null; // Both the good and bad transactions are included expect(goodTxReceipt).toBeDefined(); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index f1af8d9c9adf..4a40eef5e2ec 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -31,7 +31,7 @@ describe('e2e_deploy_contract private initialization', () => { // The function has a noinitcheck flag so it can be called without initialization. it('executes a noinitcheck function in an uninitialized contract', async () => { const contract = await t.registerContract(wallet, TestContract); - const receipt = await contract.methods.emit_nullifier(10).send({ from: defaultAccountAddress }); + const { receipt } = await contract.methods.emit_nullifier(10).send({ from: defaultAccountAddress }); const txEffects = await aztecNode.getTxEffect(receipt.txHash); const expected = await siloNullifier(contract.address, new Fr(10)); diff --git a/yarn-project/end-to-end/src/e2e_double_spend.test.ts b/yarn-project/end-to-end/src/e2e_double_spend.test.ts index 6b4e2053b380..3cc69dec717d 100644 --- a/yarn-project/end-to-end/src/e2e_double_spend.test.ts +++ b/yarn-project/end-to-end/src/e2e_double_spend.test.ts @@ -25,7 +25,7 @@ describe('e2e_double_spend', () => { logger, } = await setup(1)); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); logger.info(`Test contract deployed at ${contract.address}`); }); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts index 80c2fec76ff0..a584e5e3f9e5 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts @@ -125,7 +125,7 @@ describe('e2e_epochs/epochs_invalidate_block', () => { // Send a few transactions so the sequencer builds multiple blocks in the checkpoint // We'll later check that the first tx at least was picked up and mined logger.warn('Sending multiple transactions to trigger block building'); - const [sentTx] = await timesAsync(8, i => + const [{ txHash: sentTx }] = await timesAsync(8, i => testContract.methods.emit_nullifier(BigInt(i + 1)).send({ from: context.accounts[0], wait: NO_WAIT }), ); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts index 1917f419e9f4..177467460c9b 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts @@ -117,8 +117,8 @@ describe('e2e_epochs/epochs_mbps', () => { // Unlike emit_nullifier (which has #[noinitcheck]), cross-chain methods require a deployed contract. if (deployCrossChainContract) { logger.warn(`Deploying cross-chain test contract before stopping initial sequencer`); - crossChainContract = await TestContract.deploy(wallet).send({ from }); - logger.warn(`Cross-chain test contract deployed at ${crossChainContract.address}`); + ({ contract: crossChainContract } = await TestContract.deploy(wallet).send({ from })); + logger.warn(`Cross-chain test contract deployed at ${crossChainContract!.address}`); } // Halt block building in initial aztec node, which was not set up as a validator. diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts index 846cd5f82b96..bc5a6c18eb57 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts @@ -46,7 +46,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { // Deploy a contract that consumes L1 to L2 messages await context.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); logger.warn(`Deploying test contract`); - const testContract = await TestContract.deploy(context.wallet).send({ from: context.accounts[0] }); + const { contract: testContract } = await TestContract.deploy(context.wallet).send({ from: context.accounts[0] }); logger.warn(`Test contract deployed at ${testContract.address}`); // Send an l1 to l2 message to be consumed from the contract @@ -64,7 +64,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { // And we consume the message using the test contract. It's important that we don't wait for the membership witness // to be available, since we want to test the scenario where the message becomes available on the same block the tx lands. logger.warn(`Consuming message ${message.content.toString()} from the contract at ${testContract.address}`); - const txReceipt = await testContract.methods + const { receipt: txReceipt } = await testContract.methods .consume_message_from_arbitrary_sender_public( message.content, secret, @@ -90,7 +90,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { expect(provenBlockNumber).toBeGreaterThanOrEqual(txReceipt.blockNumber!); // Should not be able to consume the message again. - const failedReceipt = await testContract.methods + const { receipt: failedReceipt } = await testContract.methods .consume_message_from_arbitrary_sender_public( message.content, secret, diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index d6efb8165374..04826a9312f7 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -41,11 +41,13 @@ describe('e2e_escrow_contract', () => { const escrowDeployment = EscrowContract.deployWithPublicKeys(escrowPublicKeys, wallet, owner); const escrowInstance = await escrowDeployment.getInstance(); await wallet.registerContract(escrowInstance, EscrowContract.artifact, escrowSecretKey); - escrowContract = await escrowDeployment.send({ from: owner }); + ({ contract: escrowContract } = await escrowDeployment.send({ from: owner })); logger.info(`Escrow contract deployed at ${escrowContract.address}`); // Deploy Token contract and mint funds for the escrow contract - token = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ from: owner }); + ({ contract: token } = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ + from: owner, + })); await mintTokensToPrivate(token, owner, escrowContract.address, 100n); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index 4645fc1dcc77..64d89526709f 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -47,7 +47,7 @@ describe('Logs', () => { await ensureAccountContractsPublished(wallet, [account1Address, account2Address]); log.warn(`Deploying test contract`); - testLogContract = await TestLogContract.deploy(wallet).send({ from: account1Address }); + ({ contract: testLogContract } = await TestLogContract.deploy(wallet).send({ from: account1Address })); }); afterAll(() => teardown()); @@ -57,9 +57,12 @@ describe('Logs', () => { const preimages = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple, 5>; const txs = await Promise.all( - preimages.map(preimage => - testLogContract.methods.emit_encrypted_events(account2Address, preimage).send({ from: account1Address }), - ), + preimages.map(async preimage => { + const { receipt } = await testLogContract.methods + .emit_encrypted_events(account2Address, preimage) + .send({ from: account1Address }); + return receipt; + }), ); const firstBlockNumber = Math.min(...txs.map(tx => tx.blockNumber!)); @@ -124,13 +127,13 @@ describe('Logs', () => { const preimage = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple, 5>; let i = 0; - const firstTx = await testLogContract.methods + const { receipt: firstTx } = await testLogContract.methods .emit_unencrypted_events(preimage[i]) .send({ from: account1Address }); await timesParallel(3, () => testLogContract.methods.emit_unencrypted_events(preimage[++i]).send({ from: account1Address }), ); - const lastTx = await testLogContract.methods + const { receipt: lastTx } = await testLogContract.methods .emit_unencrypted_events(preimage[++i]) .send({ from: account1Address }); @@ -181,7 +184,9 @@ describe('Logs', () => { const c = await AztecAddress.random(); const extra = Fr.random(); - const tx = await testLogContract.methods.emit_nested_event(a, b, c, extra).send({ from: account1Address }); + const { receipt: tx } = await testLogContract.methods + .emit_nested_event(a, b, c, extra) + .send({ from: account1Address }); const collectedEvents = await getPublicEvents( aztecNode, @@ -212,7 +217,7 @@ describe('Logs', () => { const tx1NumLogs = 10; { // Call the private function that emits two encrypted logs per call and recursively nests 4 times - const tx = await testLogContract.methods + const { receipt: tx } = await testLogContract.methods .emit_encrypted_events_nested(account2Address, 4) .send({ from: account1Address }); @@ -231,7 +236,7 @@ describe('Logs', () => { const tx2NumLogs = 6; { // Call the private function that emits two encrypted logs per call and recursively nests 2 times - const tx = await testLogContract.methods + const { receipt: tx } = await testLogContract.methods .emit_encrypted_events_nested(account2Address, 2) .send({ from: account1Address }); diff --git a/yarn-project/end-to-end/src/e2e_event_only.test.ts b/yarn-project/end-to-end/src/e2e_event_only.test.ts index 7dd0c3859451..d2b036f601a0 100644 --- a/yarn-project/end-to-end/src/e2e_event_only.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_only.test.ts @@ -26,14 +26,16 @@ describe('EventOnly', () => { accounts: [defaultAccountAddress], } = await setup(1)); await ensureAccountContractsPublished(wallet, [defaultAccountAddress]); - eventOnlyContract = await EventOnlyContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: eventOnlyContract } = await EventOnlyContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); it('emits and retrieves a private event for a contract with no notes', async () => { const value = Fr.random(); - const tx = await eventOnlyContract.methods.emit_event_for_msg_sender(value).send({ from: defaultAccountAddress }); + const { receipt: tx } = await eventOnlyContract.methods + .emit_event_for_msg_sender(value) + .send({ from: defaultAccountAddress }); const events = await wallet.getPrivateEvents(EventOnlyContract.events.TestEvent, { contractAddress: eventOnlyContract.address, diff --git a/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts b/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts index 78e3be77ee02..18240f671298 100644 --- a/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts +++ b/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts @@ -25,7 +25,7 @@ describe('e2e_expiration_timestamp', () => { aztecNode, accounts: [defaultAccountAddress], } = await setup()); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts index b93a429b2265..1323bd1ba00a 100644 --- a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts @@ -89,7 +89,7 @@ describe('e2e_fees account_init', () => { const [bobsInitialGas] = await t.getGasBalanceFn(bobsAddress); expect(bobsInitialGas).toEqual(mintAmount); - const tx = await bobsDeployMethod.send({ from: AztecAddress.ZERO, wait: { returnReceipt: true } }); + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, wait: { returnReceipt: true } }); expect(tx.transactionFee!).toBeGreaterThan(0n); await expect(t.getGasBalanceFn(bobsAddress)).resolves.toEqual([bobsInitialGas - tx.transactionFee!]); @@ -98,7 +98,7 @@ describe('e2e_fees account_init', () => { it('pays natively in the Fee Juice by bridging funds themselves', async () => { const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(bobsAddress); const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobsAddress, claim); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { returnReceipt: true }, @@ -118,7 +118,7 @@ describe('e2e_fees account_init', () => { const maxFeesPerGas = (await aztecNode.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PrivateFeePaymentMethod(bananaFPC.address, bobsAddress, wallet, gasSettings); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { returnReceipt: true }, @@ -147,7 +147,7 @@ describe('e2e_fees account_init', () => { const maxFeesPerGas = (await aztecNode.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PublicFeePaymentMethod(bananaFPC.address, bobsAddress, wallet, gasSettings); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, skipInstancePublication: false, fee: { paymentMethod }, @@ -180,7 +180,7 @@ describe('e2e_fees account_init', () => { await t.mintPrivateBananas(mintedBananas, bobsAddress); const [aliceBalanceBefore] = await t.getGasBalanceFn(aliceAddress); - const tx = await SchnorrAccountContractInterface.deployWithPublicKeys( + const { receipt: tx } = await SchnorrAccountContractInterface.deployWithPublicKeys( bobsPublicKeys, wallet, bobsSigningPubKey.x, diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 0c4c54c5032c..440f1949acf8 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -84,7 +84,7 @@ describe('e2e_fees failures', () => { const currentSequencerRewards = await t.getCoinbaseSequencerRewards(); - const txReceipt = await bananaCoin.methods + const { receipt: txReceipt } = await bananaCoin.methods .transfer_in_public(aliceAddress, sequencerAddress, outrageousPublicAmountAliceDoesNotHave, 0) .send({ from: aliceAddress, @@ -187,7 +187,7 @@ describe('e2e_fees failures', () => { ); // if we skip simulation, it includes the failed TX - const txReceipt = await bananaCoin.methods + const { receipt: txReceipt } = await bananaCoin.methods .transfer_in_public(aliceAddress, sequencerAddress, outrageousPublicAmountAliceDoesNotHave, 0) .send({ from: aliceAddress, @@ -285,7 +285,7 @@ describe('e2e_fees failures', () => { }), ).rejects.toThrow(); - const receipt = await bananaCoin.methods + const { receipt } = await bananaCoin.methods .mint_to_public(aliceAddress, 1n) // random operation .send({ from: aliceAddress, diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts index f90dfadf2e7c..16889aef501f 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts @@ -43,7 +43,9 @@ describe('e2e_fees Fee Juice payments', () => { describe('without initial funds', () => { beforeAll(async () => { - expect(await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress })).toEqual(0n); + expect( + (await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress })).result, + ).toEqual(0n); }); it('fails to simulate a tx', async () => { @@ -63,10 +65,12 @@ describe('e2e_fees Fee Juice payments', () => { it('claims bridged funds and pays with them on the same tx', async () => { const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(bobAddress); const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobAddress, claim); - const receipt = await feeJuiceContract.methods + const { receipt } = await feeJuiceContract.methods .check_balance(0n) .send({ from: bobAddress, fee: { gasSettings, paymentMethod } }); - const endBalance = await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress }); + const { result: endBalance } = await feeJuiceContract.methods + .balance_of_public(bobAddress) + .simulate({ from: bobAddress }); expect(endBalance).toBeGreaterThan(0n); expect(endBalance).toBeLessThan(claim.claimAmount); @@ -76,28 +80,30 @@ describe('e2e_fees Fee Juice payments', () => { describe('with initial funds', () => { it('sends tx with payment in Fee Juice with public calls', async () => { - const initialBalance = await feeJuiceContract.methods + const { result: initialBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const { transactionFee } = await bananaCoin.methods + const { + receipt: { transactionFee }, + } = await bananaCoin.methods .transfer_in_public(aliceAddress, bobAddress, 1n, 0n) .send({ fee: { gasSettings }, from: aliceAddress }); expect(transactionFee).toBeGreaterThan(0n); - const endBalance = await feeJuiceContract.methods + const { result: endBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); expect(endBalance).toBeLessThan(initialBalance); }); it('sends tx fee payment in Fee Juice with no public calls', async () => { - const initialBalance = await feeJuiceContract.methods + const { result: initialBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const { transactionFee } = await bananaCoin.methods - .transfer(bobAddress, 1n) - .send({ fee: { gasSettings }, from: aliceAddress }); + const { + receipt: { transactionFee }, + } = await bananaCoin.methods.transfer(bobAddress, 1n).send({ fee: { gasSettings }, from: aliceAddress }); expect(transactionFee).toBeGreaterThan(0n); - const endBalance = await feeJuiceContract.methods + const { result: endBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); expect(endBalance).toBeLessThan(initialBalance); diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts index 5ac0ec444727..83fe2d1b837b 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts @@ -26,7 +26,7 @@ describe('e2e_fees fee settings', () => { await t.setup(); ({ aliceAddress, wallet, gasSettings, cheatCodes, aztecNode } = t); - testContract = await TestContract.deploy(wallet).send({ from: aliceAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: aliceAddress })); gasSettings = { ...gasSettings, maxFeesPerGas: undefined }; }); diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index 22c893fad26b..5e69fe195ee0 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -157,11 +157,15 @@ export class FeesTest { /** Alice mints bananaCoin tokens privately to the target address and redeems them. */ async mintPrivateBananas(amount: bigint, address: AztecAddress) { - const balanceBefore = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceBefore } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); await mintTokensToPrivate(this.bananaCoin, this.aliceAddress, address, amount); - const balanceAfter = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceAfter } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); expect(balanceAfter).toEqual(balanceBefore + amount); } @@ -223,7 +227,7 @@ export class FeesTest { async applyDeployBananaToken() { this.logger.info('Applying deploy banana token setup'); - const bananaCoin = await BananaCoin.deploy(this.wallet, this.aliceAddress, 'BC', 'BC', 18n).send({ + const { contract: bananaCoin } = await BananaCoin.deploy(this.wallet, this.aliceAddress, 'BC', 'BC', 18n).send({ from: this.aliceAddress, }); this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`); @@ -244,7 +248,7 @@ export class FeesTest { expect((await this.wallet.getContractMetadata(feeJuiceContract.address)).isContractPublished).toBe(true); const bananaCoin = this.bananaCoin; - const bananaFPC = await FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin).send({ + const { contract: bananaFPC } = await FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin).send({ from: this.aliceAddress, }); diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index 417cdb495cd0..47fd12c85569 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -88,9 +88,13 @@ describe('e2e_fees gas_estimation', () => { paymentMethod?: FeePaymentMethod, ) => Promise.all( - [GasSettings.from({ ...gasSettings, ...limits }), gasSettings].map(gasSettings => - makeTransferRequest().send({ from: aliceAddress, fee: { gasSettings, paymentMethod } }), - ), + [GasSettings.from({ ...gasSettings, ...limits }), gasSettings].map(async gasSettings => { + const { receipt } = await makeTransferRequest().send({ + from: aliceAddress, + fee: { gasSettings, paymentMethod }, + }); + return receipt; + }), ); const logGasEstimate = (estimatedGas: Pick) => @@ -194,10 +198,10 @@ describe('e2e_fees gas_estimation', () => { }); logGasEstimate(estimatedGas); - const [withEstimate, withoutEstimate] = (await Promise.all([ + const [{ receipt: withEstimate }, { receipt: withoutEstimate }] = (await Promise.all([ deployMethod().send(deployOpts(estimatedGas)), deployMethod().send(deployOpts()), - ])) as unknown as DeployTxReceipt[]; + ])) as unknown as { receipt: DeployTxReceipt }[]; // Estimation should yield that teardown has no cost, so should send the tx with zero for teardown expect(withEstimate.transactionFee!).toEqual(withoutEstimate.transactionFee!); diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index ebb72b1f9b3e..9563d02815da 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -155,7 +155,7 @@ describe('e2e_fees private_payment', () => { * increase Alice's private banana balance by feeAmount by finalizing partial note */ const newlyMintedBananas = 10n; - const tx = await bananaCoin.methods.mint_to_private(aliceAddress, newlyMintedBananas).send({ + const { receipt: tx } = await bananaCoin.methods.mint_to_private(aliceAddress, newlyMintedBananas).send({ from: aliceAddress, fee: { paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), @@ -200,12 +200,14 @@ describe('e2e_fees private_payment', () => { * increase Alice's private banana balance by feeAmount by finalizing partial note */ const amountTransferredToPrivate = 1n; - const tx = await bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate).send({ - from: aliceAddress, - fee: { - paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_to_private(aliceAddress, amountTransferredToPrivate) + .send({ + from: aliceAddress, + fee: { + paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), + }, + }); const feeAmount = tx.transactionFee!; @@ -249,7 +251,7 @@ describe('e2e_fees private_payment', () => { * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note * increase Alice's private banana balance by feeAmount by finalizing partial note */ - const tx = await new BatchCall(wallet, [ + const { receipt: tx } = await new BatchCall(wallet, [ bananaCoin.methods.transfer(bobAddress, amountTransferredInPrivate), bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate), ]).send({ @@ -283,7 +285,7 @@ describe('e2e_fees private_payment', () => { it('rejects txs that dont have enough balance to cover gas costs', async () => { // deploy a copy of bananaFPC but don't fund it! - const bankruptFPC = await FPCContract.deploy(wallet, bananaCoin.address, aliceAddress).send({ + const { contract: bankruptFPC } = await FPCContract.deploy(wallet, bananaCoin.address, aliceAddress).send({ from: aliceAddress, }); diff --git a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts index 51e9109a90dc..b059938777db 100644 --- a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts @@ -59,12 +59,14 @@ describe('e2e_fees public_payment', () => { it('pays fees for tx that make public transfer', async () => { const bananasToSendToBob = 10n; - const tx = await bananaCoin.methods.transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0).send({ - from: aliceAddress, - fee: { - paymentMethod: new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0) + .send({ + from: aliceAddress, + fee: { + paymentMethod: new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), + }, + }); const feeAmount = tx.transactionFee!; diff --git a/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts index aac9aaa557ee..ec9726d9d129 100644 --- a/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts @@ -56,13 +56,15 @@ describe('e2e_fees sponsored_public_payment', () => { it('pays fees for tx that makes a public transfer', async () => { // docs:start:sponsored_fpc_simple const bananasToSendToBob = 10n; - const tx = await bananaCoin.methods.transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0).send({ - from: aliceAddress, - fee: { - gasSettings, - paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPC.address), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0) + .send({ + from: aliceAddress, + fee: { + gasSettings, + paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPC.address), + }, + }); // docs:end:sponsored_fpc_simple const feeAmount = tx.transactionFee!; diff --git a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts index d424b78be5f1..d0a9cedf6b12 100644 --- a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts +++ b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts @@ -51,9 +51,9 @@ describe('Kernelless simulation', () => { ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: liquidityToken } = await deployToken(wallet, adminAddress, 0n, logger)); - amm = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ + ({ contract: amm } = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ from: adminAddress, - }); + })); await liquidityToken.methods.set_minter(amm.address, true).send({ from: adminAddress }); @@ -75,15 +75,15 @@ describe('Kernelless simulation', () => { async function getWalletBalances(lpAddress: AztecAddress): Promise { return { - token0: await token0.methods.balance_of_private(lpAddress).simulate({ from: lpAddress }), - token1: await token1.methods.balance_of_private(lpAddress).simulate({ from: lpAddress }), + token0: (await token0.methods.balance_of_private(lpAddress).simulate({ from: lpAddress })).result, + token1: (await token1.methods.balance_of_private(lpAddress).simulate({ from: lpAddress })).result, }; } async function getAmmBalances(): Promise { return { - token0: await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), - token1: await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), + token0: (await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, + token1: (await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, }; } @@ -224,7 +224,7 @@ describe('Kernelless simulation', () => { const nonceForAuthwits = Fr.random(); - const amountOutMin = await amm.methods + const { result: amountOutMin } = await amm.methods .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) .simulate({ from: swapperAddress }); @@ -268,7 +268,9 @@ describe('Kernelless simulation', () => { let pendingNoteHashesContract: PendingNoteHashesContract; beforeAll(async () => { - pendingNoteHashesContract = await PendingNoteHashesContract.deploy(wallet).send({ from: adminAddress }); + ({ contract: pendingNoteHashesContract } = await PendingNoteHashesContract.deploy(wallet).send({ + from: adminAddress, + })); }); it('squashing produces same gas estimates as with-kernels path', async () => { @@ -306,7 +308,9 @@ describe('Kernelless simulation', () => { let pendingNoteHashesContract: PendingNoteHashesContract; beforeAll(async () => { - pendingNoteHashesContract = await PendingNoteHashesContract.deploy(wallet).send({ from: adminAddress }); + ({ contract: pendingNoteHashesContract } = await PendingNoteHashesContract.deploy(wallet).send({ + from: adminAddress, + })); }); it('verifies settled read requests against the note hash tree', async () => { diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts index 82ecf79ca366..9e56bb423b87 100644 --- a/yarn-project/end-to-end/src/e2e_keys.test.ts +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -45,7 +45,7 @@ describe('Keys', () => { initialFundedAccounts, } = await setup(1)); - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); secret = initialFundedAccounts[0].secret; }); @@ -125,7 +125,7 @@ describe('Keys', () => { const expectedOvskApp = await computeAppSecretKey(ovskM, testContract.address, 'ov'); // Get the ovsk_app via the test contract - const ovskAppBigInt = await testContract.methods + const { result: ovskAppBigInt } = await testContract.methods .get_ovsk_app(ovpkMHash) .simulate({ from: defaultAccountAddress }); const ovskApp = new Fr(ovskAppBigInt); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 5c143383ebb1..22c1964fe1f8 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -39,11 +39,13 @@ describe('e2e_lending_contract', () => { const deployContracts = async () => { logger.info(`Deploying price feed contract...`); - const priceFeedContract = await PriceFeedContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: priceFeedContract } = await PriceFeedContract.deploy(wallet).send({ + from: defaultAccountAddress, + }); logger.info(`Price feed deployed to ${priceFeedContract.address}`); logger.info(`Deploying collateral asset feed contract...`); - const collateralAsset = await TokenContract.deploy( + const { contract: collateralAsset } = await TokenContract.deploy( wallet, defaultAccountAddress, 'TokenName', @@ -53,13 +55,19 @@ describe('e2e_lending_contract', () => { logger.info(`Collateral asset deployed to ${collateralAsset.address}`); logger.info(`Deploying stable coin contract...`); - const stableCoin = await TokenContract.deploy(wallet, defaultAccountAddress, 'TokenName', 'TokenSymbol', 18).send({ + const { contract: stableCoin } = await TokenContract.deploy( + wallet, + defaultAccountAddress, + 'TokenName', + 'TokenSymbol', + 18, + ).send({ from: defaultAccountAddress, }); logger.info(`Stable coin asset deployed to ${stableCoin.address}`); logger.info(`Deploying L2 public contract...`); - const lendingContract = await LendingContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: lendingContract } = await LendingContract.deploy(wallet).send({ from: defaultAccountAddress }); logger.info(`CDP deployed at ${lendingContract.address}`); await collateralAsset.methods.set_minter(lendingContract.address, true).send({ from: defaultAccountAddress }); diff --git a/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts b/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts index a158c05540f7..bb9df36ca251 100644 --- a/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts +++ b/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts @@ -29,9 +29,9 @@ describe('e2e_mempool_limit', () => { throw new Error('Aztec node admin API must be available for this test'); } - token = await TokenContract.deploy(wallet, defaultAccountAddress, 'TEST', 'T', 18).send({ + ({ contract: token } = await TokenContract.deploy(wallet, defaultAccountAddress, 'TEST', 'T', 18).send({ from: defaultAccountAddress, - }); + })); await token.methods.mint_to_public(defaultAccountAddress, 10n ** 18n).send({ from: defaultAccountAddress }); }); diff --git a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts index 35dccd29e152..47ec290401f2 100644 --- a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts @@ -114,7 +114,7 @@ describe('e2e_multi_validator_node', () => { const sender = ownerAddress; logger.info(`Deploying contract from ${sender}`); - const tx = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, @@ -177,7 +177,7 @@ describe('e2e_multi_validator_node', () => { logger.info(`Deploying contract from ${sender}`); const deployer = new ContractDeployer(artifact, wallet); - const tx = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts b/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts index e31f66cfb61d..38d8ce4ee3f4 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts @@ -38,7 +38,7 @@ describe('e2e_multiple_blobs', () => { } = await setup(1)); aztecNodeAdmin = maybeAztecNodeAdmin!; - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -71,10 +71,10 @@ describe('e2e_multiple_blobs', () => { expect(provenTxs.length).toBe(TX_COUNT); // Send them simultaneously to be picked up by the sequencer - const txHashes = await Promise.all(provenTxs.map(tx => tx.send({ from: defaultAccountAddress, wait: NO_WAIT }))); + const sendResults = await Promise.all(provenTxs.map(tx => tx.send({ from: defaultAccountAddress, wait: NO_WAIT }))); // Wait for all to be mined const receipts = await Promise.all( - txHashes.map(txHash => { + sendResults.map(({ txHash }) => { return waitForTx(aztecNode, txHash); }), ); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts index 27449075dfe9..aab6d6fb59c4 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts @@ -15,8 +15,8 @@ describe('e2e_nested_contract manual', () => { }); beforeEach(async () => { - importerContract = await ImportTestContract.deploy(wallet).send({ from: defaultAccountAddress }); - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: importerContract } = await ImportTestContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(async () => { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts index ac9512f43421..0e19664a5662 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -19,8 +19,8 @@ describe('e2e_nested_contract manual_enqueue', () => { }); beforeEach(async () => { - parentContract = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress }); - childContract = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: parentContract } = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: childContract } = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(async () => { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts index 3b47116fcb96..a699ea8f1764 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts @@ -48,7 +48,7 @@ describe('e2e_nested_contract manual', () => { parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n), ]; - const tx = await new BatchCall(wallet, actions).send({ from: defaultAccountAddress }); + const { receipt: tx } = await new BatchCall(wallet, actions).send({ from: defaultAccountAddress }); const extendedLogs = ( await aztecNode.getPublicLogs({ fromBlock: tx.blockNumber!, diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts index 5632daf6c133..769db81c1ba0 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts @@ -65,9 +65,11 @@ export class NestedContractTest { async applyManual() { this.logger.info('Deploying parent and child contracts'); - const parentContract = await ParentContract.deploy(this.wallet).send({ from: this.defaultAccountAddress }); - const childContract = await ChildContract.deploy(this.wallet).send({ from: this.defaultAccountAddress }); - this.parentContract = parentContract; - this.childContract = childContract; + ({ contract: this.parentContract } = await ParentContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress, + })); + ({ contract: this.childContract } = await ChildContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress, + })); } } diff --git a/yarn-project/end-to-end/src/e2e_nft.test.ts b/yarn-project/end-to-end/src/e2e_nft.test.ts index 35a2e8c7310d..7e7f8da1079a 100644 --- a/yarn-project/end-to-end/src/e2e_nft.test.ts +++ b/yarn-project/end-to-end/src/e2e_nft.test.ts @@ -33,7 +33,9 @@ describe('NFT', () => { ({ teardown, wallet, accounts } = await setup(4)); [adminAddress, minterAddress, user1Address, user2Address] = accounts; - nftContract = await NFTContract.deploy(wallet, adminAddress, 'FROG', 'FRG').send({ from: adminAddress }); + ({ contract: nftContract } = await NFTContract.deploy(wallet, adminAddress, 'FROG', 'FRG').send({ + from: adminAddress, + })); }); afterAll(() => teardown()); @@ -41,13 +43,15 @@ describe('NFT', () => { // NOTE: This test is sequential and each test case depends on the previous one it('sets minter', async () => { await nftContract.methods.set_minter(minterAddress, true).send({ from: adminAddress }); - const isMinterAMinter = await nftContract.methods.is_minter(minterAddress).simulate({ from: minterAddress }); + const { result: isMinterAMinter } = await nftContract.methods + .is_minter(minterAddress) + .simulate({ from: minterAddress }); expect(isMinterAMinter).toBe(true); }); it('minter mints to a user', async () => { await nftContract.methods.mint(user1Address, TOKEN_ID).send({ from: minterAddress }); - const ownerAfterMint = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: ownerAfterMint } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(ownerAfterMint).toEqual(user1Address); }); @@ -57,7 +61,7 @@ describe('NFT', () => { const recipient = user2Address; await nftContract.methods.transfer_to_private(recipient, TOKEN_ID).send({ from: user1Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(publicOwnerAfter).toEqual(AztecAddress.ZERO); }); @@ -74,19 +78,21 @@ describe('NFT', () => { it('transfers to public', async () => { await nftContract.methods.transfer_to_public(user1Address, user2Address, TOKEN_ID, 0).send({ from: user1Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(publicOwnerAfter).toEqual(user2Address); }); it('transfers in public', async () => { await nftContract.methods.transfer_in_public(user2Address, user1Address, TOKEN_ID, 0).send({ from: user2Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user2Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user2Address }); expect(publicOwnerAfter).toEqual(user1Address); }); const getPrivateNfts = async (owner: AztecAddress) => { - const [nfts, pageLimitReached] = await nftContract.methods.get_private_nfts(owner, 0).simulate({ from: owner }); + const { + result: [nfts, pageLimitReached], + } = await nftContract.methods.get_private_nfts(owner, 0).simulate({ from: owner }); if (pageLimitReached) { throw new Error('Page limit reached and pagination not implemented in test'); } diff --git a/yarn-project/end-to-end/src/e2e_note_getter.test.ts b/yarn-project/end-to-end/src/e2e_note_getter.test.ts index d8192b71827c..c48c9f291a53 100644 --- a/yarn-project/end-to-end/src/e2e_note_getter.test.ts +++ b/yarn-project/end-to-end/src/e2e_note_getter.test.ts @@ -34,7 +34,7 @@ describe('e2e_note_getter', () => { let contract: NoteGetterContract; beforeAll(async () => { - contract = await NoteGetterContract.deploy(wallet).send({ from: defaultAddress }); + ({ contract } = await NoteGetterContract.deploy(wallet).send({ from: defaultAddress })); }); it('inserts notes from 0-9, then makes multiple queries specifying the total suite of comparators', async () => { @@ -47,7 +47,14 @@ describe('e2e_note_getter', () => { // We insert a note with value 5 twice to better test the comparators await contract.methods.insert_note(5).send({ from: defaultAddress }); - const [returnEq, returnNeq, returnLt, returnGt, returnLte, returnGte] = await Promise.all([ + const [ + { result: returnEq }, + { result: returnNeq }, + { result: returnLt }, + { result: returnGt }, + { result: returnLte }, + { result: returnGte }, + ] = await Promise.all([ contract.methods.read_note_values(defaultAddress, Comparator.EQ, 5).simulate({ from: defaultAddress }), contract.methods.read_note_values(defaultAddress, Comparator.NEQ, 5).simulate({ from: defaultAddress }), contract.methods.read_note_values(defaultAddress, Comparator.LT, 5).simulate({ from: defaultAddress }), @@ -78,7 +85,7 @@ describe('e2e_note_getter', () => { const makeTxHybrid = false; beforeAll(async () => { - contract = await TestContract.deploy(wallet).send({ from: defaultAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAddress })); owner = defaultAddress; }); @@ -93,10 +100,10 @@ describe('e2e_note_getter', () => { }); async function assertNoteIsReturned(storageSlot: number, expectedValue: number, activeOrNullified: boolean) { - const viewNotesResult = await contract.methods + const { result: viewNotesResult } = await contract.methods .call_view_notes(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); - const getNotesResult = await contract.methods + const { result: getNotesResult } = await contract.methods .call_get_notes(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); @@ -155,10 +162,10 @@ describe('e2e_note_getter', () => { await contract.methods.call_destroy_note(owner, storageSlot).send({ from: defaultAddress }); // We now fetch multiple notes, and get both the active and the nullified one. - const viewNotesManyResult = await contract.methods + const { result: viewNotesManyResult } = await contract.methods .call_view_notes_many(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); - const getNotesManyResult = await contract.methods + const { result: getNotesManyResult } = await contract.methods .call_get_notes_many(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); diff --git a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts index 624433699dd6..c045303bed85 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts @@ -33,8 +33,8 @@ describe('e2e_offchain_effect', () => { accounts: [defaultAccountAddress], aztecNode, } = await setup(1)); - contract1 = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress }); - contract2 = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: contract1 } = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: contract2 } = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -164,7 +164,9 @@ describe('e2e_offchain_effect', () => { .simulate({ from: defaultAccountAddress }); // Get the note value - const noteValue = await contract1.methods.get_note_value(owner).simulate({ from: defaultAccountAddress }); + const { result: noteValue } = await contract1.methods + .get_note_value(owner) + .simulate({ from: defaultAccountAddress }); expect(noteValue).toBe(value); }); }); diff --git a/yarn-project/end-to-end/src/e2e_orderbook.test.ts b/yarn-project/end-to-end/src/e2e_orderbook.test.ts index 1ac98d5ae223..22efa02edd17 100644 --- a/yarn-project/end-to-end/src/e2e_orderbook.test.ts +++ b/yarn-project/end-to-end/src/e2e_orderbook.test.ts @@ -52,9 +52,9 @@ describe('Orderbook', () => { ({ contract: token0 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); - orderbook = await OrderbookContract.deploy(wallet, token0.address, token1.address).send({ + ({ contract: orderbook } = await OrderbookContract.deploy(wallet, token0.address, token1.address).send({ from: adminAddress, - }); + })); // Mint tokens to maker and taker await mintTokensToPrivate(token0, adminAddress, makerAddress, bidAmount); @@ -95,7 +95,9 @@ describe('Orderbook', () => { orderId = orderCreatedEvents[0].event.order_id; // Get order from orderbook and verify details - const [order, isFulfilled] = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); + const { + result: [order, isFulfilled], + } = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); expect(order.bid_amount).toEqual(bidAmount); expect(order.ask_amount).toEqual(askAmount); expect(order.bid_token_is_zero).toBeTrue(); @@ -103,10 +105,12 @@ describe('Orderbook', () => { // At this point, bidAmount of token0 should be transferred to the public balance of the orderbook and maker // should have 0. - const orderbookBalances0 = await token0.methods + const { result: orderbookBalances0 } = await token0.methods .balance_of_public(orderbook.address) .simulate({ from: makerAddress }); - const makerBalances0 = await token0.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); + const { result: makerBalances0 } = await token0.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); expect(orderbookBalances0).toEqual(bidAmount); expect(makerBalances0).toEqual(0n); }); @@ -142,10 +146,18 @@ describe('Orderbook', () => { expect(orderFulfilledEvents[0].event.order_id).toEqual(orderId); // Verify balances after order fulfillment - const makerBalances0 = await token0.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); - const makerBalances1 = await token1.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); - const takerBalances0 = await token0.methods.balance_of_private(takerAddress).simulate({ from: takerAddress }); - const takerBalances1 = await token1.methods.balance_of_private(takerAddress).simulate({ from: takerAddress }); + const { result: makerBalances0 } = await token0.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); + const { result: makerBalances1 } = await token1.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); + const { result: takerBalances0 } = await token0.methods + .balance_of_private(takerAddress) + .simulate({ from: takerAddress }); + const { result: takerBalances1 } = await token1.methods + .balance_of_private(takerAddress) + .simulate({ from: takerAddress }); // Full maker token 0 balance should be transferred to taker and hence maker should have 0 expect(makerBalances0).toEqual(0n); @@ -157,7 +169,9 @@ describe('Orderbook', () => { expect(takerBalances1).toEqual(0n); // Verify that the order is fulfilled - const [_, isFulfilled] = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); + const { + result: [_, isFulfilled], + } = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); expect(isFulfilled).toBeTrue(); }); }); diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index fb310765f04e..56f8df05058a 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -56,8 +56,8 @@ describe('e2e_ordering', () => { let pubSetValueSelector: FunctionSelector; beforeEach(async () => { - parent = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress }); - child = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: parent } = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: child } = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress })); pubSetValueSelector = await child.methods.pub_set_value.selector(); }, TIMEOUT); diff --git a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts index a1964ef8f74b..786b2c9aa7a5 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts @@ -285,7 +285,7 @@ describe('e2e_p2p_add_rollup', () => { const aliceAddress = aliceAccountManager.address; - const testContract = await TestContract.deploy(wallet).send({ from: aliceAddress }); + const { contract: testContract } = await TestContract.deploy(wallet).send({ from: aliceAddress }); const [secret, secretHash] = await generateClaimSecret(); @@ -304,7 +304,7 @@ describe('e2e_p2p_add_rollup', () => { // We poll isL1ToL2MessageSynced endpoint until the message is available await retryUntil(async () => await node.isL1ToL2MessageSynced(msgHash), 'message sync', 10); - const receipt = await testContract.methods + const { receipt } = await testContract.methods .create_l2_to_l1_message_arbitrary_recipient_private(contentOutFromRollup, ethRecipient) .send({ from: aliceAddress }); diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index ed470053f9b0..c967073a0ac6 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -322,8 +322,9 @@ export class P2PNetworkTest { throw new Error('Call setupAccount before deploying spam contract'); } - const spamContract = await SpamContract.deploy(this.wallet).send({ from: this.defaultAccountAddress! }); - this.spamContract = spamContract; + ({ contract: this.spamContract } = await SpamContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress!, + })); } async removeInitialNode() { diff --git a/yarn-project/end-to-end/src/e2e_p2p/shared.ts b/yarn-project/end-to-end/src/e2e_p2p/shared.ts index 5b3450673e24..a74488fef1c0 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/shared.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/shared.ts @@ -41,7 +41,7 @@ export const submitComplexTxsTo = async ( const spamCount = 15; for (let i = 0; i < numTxs; i++) { const method = spamContract.methods.spam(seed + BigInt(i * spamCount), spamCount, !!opts.callPublic); - const txHash = await method.send({ from, wait: NO_WAIT }); + const { txHash } = await method.send({ from, wait: NO_WAIT }); logger.info(`Tx sent with hash ${txHash.toString()}`); txs.push(txHash); } diff --git a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts index 3b01932f8edf..9982087e83dd 100644 --- a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts @@ -75,7 +75,7 @@ describe('e2e_pending_note_hashes_contract', () => { const deployContract = async () => { logger.debug(`Deploying L2 contract...`); - contract = await PendingNoteHashesContract.deploy(wallet).send({ from: owner }); + ({ contract } = await PendingNoteHashesContract.deploy(wallet).send({ from: owner })); logger.info(`L2 contract deployed at ${contract.address}`); return contract; }; diff --git a/yarn-project/end-to-end/src/e2e_phase_check.test.ts b/yarn-project/end-to-end/src/e2e_phase_check.test.ts index 03ba19047c61..581611cbbd71 100644 --- a/yarn-project/end-to-end/src/e2e_phase_check.test.ts +++ b/yarn-project/end-to-end/src/e2e_phase_check.test.ts @@ -35,7 +35,7 @@ describe('Phase check', () => { accounts: [defaultAccountAddress], } = await setup(1, { genesisPublicData: [genesisBalanceEntry] })); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); sponsoredFPC = await SponsoredFPCNoEndSetupContract.deploy(wallet).register({ contractAddressSalt: new Fr(SPONSORED_FPC_SALT), }); diff --git a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts index 86809063a08c..6c94f188985a 100644 --- a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts @@ -25,7 +25,7 @@ describe('e2e_voting_contract', () => { accounts: [owner], } = await setup(1)); - votingContract = await PrivateVotingContract.deploy(wallet, owner).send({ from: owner }); + ({ contract: votingContract } = await PrivateVotingContract.deploy(wallet, owner).send({ from: owner })); logger.info(`Counter contract deployed at ${votingContract.address}`); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/client.test.ts b/yarn-project/end-to-end/src/e2e_prover/client.test.ts index 7ff197d01a71..aef6fcd86af6 100644 --- a/yarn-project/end-to-end/src/e2e_prover/client.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/client.test.ts @@ -71,12 +71,14 @@ describe('client_prover', () => { ); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods + .balance_of_private(sender) + .simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/e2e_prover/full.test.ts b/yarn-project/end-to-end/src/e2e_prover/full.test.ts index eb3dc3e82529..7d70a5b443d2 100644 --- a/yarn-project/end-to-end/src/e2e_prover/full.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/full.test.ts @@ -87,12 +87,14 @@ describe('full_prover', () => { ); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods + .balance_of_private(sender) + .simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); @@ -180,12 +182,12 @@ describe('full_prover', () => { return; } // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); const privateSendAmount = privateBalance / 20n; expect(privateSendAmount).toBeGreaterThan(0n); const firstPrivateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 50a8db649dd6..2c9b8142105c 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -53,7 +53,7 @@ describe('e2e_pruned_blocks', () => { aztecProofSubmissionEpochs: 1024, // effectively do not reorg })); - token = await TokenContract.deploy(wallet, admin, 'TEST', '$TST', 18).send({ from: admin }); + ({ contract: token } = await TokenContract.deploy(wallet, admin, 'TEST', '$TST', 18).send({ from: admin })); logger.info(`L2 token contract deployed at ${token.address}`); }); @@ -76,7 +76,9 @@ describe('e2e_pruned_blocks', () => { // mint transaction that the node will drop the block corresponding to the first mint, resulting in errors if PXE // tried to access any historical information related to it (which it shouldn't). - const firstMintReceipt = await token.methods.mint_to_private(sender, MINT_AMOUNT / 2n).send({ from: admin }); + const { receipt: firstMintReceipt } = await token.methods + .mint_to_private(sender, MINT_AMOUNT / 2n) + .send({ from: admin }); const firstMintTxEffect = await aztecNode.getTxEffect(firstMintReceipt.txHash); // mint_to_private should create just one new note with the minted amount diff --git a/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts b/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts index a5ed696875eb..daed07568294 100644 --- a/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts @@ -60,7 +60,7 @@ describe(`deploys and transfers a private only token`, () => { ); const tokenInstance = await tokenDeployment.getInstance(); await wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey); - const token = await tokenDeployment.send({ + const { contract: token } = await tokenDeployment.send({ from: deployerAddress, universalDeploy: true, skipInstancePublication: true, @@ -76,8 +76,12 @@ describe(`deploys and transfers a private only token`, () => { logger.info(`Transfer completed`); - const balanceDeployer = await token.methods.get_balance(deployerAddress).simulate({ from: deployerAddress }); - const balanceRecipient = await token.methods.get_balance(recipientAddress).simulate({ from: recipientAddress }); + const { result: balanceDeployer } = await token.methods + .get_balance(deployerAddress) + .simulate({ from: deployerAddress }); + const { result: balanceRecipient } = await token.methods + .get_balance(recipientAddress) + .simulate({ from: recipientAddress }); logger.info(`Deployer balance: ${balanceDeployer}, Recipient balance: ${balanceRecipient}`); diff --git a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts index c84eeadc62b0..b30a5cefdfe8 100644 --- a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts +++ b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts @@ -21,7 +21,7 @@ describe('e2e scope isolation', () => { ({ teardown, wallet, accounts } = await setup(3)); [alice, bob, charlie] = accounts; - contract = await ScopeTestContract.deploy(wallet).send({ from: alice }); + ({ contract } = await ScopeTestContract.deploy(wallet).send({ from: alice })); // Alice and bob create a note for themselves (used by multiple tests below) await contract.methods.create_note(alice, Number(ALICE_NOTE_VALUE)).send({ from: alice }); diff --git a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts index b223bc086f1f..8795dc257a6f 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts @@ -112,7 +112,7 @@ describe('e2e_gov_proposal', () => { // Deploy a test contract to send msgs via the outbox, since this increases // gas cost of a proposal, which has triggered oog errors in the past. - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); logger.warn(`Deployed test contract at ${testContract.address}`); await cheatCodes.rollup.advanceToEpoch(EpochNumber(4)); @@ -169,11 +169,12 @@ describe('e2e_gov_proposal', () => { // since we wait for the txs to be mined, and do so `roundDuration` times. // Simultaneously, we should be voting for the proposal in every slot. for (let i = 0; i < roundDuration; i++) { - const txHashes = await timesAsync(TXS_PER_BLOCK, () => - testContract.methods + const txHashes = await timesAsync(TXS_PER_BLOCK, async () => { + const { txHash } = await testContract.methods .create_l2_to_l1_message_arbitrary_recipient_private(Fr.random(), EthAddress.random()) - .send({ from: defaultAccountAddress, wait: NO_WAIT }), - ); + .send({ from: defaultAccountAddress, wait: NO_WAIT }); + return txHash; + }); await Promise.all( txHashes.map((hash, j) => { logger.info(`Waiting for tx ${i}-${j}: ${hash} to be mined`); diff --git a/yarn-project/end-to-end/src/e2e_simple.test.ts b/yarn-project/end-to-end/src/e2e_simple.test.ts index 0fcedb023202..1dbfe5861776 100644 --- a/yarn-project/end-to-end/src/e2e_simple.test.ts +++ b/yarn-project/end-to-end/src/e2e_simple.test.ts @@ -72,7 +72,7 @@ describe('e2e_simple', () => { const deployer = new ContractDeployer(artifact, wallet); const sender = ownerAddress; - const txReceipt = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: txReceipt } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 104c9f18a72e..bd1ccd15845b 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -33,7 +33,7 @@ describe('e2e_state_vars', () => { wallet, accounts: [defaultAccountAddress], } = await setup(1)); - contract = await StateVarsContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await StateVarsContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -51,7 +51,7 @@ describe('e2e_state_vars', () => { await contract.methods.initialize_public_immutable(1).send({ from: defaultAccountAddress }); - const read = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); + const { result: read } = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); expect(read).toEqual({ account: defaultAccountAddress, value: read.value }); }); @@ -131,7 +131,7 @@ describe('e2e_state_vars', () => { .simulate({ from: defaultAccountAddress }), ).toEqual(false); // Send the transaction and wait for it to be mined (wait function throws if the tx is not mined) - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .initialize_private(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -168,9 +168,9 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE); }); @@ -180,10 +180,10 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const noteBefore = await contract.methods + const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -193,7 +193,7 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const noteAfter = await contract.methods + const { result: noteAfter } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); @@ -206,7 +206,7 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS + 2n, VALUE + 1n) .send({ from: defaultAccountAddress }); @@ -216,9 +216,9 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE + 1n); }); @@ -228,10 +228,12 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const noteBefore = await contract.methods + const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - const txReceipt = await contract.methods.increase_private_value().send({ from: defaultAccountAddress }); + const { receipt: txReceipt } = await contract.methods + .increase_private_value() + .send({ from: defaultAccountAddress }); const txEffects = await aztecNode.getTxEffect(txReceipt.txHash); @@ -239,9 +241,9 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(noteBefore.value + 1n); }); }); @@ -260,7 +262,7 @@ describe('e2e_state_vars', () => { expect( await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), ).toEqual(false); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .initialize_private_immutable(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -290,7 +292,9 @@ describe('e2e_state_vars', () => { expect( await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), ).toEqual(true); - const { value } = await contract.methods + const { + result: { value }, + } = await contract.methods .view_private_immutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE); @@ -310,9 +314,9 @@ describe('e2e_state_vars', () => { beforeAll(async () => { // We use the auth contract here because has a nice, clear, simple implementation of Delayed Public Mutable - authContract = await AuthContract.deploy(wallet, defaultAccountAddress).send({ + ({ contract: authContract } = await AuthContract.deploy(wallet, defaultAccountAddress).send({ from: defaultAccountAddress, - }); + })); if (aztecSlotDuration !== 36) { throw new Error( diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 9576d39f7845..1e2cc3117f1b 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -21,8 +21,8 @@ describe('e2e_static_calls', () => { accounts: [owner], } = await setup()); sender = owner; - parentContract = await StaticParentContract.deploy(wallet).send({ from: owner }); - childContract = await StaticChildContract.deploy(wallet).send({ from: owner }); + ({ contract: parentContract } = await StaticParentContract.deploy(wallet).send({ from: owner })); + ({ contract: childContract } = await StaticChildContract.deploy(wallet).send({ from: owner })); // We create a note in the set, such that later reads doesn't fail due to get_notes returning 0 notes await childContract.methods.private_set_value(42n, owner, sender).send({ from: owner }); diff --git a/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts b/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts index cdee4c492b29..70c6ce8b0b8f 100644 --- a/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts +++ b/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts @@ -13,7 +13,7 @@ describe('Storage proof', () => { beforeAll(async () => { ctx = await setup(1); - contract = await StorageProofTestContract.deploy(ctx.wallet).send({ from: ctx.accounts[0] }); + ({ contract } = await StorageProofTestContract.deploy(ctx.wallet).send({ from: ctx.accounts[0] })); }); afterAll(async () => { @@ -26,7 +26,7 @@ describe('Storage proof', () => { ctx.logger.info('Sending storage proof TX...'); - const receipt = await contract.methods + const { receipt } = await contract.methods .storage_proof(ethAddress, slotKey, slotContents, root) .with({ capsules }) .send({ from: ctx.accounts[0] }); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index 76a54cd05832..c93560f43064 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -203,7 +203,7 @@ class TestVariant { ); this.contractAddresses.push(accountManager.address); const deployMethod = await accountManager.getDeployMethod(); - const txHash = await deployMethod.send({ + const { txHash } = await deployMethod.send({ from: deployAccount, skipClassPublication: true, skipInstancePublication: true, @@ -218,7 +218,9 @@ class TestVariant { for (let i = 0; i < this.txCount; i++) { const recipient = this.accounts[(i + 1) % this.txCount]; const tk = TokenContract.at(this.token.address, this.wallet); - txHashes.push(await tk.methods.transfer(recipient, 1n).send({ from: this.accounts[i], wait: NO_WAIT })); + txHashes.push( + (await tk.methods.transfer(recipient, 1n).send({ from: this.accounts[i], wait: NO_WAIT })).txHash, + ); } return txHashes; } else if (this.txComplexity == TxComplexity.PublicTransfer) { @@ -229,7 +231,7 @@ class TestVariant { const recipient = this.accounts[(i + 1) % this.txCount]; const tk = TokenContract.at(this.token.address, this.wallet); txHashes.push( - await tk.methods.transfer_in_public(sender, recipient, 1n, 0).send({ from: sender, wait: NO_WAIT }), + (await tk.methods.transfer_in_public(sender, recipient, 1n, 0).send({ from: sender, wait: NO_WAIT })).txHash, ); } return txHashes; @@ -247,7 +249,7 @@ class TestVariant { ]); this.seed += 100n; - txHashes.push(await batch.send({ from: this.accounts[0], wait: NO_WAIT })); + txHashes.push((await batch.send({ from: this.accounts[0], wait: NO_WAIT })).txHash); } return txHashes; } else { @@ -340,10 +342,16 @@ describe('e2e_synching', () => { variant.setWallet(wallet); // Deploy a token, such that we could use it - const token = await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + const { contract: token } = await TokenContract.deploy( + wallet, + defaultAccountAddress, + 'TestToken', + 'TST', + 18n, + ).send({ from: defaultAccountAddress, }); - const spam = await SpamContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: spam } = await SpamContract.deploy(wallet).send({ from: defaultAccountAddress }); variant.setToken(token); variant.setSpam(spam); @@ -542,15 +550,21 @@ describe('e2e_synching', () => { const defaultAccountAddress = (await variant.deployAccounts(opts.initialFundedAccounts!.slice(0, 1)))[0]; contracts.push( - await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ - from: defaultAccountAddress, - }), + ( + await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + from: defaultAccountAddress, + }) + ).contract, + ); + contracts.push( + (await SchnorrHardcodedAccountContract.deploy(wallet).send({ from: defaultAccountAddress })).contract, ); - contracts.push(await SchnorrHardcodedAccountContract.deploy(wallet).send({ from: defaultAccountAddress })); contracts.push( - await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ - from: defaultAccountAddress, - }), + ( + await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + from: defaultAccountAddress, + }) + ).contract, ); await watcher.stop(); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts index 1c9b670771ca..b9760a983627 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts @@ -27,7 +27,7 @@ describe('e2e_token_contract burn', () => { describe('public', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_public(adminAddress, amount, 0).send({ from: adminAddress }); @@ -36,7 +36,7 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -61,7 +61,9 @@ describe('e2e_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -70,7 +72,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); const authwitNonce = 1; @@ -82,7 +86,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -91,7 +97,9 @@ describe('e2e_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -109,7 +117,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -132,7 +142,9 @@ describe('e2e_token_contract burn', () => { describe('private', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_private(adminAddress, amount, 0).send({ from: adminAddress }); @@ -140,7 +152,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -160,7 +174,9 @@ describe('e2e_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -169,7 +185,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -180,7 +198,9 @@ describe('e2e_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -195,7 +215,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -214,7 +236,9 @@ describe('e2e_token_contract burn', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index a986ab6799ba..90bc22162730 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -23,7 +23,9 @@ describe('e2e_token_contract private transfer recursion', () => { // itself to consume them all (since it retrieves 2 notes on the first pass and 8 in each subsequent pass). const totalNotes = 16; const totalBalance = await mintNotes(wallet, adminAddress, adminAddress, asset, Array(totalNotes).fill(10n)); - const txReceipt = await asset.methods.transfer(account1Address, totalBalance).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods + .transfer(account1Address, totalBalance) + .send({ from: adminAddress }); const txEffects = await node.getTxEffect(txReceipt.txHash); // We should have nullified all notes, plus an extra nullifier for the transaction and one for the event commitment. @@ -59,7 +61,7 @@ describe('e2e_token_contract private transfer recursion', () => { const totalBalance = await mintNotes(wallet, adminAddress, adminAddress, asset, noteAmounts); const toSend = totalBalance - expectedChange; - const txReceipt = await asset.methods.transfer(account1Address, toSend).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods.transfer(account1Address, toSend).send({ from: adminAddress }); const txEffects = await node.getTxEffect(txReceipt.txHash); // We should have nullified all notes, plus an extra nullifier for the transaction and one for the event commitment. @@ -93,7 +95,9 @@ describe('e2e_token_contract private transfer recursion', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts index a564a5610fed..ef69b7556739 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts @@ -22,24 +22,30 @@ describe('e2e_token_contract reading constants', () => { }); it('check name private', async () => { - const name = readFieldCompressedString(await t.asset.methods.private_get_name().simulate({ from: t.adminAddress })); + const name = readFieldCompressedString( + (await t.asset.methods.private_get_name().simulate({ from: t.adminAddress })).result, + ); expect(name).toBe(TOKEN_NAME); }); it('check name public', async () => { - const name = readFieldCompressedString(await t.asset.methods.public_get_name().simulate({ from: t.adminAddress })); + const name = readFieldCompressedString( + (await t.asset.methods.public_get_name().simulate({ from: t.adminAddress })).result, + ); expect(name).toBe(TOKEN_NAME); }); it('check symbol private', async () => { const sym = readFieldCompressedString( - await t.asset.methods.private_get_symbol().simulate({ from: t.adminAddress }), + (await t.asset.methods.private_get_symbol().simulate({ from: t.adminAddress })).result, ); expect(sym).toBe(TOKEN_SYMBOL); }); it('check symbol public', async () => { - const sym = readFieldCompressedString(await t.asset.methods.public_get_symbol().simulate({ from: t.adminAddress })); + const sym = readFieldCompressedString( + (await t.asset.methods.public_get_symbol().simulate({ from: t.adminAddress })).result, + ); expect(sym).toBe(TOKEN_SYMBOL); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index ebfde8461643..f6462de40520 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -81,24 +81,28 @@ export class TokenContractTest { await publicDeployAccounts(this.wallet, [this.adminAddress, this.account1Address]); this.logger.verbose(`Deploying TokenContract...`); - this.asset = await TokenContract.deploy( + ({ contract: this.asset } = await TokenContract.deploy( this.wallet, this.adminAddress, TokenContractTest.TOKEN_NAME, TokenContractTest.TOKEN_SYMBOL, TokenContractTest.TOKEN_DECIMALS, - ).send({ from: this.adminAddress }); + ).send({ from: this.adminAddress })); this.logger.verbose(`Token deployed to ${this.asset.address}`); this.logger.verbose(`Deploying bad account...`); - this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.badAccount.address}.`); // Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender // (so their notes are in scope), but msg_sender in the target must differ from the note owner // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender. this.logger.verbose(`Deploying generic proxy...`); - this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`); this.tokenSim = new TokenSimulator(this.asset, this.wallet, this.adminAddress, this.logger, [ @@ -140,7 +144,9 @@ export class TokenContractTest { await asset.methods.mint_to_public(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPublic(adminAddress, amount); - const publicBalance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: publicBalance } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(adminAddress)); @@ -148,11 +154,13 @@ export class TokenContractTest { await mintTokensToPrivate(asset, adminAddress, adminAddress, amount); tokenSim.mintPrivate(adminAddress, amount); - const privateBalance = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: privateBalance } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(adminAddress)); - const totalSupply = await asset.methods.total_supply().simulate({ from: adminAddress }); + const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: adminAddress }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts index d801532f8478..564707a74c77 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts @@ -24,11 +24,11 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); - const txReceipt = await asset.methods.transfer(account1Address, amount).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods.transfer(account1Address, amount).send({ from: adminAddress }); tokenSim.transferPrivate(adminAddress, account1Address, amount); const events = await wallet.getPrivateEvents(TokenContract.events.Transfer, { @@ -53,7 +53,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer less than balance to non-deployed account', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); @@ -68,7 +68,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer to self', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer(adminAddress, amount).send({ from: adminAddress }); @@ -77,7 +77,9 @@ describe('e2e_token_contract transfer private', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.transfer(account1Address, amount).simulate({ from: adminAddress })).rejects.toThrow( diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts index 6177d2845832..3ea047560895 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts @@ -25,7 +25,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -45,7 +45,9 @@ describe('e2e_token_contract transfer private', () => { describe('failure cases', () => { it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -61,8 +63,12 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_private(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -88,7 +94,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -107,7 +115,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -129,7 +139,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other, cancelled authwit', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts index 689a99d61bc9..3bb951b28192 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts @@ -43,7 +43,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_in_public(adminAddress, account1Address, amount, 0).send({ from: adminAddress }); @@ -52,7 +52,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer to self', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_in_public(adminAddress, adminAddress, amount, 0).send({ from: adminAddress }); @@ -61,7 +61,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -90,7 +90,7 @@ describe('e2e_token_contract transfer public', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -101,7 +101,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 - 1n; const authwitNonce = 1; await expect( @@ -114,7 +114,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -125,8 +125,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -152,8 +154,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -178,8 +182,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -203,7 +209,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, cancelled authwit', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -232,7 +238,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, cancelled authwit, flow 2', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts index 051ba9370fce..2688fea57172 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts @@ -22,7 +22,7 @@ describe('e2e_token_contract transfer_to_private', () => { }); it('to self', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); @@ -34,7 +34,7 @@ describe('e2e_token_contract transfer_to_private', () => { }); it('to someone else', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); @@ -47,7 +47,9 @@ describe('e2e_token_contract transfer_to_private', () => { describe('failure cases', () => { it('to self (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts index 5394130c2429..1ff6f31ef53b 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts @@ -26,7 +26,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of self', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv / 2n; expect(amount).toBeGreaterThan(0n); @@ -36,7 +38,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -56,7 +60,9 @@ describe('e2e_token_contract transfer_to_public', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -66,7 +72,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -78,7 +86,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -93,7 +103,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts index 6dad123c594a..59f38bfeedea 100644 --- a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts @@ -106,7 +106,9 @@ export class FullProverTest { await publicDeployAccounts(this.wallet, this.accounts.slice(0, 2)); this.logger.info('Applying base setup: deploying token contract'); - const { contract: asset, instance } = await TokenContract.deploy( + const { + receipt: { contract: asset, instance }, + } = await TokenContract.deploy( this.wallet, this.accounts[0], FullProverTest.TOKEN_NAME, @@ -121,7 +123,7 @@ export class FullProverTest { this.tokenSim = new TokenSimulator(this.fakeProofsAsset, this.wallet, this.accounts[0], this.logger, this.accounts); - expect(await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).toBe( + expect((await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).result).toBe( this.accounts[0].toBigInt(), ); } @@ -310,16 +312,20 @@ export class FullProverTest { } = this; tokenSim.mintPublic(address, publicAmount); - const publicBalance = await fakeProofsAsset.methods.balance_of_public(address).simulate({ from: address }); + const { result: publicBalance } = await fakeProofsAsset.methods + .balance_of_public(address) + .simulate({ from: address }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address)); tokenSim.mintPrivate(address, publicAmount); - const privateBalance = await fakeProofsAsset.methods.balance_of_private(address).simulate({ from: address }); + const { result: privateBalance } = await fakeProofsAsset.methods + .balance_of_private(address) + .simulate({ from: address }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address)); - const totalSupply = await fakeProofsAsset.methods.total_supply().simulate({ from: address }); + const { result: totalSupply } = await fakeProofsAsset.methods.total_supply().simulate({ from: address }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); } diff --git a/yarn-project/end-to-end/src/fixtures/setup.ts b/yarn-project/end-to-end/src/fixtures/setup.ts index 6def0cbc67f6..76b3ab8b2574 100644 --- a/yarn-project/end-to-end/src/fixtures/setup.ts +++ b/yarn-project/end-to-end/src/fixtures/setup.ts @@ -754,7 +754,9 @@ export function getBalancesFn( ): (...addresses: (AztecAddress | { address: AztecAddress })[]) => Promise { const balances = async (...addressLikes: (AztecAddress | { address: AztecAddress })[]) => { const addresses = addressLikes.map(addressLike => ('address' in addressLike ? addressLike.address : addressLike)); - const b = await Promise.all(addresses.map(address => method(address).simulate({ from: address }))); + const b = await Promise.all( + addresses.map(async address => (await method(address).simulate({ from: address })).result), + ); const debugString = `${symbol} balances: ${addresses.map((address, i) => `${address}: ${b[i]}`).join(', ')}`; logger.verbose(debugString); return b; @@ -872,7 +874,7 @@ export async function publicDeployAccounts( const batch = new BatchCall(wallet, calls); - const txReceipt = await batch.send({ from: accountsToDeploy[0] }); + const { receipt: txReceipt } = await batch.send({ from: accountsToDeploy[0] }); if (waitUntilProven) { if (!node) { throw new Error('Need to provide an AztecNode to wait for proven.'); diff --git a/yarn-project/end-to-end/src/fixtures/token_utils.ts b/yarn-project/end-to-end/src/fixtures/token_utils.ts index 47e9c67e78ba..ede82e6966c2 100644 --- a/yarn-project/end-to-end/src/fixtures/token_utils.ts +++ b/yarn-project/end-to-end/src/fixtures/token_utils.ts @@ -6,7 +6,9 @@ import { TokenContract } from '@aztec/noir-contracts.js/Token'; export async function deployToken(wallet: Wallet, admin: AztecAddress, initialAdminBalance: bigint, logger: Logger) { logger.info(`Deploying Token contract...`); - const { contract, instance } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({ + const { + receipt: { contract, instance }, + } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({ from: admin, wait: { returnReceipt: true }, }); @@ -38,7 +40,7 @@ export async function expectTokenBalance( ) { // Then check the balance const contractWithWallet = TokenContract.at(token.address, wallet); - const balance = await contractWithWallet.methods.balance_of_private(owner).simulate({ from: owner }); + const { result: balance } = await contractWithWallet.methods.balance_of_private(owner).simulate({ from: owner }); logger.info(`Account ${owner} balance: ${balance}`); expect(balance).toBe(expectedBalance); } diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 9eaf2473b961..b9f1893bf500 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -73,7 +73,7 @@ describe('guides/writing_an_account_contract', () => { const address = account.address; logger.info(`Deployed account contract at ${address}`); - const token = await TokenContract.deploy(wallet, fundedAccount, 'TokenName', 'TokenSymbol', 18).send({ + const { contract: token } = await TokenContract.deploy(wallet, fundedAccount, 'TokenName', 'TokenSymbol', 18).send({ from: fundedAccount, }); logger.info(`Deployed token contract at ${token.address}`); @@ -81,7 +81,7 @@ describe('guides/writing_an_account_contract', () => { const mintAmount = 50n; await token.methods.mint_to_private(address, mintAmount).send({ from: fundedAccount }); - const balance = await token.methods.balance_of_private(address).simulate({ from: address }); + const { result: balance } = await token.methods.balance_of_private(address).simulate({ from: address }); logger.info(`Balance of wallet is now ${balance}`); expect(balance).toEqual(50n); diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index be4564b2a67d..c1b2b39780eb 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -73,22 +73,26 @@ export async function deployAndInitializeTokenAndBridgeContracts( }); // deploy l2 token - const token = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ from: owner }); + const { contract: token } = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ + from: owner, + }); // deploy l2 token bridge and attach to the portal - const bridge = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({ from: owner }); + const { contract: bridge } = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({ + from: owner, + }); - if ((await token.methods.get_admin().simulate({ from: owner })) !== owner.toBigInt()) { + if ((await token.methods.get_admin().simulate({ from: owner })).result !== owner.toBigInt()) { throw new Error(`Token admin is not ${owner}`); } - if (!(await bridge.methods.get_config().simulate({ from: owner })).token.equals(token.address)) { + if (!(await bridge.methods.get_config().simulate({ from: owner })).result.token.equals(token.address)) { throw new Error(`Bridge token is not ${token.address}`); } // make the bridge a minter on the token: await token.methods.set_minter(bridge.address, true).send({ from: owner }); - if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })) === 1n) { + if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })).result === 1n) { throw new Error(`Bridge is not a minter`); } @@ -269,7 +273,7 @@ export class CrossChainTestHarness { authwitNonce: Fr = Fr.ZERO, authWitness: AuthWitness, ): Promise { - const withdrawReceipt = await this.l2Bridge.methods + const { receipt: withdrawReceipt } = await this.l2Bridge.methods .exit_to_l1_private(this.l2Token.address, this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ authWitnesses: [authWitness], from: this.ownerAddress }); @@ -277,7 +281,7 @@ export class CrossChainTestHarness { } async withdrawPublicFromAztecToL1(withdrawAmount: bigint, authwitNonce: Fr = Fr.ZERO): Promise { - const withdrawReceipt = await this.l2Bridge.methods + const { receipt: withdrawReceipt } = await this.l2Bridge.methods .exit_to_l1_public(this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ from: this.ownerAddress }); @@ -285,7 +289,7 @@ export class CrossChainTestHarness { } async getL2PrivateBalanceOf(owner: AztecAddress) { - return await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner }); + return (await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner })).result; } async expectPrivateBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { @@ -295,7 +299,7 @@ export class CrossChainTestHarness { } async getL2PublicBalanceOf(owner: AztecAddress) { - return await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress }); + return (await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress })).result; } async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { diff --git a/yarn-project/end-to-end/src/shared/submit-transactions.ts b/yarn-project/end-to-end/src/shared/submit-transactions.ts index 5bb2a7316a8c..b8b6c5c1a11e 100644 --- a/yarn-project/end-to-end/src/shared/submit-transactions.ts +++ b/yarn-project/end-to-end/src/shared/submit-transactions.ts @@ -19,7 +19,7 @@ export const submitTxsTo = async ( times(numTxs, async () => { const accountManager = await wallet.createSchnorrAccount(Fr.random(), Fr.random(), GrumpkinScalar.random()); const deployMethod = await accountManager.getDeployMethod(); - const txHash = await deployMethod.send({ from: submitter, wait: NO_WAIT }); + const { txHash } = await deployMethod.send({ from: submitter, wait: NO_WAIT }); logger.info(`Tx sent with hash ${txHash}`); const receipt: TxReceipt = await wallet.getTxReceipt(txHash); diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 7f25e1a2b4eb..6795cc1bf180 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -130,7 +130,9 @@ export const uniswapL1L2TestSuite = ( client: l1Client, }); // deploy l2 uniswap contract and attach to portal - uniswapL2Contract = await UniswapContract.deploy(wallet, uniswapPortalAddress).send({ from: ownerAddress }); + ({ contract: uniswapL2Contract } = await UniswapContract.deploy(wallet, uniswapPortalAddress).send({ + from: ownerAddress, + })); const registryAddress = (await aztecNode.getNodeInfo()).l1ContractAddresses.registryAddress; @@ -195,7 +197,7 @@ export const uniswapL1L2TestSuite = ( logger.info('Withdrawing weth to L1 and sending message to swap to dai'); const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = await generateClaimSecret(); - const l2UniswapInteractionReceipt = await uniswapL2Contract.methods + const { receipt: l2UniswapInteractionReceipt } = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, wethCrossChainHarness.l2Bridge.address, @@ -787,7 +789,7 @@ export const uniswapL1L2TestSuite = ( logger.info('Withdrawing weth to L1 and sending message to swap to dai'); const [, secretHashForDepositingSwappedDai] = await generateClaimSecret(); - const withdrawReceipt = await uniswapL2Contract.methods + const { receipt: withdrawReceipt } = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, wethCrossChainHarness.l2Bridge.address, @@ -915,7 +917,7 @@ export const uniswapL1L2TestSuite = ( // Call swap_public on L2 const secretHashForDepositingSwappedDai = Fr.random(); - const withdrawReceipt = await uniswapL2Contract.methods + const { receipt: withdrawReceipt } = await uniswapL2Contract.methods .swap_public( ownerAddress, wethCrossChainHarness.l2Bridge.address, diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index fe10d7ed2276..404bb3d5ad8d 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -186,14 +186,16 @@ export class LendingSimulator { expect(this.borrowed).toEqual(this.stableCoin.totalSupply - this.mintedOutside); - const asset = await this.lendingContract.methods.get_asset(0).simulate({ from: this.account.address }); + const { result: asset } = await this.lendingContract.methods.get_asset(0).simulate({ from: this.account.address }); const interestAccumulator = asset['interest_accumulator']; expect(interestAccumulator).toEqual(this.accumulator); expect(asset['last_updated_ts']).toEqual(BigInt(this.time)); for (const key of [this.account.address, AztecAddress.fromField(await this.account.key())]) { - const privatePos = await this.lendingContract.methods.get_position(key).simulate({ from: this.account.address }); + const { result: privatePos } = await this.lendingContract.methods + .get_position(key) + .simulate({ from: this.account.address }); expect(new Fr(privatePos['collateral'])).toEqual(this.collateral[key.toString()] ?? Fr.ZERO); expect(new Fr(privatePos['static_debt'])).toEqual(this.staticDebt[key.toString()] ?? Fr.ZERO); expect(privatePos['debt']).toEqual( diff --git a/yarn-project/end-to-end/src/spartan/1tps.test.ts b/yarn-project/end-to-end/src/spartan/1tps.test.ts index b771d4621308..6d939deb5886 100644 --- a/yarn-project/end-to-end/src/spartan/1tps.test.ts +++ b/yarn-project/end-to-end/src/spartan/1tps.test.ts @@ -63,7 +63,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); @@ -74,16 +75,20 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); const defaultAccountAddress = testAccounts.accounts[0]; @@ -128,7 +133,7 @@ describe('token transfer test', () => { }), ); - const recipientBalance = await testAccounts.tokenContract.methods + const { result: recipientBalance } = await testAccounts.tokenContract.methods .balance_of_public(recipient) .simulate({ from: testAccounts.tokenAdminAddress }); logger.info(`recipientBalance: ${recipientBalance}`); diff --git a/yarn-project/end-to-end/src/spartan/4epochs.test.ts b/yarn-project/end-to-end/src/spartan/4epochs.test.ts index b2dcdd2ca987..a0d42328fb7a 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -68,7 +68,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); logger.info(`Token name verified: ${name}`); @@ -85,18 +86,22 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } logger.info('Minted tokens'); expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); // For each round, make both private and public transfers @@ -132,16 +137,20 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT - ROUNDS * transferAmount).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(ROUNDS * transferAmount * BigInt(testAccounts.accounts.length)).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); }); }); diff --git a/yarn-project/end-to-end/src/spartan/n_tps.test.ts b/yarn-project/end-to-end/src/spartan/n_tps.test.ts index 95405e1b9e84..5bbdb2a94136 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps.test.ts @@ -279,10 +279,10 @@ describe('sustained N TPS test', () => { logger.info('Deploying benchmark contract...'); const sponsor = new SponsoredFeePaymentMethod(await getSponsoredFPCAddress()); - benchmarkContract = await BenchmarkingContract.deploy(localTestAccounts[0].wallet).send({ + ({ contract: benchmarkContract } = await BenchmarkingContract.deploy(localTestAccounts[0].wallet).send({ from: localTestAccounts[0].recipientAddress, fee: { paymentMethod: sponsor }, - }); + })); logger.info('Benchmark contract deployed', { address: benchmarkContract.address.toString() }); logger.info('Installing chaos mesh chart', { diff --git a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts index 5c1750c41b24..e5db55329823 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts @@ -304,10 +304,10 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { } logger.info('Deploying benchmark contract...'); - benchmarkContract = await BenchmarkingContract.deploy(wallets[0]).send({ + ({ contract: benchmarkContract } = await BenchmarkingContract.deploy(wallets[0]).send({ from: accountAddresses[0], fee: { paymentMethod: sponsor }, - }); + })); logger.info('Test setup complete'); }); diff --git a/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts b/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts index 11f3e1b220d7..802509ce0a69 100644 --- a/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts +++ b/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts @@ -72,7 +72,8 @@ describe('reqresp effectiveness under tx drop', () => { testAccounts = await deploySponsoredTestAccountsWithTokens(wallet, aztecNode, MINT_AMOUNT, logger); recipient = testAccounts.recipientAddress; const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index c7567e794750..9f5137fc4273 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -298,13 +298,9 @@ async function deployTokenAndMint( logger: Logger, ) { logger.verbose(`Deploying TokenContract...`); - const { contract: tokenContract } = await TokenContract.deploy( - wallet, - admin, - TOKEN_NAME, - TOKEN_SYMBOL, - TOKEN_DECIMALS, - ).send({ + const { + receipt: { contract: tokenContract }, + } = await TokenContract.deploy(wallet, admin, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS).send({ from: admin, fee: { paymentMethod, diff --git a/yarn-project/end-to-end/src/spartan/transfer.test.ts b/yarn-project/end-to-end/src/spartan/transfer.test.ts index b3fda8fb1186..7db4b2c0e2aa 100644 --- a/yarn-project/end-to-end/src/spartan/transfer.test.ts +++ b/yarn-project/end-to-end/src/spartan/transfer.test.ts @@ -52,7 +52,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); @@ -63,16 +64,20 @@ describe('token transfer test', () => { for (const a of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(a) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(a) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); // For each round, make both private and public transfers @@ -94,14 +99,16 @@ describe('token transfer test', () => { for (const a of testAccounts.accounts) { expect(MINT_AMOUNT - ROUNDS * transferAmount).toBe( - await testAccounts.tokenContract.methods.balance_of_public(a).simulate({ from: a }), + (await testAccounts.tokenContract.methods.balance_of_public(a).simulate({ from: a })).result, ); } expect(ROUNDS * transferAmount * BigInt(testAccounts.accounts.length)).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); }); }); diff --git a/yarn-project/stdlib/src/tx/simulated_tx.ts b/yarn-project/stdlib/src/tx/simulated_tx.ts index 3ad4e7b73c4e..5fb1ef4b8bd4 100644 --- a/yarn-project/stdlib/src/tx/simulated_tx.ts +++ b/yarn-project/stdlib/src/tx/simulated_tx.ts @@ -12,9 +12,11 @@ import { Gas } from '../gas/gas.js'; import type { GasUsed } from '../gas/gas_used.js'; import { PrivateKernelTailCircuitPublicInputs } from '../kernel/private_kernel_tail_circuit_public_inputs.js'; import { ChonkProof } from '../proofs/chonk_proof.js'; +import type { OffchainEffect } from './offchain_effect.js'; import { PrivateCallExecutionResult, PrivateExecutionResult, + collectOffchainEffects, collectSortedContractClassLogs, } from './private_execution_result.js'; import { type SimulationStats, SimulationStatsSchema } from './profiling.js'; @@ -84,6 +86,11 @@ export class TxSimulationResult { public stats?: SimulationStats, ) {} + /** Returns offchain effects collected from private execution. */ + get offchainEffects(): OffchainEffect[] { + return collectOffchainEffects(this.privateExecutionResult); + } + get gasUsed(): GasUsed { return ( this.publicOutput?.gasUsed ?? { @@ -106,7 +113,7 @@ export class TxSimulationResult { .transform(TxSimulationResult.from); } - static from(fields: Omit, 'gasUsed'>) { + static from(fields: Omit, 'gasUsed' | 'offchainEffects'>) { return new TxSimulationResult( fields.privateExecutionResult, fields.publicInputs, diff --git a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts index d3d6ffc90185..e8cb501f0e27 100644 --- a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts +++ b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts @@ -382,6 +382,7 @@ export abstract class BaseWallet implements Wallet { const feeOptions = await this.completeFeeOptions(opts.from, executionPayload.feePayer, opts.fee?.gasSettings); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(executionPayload, opts.from, feeOptions); const provenTx = await this.pxe.proveTx(txRequest, this.scopesFor(opts.from)); + const offchainEffects = provenTx.getOffchainEffects(); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { @@ -395,12 +396,13 @@ export abstract class BaseWallet implements Wallet { // If wait is NO_WAIT, return txHash immediately if (opts.wait === NO_WAIT) { - return txHash as SendReturn; + return { txHash, offchainEffects } as SendReturn; } // Otherwise, wait for the full receipt (default behavior on wait: undefined) const waitOpts = typeof opts.wait === 'object' ? opts.wait : undefined; - return (await waitForTx(this.aztecNode, txHash, waitOpts)) as SendReturn; + const receipt = await waitForTx(this.aztecNode, txHash, waitOpts); + return { receipt, offchainEffects } as SendReturn; } protected contextualizeError(err: Error, ...context: string[]): Error { From 3d45e98b0d27e9b4be266bc9bfabc0037e5c395e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 10:23:16 +0000 Subject: [PATCH 002/119] simplify SimulationReturn --- .../contract/contract_function_interaction.ts | 10 +--- .../aztec.js/src/contract/deploy_method.ts | 2 +- .../src/contract/interaction_options.ts | 46 +++++-------------- yarn-project/aztec.js/src/utils/authwit.ts | 10 +--- .../cli-wallet/src/cmds/create_account.ts | 5 +- yarn-project/cli-wallet/src/cmds/deploy.ts | 5 +- .../cli-wallet/src/cmds/deploy_account.ts | 5 +- yarn-project/cli-wallet/src/cmds/send.ts | 5 +- .../src/e2e_kernelless_simulation.test.ts | 42 ++++++++++------- 9 files changed, 56 insertions(+), 74 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 909d599891e3..743c9b99566b 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -18,8 +18,6 @@ import { type ProfileInteractionOptions, type RequestInteractionOptions, type SimulateInteractionOptions, - type SimulateWithGasEstimationOptions, - type SimulateWithMetadataOptions, type SimulationReturn, toProfileOptions, toSimulateOptions, @@ -99,15 +97,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * function or a rich object containing extra metadata, such as estimated gas costs (if requested via options), * execution statistics and emitted offchain effects */ - public async simulate(options: SimulateWithMetadataOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate(options: SimulateWithGasEstimationOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate(options?: SimulateInteractionOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc public async simulate( options: SimulateInteractionOptions = {} as SimulateInteractionOptions, - ): Promise> { + ): Promise { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const call = await this.getFunctionCall(); diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index f693ded613ed..63a9bde047f3 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -386,7 +386,7 @@ export class DeployMethod extends * @returns A simulation result object containing metadata of the execution, including gas * estimations (if requested via options), execution statistics and emitted offchain effects */ - public async simulate(options: SimulateDeployOptions): Promise> { + public async simulate(options: SimulateDeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); const simulatedTx = await this.wallet.simulateTx(executionPayload, toSimulateOptions(options)); diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 3f870dad4bd4..e1c09dfaa553 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -114,21 +114,6 @@ export type SimulateInteractionOptions = Omit & { includeMetadata?: boolean; }; -/** Simulation options that request full metadata (stats, gas estimation) in the result. */ -export type SimulateWithMetadataOptions = SimulateInteractionOptions & { - /** Request metadata in the simulation result. */ - includeMetadata: true; -}; - -/** Simulation options that request gas estimation in the result. */ -export type SimulateWithGasEstimationOptions = SimulateInteractionOptions & { - /** Fee options with gas estimation enabled. */ - fee: { - /** Request gas estimation in the simulation result. */ - estimateGas: true; - }; -}; - /** * Represents the options for profiling an interaction. */ @@ -140,27 +125,20 @@ export type ProfileInteractionOptions = SimulateInteractionOptions & { }; /** - * Represents the result type of a simulation. + * Represents the result of a simulation. * Always includes the return value and offchain effects. - * When `includeMetadata` is set to true, also includes stats and gas estimation. + * When `includeMetadata` or `fee.estimateGas` is set, also includes stats and gas estimation. */ -export type SimulationReturn = T extends true - ? { - /** Additional stats about the simulation */ - stats: SimulationStats; - /** Offchain effects generated during the simulation */ - offchainEffects: OffchainEffect[]; - /** Return value of the function */ - result: any; - /** Gas estimation results */ - estimatedGas: Pick; - } - : { - /** Return value of the function */ - result: any; - /** Offchain effects generated during the simulation */ - offchainEffects: OffchainEffect[]; - }; +export type SimulationReturn = { + /** Return value of the function */ + result: any; + /** Offchain effects generated during the simulation */ + offchainEffects: OffchainEffect[]; + /** Additional stats about the simulation. Present when `includeMetadata` is set. */ + stats?: SimulationStats; + /** Gas estimation results. Present when `includeMetadata` or `fee.estimateGas` is set. */ + estimatedGas?: Pick; +}; /** Result of sendTx when not waiting for mining. */ export type TxSendResultImmediate = { diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index c1ff57e9f454..acec99c5dd29 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -15,7 +15,6 @@ import type { SendInteractionOptionsWithoutWait, SendReturn, SimulateInteractionOptions, - SimulateWithMetadataOptions, SimulationReturn, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -265,16 +264,9 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing additional configuration for the transaction. * @returns The result of the transaction as returned by the contract function. */ - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - public override simulate(options: Omit): Promise>; - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - // eslint-disable-next-line jsdoc/require-jsdoc - public override simulate(options?: Omit): Promise>; - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - // eslint-disable-next-line jsdoc/require-jsdoc public override simulate( options: Omit = {} as Omit, - ): Promise> { + ): Promise { return super.simulate({ ...options, from: this.from } as SimulateInteractionOptions); } diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index 1279cf8b0f54..2e317c6a7799 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -83,10 +83,13 @@ export async function createAccount( }; const deployMethod = await account.getDeployMethod(); - const { estimatedGas, stats } = await deployMethod.simulate({ + const sim = await deployMethod.simulate({ ...deployAccountOpts, fee: { ...deployAccountOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/deploy.ts b/yarn-project/cli-wallet/src/cmds/deploy.ts index d4ddc6a3c968..edc7e5db29a9 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy.ts @@ -71,10 +71,13 @@ export async function deploy( skipInstancePublication, }; - const { estimatedGas, stats } = await deploy.simulate({ + const sim = await deploy.simulate({ ...deployOpts, fee: { ...deployOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/deploy_account.ts b/yarn-project/cli-wallet/src/cmds/deploy_account.ts index 7c3c6e020023..4667fa6f12c6 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy_account.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy_account.ts @@ -63,10 +63,13 @@ export async function deployAccount( }; const deployMethod = await account.getDeployMethod(); - const { estimatedGas, stats } = await deployMethod.simulate({ + const sim = await deployMethod.simulate({ ...deployAccountOpts, fee: { ...deployAccountOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/send.ts b/yarn-project/cli-wallet/src/cmds/send.ts index 30129ef78e0d..4cc3c69b4505 100644 --- a/yarn-project/cli-wallet/src/cmds/send.ts +++ b/yarn-project/cli-wallet/src/cmds/send.ts @@ -37,10 +37,13 @@ export async function send( authWitnesses, }; - const { estimatedGas, stats } = await call.simulate({ + const sim = await call.simulate({ ...sendOptions, fee: { ...sendOptions.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { return; diff --git a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts index d0a9cedf6b12..4cdd919dacad 100644 --- a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts +++ b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts @@ -237,10 +237,12 @@ describe('Kernelless simulation', () => { ); wallet.enableSimulatedSimulations(); - const { estimatedGas: swapKernellessGas } = await swapExactTokensInteraction.simulate({ - from: swapperAddress, - includeMetadata: true, - }); + const swapKernellessGas = ( + await swapExactTokensInteraction.simulate({ + from: swapperAddress, + includeMetadata: true, + }) + ).estimatedGas!; const swapAuthwit = await wallet.createAuthWit(swapperAddress, { caller: amm.address, @@ -248,11 +250,13 @@ describe('Kernelless simulation', () => { }); wallet.disableSimulatedSimulations(); - const { estimatedGas: swapWithKernelsGas } = await swapExactTokensInteraction.simulate({ - from: swapperAddress, - includeMetadata: true, - authWitnesses: [swapAuthwit], - }); + const swapWithKernelsGas = ( + await swapExactTokensInteraction.simulate({ + from: swapperAddress, + includeMetadata: true, + authWitnesses: [swapAuthwit], + }) + ).estimatedGas!; logger.info(`Kernelless gas: L2=${swapKernellessGas.gasLimits.l2Gas} DA=${swapKernellessGas.gasLimits.daGas}`); logger.info( @@ -285,16 +289,20 @@ describe('Kernelless simulation', () => { ); wallet.enableSimulatedSimulations(); - const { estimatedGas: kernellessGas } = await interaction.simulate({ - from: adminAddress, - includeMetadata: true, - }); + const kernellessGas = ( + await interaction.simulate({ + from: adminAddress, + includeMetadata: true, + }) + ).estimatedGas!; wallet.disableSimulatedSimulations(); - const { estimatedGas: withKernelsGas } = await interaction.simulate({ - from: adminAddress, - includeMetadata: true, - }); + const withKernelsGas = ( + await interaction.simulate({ + from: adminAddress, + includeMetadata: true, + }) + ).estimatedGas!; logger.info(`Kernelless gas: L2=${kernellessGas.gasLimits.l2Gas} DA=${kernellessGas.gasLimits.daGas}`); logger.info(`With kernels gas: L2=${withKernelsGas.gasLimits.l2Gas} DA=${withKernelsGas.gasLimits.daGas}`); From 779670619b4b2829fe43d9bc653aefe9bc8bd8c2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 10:37:36 +0000 Subject: [PATCH 003/119] add migration notes --- .../docs/resources/migration_notes.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 1ca5f170988b..1f7ab2208bff 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -30,6 +30,76 @@ The `simulateUtility` method and related types have been renamed to `executeUtil + let result = env.execute_utility(my_contract_address, selector); ``` +### [Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects + +All SDK interaction methods now return structured objects that include `offchainEffects` alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. + +**`simulate()` — always returns `{ result, offchainEffects }` object:** + +```diff +- const value = await contract.methods.foo(args).simulate({ from: sender }); ++ const { result: value } = await contract.methods.foo(args).simulate({ from: sender }); +``` + +When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` are also available as optional fields on the same object: + +```diff +- const { stats, estimatedGas } = await contract.methods.foo(args).simulate({ ++ const sim = await contract.methods.foo(args).simulate({ + from: sender, + includeMetadata: true, + }); ++ const stats = sim.stats!; ++ const estimatedGas = sim.estimatedGas!; +``` + +`SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields. + +**`send()` — returns `{ receipt, offchainEffects }` object:** + +```diff +- const receipt = await contract.methods.foo(args).send({ from: sender }); ++ const { receipt } = await contract.methods.foo(args).send({ from: sender }); +``` + +When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `TxHash`: + +```diff +- const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ++ const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); +``` + +**Deploy — returns `{ contract, offchainEffects }` object:** + +```diff +- const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); ++ const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); +``` + +**Custom wallet implementations — `sendTx()` must return objects:** + +If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): + +```diff + async sendTx(executionPayload, opts) { + const provenTx = await this.pxe.proveTx(...); ++ const offchainEffects = provenTx.getOffchainEffects(); + const tx = await provenTx.toTx(); + const txHash = tx.getTxHash(); + await this.aztecNode.sendTx(tx); + + if (opts.wait === NO_WAIT) { +- return txHash; ++ return { txHash, offchainEffects }; + } + const receipt = await waitForTx(this.aztecNode, txHash, opts.wait); +- return receipt; ++ return { receipt, offchainEffects }; + } +``` + +**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. + ### [Protocol] `include_by_timestamp` renamed to `expiration_timestamp` The `include_by_timestamp` field has been renamed to `expiration_timestamp` across the protocol to better convey its meaning. From 6313c8b03261e378f16dd6b51fdcfb0f2d5bea62 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:23:09 +0000 Subject: [PATCH 004/119] add offchainEffects usage after send() e2e --- .../docs/resources/migration_notes.md | 4 +++- .../aztec.js/src/contract/deploy_method.ts | 8 +++---- .../src/e2e_fees/gas_estimation.test.ts | 9 +++++--- .../src/e2e_offchain_effect.test.ts | 22 +++++++++++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 1f7ab2208bff..990aa0968711 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -69,13 +69,15 @@ When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `T + const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ``` -**Deploy — returns `{ contract, offchainEffects }` object:** +**Deploy — returns `{ contract, receipt, offchainEffects }` object:** ```diff - const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); + const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); ``` +The deploy receipt is also available via `receipt` if needed (e.g. for `receipt.txHash` or `receipt.transactionFee`). + **Custom wallet implementations — `sendTx()` must return objects:** If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 63a9bde047f3..75b46664a619 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -148,6 +148,8 @@ export type DeployReturn; /** Offchain effects generated during proving. */ offchainEffects: OffchainEffect[]; }; @@ -322,15 +324,11 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class public override send(options: DeployOptionsWithoutWait): Promise>; - - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, ): Promise>; - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override async send(options: DeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); @@ -357,7 +355,7 @@ export class DeployMethod extends return { receipt: { ...receipt, contract, instance }, offchainEffects }; } - return { contract, offchainEffects }; + return { contract, receipt, offchainEffects }; } /** diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index 47fd12c85569..b456cd167784 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -104,10 +104,11 @@ describe('e2e_fees gas_estimation', () => { }); it('estimates gas with Fee Juice payment method', async () => { - const { estimatedGas } = await makeTransferRequest().simulate({ + const sim = await makeTransferRequest().simulate({ from: aliceAddress, fee: { gasSettings, estimateGas: true, estimatedGasPadding: 0 }, }); + const estimatedGas = sim.estimatedGas!; logGasEstimate(estimatedGas); const sequencer = t.context.sequencer!.getSequencer(); @@ -147,10 +148,11 @@ describe('e2e_fees gas_estimation', () => { ); const paymentMethod = new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettingsForEstimation); - const { estimatedGas } = await makeTransferRequest().simulate({ + const sim2 = await makeTransferRequest().simulate({ from: aliceAddress, fee: { paymentMethod, estimatedGasPadding: 0, estimateGas: true }, }); + const estimatedGas = sim2.estimatedGas!; logGasEstimate(estimatedGas); const [withEstimate, withoutEstimate] = await sendTransfers(estimatedGas, paymentMethod); @@ -188,7 +190,7 @@ describe('e2e_fees gas_estimation', () => { }; }; - const { estimatedGas } = await deployMethod().simulate({ + const sim3 = await deployMethod().simulate({ from: aliceAddress, skipClassPublication: true, fee: { @@ -196,6 +198,7 @@ describe('e2e_fees gas_estimation', () => { estimatedGasPadding: 0, }, }); + const estimatedGas = sim3.estimatedGas!; logGasEstimate(estimatedGas); const [{ receipt: withEstimate }, { receipt: withoutEstimate }] = (await Promise.all([ diff --git a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts index c045303bed85..9968050c18b7 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts @@ -39,6 +39,28 @@ describe('e2e_offchain_effect', () => { afterAll(() => teardown()); + it('should return offchain effects from send()', async () => { + const effects = Array(2) + .fill(null) + .map(() => ({ + data: [Fr.random(), Fr.random(), Fr.random(), Fr.random(), Fr.random()], + // eslint-disable-next-line camelcase + next_contract: contract1.address, + })); + + const { receipt, offchainEffects } = await contract1.methods + .emit_offchain_effects(effects) + .send({ from: defaultAccountAddress }); + + expect(receipt.hasExecutionSucceeded()).toBe(true); + // Effects are popped from the end of the BoundedVec, so they come out reversed + expect(offchainEffects).toHaveLength(2); + expect(offchainEffects[0].contractAddress).toEqual(contract1.address); + expect(offchainEffects[0].data).toEqual(effects[1].data); + expect(offchainEffects[1].contractAddress).toEqual(contract1.address); + expect(offchainEffects[1].data).toEqual(effects[0].data); + }); + it('should emit offchain effects', async () => { const effects = Array(3) .fill(null) From 8d1c75284977e92433ca7656e85bb9510ba118d2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:43:21 +0000 Subject: [PATCH 005/119] fix playground --- .../src/components/contract/components/FunctionCard.tsx | 2 +- playground/src/hooks/useTransaction.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/playground/src/components/contract/components/FunctionCard.tsx b/playground/src/components/contract/components/FunctionCard.tsx index 6f4c9e5415a2..21f3b2696b2e 100644 --- a/playground/src/components/contract/components/FunctionCard.tsx +++ b/playground/src/components/contract/components/FunctionCard.tsx @@ -90,7 +90,7 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested let result; try { const call = contract.methods[fnName](...parameters); - result = await call.simulate({ from, skipFeeEnforcement: true }); + ({ result } = await call.simulate({ from, skipFeeEnforcement: true })); const stringResult = JSON.stringify(result, (key, value) => { if (typeof value === 'bigint') { return value.toString(); diff --git a/playground/src/hooks/useTransaction.tsx b/playground/src/hooks/useTransaction.tsx index 202c6017481f..9f1e6832e6c2 100644 --- a/playground/src/hooks/useTransaction.tsx +++ b/playground/src/hooks/useTransaction.tsx @@ -47,10 +47,10 @@ export function useTransaction() { if (interaction instanceof DeployMethod) { const { from, fee, ...deployOpts } = opts as DeployOptions; - txHash = await interaction.send({ from, fee, ...deployOpts, wait: NO_WAIT }); + ({ txHash } = await interaction.send({ from, fee, ...deployOpts, wait: NO_WAIT })); } else { const { from, fee, authWitnesses, capsules } = opts as SendInteractionOptions; - txHash = await interaction.send({ from, fee, authWitnesses, capsules, wait: NO_WAIT }); + ({ txHash } = await interaction.send({ from, fee, authWitnesses, capsules, wait: NO_WAIT })); } setCurrentTx({ From 6accc0c9b70392e4fc5be3cade2feab687a58ec7 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:55:50 +0000 Subject: [PATCH 006/119] fix boxes --- boxes/boxes/react/src/hooks/useContract.tsx | 4 ++-- boxes/boxes/react/src/hooks/useNumber.tsx | 4 ++-- boxes/boxes/vanilla/app/main.ts | 2 +- boxes/boxes/vite/src/hooks/useContract.tsx | 4 ++-- boxes/boxes/vite/src/hooks/useNumber.tsx | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boxes/boxes/react/src/hooks/useContract.tsx b/boxes/boxes/react/src/hooks/useContract.tsx index 5b3c7aca373f..ff180dfa5b59 100644 --- a/boxes/boxes/react/src/hooks/useContract.tsx +++ b/boxes/boxes/react/src/hooks/useContract.tsx @@ -24,10 +24,10 @@ export function useContract() { contractAddressSalt: salt, }); - const contract = await toast.promise(deploymentPromise, { + const { contract } = await toast.promise(deploymentPromise, { pending: 'Deploying contract...', success: { - render: ({ data }) => `Address: ${data.address}`, + render: ({ data }) => `Address: ${data.contract.address}`, }, error: 'Error deploying contract', }); diff --git a/boxes/boxes/react/src/hooks/useNumber.tsx b/boxes/boxes/react/src/hooks/useNumber.tsx index 8809b6555b42..7435cf5ebfa7 100644 --- a/boxes/boxes/react/src/hooks/useNumber.tsx +++ b/boxes/boxes/react/src/hooks/useNumber.tsx @@ -11,10 +11,10 @@ export function useNumber({ contract }: { contract: Contract }) { setWait(true); const defaultAccountAddress = deployerEnv.getDefaultAccountAddress(); - const viewTxReceipt = await contract!.methods + const { result } = await contract!.methods .getNumber(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - toast(`Number is: ${viewTxReceipt.value}`); + toast(`Number is: ${result}`); setWait(false); }; diff --git a/boxes/boxes/vanilla/app/main.ts b/boxes/boxes/vanilla/app/main.ts index fa55a4cae208..ddce11af435c 100644 --- a/boxes/boxes/vanilla/app/main.ts +++ b/boxes/boxes/vanilla/app/main.ts @@ -200,7 +200,7 @@ async function updateVoteTally(wallet: Wallet, from: AztecAddress) { const batchResult = await new BatchCall(wallet, payloads).simulate({ from }); - batchResult.forEach((value, i) => { + batchResult.forEach(({ result: value }, i) => { results[i + 1] = value; }); diff --git a/boxes/boxes/vite/src/hooks/useContract.tsx b/boxes/boxes/vite/src/hooks/useContract.tsx index 5b3c7aca373f..ff180dfa5b59 100644 --- a/boxes/boxes/vite/src/hooks/useContract.tsx +++ b/boxes/boxes/vite/src/hooks/useContract.tsx @@ -24,10 +24,10 @@ export function useContract() { contractAddressSalt: salt, }); - const contract = await toast.promise(deploymentPromise, { + const { contract } = await toast.promise(deploymentPromise, { pending: 'Deploying contract...', success: { - render: ({ data }) => `Address: ${data.address}`, + render: ({ data }) => `Address: ${data.contract.address}`, }, error: 'Error deploying contract', }); diff --git a/boxes/boxes/vite/src/hooks/useNumber.tsx b/boxes/boxes/vite/src/hooks/useNumber.tsx index 8809b6555b42..7435cf5ebfa7 100644 --- a/boxes/boxes/vite/src/hooks/useNumber.tsx +++ b/boxes/boxes/vite/src/hooks/useNumber.tsx @@ -11,10 +11,10 @@ export function useNumber({ contract }: { contract: Contract }) { setWait(true); const defaultAccountAddress = deployerEnv.getDefaultAccountAddress(); - const viewTxReceipt = await contract!.methods + const { result } = await contract!.methods .getNumber(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - toast(`Number is: ${viewTxReceipt.value}`); + toast(`Number is: ${result}`); setWait(false); }; From 03192e50d46e2f1ae76b69f93f705e2229242fad Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 12:34:23 +0000 Subject: [PATCH 007/119] fix aztec.js for boxes --- .../aztec.js/src/contract/base_contract_interaction.ts | 6 ++++-- yarn-project/aztec.js/src/contract/deploy_method.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index bfa319bc77df..261d7f43e55d 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -1,6 +1,6 @@ import { createLogger } from '@aztec/foundation/log'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; -import type { Capsule, ExecutionPayload, TxReceipt } from '@aztec/stdlib/tx'; +import type { Capsule, ExecutionPayload, OffchainEffect, TxReceipt } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { @@ -42,7 +42,9 @@ export abstract class BaseContractInteraction { * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } - public send(options: SendInteractionOptionsWithoutWait): Promise>; + public send( + options: SendInteractionOptionsWithoutWait, + ): Promise<{ receipt: TReturn; offchainEffects: OffchainEffect[] }>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 75b46664a619..cca6040e3975 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -324,7 +324,9 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send(options: DeployOptionsWithoutWait): Promise>; + public override send( + options: DeployOptionsWithoutWait, + ): Promise<{ contract: TContract; receipt: DeployTxReceipt; offchainEffects: OffchainEffect[] }>; // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, From 3efe7577c4d918cc1c13114df97e1f2ae7fe8d93 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 12:55:49 +0000 Subject: [PATCH 008/119] sort out types --- yarn-project/aztec.js/src/api/contract.ts | 3 ++ .../src/contract/base_contract_interaction.ts | 7 ++-- .../aztec.js/src/contract/deploy_method.ts | 32 +++++++++---------- .../src/contract/interaction_options.ts | 11 ++----- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/yarn-project/aztec.js/src/api/contract.ts b/yarn-project/aztec.js/src/api/contract.ts index d3e9a692c67f..1dc15515abc8 100644 --- a/yarn-project/aztec.js/src/api/contract.ts +++ b/yarn-project/aztec.js/src/api/contract.ts @@ -57,6 +57,8 @@ export { type GasSettingsOption, type SendReturn, type SimulationReturn, + type TxSendResultImmediate, + type TxSendResultMined, toProfileOptions, toSendOptions, toSimulateOptions, @@ -67,6 +69,7 @@ export { ContractBase, type ContractMethod, type ContractStorageLayout } from '. export { BatchCall } from '../contract/batch_call.js'; export { type DeployOptions, + type DeployResultMined, type DeployReturn, type DeployTxReceipt, type DeployWaitOptions, diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index 261d7f43e55d..12983df3a0ea 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -1,6 +1,6 @@ import { createLogger } from '@aztec/foundation/log'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; -import type { Capsule, ExecutionPayload, OffchainEffect, TxReceipt } from '@aztec/stdlib/tx'; +import type { Capsule, ExecutionPayload, TxReceipt } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { @@ -9,6 +9,7 @@ import { type SendInteractionOptions, type SendInteractionOptionsWithoutWait, type SendReturn, + type TxSendResultMined, toSendOptions, } from './interaction_options.js'; @@ -42,9 +43,7 @@ export abstract class BaseContractInteraction { * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } - public send( - options: SendInteractionOptionsWithoutWait, - ): Promise<{ receipt: TReturn; offchainEffects: OffchainEffect[] }>; + public send(options: SendInteractionOptionsWithoutWait): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index cca6040e3975..aed5520baf57 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -28,6 +28,7 @@ import { type SimulationInteractionFeeOptions, type SimulationReturn, type TxSendResultImmediate, + type TxSendResultMined, toProfileOptions, toSendOptions, toSimulateOptions, @@ -136,23 +137,22 @@ type WaitWithReturnReceipt = { * - If wait has returnReceipt: true, returns DeployTxReceipt after waiting. * - Otherwise (undefined or DeployWaitOptions without returnReceipt), returns TContract after waiting. */ +/** Result of deploying a contract when waiting for mining (default case). */ +export type DeployResultMined = { + /** The deployed contract instance. */ + contract: TContract; + /** The deploy transaction receipt. */ + receipt: DeployTxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; + +/** Conditional return type for deploy based on wait options. */ export type DeployReturn = W extends NoWait ? TxSendResultImmediate : W extends WaitWithReturnReceipt - ? { - /** The deploy transaction receipt. */ - receipt: DeployTxReceipt; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - } - : { - /** The deployed contract instance. */ - contract: TContract; - /** The deploy transaction receipt. */ - receipt: DeployTxReceipt; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - }; + ? TxSendResultMined> + : DeployResultMined; /** * Contract interaction for deployment. @@ -324,9 +324,7 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send( - options: DeployOptionsWithoutWait, - ): Promise<{ contract: TContract; receipt: DeployTxReceipt; offchainEffects: OffchainEffect[] }>; + public override send(options: DeployOptionsWithoutWait): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index e1c09dfaa553..0510fa703cd9 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -149,9 +149,9 @@ export type TxSendResultImmediate = { }; /** Result of sendTx when waiting for mining. */ -export type TxSendResultMined = { +export type TxSendResultMined = { /** The transaction receipt. */ - receipt: TxReceipt; + receipt: TReturn; /** Offchain effects generated during proving. */ offchainEffects: OffchainEffect[]; }; @@ -163,12 +163,7 @@ export type TxSendResultMined = { */ export type SendReturn = T extends NoWait ? TxSendResultImmediate - : { - /** The transaction receipt. */ - receipt: TReturn; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - }; + : TxSendResultMined; /** * Transforms and cleans up the higher level SendInteractionOptions defined by the interaction into From 57af637102c90c144d0eefb0d908c3a75025df96 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 13:11:10 +0000 Subject: [PATCH 009/119] fix authwit types --- yarn-project/aztec.js/src/utils/authwit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index acec99c5dd29..b9b889907ba5 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -16,6 +16,7 @@ import type { SendReturn, SimulateInteractionOptions, SimulationReturn, + TxSendResultMined, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -288,8 +289,7 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing 'fee' options information * @returns A TxReceipt (if wait is true/undefined) or TxHash (if wait is false) */ - // Overload for when wait is not specified at all - public override send(options?: Omit): Promise>; + public override send(options?: Omit): Promise; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public override send( From 3a2718f02dd7ecf88502ea25cd02c5838b968c5b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 13:50:07 +0000 Subject: [PATCH 010/119] fix e2e --- .../end-to-end/src/e2e_avm_simulator.test.ts | 22 +++++++++------- .../access_control.test.ts | 8 +++--- .../src/e2e_contract_updates.test.ts | 14 +++++----- .../contract_class_registration.test.ts | 20 ++++++++++---- .../e2e_deploy_contract/deploy_method.test.ts | 26 ++++++++++++------- .../private_initialization.test.ts | 14 +++++----- .../src/e2e_private_voting_contract.test.ts | 2 +- .../end-to-end/src/e2e_pruned_blocks.test.ts | 6 +++-- .../e2e_token_contract/access_control.test.ts | 8 +++--- .../src/e2e_token_contract/minting.test.ts | 12 ++++++--- .../e2e_token_contract/token_contract_test.ts | 2 +- .../transfer_in_private.test.ts | 12 ++++++--- .../transfer_in_public.test.ts | 24 ++++++++++------- 13 files changed, 104 insertions(+), 66 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 1cf55068255e..b40af1162298 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -119,16 +119,18 @@ describe('e2e_avm_simulator', () => { describe('Storage', () => { it('Modifies storage (Field)', async () => { await avmContract.methods.set_storage_single(20n).send({ from: defaultAccountAddress }); - expect(await avmContract.methods.read_storage_single().simulate({ from: defaultAccountAddress })).toEqual(20n); + expect( + (await avmContract.methods.read_storage_single().simulate({ from: defaultAccountAddress })).result, + ).toEqual(20n); }); it('Modifies storage (Map)', async () => { const address = AztecAddress.fromBigInt(9090n); await avmContract.methods.set_storage_map(address, 100).send({ from: defaultAccountAddress }); await avmContract.methods.add_storage_map(address, 100).send({ from: defaultAccountAddress }); - expect(await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).toEqual( - 200n, - ); + expect( + (await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).result, + ).toEqual(200n); }); it('Preserves storage across enqueued public calls', async () => { @@ -139,9 +141,9 @@ describe('e2e_avm_simulator', () => { avmContract.methods.add_storage_map(address, 100), ]).send({ from: defaultAccountAddress }); // On a separate tx, we check the result. - expect(await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).toEqual( - 200n, - ); + expect( + (await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).result, + ).toEqual(200n); }); }); @@ -261,9 +263,9 @@ describe('e2e_avm_simulator', () => { describe('Storage', () => { it('Read immutable (initialized) storage (Field)', async () => { - expect(await avmContract.methods.read_storage_immutable().simulate({ from: defaultAccountAddress })).toEqual( - 42n, - ); + expect( + (await avmContract.methods.read_storage_immutable().simulate({ from: defaultAccountAddress })).result, + ).toEqual(42n); }); }); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts index 97e8bbb19c23..76f015066d44 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts @@ -23,7 +23,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.adminAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.adminAddress).simulate({ from: t.adminAddress })).result).toEqual( adminMinterRole.toNoirStruct(), ); }); @@ -34,7 +34,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).result).toEqual( adminRole.toNoirStruct(), ); }); @@ -45,7 +45,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).result).toEqual( noRole.toNoirStruct(), ); }); @@ -58,7 +58,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.blacklistedAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.blacklistedAddress).simulate({ from: t.adminAddress })).result).toEqual( blacklistRole.toNoirStruct(), ); }); diff --git a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts index 699cb2bb459d..5a9c3a4e1c62 100644 --- a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts +++ b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts @@ -128,9 +128,10 @@ describe('e2e_contract_updates', () => { it('should update the contract', async () => { expect( - await contract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + (await contract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress })) + .result, ).toEqual(INITIAL_UPDATABLE_CONTRACT_VALUE); - expect(await contract.methods.get_public_value().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_public_value().simulate({ from: defaultAccountAddress })).result).toEqual( INITIAL_UPDATABLE_CONTRACT_VALUE, ); await contract.methods.update_to(updatedContractClassId).send({ from: defaultAccountAddress }); @@ -143,25 +144,26 @@ describe('e2e_contract_updates', () => { await updatedContract.methods.set_private_value().send({ from: defaultAccountAddress }); // Read state that was changed by the previous tx expect( - await updatedContract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + (await updatedContract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress })) + .result, ).toEqual(UPDATED_CONTRACT_PUBLIC_VALUE); // Call a public method with a new implementation await updatedContract.methods.set_public_value().send({ from: defaultAccountAddress }); - expect(await updatedContract.methods.get_public_value().simulate({ from: defaultAccountAddress })).toEqual( + expect((await updatedContract.methods.get_public_value().simulate({ from: defaultAccountAddress })).result).toEqual( UPDATED_CONTRACT_PUBLIC_VALUE, ); }); it('should change the update delay and then update the contract', async () => { - expect(await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).result).toEqual( BigInt(DEFAULT_TEST_UPDATE_DELAY), ); // Increases the delay so it should happen immediately await contract.methods.set_update_delay(BigInt(MINIMUM_UPDATE_DELAY) + 1n).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).result).toEqual( BigInt(MINIMUM_UPDATE_DELAY) + 1n, ); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index d027cd6c4b39..37da34dd7f3c 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -149,7 +149,9 @@ describe('e2e_deploy_contract contract class registration', () => { it('calls a public function with no init check on the deployed instance', async () => { const whom = await AztecAddress.random(); await contract.methods.increment_public_value_no_init_check(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); @@ -161,7 +163,9 @@ describe('e2e_deploy_contract contract class registration', () => { expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); // Meanwhile we check we didn't increment the value - expect(await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).toEqual(0n); + expect( + (await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).result, + ).toEqual(0n); }); it('refuses to initialize the instance with wrong args via a private function', async () => { @@ -174,7 +178,9 @@ describe('e2e_deploy_contract contract class registration', () => { await contract.methods.constructor(...initArgs).send({ from: defaultAccountAddress }); const whom = await AztecAddress.random(); await contract.methods.increment_public_value(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); @@ -199,14 +205,18 @@ describe('e2e_deploy_contract contract class registration', () => { .public_constructor(whom, 43) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); - expect(await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).toEqual(0n); + expect( + (await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).result, + ).toEqual(0n); }); it('initializes the contract and calls a public function', async () => { await contract.methods.public_constructor(...initArgs).send({ from: defaultAccountAddress }); const whom = await AztecAddress.random(); await contract.methods.increment_public_value(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index 9ce2b7366aea..ed7cfba703fb 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -42,10 +42,12 @@ describe('e2e_deploy_contract deploy method', () => { // docs:start:deploy_basic const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); // docs:end:deploy_basic - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(42n); logger.debug(`Calling public method on stateful test contract at ${contract.address.toString()}`); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(84n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 84n, + ); // docs:start:verify_deployment const metadata = await wallet.getContractMetadata(contract.address); const classMetadata = await wallet.getContractClassMetadata(metadata.instance!.currentContractClassId); @@ -60,9 +62,11 @@ describe('e2e_deploy_contract deploy method', () => { const opts = { universalDeploy: true, from: defaultAccountAddress }; const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); // docs:end:deploy_universal - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(42n); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(84n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 84n, + ); }); it('publicly deploys and calls a public function from the constructor', async () => { @@ -72,7 +76,7 @@ describe('e2e_deploy_contract deploy method', () => { from: defaultAccountAddress, }); // docs:end:deploy_token - expect(await token.methods.is_minter(owner).simulate({ from: defaultAccountAddress })).toEqual(true); + expect((await token.methods.is_minter(owner).simulate({ from: defaultAccountAddress })).result).toEqual(true); }); it('publicly deploys and initializes via a public function', async () => { @@ -87,10 +91,12 @@ describe('e2e_deploy_contract deploy method', () => { from: defaultAccountAddress, }); // docs:end:deploy_with_opts - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 42n, + ); logger.debug(`Calling a private function to ensure the contract was properly initialized`); await contract.methods.create_note(owner, 30).send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(30n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(30n); }); it('deploys a contract with a default initializer not named constructor', async () => { @@ -99,9 +105,9 @@ describe('e2e_deploy_contract deploy method', () => { const { contract } = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); logger.debug(`Calling a function to ensure the contract was properly initialized`); await contract.methods.increment_twice(defaultAccountAddress).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).toEqual( - 12n, - ); + expect( + (await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).result, + ).toEqual(12n); }); it('publicly deploys a contract with no constructor', async () => { diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index 4a40eef5e2ec..a6814602e130 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -45,11 +45,11 @@ describe('e2e_deploy_contract private initialization', () => { const contract = await t.registerContract(wallet, NoConstructorContract); await expect( contract.methods.is_private_mutable_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), - ).resolves.toEqual(false); + ).resolves.toEqual(expect.objectContaining({ result: false })); await contract.methods.initialize_private_mutable(42).send({ from: defaultAccountAddress }); await expect( contract.methods.is_private_mutable_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), - ).resolves.toEqual(true); + ).resolves.toEqual(expect.objectContaining({ result: true })); }); // Tests privately initializing an undeployed contract. Also requires pxe registration in advance. @@ -60,10 +60,10 @@ describe('e2e_deploy_contract private initialization', () => { logger.info(`Calling the constructor for ${contract.address}`); await contract.methods.constructor(...initArgs).send({ from: defaultAccountAddress }); logger.info(`Checking if the constructor was run for ${contract.address}`); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(42n); logger.info(`Calling a private function that requires initialization on ${contract.address}`); await contract.methods.create_note(owner, 10).send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); // Tests privately initializing multiple undeployed contracts on the same tx through an account contract. @@ -75,8 +75,8 @@ describe('e2e_deploy_contract private initialization', () => { ); const calls = contracts.map((c, i) => c.methods.constructor(...initArgs[i])); await new BatchCall(wallet, calls).send({ from: defaultAccountAddress }); - expect(await contracts[0].methods.summed_values(owner).simulate({ from: owner })).toEqual(42n); - expect(await contracts[1].methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contracts[0].methods.summed_values(owner).simulate({ from: owner })).result).toEqual(42n); + expect((await contracts[1].methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); it('initializes and calls a private function in a single tx', async () => { @@ -89,7 +89,7 @@ describe('e2e_deploy_contract private initialization', () => { ]); logger.info(`Executing constructor and private function in batch at ${contract.address}`); await batch.send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); it('refuses to initialize a contract twice', async () => { diff --git a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts index 6c94f188985a..6a60739c2d36 100644 --- a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts @@ -40,7 +40,7 @@ describe('e2e_voting_contract', () => { await votingContract.methods.start_vote(electionId).send({ from: owner }); await votingContract.methods.cast_vote(electionId, candidate).send({ from: owner }); - expect(await votingContract.methods.get_tally(electionId, candidate).simulate({ from: owner })).toBe(1n); + expect((await votingContract.methods.get_tally(electionId, candidate).simulate({ from: owner })).result).toBe(1n); // We try voting again, but our TX is dropped due to trying to emit duplicate nullifiers // first confirm that it fails simulation diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 2c9b8142105c..02db37673052 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -124,7 +124,9 @@ describe('e2e_pruned_blocks', () => { await token.methods.transfer(recipient, MINT_AMOUNT).send({ from: sender }); - expect(await token.methods.balance_of_private(recipient).simulate({ from: recipient })).toEqual(MINT_AMOUNT); - expect(await token.methods.balance_of_private(sender).simulate({ from: sender })).toEqual(0n); + expect((await token.methods.balance_of_private(recipient).simulate({ from: recipient })).result).toEqual( + MINT_AMOUNT, + ); + expect((await token.methods.balance_of_private(sender).simulate({ from: sender })).result).toEqual(0n); }); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts index cab2d394475f..50ac4a7f36be 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts @@ -18,17 +18,19 @@ describe('e2e_token_contract access control', () => { it('Set admin', async () => { await t.asset.methods.set_admin(t.account1Address).send({ from: t.adminAddress }); - expect(await t.asset.methods.get_admin().simulate({ from: t.adminAddress })).toBe(t.account1Address.toBigInt()); + expect((await t.asset.methods.get_admin().simulate({ from: t.adminAddress })).result).toBe( + t.account1Address.toBigInt(), + ); }); it('Add minter as admin', async () => { await t.asset.methods.set_minter(t.account1Address, true).send({ from: t.account1Address }); - expect(await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).toBe(true); + expect((await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).result).toBe(true); }); it('Revoke minter as admin', async () => { await t.asset.methods.set_minter(t.account1Address, false).send({ from: t.account1Address }); - expect(await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).toBe(false); + expect((await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).result).toBe(false); }); describe('failure cases', () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts index bc6a1251a833..e7479801a3a6 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts @@ -25,10 +25,12 @@ describe('e2e_token_contract minting', () => { await asset.methods.mint_to_public(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPublic(adminAddress, amount); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual( + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( tokenSim.balanceOfPublic(adminAddress), ); - expect(await asset.methods.total_supply().simulate({ from: adminAddress })).toEqual(tokenSim.totalSupply); + expect((await asset.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + tokenSim.totalSupply, + ); }); describe('failure cases', () => { @@ -61,10 +63,12 @@ describe('e2e_token_contract minting', () => { await asset.methods.mint_to_private(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPrivate(adminAddress, amount); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual( + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( tokenSim.balanceOfPrivate(adminAddress), ); - expect(await asset.methods.total_supply().simulate({ from: adminAddress })).toEqual(tokenSim.totalSupply); + expect((await asset.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + tokenSim.totalSupply, + ); }); describe('failure cases', () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index f6462de40520..bbc7f024fd19 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -110,7 +110,7 @@ export class TokenContractTest { this.account1Address, ]); - expect(await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).toBe( + expect((await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).result).toBe( this.adminAddress.toBigInt(), ); } diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts index 3ea047560895..385c912cec06 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts @@ -80,10 +80,12 @@ describe('e2e_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow('Assertion failed: Balance too low'); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { @@ -135,7 +137,9 @@ describe('e2e_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, + ); }); it('transfer on behalf of other, cancelled authwit', async () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts index 3bb951b28192..f50c89d80b5a 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts @@ -147,10 +147,12 @@ describe('e2e_token_contract transfer public', () => { U128_UNDERFLOW_ERROR, ); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { @@ -175,10 +177,12 @@ describe('e2e_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: account1Address })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { @@ -202,10 +206,12 @@ describe('e2e_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: account1Address })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, cancelled authwit', async () => { From 2cb9c2cf1658ef1a6cd227833fd6b40f121a81b9 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:10:57 +0000 Subject: [PATCH 011/119] fix more e2e --- yarn-project/end-to-end/src/simulators/token_simulator.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/simulators/token_simulator.ts b/yarn-project/end-to-end/src/simulators/token_simulator.ts index f29f6403a4d2..a2065beb4426 100644 --- a/yarn-project/end-to-end/src/simulators/token_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/token_simulator.ts @@ -109,7 +109,9 @@ export class TokenSimulator { await Promise.all( chunk(calls, 5).map(batch => new BatchCall(this.defaultWallet, batch).simulate({ from: this.defaultAddress })), ) - ).flat(); + ) + .flat() + .map(r => r.result); expect(results[0]).toEqual(this.totalSupply); // Check that all our balances match @@ -123,7 +125,9 @@ export class TokenSimulator { const wallet = this.lookupProvider.get(address.toString()); const asset = wallet ? this.token.withWallet(wallet) : this.token; - const actualPrivateBalance = await asset.methods.balance_of_private(address).simulate({ from: address }); + const { result: actualPrivateBalance } = await asset.methods + .balance_of_private(address) + .simulate({ from: address }); expect(actualPrivateBalance).toEqual(this.balanceOfPrivate(address)); } } From 660ee5e134d1eed25b113c5a4bbb3299141fb12b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:37:21 +0000 Subject: [PATCH 012/119] fix e2e tests --- .../src/e2e_token_contract/reading_constants.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts index ef69b7556739..185ac4231e8e 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts @@ -50,12 +50,12 @@ describe('e2e_token_contract reading constants', () => { }); it('check decimals private', async () => { - const dec = await t.asset.methods.private_get_decimals().simulate({ from: t.adminAddress }); + const { result: dec } = await t.asset.methods.private_get_decimals().simulate({ from: t.adminAddress }); expect(dec).toBe(TOKEN_DECIMALS); }); it('check decimals public', async () => { - const dec = await t.asset.methods.public_get_decimals().simulate({ from: t.adminAddress }); + const { result: dec } = await t.asset.methods.public_get_decimals().simulate({ from: t.adminAddress }); expect(dec).toBe(TOKEN_DECIMALS); }); }); From 70aaf1af5773f975a36dcce750b5919ea67bb228 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:53:22 +0000 Subject: [PATCH 013/119] update docs examples --- docs/examples/ts/aztecjs_advanced/index.ts | 28 +++++++++---------- docs/examples/ts/aztecjs_authwit/index.ts | 2 +- docs/examples/ts/aztecjs_connection/index.ts | 6 ++-- .../ts/aztecjs_getting_started/index.ts | 14 +++++----- docs/examples/ts/aztecjs_testing/index.ts | 12 ++++---- docs/examples/ts/bob_token_contract/index.ts | 14 ++++++---- .../ts/recursive_verification/index.ts | 10 +++---- docs/examples/ts/token_bridge/index.ts | 12 ++++---- 8 files changed, 51 insertions(+), 47 deletions(-) diff --git a/docs/examples/ts/aztecjs_advanced/index.ts b/docs/examples/ts/aztecjs_advanced/index.ts index 85f3a2aef2ca..bd138a88ff79 100644 --- a/docs/examples/ts/aztecjs_advanced/index.ts +++ b/docs/examples/ts/aztecjs_advanced/index.ts @@ -30,7 +30,7 @@ const [aliceAddress, bobAddress] = await Promise.all( // docs:start:deploy_basic_local // wallet and aliceAddress are from the connection guide // Deploy with constructor arguments -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", @@ -49,7 +49,7 @@ await wallet.registerContract(sponsoredFPCInstance, SponsoredFPCContract.artifac const sponsoredPaymentMethod = new SponsoredFeePaymentMethod(sponsoredFPCInstance.address); // wallet is from the connection guide; sponsoredPaymentMethod is from the fees guide -const sponsoredContract = await TokenContract.deploy( +const { contract: sponsoredContract } = await TokenContract.deploy( wallet, aliceAddress, "SponsoredToken", @@ -62,7 +62,7 @@ const sponsoredContract = await TokenContract.deploy( // wallet and aliceAddress are from the connection guide const customSalt = Fr.random(); -const saltedContract = await TokenContract.deploy( +const { contract: saltedContract } = await TokenContract.deploy( wallet, aliceAddress, "SaltedToken", @@ -95,7 +95,7 @@ console.log(`Contract will deploy at: ${predictedAddress}`); // token is from the deployment step above; aliceAddress is from the connection guide try { // Try calling a view function - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); console.log("Contract is callable, balance:", balance); @@ -128,7 +128,7 @@ await token.methods // docs:start:no_wait_deploy // Use NO_WAIT to get the transaction hash immediately and track deployment -const txHash = await TokenContract.deploy( +const { txHash } = await TokenContract.deploy( wallet, aliceAddress, "AnotherToken", @@ -148,7 +148,7 @@ console.log(`Deployed in block ${receipt.blockNumber}`); // docs:start:no_wait_transaction // Use NO_WAIT for regular transactions too -const transferTxHash = await token.methods +const { txHash: transferTxHash } = await token.methods .transfer(bobAddress, 100n) .send({ from: aliceAddress, wait: NO_WAIT }); @@ -166,7 +166,7 @@ const batch = new BatchCall(wallet, [ token.methods.transfer(bobAddress, 200n), ]); -const batchReceipt = await batch.send({ from: aliceAddress }); +const { receipt: batchReceipt } = await batch.send({ from: aliceAddress }); console.log(`Batch executed in block ${batchReceipt.blockNumber}`); // docs:end:batch_call @@ -193,7 +193,7 @@ console.log( // docs:start:query_tx_status // Query transaction status after sending without waiting -const statusTxHash = await token.methods +const { txHash: statusTxHash } = await token.methods .transfer(bobAddress, 10n) .send({ from: aliceAddress, wait: NO_WAIT }); @@ -207,7 +207,7 @@ console.log(`Transaction fee: ${txReceipt.transactionFee}`); // docs:start:deploy_with_dependencies // Deploy contracts with dependencies - deploy sequentially and pass addresses -const baseToken = await TokenContract.deploy( +const { contract: baseToken } = await TokenContract.deploy( wallet, aliceAddress, "BaseToken", @@ -216,7 +216,7 @@ const baseToken = await TokenContract.deploy( ).send({ from: aliceAddress }); // A second contract could reference the first (example pattern) -const derivedToken = await TokenContract.deploy( +const { contract: derivedToken } = await TokenContract.deploy( wallet, baseToken.address, // Use first contract's address as admin "DerivedToken", @@ -233,13 +233,13 @@ console.log(`Derived token at: ${derivedToken.address.toString()}`); const contracts = await Promise.all([ TokenContract.deploy(wallet, aliceAddress, "Token1", "T1", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), TokenContract.deploy(wallet, aliceAddress, "Token2", "T2", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), TokenContract.deploy(wallet, aliceAddress, "Token3", "T3", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), ]); console.log(`Contract 1 at: ${contracts[0].address}`); @@ -249,7 +249,7 @@ console.log(`Contract 3 at: ${contracts[2].address}`); // docs:start:skip_initialization // Deploy without running the constructor using skipInitialization -const delayedToken = await TokenContract.deploy( +const { contract: delayedToken } = await TokenContract.deploy( wallet, aliceAddress, "DelayedToken", diff --git a/docs/examples/ts/aztecjs_authwit/index.ts b/docs/examples/ts/aztecjs_authwit/index.ts index da0f03a80be0..04833cd90951 100644 --- a/docs/examples/ts/aztecjs_authwit/index.ts +++ b/docs/examples/ts/aztecjs_authwit/index.ts @@ -23,7 +23,7 @@ const [aliceAddress, bobAddress] = await Promise.all( }), ); -const tokenContract = await TokenContract.deploy( +const { contract: tokenContract } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", diff --git a/docs/examples/ts/aztecjs_connection/index.ts b/docs/examples/ts/aztecjs_connection/index.ts index 4267a6597b9b..5b4074c9b5ec 100644 --- a/docs/examples/ts/aztecjs_connection/index.ts +++ b/docs/examples/ts/aztecjs_connection/index.ts @@ -91,7 +91,7 @@ console.log("Account deployed:", metadata.isContractInitialized); // docs:start:deploy_contract import { TokenContract } from "@aztec/noir-contracts.js/Token"; -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", @@ -103,7 +103,7 @@ console.log(`Token deployed at: ${token.address.toString()}`); // docs:end:deploy_contract // docs:start:send_transaction -const receipt = await token.methods +const { receipt } = await token.methods .mint_to_public(aliceAddress, 1000n) .send({ from: aliceAddress }); @@ -112,7 +112,7 @@ console.log(`Transaction fee: ${receipt.transactionFee}`); // docs:end:send_transaction // docs:start:simulate_function -const balance = await token.methods +const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); diff --git a/docs/examples/ts/aztecjs_getting_started/index.ts b/docs/examples/ts/aztecjs_getting_started/index.ts index 9b3e7ec27ff5..de5a4ac22eae 100644 --- a/docs/examples/ts/aztecjs_getting_started/index.ts +++ b/docs/examples/ts/aztecjs_getting_started/index.ts @@ -13,7 +13,7 @@ await wallet.createSchnorrAccount(bob.secret, bob.salt); // docs:start:deploy import { TokenContract } from "@aztec/noir-contracts.js/Token"; -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, alice.address, "TokenName", @@ -29,11 +29,11 @@ await token.methods // docs:end:mint // docs:start:check_balances -let aliceBalance = await token.methods +let { result: aliceBalance } = await token.methods .balance_of_private(alice.address) .simulate({ from: alice.address }); console.log(`Alice's balance: ${aliceBalance}`); -let bobBalance = await token.methods +let { result: bobBalance } = await token.methods .balance_of_private(bob.address) .simulate({ from: bob.address }); console.log(`Bob's balance: ${bobBalance}`); @@ -41,9 +41,9 @@ console.log(`Bob's balance: ${bobBalance}`); // docs:start:transfer await token.methods.transfer(bob.address, 10).send({ from: alice.address }); -bobBalance = await token.methods +({ result: bobBalance } = await token.methods .balance_of_private(bob.address) - .simulate({ from: bob.address }); + .simulate({ from: bob.address })); console.log(`Bob's balance: ${bobBalance}`); // docs:end:transfer @@ -55,8 +55,8 @@ await token.methods.set_minter(bob.address, true).send({ from: alice.address }); await token.methods .mint_to_private(bob.address, 100) .send({ from: bob.address }); -bobBalance = await token.methods +({ result: bobBalance } = await token.methods .balance_of_private(bob.address) - .simulate({ from: bob.address }); + .simulate({ from: bob.address })); console.log(`Bob's balance: ${bobBalance}`); // docs:end:bob_mints diff --git a/docs/examples/ts/aztecjs_testing/index.ts b/docs/examples/ts/aztecjs_testing/index.ts index 68af11270b77..0c50ac8e5a33 100644 --- a/docs/examples/ts/aztecjs_testing/index.ts +++ b/docs/examples/ts/aztecjs_testing/index.ts @@ -26,7 +26,7 @@ async function setup() { }), ); - token = await TokenContract.deploy( + ({ contract: token } = await TokenContract.deploy( wallet, aliceAddress, "Test", @@ -34,7 +34,7 @@ async function setup() { 18, ).send({ from: aliceAddress, - }); + })); } // Test: mints tokens to an account @@ -43,7 +43,7 @@ async function testMintTokens() { .mint_to_public(aliceAddress, 1000n) .send({ from: aliceAddress }); - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); @@ -63,10 +63,10 @@ async function testTransferTokens() { // Transfer to bob using the simple transfer method await token.methods.transfer(bobAddress, 100n).send({ from: aliceAddress }); - const aliceBalance = await token.methods + const { result: aliceBalance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const bobBalance = await token.methods + const { result: bobBalance } = await token.methods .balance_of_public(bobAddress) .simulate({ from: bobAddress }); @@ -77,7 +77,7 @@ async function testTransferTokens() { // Test: reverts when transferring more than balance async function testRevertOnOverTransfer() { - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); diff --git a/docs/examples/ts/bob_token_contract/index.ts b/docs/examples/ts/bob_token_contract/index.ts index ec71b47da703..9b154e1279a3 100644 --- a/docs/examples/ts/bob_token_contract/index.ts +++ b/docs/examples/ts/bob_token_contract/index.ts @@ -15,16 +15,20 @@ async function getBalances( await Promise.all([ contract.methods .public_balance_of(aliceAddress) - .simulate({ from: aliceAddress }), + .simulate({ from: aliceAddress }) + .then(({ result }) => result), contract.methods .private_balance_of(aliceAddress) - .simulate({ from: aliceAddress }), + .simulate({ from: aliceAddress }) + .then(({ result }) => result), contract.methods .public_balance_of(bobAddress) - .simulate({ from: bobAddress }), + .simulate({ from: bobAddress }) + .then(({ result }) => result), contract.methods .private_balance_of(bobAddress) - .simulate({ from: bobAddress }), + .simulate({ from: bobAddress }) + .then(({ result }) => result), ]).then( ([ alicePublicBalance, @@ -69,7 +73,7 @@ async function main() { const aliceAddress = aliceAccountManager.address; const bobClinicAddress = bobClinicAccountManager.address; - const bobToken = await BobTokenContract.deploy(wallet).send({ + const { contract: bobToken } = await BobTokenContract.deploy(wallet).send({ from: giggleAddress, }); diff --git a/docs/examples/ts/recursive_verification/index.ts b/docs/examples/ts/recursive_verification/index.ts index 43712fcd38eb..423d1c0d287d 100644 --- a/docs/examples/ts/recursive_verification/index.ts +++ b/docs/examples/ts/recursive_verification/index.ts @@ -65,7 +65,7 @@ async function main() { // Step 2: Deploy ValueNotEqual contract // Constructor args: initial counter (10), owner, VK hash - const valueNotEqual = await ValueNotEqualContract.deploy( + const { contract: valueNotEqual } = await ValueNotEqualContract.deploy( wallet, 10, // Initial counter value accounts[0].item, // Owner address @@ -84,9 +84,9 @@ async function main() { // Step 3: Read initial counter value // simulate() executes without submitting a transaction - let counterValue = await valueNotEqual.methods + let counterValue = (await valueNotEqual.methods .get_counter(accounts[0].item) - .simulate({ from: accounts[0].item }); + .simulate({ from: accounts[0].item })).result; console.log(`Counter value: ${counterValue}`); // Should be 10 // Step 4: Call increment() with proof data @@ -107,9 +107,9 @@ async function main() { await interaction.send(opts); // Step 6: Read updated counter - counterValue = await valueNotEqual.methods + counterValue = (await valueNotEqual.methods .get_counter(accounts[0].item) - .simulate({ from: accounts[0].item }); + .simulate({ from: accounts[0].item })).result; console.log(`Counter value: ${counterValue}`); // Should be 11 assert(counterValue === 11n, "Counter should be 11 after verification"); diff --git a/docs/examples/ts/token_bridge/index.ts b/docs/examples/ts/token_bridge/index.ts index 74ad78653034..81c410bae820 100644 --- a/docs/examples/ts/token_bridge/index.ts +++ b/docs/examples/ts/token_bridge/index.ts @@ -69,11 +69,11 @@ console.log(`NFTPortal: ${portalAddress}\n`); // docs:start:deploy_l2_contracts console.log("Deploying L2 contracts...\n"); -const l2Nft = await NFTPunkContract.deploy(aztecWallet, account.address).send({ +const { contract: l2Nft } = await NFTPunkContract.deploy(aztecWallet, account.address).send({ from: account.address, }); -const l2Bridge = await NFTBridgeContract.deploy( +const { contract: l2Bridge } = await NFTBridgeContract.deploy( aztecWallet, l2Nft.address, ).send({ from: account.address }); @@ -222,7 +222,7 @@ await mine2Blocks(aztecWallet, account.address); // Check notes before claiming (should be 0) console.log("Checking notes before claim..."); -const notesBefore = await l2Nft.methods +const { result: notesBefore } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesBefore}`); @@ -235,7 +235,7 @@ console.log("NFT claimed on L2\n"); // Check notes after claiming (should be 1) console.log("Checking notes after claim..."); -const notesAfterClaim = await l2Nft.methods +const { result: notesAfterClaim } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesAfterClaim}\n`); @@ -249,7 +249,7 @@ await mine2Blocks(aztecWallet, account.address); const recipientEthAddress = EthAddress.fromString(ownerEthAddress); -const exitReceipt = await l2Bridge.methods +const { receipt: exitReceipt } = await l2Bridge.methods .exit(new Fr(Number(tokenId)), recipientEthAddress) .send({ from: account.address }); @@ -257,7 +257,7 @@ console.log(`Exit message sent (block: ${exitReceipt.blockNumber})\n`); // Check notes after burning (should be 0 again) console.log("Checking notes after burn..."); -const notesAfterBurn = await l2Nft.methods +const { result: notesAfterBurn } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesAfterBurn}\n`); From 0f78a1eff0adab6b682b70215e8602befe26f197 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 09:04:33 +0000 Subject: [PATCH 014/119] fix more e2e tests --- .../src/e2e_crowdfunding_and_claim.test.ts | 8 +- .../end-to-end/src/e2e_partial_notes.test.ts | 3 +- .../src/e2e_scope_isolation.test.ts | 12 +- .../end-to-end/src/e2e_state_vars.test.ts | 136 ++++++++++++------ .../private_transfer_recursion.test.ts | 4 +- .../src/shared/gas_portal_test_harness.ts | 2 +- .../end-to-end/src/spartan/reorg.test.ts | 16 ++- 7 files changed, 118 insertions(+), 63 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index dc6b292382e8..d09907c60c22 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -146,10 +146,12 @@ describe('e2e_crowdfunding_and_claim', () => { } // Since the RWT is minted 1:1 with the DNT, the balance of the reward token should be equal to the donation amount - const balanceRWT = await rewardToken.methods.balance_of_public(donor1Address).simulate({ from: operatorAddress }); + const { result: balanceRWT } = await rewardToken.methods + .balance_of_public(donor1Address) + .simulate({ from: operatorAddress }); expect(balanceRWT).toEqual(donationAmount); - const balanceDNTBeforeWithdrawal = await donationToken.methods + const { result: balanceDNTBeforeWithdrawal } = await donationToken.methods .balance_of_private(operatorAddress) .simulate({ from: operatorAddress }); expect(balanceDNTBeforeWithdrawal).toEqual(0n); @@ -157,7 +159,7 @@ describe('e2e_crowdfunding_and_claim', () => { // 3) At last, we withdraw the raised funds from the crowdfunding contract to the operator's address await crowdfundingContract.methods.withdraw(donationAmount).send({ from: operatorAddress }); - const balanceDNTAfterWithdrawal = await donationToken.methods + const { result: balanceDNTAfterWithdrawal } = await donationToken.methods .balance_of_private(operatorAddress) .simulate({ from: operatorAddress }); diff --git a/yarn-project/end-to-end/src/e2e_partial_notes.test.ts b/yarn-project/end-to-end/src/e2e_partial_notes.test.ts index 104d7ef3b215..8742a20129a0 100644 --- a/yarn-project/end-to-end/src/e2e_partial_notes.test.ts +++ b/yarn-project/end-to-end/src/e2e_partial_notes.test.ts @@ -43,7 +43,8 @@ describe('partial notes', () => { it('mint to private', async () => { await mintTokensToPrivate(token0, adminAddress, liquidityProviderAddress, INITIAL_TOKEN_BALANCE); expect( - await token0.methods.balance_of_private(liquidityProviderAddress).simulate({ from: liquidityProviderAddress }), + (await token0.methods.balance_of_private(liquidityProviderAddress).simulate({ from: liquidityProviderAddress })) + .result, ).toEqual(INITIAL_TOKEN_BALANCE); }); }); diff --git a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts index b30a5cefdfe8..5c8e4e2fdfc2 100644 --- a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts +++ b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts @@ -32,7 +32,7 @@ describe('e2e scope isolation', () => { describe('external private', () => { it('owner can read own notes', async () => { - const value = await contract.methods.read_note(alice).simulate({ from: alice }); + const { result: value } = await contract.methods.read_note(alice).simulate({ from: alice }); expect(value).toEqual(ALICE_NOTE_VALUE); }); @@ -47,8 +47,8 @@ describe('e2e scope isolation', () => { }); it('each account can access their isolated state on a shared wallet', async () => { - const aliceValue = await contract.methods.read_note(alice).simulate({ from: alice }); - const bobValue = await contract.methods.read_note(bob).simulate({ from: bob }); + const { result: aliceValue } = await contract.methods.read_note(alice).simulate({ from: alice }); + const { result: bobValue } = await contract.methods.read_note(bob).simulate({ from: bob }); expect(aliceValue).toEqual(ALICE_NOTE_VALUE); expect(bobValue).toEqual(BOB_NOTE_VALUE); @@ -57,7 +57,7 @@ describe('e2e scope isolation', () => { describe('external utility', () => { it('owner can read own notes', async () => { - const value = await contract.methods.read_note_utility(alice).simulate({ from: alice }); + const { result: value } = await contract.methods.read_note_utility(alice).simulate({ from: alice }); expect(value).toEqual(ALICE_NOTE_VALUE); }); @@ -74,8 +74,8 @@ describe('e2e scope isolation', () => { }); it('each account can access their isolated state on a shared wallet', async () => { - const aliceValue = await contract.methods.read_note_utility(alice).simulate({ from: alice }); - const bobValue = await contract.methods.read_note_utility(bob).simulate({ from: bob }); + const { result: aliceValue } = await contract.methods.read_note_utility(alice).simulate({ from: alice }); + const { result: bobValue } = await contract.methods.read_note_utility(bob).simulate({ from: bob }); expect(aliceValue).toEqual(ALICE_NOTE_VALUE); expect(bobValue).toEqual(BOB_NOTE_VALUE); diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index bd1ccd15845b..8836ef6fe2d8 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -62,11 +62,13 @@ describe('e2e_state_vars', () => { // 2. A constrained private function that calls another private function that reads. // The indirect, adds 1 to the point to ensure that we are returning the correct value. - const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_private(), - contract.methods.get_public_immutable_constrained_private_indirect(), - contract.methods.get_public_immutable(), - ]).simulate({ from: defaultAccountAddress }); + const [a, b, c] = ( + await new BatchCall(wallet, [ + contract.methods.get_public_immutable_constrained_private(), + contract.methods.get_public_immutable_constrained_private_indirect(), + contract.methods.get_public_immutable(), + ]).simulate({ from: defaultAccountAddress }) + ).map((r: any) => r.result); expect(a).toEqual(c); expect(b).toEqual({ account: c.account, value: c.value + 1n }); @@ -79,11 +81,13 @@ describe('e2e_state_vars', () => { // 2. A constrained public function that calls another public function that reads. // The indirect, adds 1 to the point to ensure that we are returning the correct value. - const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_public(), - contract.methods.get_public_immutable_constrained_public_indirect(), - contract.methods.get_public_immutable(), - ]).simulate({ from: defaultAccountAddress }); + const [a, b, c] = ( + await new BatchCall(wallet, [ + contract.methods.get_public_immutable_constrained_public(), + contract.methods.get_public_immutable_constrained_public_indirect(), + contract.methods.get_public_immutable(), + ]).simulate({ from: defaultAccountAddress }) + ).map((r: any) => r.result); expect(a).toEqual(c); expect(b).toEqual({ account: c.account, value: c.value + 1n }); @@ -95,10 +99,10 @@ describe('e2e_state_vars', () => { // Reads the value using a utility function checking the return values with: // 1. A constrained public function that reads 5 times directly (going beyond the previous 4 Field return value) - const a = await contract.methods + const { result: a } = await contract.methods .get_public_immutable_constrained_public_multiple() .simulate({ from: defaultAccountAddress }); - const c = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); + const { result: c } = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); expect(a).toEqual([c, c, c, c, c]); }); @@ -115,9 +119,11 @@ describe('e2e_state_vars', () => { describe('PrivateMutable', () => { it('fail to read uninitialized PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); await expect( contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }), @@ -126,9 +132,11 @@ describe('e2e_state_vars', () => { it('initialize PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); // Send the transaction and wait for it to be mined (wait function throws if the tx is not mined) const { receipt: txReceipt } = await contract.methods @@ -140,33 +148,41 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the initializer expect(txEffects?.data.nullifiers.length).toEqual(2); expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('fail to reinitialize', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); await expect( contract.methods.initialize_private(RANDOMNESS, VALUE).send({ from: defaultAccountAddress }), ).rejects.toThrow(); expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('read initialized PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: { value }, @@ -176,9 +192,11 @@ describe('e2e_state_vars', () => { it('replace with same value', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) @@ -202,9 +220,11 @@ describe('e2e_state_vars', () => { it('replace PrivateMutable with other values', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS + 2n, VALUE + 1n) @@ -224,9 +244,11 @@ describe('e2e_state_vars', () => { it('replace PrivateMutable dependent on prior value', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) @@ -251,7 +273,11 @@ describe('e2e_state_vars', () => { describe('PrivateImmutable', () => { it('fail to read uninitialized PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); await expect( contract.methods.view_private_immutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }), @@ -260,7 +286,11 @@ describe('e2e_state_vars', () => { it('initialize PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); const { receipt: txReceipt } = await contract.methods .initialize_private_immutable(RANDOMNESS, VALUE) @@ -272,25 +302,41 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the initializer expect(txEffects?.data.nullifiers.length).toEqual(2); expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('fail to reinitialize', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); await expect( contract.methods.initialize_private_immutable(RANDOMNESS, VALUE).send({ from: defaultAccountAddress }), ).rejects.toThrow(); expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('read initialized PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: { value }, diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index 90bc22162730..ffa5688df3b5 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -69,7 +69,9 @@ describe('e2e_token_contract private transfer recursion', () => { // We should have created two new notes, one for the recipient and one for the sender (with the change) expect(txEffects!.data.noteHashes.length).toBe(2); - const senderBalance = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: senderBalance } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); expect(senderBalance).toEqual(expectedChange); const events = await wallet.getPrivateEvents(TokenContract.events.Transfer, { diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 4062e7def159..2e8426fa1351 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -131,7 +131,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness { } async getL2PublicBalanceOf(owner: AztecAddress) { - return await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner }); + return (await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner })).result; } async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { diff --git a/yarn-project/end-to-end/src/spartan/reorg.test.ts b/yarn-project/end-to-end/src/spartan/reorg.test.ts index 946f4f4a3b1a..e01d4ee06f20 100644 --- a/yarn-project/end-to-end/src/spartan/reorg.test.ts +++ b/yarn-project/end-to-end/src/spartan/reorg.test.ts @@ -32,16 +32,20 @@ const debugLogger = createLogger('e2e:spartan-test:reorg'); async function checkBalances(testAccounts: TestAccounts, mintAmount: bigint, totalAmountTransferred: bigint) { for (const acc of testAccounts.accounts) { expect( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ).toBe(mintAmount - totalAmountTransferred); } expect( - await testAccounts.tokenContract.methods - .balance_of_public(testAccounts.recipientAddress) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(testAccounts.recipientAddress) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ).toBe(totalAmountTransferred * BigInt(testAccounts.accounts.length)); } From 71c5d2552c120bd8d136b39029b56143245621ef Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 10:14:58 +0000 Subject: [PATCH 015/119] merge --- boxes/boxes/vanilla/scripts/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boxes/boxes/vanilla/scripts/deploy.ts b/boxes/boxes/vanilla/scripts/deploy.ts index b02edaa7e6b1..e0e0f971a739 100644 --- a/boxes/boxes/vanilla/scripts/deploy.ts +++ b/boxes/boxes/vanilla/scripts/deploy.ts @@ -71,7 +71,7 @@ async function deployContract(wallet: Wallet, deployer: AztecAddress) { const sponsoredPFCContract = await getSponsoredPFCContract(); - const contract = await PrivateVotingContract.deploy(wallet, deployer).send({ + const { contract } = await PrivateVotingContract.deploy(wallet, deployer).send({ from: deployer, contractAddressSalt: salt, fee: { From 6b58772c5aa6697451151c8f9cf5c1b85dd9795c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 10:51:25 +0000 Subject: [PATCH 016/119] another e2e fix --- boxes/boxes/vanilla/app/embedded-wallet.ts | 2 +- .../aztec.js/src/contract/batch_call.test.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boxes/boxes/vanilla/app/embedded-wallet.ts b/boxes/boxes/vanilla/app/embedded-wallet.ts index 04951e383ee9..bbf32271f6b5 100644 --- a/boxes/boxes/vanilla/app/embedded-wallet.ts +++ b/boxes/boxes/vanilla/app/embedded-wallet.ts @@ -162,7 +162,7 @@ export class EmbeddedWallet extends EmbeddedWalletBase { wait: { timeout: 120 }, }; - const receipt = await deployMethod.send(deployOpts); + const { receipt } = await deployMethod.send(deployOpts); logger.info('Account deployed', receipt); diff --git a/yarn-project/aztec.js/src/contract/batch_call.test.ts b/yarn-project/aztec.js/src/contract/batch_call.test.ts index 976884e68745..740105f9c9fd 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.test.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.test.ts @@ -166,13 +166,13 @@ describe('BatchCall', () => { expect(results).toHaveLength(4); // First utility - decoded from Fr[] to bigint (single field returns the value directly, not as array) - expect(results[0]).toEqual(utilityResult1.result[0].toBigInt()); + expect(results[0].result).toEqual(utilityResult1.result[0].toBigInt()); // Results[1] will be the decoded private values (decoded from privateReturnValues) - expect(results[1]).toEqual(privateReturnValues.map(v => v.toBigInt())); // Private call (decoded) + expect(results[1].result).toEqual(privateReturnValues.map(v => v.toBigInt())); // Private call (decoded) // Second utility - decoded from Fr[] to bigint - expect(results[2]).toEqual(utilityResult2.result[0].toBigInt()); + expect(results[2].result).toEqual(utilityResult2.result[0].toBigInt()); // Results[3] will be the decoded public value (single value is returned directly, not as array) - expect(results[3]).toEqual(publicReturnValues[0].toBigInt()); // Public call (decoded) + expect(results[3].result).toEqual(publicReturnValues[0].toBigInt()); // Public call (decoded) }); it('should handle only utility calls without calling simulateTx', async () => { @@ -215,8 +215,8 @@ describe('BatchCall', () => { // Verify results - decoded from Fr[] to bigint expect(results).toHaveLength(2); - expect(results[0]).toEqual(utilityResult1.result[0].toBigInt()); - expect(results[1]).toEqual(utilityResult2.result[0].toBigInt()); + expect(results[0].result).toEqual(utilityResult1.result[0].toBigInt()); + expect(results[1].result).toEqual(utilityResult2.result[0].toBigInt()); }); it('should handle only private/public calls using wallet.batch with simulateTx', async () => { @@ -259,8 +259,8 @@ describe('BatchCall', () => { // Verify results (decoded) expect(results).toHaveLength(2); - expect(results[0]).toEqual(privateReturnValues[0].toBigInt()); // Single value returned directly - expect(results[1]).toEqual(publicReturnValues[0].toBigInt()); // Single value returned directly + expect(results[0].result).toEqual(privateReturnValues[0].toBigInt()); // Single value returned directly + expect(results[1].result).toEqual(publicReturnValues[0].toBigInt()); // Single value returned directly }); it('should handle empty batch', async () => { From fe599bce66e85c948a0d146b174e2dd7ce28c782 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 11:48:49 +0000 Subject: [PATCH 017/119] fix e2es after rebase --- .../end-to-end/src/e2e_sequencer/reload_keystore.test.ts | 4 ++-- yarn-project/end-to-end/src/spartan/setup_test_wallets.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts index 09deed9db69d..667728c4056b 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts @@ -130,7 +130,7 @@ describe('e2e_reload_keystore', () => { // Send a tx and verify the block uses the initial coinbase const deployer = new ContractDeployer(artifact, wallet); - const sentTx1 = await deployer.deploy(ownerAddress, ownerAddress, 1).send({ + const { txHash: sentTx1 } = await deployer.deploy(ownerAddress, ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(1), skipClassPublication: true, @@ -197,7 +197,7 @@ describe('e2e_reload_keystore', () => { // Whichever validator is the proposer, its coinbase must be from the reloaded keystore. const allNewCoinbasesLower = newCoinbases.map(c => c.toString().toLowerCase()); - const sentTx2 = await deployer.deploy(ownerAddress, ownerAddress, 2).send({ + const { txHash: sentTx2 } = await deployer.deploy(ownerAddress, ownerAddress, 2).send({ from: ownerAddress, contractAddressSalt: new Fr(2), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index 9f5137fc4273..1f5f53fd61f5 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -130,7 +130,7 @@ async function deployAccountWithDiagnostics( const deployMethod = await account.getDeployMethod(); let txHash; try { - txHash = await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: NO_WAIT }); + ({ txHash } = await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: NO_WAIT })); await waitForTx(aztecNode, txHash, { timeout: 2400 }); logger.info(`${accountLabel} deployed at ${account.address}`); } catch (error) { From 37bca03c4142cac6dc291df5376179f7c7503d37 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 17:11:23 +0000 Subject: [PATCH 018/119] bring migration notes up to date --- .../docs/resources/migration_notes.md | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 6e34bfbb3e63..5fd494a86c41 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,78 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects + +All SDK interaction methods now return structured objects that include `offchainEffects` alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. + +**`simulate()` — always returns `{ result, offchainEffects }` object:** + +```diff +- const value = await contract.methods.foo(args).simulate({ from: sender }); ++ const { result: value } = await contract.methods.foo(args).simulate({ from: sender }); +``` + +When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` are also available as optional fields on the same object: + +```diff +- const { stats, estimatedGas } = await contract.methods.foo(args).simulate({ ++ const sim = await contract.methods.foo(args).simulate({ + from: sender, + includeMetadata: true, + }); ++ const stats = sim.stats!; ++ const estimatedGas = sim.estimatedGas!; +``` + +`SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields. + +**`send()` — returns `{ receipt, offchainEffects }` object:** + +```diff +- const receipt = await contract.methods.foo(args).send({ from: sender }); ++ const { receipt } = await contract.methods.foo(args).send({ from: sender }); +``` + +When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `TxHash`: + +```diff +- const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ++ const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); +``` + +**Deploy — returns `{ contract, receipt, offchainEffects }` object:** + +```diff +- const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); ++ const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); +``` + +The deploy receipt is also available via `receipt` if needed (e.g. for `receipt.txHash` or `receipt.transactionFee`). + +**Custom wallet implementations — `sendTx()` must return objects:** + +If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): + +```diff + async sendTx(executionPayload, opts) { + const provenTx = await this.pxe.proveTx(...); ++ const offchainEffects = provenTx.getOffchainEffects(); + const tx = await provenTx.toTx(); + const txHash = tx.getTxHash(); + await this.aztecNode.sendTx(tx); + + if (opts.wait === NO_WAIT) { +- return txHash; ++ return { txHash, offchainEffects }; + } + const receipt = await waitForTx(this.aztecNode, txHash, opts.wait); +- return receipt; ++ return { receipt, offchainEffects }; + } +``` + +**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. + ### `aztec new` and `aztec init` now create a 2-crate workspace `aztec new` and `aztec init` now create a workspace with two crates instead of a single contract crate: @@ -101,78 +173,6 @@ The `simulateUtility` method and related types have been renamed to `executeUtil + let result = env.execute_utility(my_contract_address, selector); ``` -### [Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects - -All SDK interaction methods now return structured objects that include `offchainEffects` alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. - -**`simulate()` — always returns `{ result, offchainEffects }` object:** - -```diff -- const value = await contract.methods.foo(args).simulate({ from: sender }); -+ const { result: value } = await contract.methods.foo(args).simulate({ from: sender }); -``` - -When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` are also available as optional fields on the same object: - -```diff -- const { stats, estimatedGas } = await contract.methods.foo(args).simulate({ -+ const sim = await contract.methods.foo(args).simulate({ - from: sender, - includeMetadata: true, - }); -+ const stats = sim.stats!; -+ const estimatedGas = sim.estimatedGas!; -``` - -`SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields. - -**`send()` — returns `{ receipt, offchainEffects }` object:** - -```diff -- const receipt = await contract.methods.foo(args).send({ from: sender }); -+ const { receipt } = await contract.methods.foo(args).send({ from: sender }); -``` - -When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `TxHash`: - -```diff -- const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); -+ const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); -``` - -**Deploy — returns `{ contract, receipt, offchainEffects }` object:** - -```diff -- const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); -+ const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); -``` - -The deploy receipt is also available via `receipt` if needed (e.g. for `receipt.txHash` or `receipt.transactionFee`). - -**Custom wallet implementations — `sendTx()` must return objects:** - -If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): - -```diff - async sendTx(executionPayload, opts) { - const provenTx = await this.pxe.proveTx(...); -+ const offchainEffects = provenTx.getOffchainEffects(); - const tx = await provenTx.toTx(); - const txHash = tx.getTxHash(); - await this.aztecNode.sendTx(tx); - - if (opts.wait === NO_WAIT) { -- return txHash; -+ return { txHash, offchainEffects }; - } - const receipt = await waitForTx(this.aztecNode, txHash, opts.wait); -- return receipt; -+ return { receipt, offchainEffects }; - } -``` - -**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. - ## 4.0.0-devnet.2-patch.0 ### [Protocol] `include_by_timestamp` renamed to `expiration_timestamp` From 0a7c6cb9c1ac83c7f8cde4550fddce6f53855eec Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 18:09:43 +0000 Subject: [PATCH 019/119] fix lint issue --- yarn-project/aztec.js/src/contract/deploy_method.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 6d2c423a9696..ed0de5ba6143 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -13,10 +13,8 @@ import { type Capsule, HashedValues, type OffchainEffect, - TxHash, type TxProfileResult, type TxReceipt, - collectOffchainEffects, } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; From 68029232b15cc9d17e1aec0573ca4ae164197d77 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 18:30:03 +0000 Subject: [PATCH 020/119] fix new e2es --- yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts | 6 +++--- yarn-project/end-to-end/src/e2e_custom_message.test.ts | 6 +++--- yarn-project/end-to-end/src/e2e_pxe.test.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts index 4256ab0ca551..2bc0799dacde 100644 --- a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts +++ b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts @@ -604,12 +604,12 @@ describe('HA Full Setup', () => { skipInstancePublication: true, wait: { returnReceipt: true }, }); - expect(receipt.blockNumber).toBeDefined(); - const [block] = await aztecNode.getCheckpointedBlocks(receipt.blockNumber!, 1); + expect(receipt.receipt.blockNumber).toBeDefined(); + const [block] = await aztecNode.getCheckpointedBlocks(receipt.receipt.blockNumber!, 1); const [cp] = await aztecNode.getCheckpoints(block!.checkpointNumber, 1); const att = cp.attestations.filter(a => !a.signature.isEmpty()); expect(att.length).toBeGreaterThanOrEqual(quorum); - logger.info(`Phase 2: block ${receipt.blockNumber}, ${att.length} attestations (quorum ${quorum})`); + logger.info(`Phase 2: block ${receipt.receipt.blockNumber}, ${att.length} attestations (quorum ${quorum})`); } finally { // Restore each node's saved initial keystore so subsequent tests see original state for (let i = 0; i < NODE_COUNT; i++) { diff --git a/yarn-project/end-to-end/src/e2e_custom_message.test.ts b/yarn-project/end-to-end/src/e2e_custom_message.test.ts index ce29ec91dae1..0ff387019d2e 100644 --- a/yarn-project/end-to-end/src/e2e_custom_message.test.ts +++ b/yarn-project/end-to-end/src/e2e_custom_message.test.ts @@ -26,7 +26,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { accounts: [account], } = await setup(1)); await ensureAccountContractsPublished(wallet, [account]); - contract = await CustomMessageContract.deploy(wallet).send({ from: account }); + ({ contract } = await CustomMessageContract.deploy(wallet).send({ from: account })); }); afterAll(() => teardown()); @@ -34,7 +34,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { it('reassembles a multi-log event from multiple private logs', async () => { const values = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; - const tx = await contract.methods + const { receipt: tx } = await contract.methods .emit_multi_log_event(values[0], values[1], values[2], values[3], account) .send({ from: account }); @@ -56,7 +56,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { const valuesA = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; const valuesB = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; - const tx = await new BatchCall(wallet, [ + const { receipt: tx } = await new BatchCall(wallet, [ contract.methods.emit_multi_log_event(valuesA[0], valuesA[1], valuesA[2], valuesA[3], account), contract.methods.emit_multi_log_event(valuesB[0], valuesB[1], valuesB[2], valuesB[3], account), ]).send({ from: account }); diff --git a/yarn-project/end-to-end/src/e2e_pxe.test.ts b/yarn-project/end-to-end/src/e2e_pxe.test.ts index 5f86fc373e11..efeb60c27cbe 100644 --- a/yarn-project/end-to-end/src/e2e_pxe.test.ts +++ b/yarn-project/end-to-end/src/e2e_pxe.test.ts @@ -21,7 +21,7 @@ describe('e2e_pxe', () => { wallet, accounts: [defaultAccountAddress], } = await setup()); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); From 1303d5fa3580cb0d85ce2b67ad1df487785fcdc0 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 25 Feb 2026 17:41:27 +0000 Subject: [PATCH 021/119] trivial versions of offchain enqueue and inbox --- noir-projects/noir-contracts/Nargo.toml | 1 + .../test/offchain_payment_contract/Nargo.toml | 9 ++ .../offchain_payment_contract/src/main.nr | 112 ++++++++++++++++++ .../src/e2e_offchain_payment_qr.test.ts | 78 ++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 noir-projects/noir-contracts/contracts/test/offchain_payment_contract/Nargo.toml create mode 100644 noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr create mode 100644 yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index 1f4341028648..9fec6b305d15 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -48,6 +48,7 @@ members = [ "contracts/test/no_constructor_contract", "contracts/test/note_getter_contract", "contracts/test/offchain_effect_contract", + "contracts/test/offchain_payment_contract", "contracts/test/only_self_contract", "contracts/test/oracle_version_check_contract", "contracts/test/parent_contract", diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/Nargo.toml new file mode 100644 index 000000000000..3cc953ef4a37 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "offchain_payment_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../../aztec-nr/aztec" } +balance_set = { path = "../../../../aztec-nr/balance-set" } diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr new file mode 100644 index 000000000000..54fa9f935a75 --- /dev/null +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -0,0 +1,112 @@ +use aztec::macros::aztec; + +#[aztec] +contract OffchainPayment { + use aztec::{ + capsules::CapsuleArray, + context::UtilityContext, + macros::{functions::external, storage::storage}, + messages::{ + discovery::{CustomMessageHandler, process_message::process_message_ciphertext}, + encoding::MESSAGE_CIPHERTEXT_LEN, + message_delivery::MessageDelivery, + processing::{MessageContext, validate_and_store_enqueued_notes_and_events}, + }, + protocol::{ + address::AztecAddress, + constants::MAX_NOTE_HASHES_PER_TX, + hash::sha256_to_field, + traits::{Deserialize, Serialize}, + }, + state_vars::Owned, + }; + + use balance_set::BalanceSet; + + pub global OFFCHAIN_INBOX_SLOT: Field = + sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_INBOX_SLOT".as_bytes()); + + #[derive(Serialize, Deserialize)] + pub struct PendingOffchainMsg { + pub ciphertext: BoundedVec, + pub recipient: AztecAddress, + // Use 0 as a sentinel for tx-less messages. + pub tx_hash: Field, + pub unique_note_hashes_in_tx: BoundedVec, + pub first_nullifier_in_tx: Field, + } + + #[storage] + struct Storage { + balances: Owned, Context>, + } + + #[external("private")] + fn mint(amount: u128, recipient: AztecAddress) { + // Minted notes are delivered onchain to ensure the recipient can always discover them. + self.storage.balances.at(recipient).add(amount).deliver(MessageDelivery.ONCHAIN_CONSTRAINED); + } + + #[external("private")] + fn transfer_offchain(amount: u128, recipient: AztecAddress) { + let sender = self.msg_sender(); + + // Sender change note and recipient note are both delivered offchain. + self.storage.balances.at(sender).sub(amount).deliver(MessageDelivery.OFFCHAIN); + + self.storage.balances.at(recipient).add(amount).deliver(MessageDelivery.OFFCHAIN); + } + + #[external("utility")] + unconstrained fn get_balance(owner: AztecAddress) -> u128 { + self.storage.balances.at(owner).balance_of() + } + + // Enqueue an offchain-delivered message into the contract-owned inbox. + #[external("utility")] + unconstrained fn offchain_enqueue( + ciphertext: BoundedVec, + recipient: AztecAddress, + tx_hash: Field, + unique_note_hashes_in_tx: BoundedVec, + first_nullifier_in_tx: Field, + ) { + let addr = UtilityContext::new().this_address(); + CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT).push( + PendingOffchainMsg { + ciphertext, + recipient, + tx_hash, + unique_note_hashes_in_tx, + first_nullifier_in_tx, + }, + ); + } + + // Naive inbox sync: processes all pending messages using the stored tx context. + #[external("utility")] + unconstrained fn offchain_sync_inbox() { + let addr = UtilityContext::new().this_address(); + let inbox = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); + + inbox.for_each(|i, msg: PendingOffchainMsg| { + let message_context = MessageContext { + tx_hash: msg.tx_hash, + unique_note_hashes_in_tx: msg.unique_note_hashes_in_tx, + first_nullifier_in_tx: msg.first_nullifier_in_tx, + recipient: msg.recipient, + }; + + process_message_ciphertext( + addr, + _compute_note_hash_and_nullifier, + Option::>::none(), + msg.ciphertext, + message_context, + ); + + validate_and_store_enqueued_notes_and_events(addr); + inbox.remove(i); + }); + } +} diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts new file mode 100644 index 000000000000..981048aa284a --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -0,0 +1,78 @@ +import { AztecAddress } from '@aztec/aztec.js/addresses'; +import type { AztecNode } from '@aztec/aztec.js/node'; +import { PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants'; +import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; +import { MessageContext } from '@aztec/stdlib/logs'; +import { OFFCHAIN_MESSAGE_IDENTIFIER } from '@aztec/stdlib/tx'; + +import { jest } from '@jest/globals'; + +import { setup } from './fixtures/utils.js'; +import type { TestWallet } from './test-wallet/test_wallet.js'; +import { proveInteraction } from './test-wallet/utils.js'; + +const TIMEOUT = 120_000; + +describe('e2e_offchain_payment_qr', () => { + let contract: OffchainPaymentContract; + let aztecNode: AztecNode; + + let wallet: TestWallet; + let accounts: AztecAddress[]; + let teardown: () => Promise; + + jest.setTimeout(TIMEOUT); + + beforeAll(async () => { + ({ teardown, wallet, accounts, aztecNode } = await setup(2)); + ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); + }); + + afterAll(() => teardown()); + + it('processes an offchain-delivered private payment via QR-style handoff', async () => { + const [alice, bob] = accounts; + + const mintAmount = 100n; + const paymentAmount = 40n; + + // Mint to Alice using onchain delivery so she can spend the note. + await contract.methods.mint(mintAmount, alice).send({ from: alice }); + + // Alice prepares the private transfer which emits offchain effects. + const provenTx = await proveInteraction(wallet, contract.methods.transfer_offchain(paymentAmount, bob), { + from: alice, + }); + const { txHash } = await provenTx.send(); + + const offchainEffects = provenTx.offchainEffects; + expect(offchainEffects.length).toBeGreaterThan(0); + + // QR payload is the offchain effect for Bob. + const effectForBob = offchainEffects.find( + effect => effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER) && effect.data[1].equals(bob.toField()), + ); + expect(effectForBob).toBeTruthy(); + + const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); + + // Bob processes the message after the tx is available. + const txEffect = (await aztecNode.getTxEffect(txHash))!.data; + const messageContext = MessageContext.fromTxEffectAndRecipient(txEffect, bob); + + await contract.methods + .offchain_enqueue( + ciphertext, + bob, + messageContext.txHash.hash, + messageContext.uniqueNoteHashesInTx, + messageContext.firstNullifierInTx, + ) + .simulate({ from: bob }); + + await contract.methods.offchain_sync_inbox().simulate({ from: bob }); + + const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); + expect(bobBalance).toBe(paymentAmount); + }); +}); From 0598298edbc4d21fff22c4e5c8f8e4b9e5e062f5 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 25 Feb 2026 19:04:01 +0000 Subject: [PATCH 022/119] resolve message context via oracles --- .../processing/message_context_response.nr | 10 +++ .../aztec/src/messages/processing/mod.nr | 19 +++++ .../aztec/src/oracle/message_processing.nr | 19 +++++ .../offchain_payment_contract/src/main.nr | 83 +++++++++++++------ .../src/e2e_offchain_payment_qr.test.ts | 20 +---- .../noir-structs/message_context_response.ts | 55 ++++++++++++ .../oracle/interfaces.ts | 5 ++ .../oracle/oracle.ts | 13 +++ .../oracle/utility_execution_oracle.ts | 55 ++++++++++++ yarn-project/txe/src/rpc_translator.ts | 18 ++++ 10 files changed, 253 insertions(+), 44 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/messages/processing/message_context_response.nr create mode 100644 yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context_response.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context_response.nr new file mode 100644 index 000000000000..3ea2db1c5150 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context_response.nr @@ -0,0 +1,10 @@ +use crate::protocol::constants::MAX_NOTE_HASHES_PER_TX; +use crate::protocol::traits::Deserialize; + +/// Response containing the transaction context needed to process an offchain-delivered message. +#[derive(Deserialize, Eq)] +pub struct MessageContextResponse { + pub tx_hash: Field, + pub unique_note_hashes_in_tx: BoundedVec, + pub first_nullifier_in_tx: Field, +} diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index 3f7fa63a0291..8a2d67cd73c0 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -2,6 +2,8 @@ pub(crate) mod event_validation_request; mod message_context; pub use message_context::MessageContext; +mod message_context_response; +pub use message_context_response::MessageContextResponse; pub(crate) mod note_validation_request; pub(crate) mod log_retrieval_request; @@ -157,6 +159,23 @@ pub unconstrained fn validate_and_store_enqueued_notes_and_events(contract_addre ); } +/// Resolves message contexts for a list of tx hashes stored in a CapsuleArray. +/// +/// The `message_context_requests_array_base_slot` must point to a CapsuleArray containing tx hashes. +/// PXE will store `Option` values into the responses array at +/// `message_context_responses_array_base_slot`. +pub unconstrained fn resolve_message_contexts( + contract_address: AztecAddress, + message_context_requests_array_base_slot: Field, + message_context_responses_array_base_slot: Field, +) { + oracle::message_processing::resolve_message_contexts( + contract_address, + message_context_requests_array_base_slot, + message_context_responses_array_base_slot, + ); +} + /// Efficiently queries the node for logs that result in the completion of all `DeliveredPendingPartialNote`s stored in /// a `CapsuleArray` by performing all node communication concurrently. Returns a second `CapsuleArray` with Options /// for the responses that correspond to the pending partial notes at the same index. diff --git a/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr b/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr index d0582872b23d..94cd8432d90e 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr @@ -48,3 +48,22 @@ unconstrained fn bulk_retrieve_logs_oracle( log_retrieval_requests_array_base_slot: Field, log_retrieval_responses_array_base_slot: Field, ) {} + +pub(crate) unconstrained fn resolve_message_contexts( + contract_address: AztecAddress, + message_context_requests_array_base_slot: Field, + message_context_responses_array_base_slot: Field, +) { + resolve_message_contexts_oracle( + contract_address, + message_context_requests_array_base_slot, + message_context_responses_array_base_slot, + ); +} + +#[oracle(utilityResolveMessageContexts)] +unconstrained fn resolve_message_contexts_oracle( + contract_address: AztecAddress, + message_context_requests_array_base_slot: Field, + message_context_responses_array_base_slot: Field, +) {} diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr index 54fa9f935a75..c0b921b0cfa5 100644 --- a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -10,14 +10,12 @@ contract OffchainPayment { discovery::{CustomMessageHandler, process_message::process_message_ciphertext}, encoding::MESSAGE_CIPHERTEXT_LEN, message_delivery::MessageDelivery, - processing::{MessageContext, validate_and_store_enqueued_notes_and_events}, - }, - protocol::{ - address::AztecAddress, - constants::MAX_NOTE_HASHES_PER_TX, - hash::sha256_to_field, - traits::{Deserialize, Serialize}, + processing::{ + MessageContext, MessageContextResponse, resolve_message_contexts, + validate_and_store_enqueued_notes_and_events, + }, }, + protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, state_vars::Owned, }; @@ -25,6 +23,12 @@ contract OffchainPayment { pub global OFFCHAIN_INBOX_SLOT: Field = sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_INBOX_SLOT".as_bytes()); + pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field( + "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes(), + ); + pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( + "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes(), + ); #[derive(Serialize, Deserialize)] pub struct PendingOffchainMsg { @@ -32,8 +36,6 @@ contract OffchainPayment { pub recipient: AztecAddress, // Use 0 as a sentinel for tx-less messages. pub tx_hash: Field, - pub unique_note_hashes_in_tx: BoundedVec, - pub first_nullifier_in_tx: Field, } #[storage] @@ -68,32 +70,61 @@ contract OffchainPayment { ciphertext: BoundedVec, recipient: AztecAddress, tx_hash: Field, - unique_note_hashes_in_tx: BoundedVec, - first_nullifier_in_tx: Field, ) { let addr = UtilityContext::new().this_address(); CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT).push( - PendingOffchainMsg { - ciphertext, - recipient, - tx_hash, - unique_note_hashes_in_tx, - first_nullifier_in_tx, - }, + PendingOffchainMsg { ciphertext, recipient, tx_hash }, ); } - // Naive inbox sync: processes all pending messages using the stored tx context. + // Naive inbox sync: resolves tx context via oracle and processes any ready messages. #[external("utility")] unconstrained fn offchain_sync_inbox() { let addr = UtilityContext::new().this_address(); - let inbox = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); + let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); + let requests: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_CONTEXT_REQUESTS_SLOT); + + // Clear any stale requests from a previous run. + requests.for_each(|i, _| { requests.remove(i); }); + + // Build a request list aligned with the inbox indices. + let mut i = 0; + let inbox_len = inbox.len(); + while i < inbox_len { + let msg = inbox.get(i); + requests.push(msg.tx_hash); + i += 1; + } + + // Ask PXE to resolve contexts for all requested tx hashes. + resolve_message_contexts( + addr, + OFFCHAIN_CONTEXT_REQUESTS_SLOT, + OFFCHAIN_CONTEXT_RESPONSES_SLOT, + ); + + let responses: CapsuleArray> = + CapsuleArray::at(addr, OFFCHAIN_CONTEXT_RESPONSES_SLOT); + assert_eq(responses.len(), inbox_len); + + let mut j = inbox_len; + while j > 0 { + j -= 1; + let maybe_ctx = responses.get(j); + // Clear response entry as we go so the array is empty for the next run. + responses.remove(j); + + let msg = inbox.get(j); + if maybe_ctx.is_none() { + // Tx not yet available; keep the message in the inbox. + continue; + } - inbox.for_each(|i, msg: PendingOffchainMsg| { + let ctx = maybe_ctx.unwrap(); let message_context = MessageContext { - tx_hash: msg.tx_hash, - unique_note_hashes_in_tx: msg.unique_note_hashes_in_tx, - first_nullifier_in_tx: msg.first_nullifier_in_tx, + tx_hash: ctx.tx_hash, + unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx, + first_nullifier_in_tx: ctx.first_nullifier_in_tx, recipient: msg.recipient, }; @@ -106,7 +137,7 @@ contract OffchainPayment { ); validate_and_store_enqueued_notes_and_events(addr); - inbox.remove(i); - }); + inbox.remove(j); + } } } diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts index 981048aa284a..a8503269d5ae 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -1,8 +1,6 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; -import type { AztecNode } from '@aztec/aztec.js/node'; import { PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; -import { MessageContext } from '@aztec/stdlib/logs'; import { OFFCHAIN_MESSAGE_IDENTIFIER } from '@aztec/stdlib/tx'; import { jest } from '@jest/globals'; @@ -15,8 +13,6 @@ const TIMEOUT = 120_000; describe('e2e_offchain_payment_qr', () => { let contract: OffchainPaymentContract; - let aztecNode: AztecNode; - let wallet: TestWallet; let accounts: AztecAddress[]; let teardown: () => Promise; @@ -24,7 +20,7 @@ describe('e2e_offchain_payment_qr', () => { jest.setTimeout(TIMEOUT); beforeAll(async () => { - ({ teardown, wallet, accounts, aztecNode } = await setup(2)); + ({ teardown, wallet, accounts } = await setup(2)); ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); }); @@ -56,19 +52,7 @@ describe('e2e_offchain_payment_qr', () => { const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); - // Bob processes the message after the tx is available. - const txEffect = (await aztecNode.getTxEffect(txHash))!.data; - const messageContext = MessageContext.fromTxEffectAndRecipient(txEffect, bob); - - await contract.methods - .offchain_enqueue( - ciphertext, - bob, - messageContext.txHash.hash, - messageContext.uniqueNoteHashesInTx, - messageContext.firstNullifierInTx, - ) - .simulate({ from: bob }); + await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); await contract.methods.offchain_sync_inbox().simulate({ from: bob }); diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts new file mode 100644 index 000000000000..2a5a7671b40e --- /dev/null +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts @@ -0,0 +1,55 @@ +import { MAX_NOTE_HASHES_PER_TX } from '@aztec/constants'; +import { range } from '@aztec/foundation/array'; +import { Fr } from '@aztec/foundation/curves/bn254'; +import type { TxHash } from '@aztec/stdlib/tx'; + +/** + * Intermediate struct used to return resolved message contexts from PXE. The + * `utilityResolveMessageContexts` oracle stores values of this type in a CapsuleArray. + */ +export class MessageContextResponse { + constructor( + public txHash: TxHash, + public uniqueNoteHashesInTx: Fr[], + public firstNullifierInTx: Fr, + ) {} + + toFields(): Fr[] { + return [ + this.txHash.hash, + ...serializeBoundedVec(this.uniqueNoteHashesInTx, MAX_NOTE_HASHES_PER_TX), + this.firstNullifierInTx, + ]; + } + + static toEmptyFields(): Fr[] { + const serializationLen = + 1 /* txHash */ + MAX_NOTE_HASHES_PER_TX + 1 /* uniqueNoteHashesInTx BVec */ + 1; /* firstNullifierInTx */ + return range(serializationLen).map(_ => Fr.zero()); + } + + static toSerializedOption(response: MessageContextResponse | null): Fr[] { + if (response) { + return [new Fr(1), ...response.toFields()]; + } else { + return [new Fr(0), ...MessageContextResponse.toEmptyFields()]; + } + } +} + +/** + * Helper function to serialize a bounded vector according to Noir's BoundedVec format + * @param values - The values to serialize + * @param maxLength - The maximum length of the bounded vector + * @returns The serialized bounded vector as Fr[] + */ +function serializeBoundedVec(values: Fr[], maxLength: number): Fr[] { + if (values.length > maxLength) { + throw new Error(`Attempted to serialize ${values} values into a BoundedVec with max length ${maxLength}`); + } + + const lengthDiff = maxLength - values.length; + const zeroPaddingArray = Array(lengthDiff).fill(Fr.ZERO); + const storage = values.concat(zeroPaddingArray); + return [...storage, new Fr(values.length)]; +} diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/interfaces.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/interfaces.ts index 5d1e5a656cfb..323f0a9e2ef3 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/interfaces.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/interfaces.ts @@ -127,6 +127,11 @@ export interface IUtilityExecutionOracle { logRetrievalRequestsArrayBaseSlot: Fr, logRetrievalResponsesArrayBaseSlot: Fr, ): Promise; + utilityResolveMessageContexts( + contractAddress: AztecAddress, + messageContextRequestsArrayBaseSlot: Fr, + messageContextResponsesArrayBaseSlot: Fr, + ): Promise; utilityStoreCapsule(contractAddress: AztecAddress, key: Fr, capsule: Fr[]): Promise; utilityLoadCapsule(contractAddress: AztecAddress, key: Fr): Promise; utilityDeleteCapsule(contractAddress: AztecAddress, key: Fr): Promise; diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts index ebfc888b4eab..ca545910b5b5 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts @@ -535,6 +535,19 @@ export class Oracle { return []; } + async utilityResolveMessageContexts( + [contractAddress]: ACVMField[], + [messageContextRequestsArrayBaseSlot]: ACVMField[], + [messageContextResponsesArrayBaseSlot]: ACVMField[], + ): Promise { + await this.handlerAsUtility().utilityResolveMessageContexts( + AztecAddress.fromString(contractAddress), + Fr.fromString(messageContextRequestsArrayBaseSlot), + Fr.fromString(messageContextResponsesArrayBaseSlot), + ); + return []; + } + async utilityStoreCapsule( [contractAddress]: ACVMField[], [slot]: ACVMField[], diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index f957d44326a8..eb62b3b01d1a 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -19,6 +19,7 @@ import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging'; import type { NoteStatus } from '@aztec/stdlib/note'; import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees'; import type { BlockHeader, Capsule } from '@aztec/stdlib/tx'; +import { TxHash } from '@aztec/stdlib/tx'; import type { AccessScopes } from '../../access_scopes.js'; import { createContractLogger, logContractMessage } from '../../contract_logging.js'; @@ -36,6 +37,7 @@ import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_ import { EventValidationRequest } from '../noir-structs/event_validation_request.js'; import { LogRetrievalRequest } from '../noir-structs/log_retrieval_request.js'; import { LogRetrievalResponse } from '../noir-structs/log_retrieval_response.js'; +import { MessageContextResponse } from '../noir-structs/message_context_response.js'; import { NoteValidationRequest } from '../noir-structs/note_validation_request.js'; import { UtilityContext } from '../noir-structs/utility_context.js'; import { pickNotes } from '../pick_notes.js'; @@ -546,6 +548,59 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra ); } + public async utilityResolveMessageContexts( + contractAddress: AztecAddress, + messageContextRequestsArrayBaseSlot: Fr, + messageContextResponsesArrayBaseSlot: Fr, + ) { + if (!this.contractAddress.equals(contractAddress)) { + throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`); + } + + const requestCapsules = await this.capsuleStore.readCapsuleArray( + contractAddress, + messageContextRequestsArrayBaseSlot, + this.jobId, + ); + + // TODO: optimize, we might be hitting the node to get the same txHash repeatedly + // TODO: we should be querying at the anchor block + const maybeMessageContexts = await Promise.all( + requestCapsules.map(async fields => { + // TODO: clarify that txHash=0 means no txHash + // TODO: think how to handle no tx case + const txHashField = fields[0] ?? Fr.ZERO; + if (txHashField.isZero()) { + return null; + } + + const txHash = TxHash.fromField(txHashField); + const txEffect = await this.aztecNode.getTxEffect(txHash); + if (!txEffect) { + return null; + } + + const data = txEffect.data; + if (data.nullifiers.length === 0) { + return null; + } + + return new MessageContextResponse(data.txHash, data.noteHashes, data.nullifiers[0]); + }), + ); + + // Requests are cleared once we're done. + await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); + + // Store Option in the response capsule array. + await this.capsuleStore.setCapsuleArray( + contractAddress, + messageContextResponsesArrayBaseSlot, + maybeMessageContexts.map(MessageContextResponse.toSerializedOption), + this.jobId, + ); + } + public utilityStoreCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 95995654f675..149b6daaf6ff 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -750,6 +750,24 @@ export class RPCTranslator { return toForeignCallResult([]); } + public async utilityResolveMessageContexts( + foreignContractAddress: ForeignCallSingle, + foreignMessageContextRequestsArrayBaseSlot: ForeignCallSingle, + foreignMessageContextResponsesArrayBaseSlot: ForeignCallSingle, + ) { + const contractAddress = AztecAddress.fromField(fromSingle(foreignContractAddress)); + const messageContextRequestsArrayBaseSlot = fromSingle(foreignMessageContextRequestsArrayBaseSlot); + const messageContextResponsesArrayBaseSlot = fromSingle(foreignMessageContextResponsesArrayBaseSlot); + + await this.handlerAsUtility().utilityResolveMessageContexts( + contractAddress, + messageContextRequestsArrayBaseSlot, + messageContextResponsesArrayBaseSlot, + ); + + return toForeignCallResult([]); + } + async utilityStoreCapsule( foreignContractAddress: ForeignCallSingle, foreignSlot: ForeignCallSingle, From 1196587975e84a06beb8a6b2ac5e2ee4abeec4b2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Thu, 26 Feb 2026 11:56:49 +0000 Subject: [PATCH 023/119] customizable offchain discovery --- .../aztec-nr/aztec/src/macros/aztec.nr | 39 +++- .../aztec/src/messages/discovery/mod.nr | 30 ++- .../messages/processing/message_context.nr | 4 +- .../aztec-nr/aztec/src/oracle/version.nr | 2 +- .../offchain_payment_contract/src/main.nr | 184 +++++++++--------- .../src/e2e_offchain_payment_qr.test.ts | 13 +- yarn-project/pxe/src/oracle_version.ts | 4 +- 7 files changed, 169 insertions(+), 107 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 233dd942a0a2..a29c7dfda040 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -13,7 +13,7 @@ use crate::{ module_has_storage, }, }, - messages::discovery::CustomMessageHandler, + messages::discovery::{CustomMessageHandler, DiscoverOffchainMessages}, }; /// Configuration for the [`aztec`] macro. @@ -30,6 +30,7 @@ use crate::{ /// ``` pub struct AztecConfig { custom_message_handler: Option>, + discover_offchain_messages: Option>, } impl AztecConfig { @@ -38,7 +39,7 @@ impl AztecConfig { /// Calling `new` is equivalent to invoking the [`aztec`] macro with no parameters. The different methods /// (e.g. [`AztecConfig::custom_message_handler`]) can then be used to change the default behavior. pub comptime fn new() -> Self { - Self { custom_message_handler: Option::none() } + Self { custom_message_handler: Option::none(), discover_offchain_messages: Option::none() } } /// Sets a handler for custom messages. @@ -46,10 +47,20 @@ impl AztecConfig { /// This enables contracts to process non-standard messages (i.e. any with a message type that is not in /// [`crate::messages::msg_type`]). /// - /// `handler` must be a `#[contract_library_method]` function that conforms to the + /// `handler` must be a function that conforms to the /// [`crate::messages::discovery::CustomMessageHandler`] type signature. - pub comptime fn custom_message_handler(_self: Self, handler: CustomMessageHandler<()>) -> Self { - Self { custom_message_handler: Option::some(handler) } + pub comptime fn custom_message_handler(self: Self, handler: CustomMessageHandler<()>) -> Self { + Self { + custom_message_handler: Option::some(handler), + discover_offchain_messages: self.discover_offchain_messages, + } + } + + /// Sets a handler that supplies offchain-delivered messages during `sync_state`. + /// + /// `hook` must be a function that conforms to the [`DiscoverOffchainMessages`] type signature. + pub comptime fn discover_offchain_messages(self: Self, hook: DiscoverOffchainMessages<()>) -> Self { + Self { custom_message_handler: self.custom_message_handler, discover_offchain_messages: Option::some(hook) } } } @@ -115,8 +126,18 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote { Option::>::none() } }; + let discover_offchain_messages_option = if config.discover_offchain_messages.is_some() { + let hook = config.discover_offchain_messages.unwrap(); + quote { Option::some($hook) } + } else { + quote { Option::>::none() } + }; + let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { - generate_sync_state(process_custom_message_option) + generate_sync_state( + process_custom_message_option, + discover_offchain_messages_option, + ) } else { quote {} }; @@ -337,7 +358,10 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - } /// Generates the `sync_state` utility function that performs message discovery. -comptime fn generate_sync_state(process_custom_message_option: Quoted) -> Quoted { +comptime fn generate_sync_state( + process_custom_message_option: Quoted, + discover_offchain_messages_option: Quoted, +) -> Quoted { quote { pub struct sync_state_parameters {} @@ -353,6 +377,7 @@ comptime fn generate_sync_state(process_custom_message_option: Quoted) -> Quoted address, _compute_note_hash_and_nullifier, $process_custom_message_option, + $discover_offchain_messages_option, ); } } diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index ce53e2b854d3..e6def0ce4232 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -9,7 +9,7 @@ pub mod process_message; use crate::{ messages::{ discovery::process_message::process_message_ciphertext, - encoding::MAX_MESSAGE_CONTENT_LEN, + encoding::{MAX_MESSAGE_CONTENT_LEN, MESSAGE_CIPHERTEXT_LEN}, logs::note::MAX_NOTE_PACKED_LEN, processing::{ get_private_logs, MessageContext, pending_tagged_log::PendingTaggedLog, @@ -19,6 +19,9 @@ use crate::{ utils::array, }; +/// Max number of offchain messages that can be processed in a single sync. +pub global MAX_OFFCHAIN_MESSAGES_PER_SYNC: u32 = 128; + pub struct NoteHashAndNullifier { /// The result of NoteHash::compute_note_hash pub note_hash: Field, @@ -78,6 +81,11 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); +/// A handler that returns offchain-delivered messages to process during sync. +/// Each item is a `(message_ciphertext, message_context)` pair. +pub type DiscoverOffchainMessages = unconstrained fn[Env]( +/* contract_address */AztecAddress) -> BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC>; + /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private /// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. /// This is the mechanism via which a contract updates its knowledge of its private state. @@ -91,6 +99,7 @@ pub unconstrained fn do_sync_state, process_custom_message: Option>, + discover_offchain_messages: Option>, ) { debug_log("Performing state synchronization"); @@ -116,6 +125,25 @@ pub unconstrained fn do_sync_state, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/version.nr b/noir-projects/aztec-nr/aztec/src/oracle/version.nr index b979532dc7dc..aa288ba56c0f 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/version.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/version.nr @@ -4,7 +4,7 @@ /// /// @dev Whenever a contract function or Noir test is run, the `utilityAssertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -pub global ORACLE_VERSION: Field = 12; +pub global ORACLE_VERSION: Field = 13; /// Asserts that the version of the oracle is compatible with the version expected by the contract. pub fn assert_compatible_oracle_version() { diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr index c0b921b0cfa5..e613d8b279d7 100644 --- a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -1,43 +1,107 @@ +use aztec::{ + capsules::CapsuleArray, + messages::{ + discovery::MAX_OFFCHAIN_MESSAGES_PER_SYNC, + encoding::MESSAGE_CIPHERTEXT_LEN, + processing::{MessageContext, MessageContextResponse, resolve_message_contexts}, + }, + protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, +}; + +pub global OFFCHAIN_INBOX_SLOT: Field = + sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_INBOX_SLOT".as_bytes()); +pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field( + "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes(), +); +pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( + "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes(), +); + +#[derive(Serialize, Deserialize)] +pub struct PendingOffchainMsg { + pub ciphertext: BoundedVec, + pub recipient: AztecAddress, + // Use 0 as a sentinel for tx-less messages. + pub tx_hash: Field, +} + +unconstrained fn offchain_sync_inbox( + address: AztecAddress, + ) -> BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC> { + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); + + // Clear any stale requests from a previous run. + requests.for_each(|i, _| { requests.remove(i); }); + + // Build a request list aligned with the inbox indices. + let mut i = 0; + let inbox_len = inbox.len(); + while i < inbox_len { + let msg = inbox.get(i); + requests.push(msg.tx_hash); + i += 1; + } + + // Ask PXE to resolve contexts for all requested tx hashes. + resolve_message_contexts( + address, + OFFCHAIN_CONTEXT_REQUESTS_SLOT, + OFFCHAIN_CONTEXT_RESPONSES_SLOT, + ); + + let responses: CapsuleArray> = + CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); + assert_eq(responses.len(), inbox_len); + + let mut ready + : BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC> = + BoundedVec::new(); + + let mut j = inbox_len; + while j > 0 { + j -= 1; + let maybe_ctx = responses.get(j); + // Clear response entry as we go so the array is empty for the next run. + responses.remove(j); + + let msg = inbox.get(j); + if maybe_ctx.is_none() { + // Tx not yet available; keep the message in the inbox. + continue; + } + + let ctx = maybe_ctx.unwrap(); + let message_context = MessageContext { + tx_hash: ctx.tx_hash, + unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx, + first_nullifier_in_tx: ctx.first_nullifier_in_tx, + recipient: msg.recipient, + }; + + ready.push((msg.ciphertext, message_context)); + inbox.remove(j); + } + + ready +} + use aztec::macros::aztec; -#[aztec] +#[aztec(::aztec::macros::AztecConfig::new().discover_offchain_messages(crate::offchain_sync_inbox))] contract OffchainPayment { use aztec::{ capsules::CapsuleArray, context::UtilityContext, macros::{functions::external, storage::storage}, - messages::{ - discovery::{CustomMessageHandler, process_message::process_message_ciphertext}, - encoding::MESSAGE_CIPHERTEXT_LEN, - message_delivery::MessageDelivery, - processing::{ - MessageContext, MessageContextResponse, resolve_message_contexts, - validate_and_store_enqueued_notes_and_events, - }, - }, - protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, + messages::{encoding::MESSAGE_CIPHERTEXT_LEN, message_delivery::MessageDelivery}, + protocol::address::AztecAddress, state_vars::Owned, }; + use crate::{OFFCHAIN_INBOX_SLOT, PendingOffchainMsg}; use balance_set::BalanceSet; - pub global OFFCHAIN_INBOX_SLOT: Field = - sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_INBOX_SLOT".as_bytes()); - pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field( - "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes(), - ); - pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( - "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes(), - ); - - #[derive(Serialize, Deserialize)] - pub struct PendingOffchainMsg { - pub ciphertext: BoundedVec, - pub recipient: AztecAddress, - // Use 0 as a sentinel for tx-less messages. - pub tx_hash: Field, - } - #[storage] struct Storage { balances: Owned, Context>, @@ -76,68 +140,4 @@ contract OffchainPayment { PendingOffchainMsg { ciphertext, recipient, tx_hash }, ); } - - // Naive inbox sync: resolves tx context via oracle and processes any ready messages. - #[external("utility")] - unconstrained fn offchain_sync_inbox() { - let addr = UtilityContext::new().this_address(); - let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); - let requests: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_CONTEXT_REQUESTS_SLOT); - - // Clear any stale requests from a previous run. - requests.for_each(|i, _| { requests.remove(i); }); - - // Build a request list aligned with the inbox indices. - let mut i = 0; - let inbox_len = inbox.len(); - while i < inbox_len { - let msg = inbox.get(i); - requests.push(msg.tx_hash); - i += 1; - } - - // Ask PXE to resolve contexts for all requested tx hashes. - resolve_message_contexts( - addr, - OFFCHAIN_CONTEXT_REQUESTS_SLOT, - OFFCHAIN_CONTEXT_RESPONSES_SLOT, - ); - - let responses: CapsuleArray> = - CapsuleArray::at(addr, OFFCHAIN_CONTEXT_RESPONSES_SLOT); - assert_eq(responses.len(), inbox_len); - - let mut j = inbox_len; - while j > 0 { - j -= 1; - let maybe_ctx = responses.get(j); - // Clear response entry as we go so the array is empty for the next run. - responses.remove(j); - - let msg = inbox.get(j); - if maybe_ctx.is_none() { - // Tx not yet available; keep the message in the inbox. - continue; - } - - let ctx = maybe_ctx.unwrap(); - let message_context = MessageContext { - tx_hash: ctx.tx_hash, - unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx, - first_nullifier_in_tx: ctx.first_nullifier_in_tx, - recipient: msg.recipient, - }; - - process_message_ciphertext( - addr, - _compute_note_hash_and_nullifier, - Option::>::none(), - msg.ciphertext, - message_context, - ); - - validate_and_store_enqueued_notes_and_events(addr); - inbox.remove(j); - } - } } diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts index a8503269d5ae..d38778ae6c95 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -1,6 +1,9 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; +import type { AztecNode } from '@aztec/aztec.js/node'; import { PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants'; +import { retryUntil } from '@aztec/foundation/retry'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; +import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; import { OFFCHAIN_MESSAGE_IDENTIFIER } from '@aztec/stdlib/tx'; import { jest } from '@jest/globals'; @@ -13,6 +16,8 @@ const TIMEOUT = 120_000; describe('e2e_offchain_payment_qr', () => { let contract: OffchainPaymentContract; + let aztecNode: AztecNode; + let aztecNodeAdmin: AztecNodeAdmin; let wallet: TestWallet; let accounts: AztecAddress[]; let teardown: () => Promise; @@ -20,7 +25,7 @@ describe('e2e_offchain_payment_qr', () => { jest.setTimeout(TIMEOUT); beforeAll(async () => { - ({ teardown, wallet, accounts } = await setup(2)); + ({ teardown, wallet, accounts, aztecNode, aztecNodeAdmin } = await setup(2)); ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); }); @@ -54,7 +59,11 @@ describe('e2e_offchain_payment_qr', () => { await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); - await contract.methods.offchain_sync_inbox().simulate({ from: bob }); + // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. + const blockBefore = await aztecNode.getBlockNumber(); + await aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); + await retryUntil(async () => (await aztecNode.getBlockNumber()) > blockBefore, 'new L2 block', 30, 1); + await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobBalance).toBe(paymentAmount); diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index 5f6dd5abc335..7a590a2d3418 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -4,9 +4,9 @@ /// /// @dev Whenever a contract function or Noir test is run, the `utilityAssertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -export const ORACLE_VERSION = 12; +export const ORACLE_VERSION = 13; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`. -export const ORACLE_INTERFACE_HASH = '666a8a7fc697f72b29dbf0ae7464db269cf5afa019acac8861f814543147dbb4'; +export const ORACLE_INTERFACE_HASH = 'eec867360248bff5ad0d7991abb19151c0628f23ed4505496d0539dad530f8b5'; From 90a32a2f8b63784d07b1a3bdb4435d2018eb7b55 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Thu, 26 Feb 2026 12:24:50 +0000 Subject: [PATCH 024/119] refactor to use capsule arrays --- .../aztec-nr/aztec/src/macros/aztec.nr | 2 +- .../aztec/src/messages/discovery/mod.nr | 30 +++++++------------ .../aztec/src/messages/processing/mod.nr | 14 ++++++++- .../offchain_payment_contract/src/main.nr | 22 +++++++------- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index a29c7dfda040..ba37b82c06da 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -13,7 +13,7 @@ use crate::{ module_has_storage, }, }, - messages::discovery::{CustomMessageHandler, DiscoverOffchainMessages}, + messages::{discovery::CustomMessageHandler, processing::DiscoverOffchainMessages}, }; /// Configuration for the [`aztec`] macro. diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index e6def0ce4232..b10a4b3632ed 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -7,21 +7,19 @@ pub mod private_notes; pub mod process_message; use crate::{ + capsules::CapsuleArray, messages::{ discovery::process_message::process_message_ciphertext, - encoding::{MAX_MESSAGE_CONTENT_LEN, MESSAGE_CIPHERTEXT_LEN}, + encoding::MAX_MESSAGE_CONTENT_LEN, logs::note::MAX_NOTE_PACKED_LEN, processing::{ - get_private_logs, MessageContext, pending_tagged_log::PendingTaggedLog, - validate_and_store_enqueued_notes_and_events, + DiscoverOffchainMessages, get_private_logs, MessageContext, OffchainMessage, + pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events, }, }, utils::array, }; -/// Max number of offchain messages that can be processed in a single sync. -pub global MAX_OFFCHAIN_MESSAGES_PER_SYNC: u32 = 128; - pub struct NoteHashAndNullifier { /// The result of NoteHash::compute_note_hash pub note_hash: Field, @@ -81,11 +79,6 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); -/// A handler that returns offchain-delivered messages to process during sync. -/// Each item is a `(message_ciphertext, message_context)` pair. -pub type DiscoverOffchainMessages = unconstrained fn[Env]( -/* contract_address */AztecAddress) -> BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC>; - /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private /// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. /// This is the mechanism via which a contract updates its knowledge of its private state. @@ -128,20 +121,17 @@ pub unconstrained fn do_sync_state = fetch(contract_address); + msgs.for_each(|i, msg| { process_message_ciphertext( contract_address, compute_note_hash_and_nullifier, process_custom_message, - message_ciphertext, - message_context, + msg.message_ciphertext, + msg.message_context, ); - i += 1; - } + msgs.remove(i); + }); } // Then we process all pending partial notes, regardless of whether they were found in the current or previous diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index 8a2d67cd73c0..8e118a2faaeb 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -15,6 +15,7 @@ use crate::{ event::EventSelector, messages::{ discovery::partial_notes::DeliveredPendingPartialNote, + encoding::MESSAGE_CIPHERTEXT_LEN, logs::{event::MAX_EVENT_SERIALIZED_LEN, note::MAX_NOTE_PACKED_LEN}, processing::{ log_retrieval_request::LogRetrievalRequest, log_retrieval_response::LogRetrievalResponse, @@ -23,7 +24,7 @@ use crate::{ }, oracle, }; -use crate::protocol::{address::AztecAddress, hash::sha256_to_field}; +use crate::protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}; use event_validation_request::EventValidationRequest; // Base slot for the pending tagged log array to which the fetch_tagged_logs oracle inserts found private logs. @@ -46,6 +47,17 @@ global LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT: Field = sha256_to_field( "AZTEC_NR::LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT".as_bytes(), ); +/// An offchain-delivered message ready for processing during sync. +#[derive(Serialize, Deserialize)] +pub struct OffchainMessage { + pub message_ciphertext: BoundedVec, + pub message_context: MessageContext, +} + +/// A handler that returns offchain-delivered messages to process during sync. +pub type DiscoverOffchainMessages = unconstrained fn[Env]( +/* contract_address */AztecAddress) -> CapsuleArray; + /// Searches for private logs emitted by `contract_address` that might contain messages for one of the local accounts, /// and stores them in a `CapsuleArray` which is then returned. pub(crate) unconstrained fn get_private_logs(contract_address: AztecAddress) -> CapsuleArray { diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr index e613d8b279d7..064f36447200 100644 --- a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -1,9 +1,10 @@ use aztec::{ capsules::CapsuleArray, messages::{ - discovery::MAX_OFFCHAIN_MESSAGES_PER_SYNC, encoding::MESSAGE_CIPHERTEXT_LEN, - processing::{MessageContext, MessageContextResponse, resolve_message_contexts}, + processing::{ + MessageContext, MessageContextResponse, OffchainMessage, resolve_message_contexts, + }, }, protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, }; @@ -16,6 +17,8 @@ pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field( pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes(), ); +pub global OFFCHAIN_READY_MESSAGES_SLOT: Field = + sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); #[derive(Serialize, Deserialize)] pub struct PendingOffchainMsg { @@ -25,11 +28,14 @@ pub struct PendingOffchainMsg { pub tx_hash: Field, } -unconstrained fn offchain_sync_inbox( - address: AztecAddress, - ) -> BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC> { +unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); + let ready: CapsuleArray = + CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); + + // Clear any stale ready messages from a previous run. + ready.for_each(|i, _| { ready.remove(i); }); // Clear any stale requests from a previous run. requests.for_each(|i, _| { requests.remove(i); }); @@ -54,10 +60,6 @@ unconstrained fn offchain_sync_inbox( CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); assert_eq(responses.len(), inbox_len); - let mut ready - : BoundedVec<(BoundedVec, MessageContext), MAX_OFFCHAIN_MESSAGES_PER_SYNC> = - BoundedVec::new(); - let mut j = inbox_len; while j > 0 { j -= 1; @@ -79,7 +81,7 @@ unconstrained fn offchain_sync_inbox( recipient: msg.recipient, }; - ready.push((msg.ciphertext, message_context)); + ready.push(OffchainMessage { message_ciphertext: msg.ciphertext, message_context }); inbox.remove(j); } From 179f59dd66fc73eb9c184ceeffece1c76e854e07 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Thu, 26 Feb 2026 13:48:56 +0000 Subject: [PATCH 025/119] handle reorgs --- .../offchain_payment_contract/src/main.nr | 32 ++++- .../src/e2e_offchain_payment_qr.test.ts | 126 ++++++++++++++++-- 2 files changed, 148 insertions(+), 10 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr index 064f36447200..a7f140827dbf 100644 --- a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -1,5 +1,6 @@ use aztec::{ capsules::CapsuleArray, + context::UtilityContext, messages::{ encoding::MESSAGE_CIPHERTEXT_LEN, processing::{ @@ -19,6 +20,7 @@ pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( ); pub global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); +pub global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 86400; #[derive(Serialize, Deserialize)] pub struct PendingOffchainMsg { @@ -26,6 +28,7 @@ pub struct PendingOffchainMsg { pub recipient: AztecAddress, // Use 0 as a sentinel for tx-less messages. pub tx_hash: Field, + pub enqueued_ts: u64, } unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray { @@ -60,6 +63,8 @@ unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray 0 { j -= 1; @@ -68,6 +73,10 @@ unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray msg.enqueued_ts + OFFCHAIN_MESSAGE_TTL_SECONDS { + inbox.remove(j); + continue; + } if maybe_ctx.is_none() { // Tx not yet available; keep the message in the inbox. continue; @@ -82,7 +91,6 @@ unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray u32 { + let addr = UtilityContext::new().this_address(); + let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); + inbox.len() + } + + #[external("utility")] + unconstrained fn get_inbox_tx_hash(index: u32) -> Field { + let addr = UtilityContext::new().this_address(); + let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); + let msg = inbox.get(index); + msg.tx_hash + } + // Enqueue an offchain-delivered message into the contract-owned inbox. #[external("utility")] unconstrained fn offchain_enqueue( @@ -139,7 +162,12 @@ contract OffchainPayment { ) { let addr = UtilityContext::new().this_address(); CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT).push( - PendingOffchainMsg { ciphertext, recipient, tx_hash }, + PendingOffchainMsg { + ciphertext, + recipient, + tx_hash, + enqueued_ts: UtilityContext::new().timestamp(), + }, ); } } diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts index d38778ae6c95..ff4addeb8d29 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -1,14 +1,17 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; +import { NO_WAIT } from '@aztec/aztec.js/contracts'; import type { AztecNode } from '@aztec/aztec.js/node'; +import { waitForTx } from '@aztec/aztec.js/node'; +import type { CheatCodes } from '@aztec/aztec/testing'; import { PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants'; import { retryUntil } from '@aztec/foundation/retry'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; -import { OFFCHAIN_MESSAGE_IDENTIFIER } from '@aztec/stdlib/tx'; +import { OFFCHAIN_MESSAGE_IDENTIFIER, TxStatus } from '@aztec/stdlib/tx'; import { jest } from '@jest/globals'; -import { setup } from './fixtures/utils.js'; +import { getLogger, setup } from './fixtures/utils.js'; import type { TestWallet } from './test-wallet/test_wallet.js'; import { proveInteraction } from './test-wallet/utils.js'; @@ -18,19 +21,41 @@ describe('e2e_offchain_payment_qr', () => { let contract: OffchainPaymentContract; let aztecNode: AztecNode; let aztecNodeAdmin: AztecNodeAdmin; + let cheatCodes: CheatCodes; let wallet: TestWallet; let accounts: AztecAddress[]; let teardown: () => Promise; + const logger = getLogger(); jest.setTimeout(TIMEOUT); beforeAll(async () => { - ({ teardown, wallet, accounts, aztecNode, aztecNodeAdmin } = await setup(2)); - ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); + ({ teardown, wallet, accounts, aztecNode, aztecNodeAdmin, cheatCodes } = await setup(2)); }); afterAll(() => teardown()); + beforeEach(async () => { + ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); + }); + + const forceEmptyBlock = async () => { + const blockBefore = await aztecNode.getBlockNumber(); + logger.info(`Forcing empty block. Current L2 block: ${blockBefore}`); + await aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); + await retryUntil( + async () => { + const current = await aztecNode.getBlockNumber(); + logger.info(`Waiting for new L2 block. Current: ${current}`); + return current > blockBefore; + }, + 'new L2 block', + 30, + 1, + ); + await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); + }; + it('processes an offchain-delivered private payment via QR-style handoff', async () => { const [alice, bob] = accounts; @@ -60,12 +85,97 @@ describe('e2e_offchain_payment_qr', () => { await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. - const blockBefore = await aztecNode.getBlockNumber(); - await aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); - await retryUntil(async () => (await aztecNode.getBlockNumber()) > blockBefore, 'new L2 block', 30, 1); - await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); + await forceEmptyBlock(); + + const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); + expect(bobBalance).toBe(paymentAmount); + }); + + it('reprocesses an offchain-delivered payment after an L1 reorg', async () => { + const [alice, bob] = accounts; + + const mintAmount = 100n; + const paymentAmount = 40n; + + await contract.methods.mint(mintAmount, alice).send({ from: alice }); + + const provenTx = await proveInteraction(wallet, contract.methods.transfer_offchain(paymentAmount, bob), { + from: alice, + }); + const receipt = await provenTx.send(); + expect(receipt.blockNumber).toBeDefined(); + const txBlockNumber = receipt.blockNumber!; + const txHash = provenTx.getTxHash(); + logger.info(`Tx included in L2 block ${txBlockNumber}`); + const offchainEffects = provenTx.offchainEffects; + const effectForBob = offchainEffects.find( + effect => effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER) && effect.data[1].equals(bob.toField()), + ); + expect(effectForBob).toBeTruthy(); + + const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); + await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); + const { result: inboxLenAfterEnqueue } = await contract.methods.get_inbox_len().simulate({ from: bob }); + logger.info(`Inbox len after enqueue: ${inboxLenAfterEnqueue}`); + expect(Number(inboxLenAfterEnqueue)).toBe(1); + + await forceEmptyBlock(); const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobBalance).toBe(paymentAmount); + const { result: inboxLenAfterSync } = await contract.methods.get_inbox_len().simulate({ from: bob }); + logger.info(`Inbox len after sync: ${inboxLenAfterSync}`); + + const checkpointed = await retryUntil( + async () => { + const blocks = await aztecNode.getCheckpointedBlocks(txBlockNumber, 1); + logger.info(`Checkpointed blocks from ${txBlockNumber}: ${blocks.length}`); + return blocks[0]; + }, + 'checkpointed block', + 30, + 1, + ); + logger.info(`Checkpointed L1 block: ${checkpointed.l1.blockNumber}`); + const l1BlockNumber = Number(checkpointed.l1.blockNumber - 1n); + + await aztecNodeAdmin.pauseSync(); + logger.info(`Paused sync. Reorging L1 to block ${l1BlockNumber}`); + await cheatCodes.eth.reorgTo(l1BlockNumber); + logger.info(`Rolling back L2 to block ${Number(txBlockNumber) - 1}`); + await aztecNodeAdmin.rollbackTo(Number(txBlockNumber) - 1); + expect(await aztecNode.getBlockNumber()).toBe(Number(txBlockNumber) - 1); + await aztecNodeAdmin.resumeSync(); + + const txEffectAfterRollback = await aztecNode.getTxEffect(txHash); + logger.info(`Tx effect after rollback present: ${!!txEffectAfterRollback}`); + expect(txEffectAfterRollback).toBeFalsy(); + + const { result: bobAfterRollback } = await contract.methods.get_balance(bob).simulate({ from: bob }); + expect(bobAfterRollback).toBe(0n); + + const { result: inboxLenAfterRollback } = await contract.methods.get_inbox_len().simulate({ from: bob }); + logger.info(`Inbox len after rollback: ${inboxLenAfterRollback}`); + expect(Number(inboxLenAfterRollback)).toBe(1); + + try { + await provenTx.send({ wait: NO_WAIT }); + } catch { + // Ignore errors if the tx is already known to the node. + } + logger.info(`Waiting for tx ${txHash.toString()} to be mined after reorg`); + await waitForTx(aztecNode, txHash, { waitForStatus: TxStatus.PROPOSED }); + + // Notice what we want to test here is that the offchain effects don't need to be re-enqueued + // for the system to re-process it. + await retryUntil( + async () => { + const { result } = await contract.methods.get_balance(bob).simulate({ from: bob }); + return result === paymentAmount ? true : undefined; + }, + 'bob balance after reorg', + 30, + 1, + ); }); }); From 007a57acf044e7be50c2cc7414280904912c4729 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Thu, 26 Feb 2026 16:21:17 +0000 Subject: [PATCH 026/119] clean up reorg test --- .../src/e2e_offchain_payment_qr.test.ts | 58 ++++++++----------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts index ff4addeb8d29..a4fd04eb8deb 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -93,89 +93,77 @@ describe('e2e_offchain_payment_qr', () => { it('reprocesses an offchain-delivered payment after an L1 reorg', async () => { const [alice, bob] = accounts; - - const mintAmount = 100n; const paymentAmount = 40n; - await contract.methods.mint(mintAmount, alice).send({ from: alice }); + await contract.methods.mint(100n, alice).send({ from: alice }); const provenTx = await proveInteraction(wallet, contract.methods.transfer_offchain(paymentAmount, bob), { from: alice, }); + const receipt = await provenTx.send(); expect(receipt.blockNumber).toBeDefined(); + const txBlockNumber = receipt.blockNumber!; const txHash = provenTx.getTxHash(); - logger.info(`Tx included in L2 block ${txBlockNumber}`); - const offchainEffects = provenTx.offchainEffects; - const effectForBob = offchainEffects.find( + const effectForBob = provenTx.offchainEffects.find( effect => effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER) && effect.data[1].equals(bob.toField()), ); expect(effectForBob).toBeTruthy(); - const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); + + // Deliver the offchain message for eventual processing await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); - const { result: inboxLenAfterEnqueue } = await contract.methods.get_inbox_len().simulate({ from: bob }); - logger.info(`Inbox len after enqueue: ${inboxLenAfterEnqueue}`); - expect(Number(inboxLenAfterEnqueue)).toBe(1); + // TODO: revisit this. The call to offchain_enqueue is a utility and as such it causes the contract to sync, which, + // in combination with our caching policies, means subsequent utility calls won't trigger a re-sync. + // Given we're hooking the offchain sync process to the general sync process, this means we won't process any new + // offchain messages until at least one block passes. + // A potential escape hatch for this is to remove the check that forbids external invocation of `sync_state`. + // That would let users trigger syncs manually to circumvent caching issues like this. await forceEmptyBlock(); + + // Check that Bob got the payment before a re-org const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobBalance).toBe(paymentAmount); - const { result: inboxLenAfterSync } = await contract.methods.get_inbox_len().simulate({ from: bob }); - logger.info(`Inbox len after sync: ${inboxLenAfterSync}`); + // Force a re-org const checkpointed = await retryUntil( async () => { const blocks = await aztecNode.getCheckpointedBlocks(txBlockNumber, 1); - logger.info(`Checkpointed blocks from ${txBlockNumber}: ${blocks.length}`); return blocks[0]; }, 'checkpointed block', 30, 1, ); - logger.info(`Checkpointed L1 block: ${checkpointed.l1.blockNumber}`); const l1BlockNumber = Number(checkpointed.l1.blockNumber - 1n); await aztecNodeAdmin.pauseSync(); - logger.info(`Paused sync. Reorging L1 to block ${l1BlockNumber}`); await cheatCodes.eth.reorgTo(l1BlockNumber); - logger.info(`Rolling back L2 to block ${Number(txBlockNumber) - 1}`); await aztecNodeAdmin.rollbackTo(Number(txBlockNumber) - 1); expect(await aztecNode.getBlockNumber()).toBe(Number(txBlockNumber) - 1); await aztecNodeAdmin.resumeSync(); + // Verify that the payment TX is no longer present after the reorg const txEffectAfterRollback = await aztecNode.getTxEffect(txHash); - logger.info(`Tx effect after rollback present: ${!!txEffectAfterRollback}`); expect(txEffectAfterRollback).toBeFalsy(); + // Verify that Bob's balance has rolled back to 0 (pre-payment value) after the reorg const { result: bobAfterRollback } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobAfterRollback).toBe(0n); - const { result: inboxLenAfterRollback } = await contract.methods.get_inbox_len().simulate({ from: bob }); - logger.info(`Inbox len after rollback: ${inboxLenAfterRollback}`); - expect(Number(inboxLenAfterRollback)).toBe(1); + // Resend the tx after the reorg + await provenTx.send({ wait: NO_WAIT }); - try { - await provenTx.send({ wait: NO_WAIT }); - } catch { - // Ignore errors if the tx is already known to the node. - } - logger.info(`Waiting for tx ${txHash.toString()} to be mined after reorg`); + // Wait for the tx to be available again await waitForTx(aztecNode, txHash, { waitForStatus: TxStatus.PROPOSED }); + // Check that the message was reprocessed and Bob has his payment again. // Notice what we want to test here is that the offchain effects don't need to be re-enqueued // for the system to re-process it. - await retryUntil( - async () => { - const { result } = await contract.methods.get_balance(bob).simulate({ from: bob }); - return result === paymentAmount ? true : undefined; - }, - 'bob balance after reorg', - 30, - 1, - ); + const { result: bobBalanceAfterResentTx } = await contract.methods.get_balance(bob).simulate({ from: bob }); + expect(bobBalanceAfterResentTx).toBe(paymentAmount); }); }); From df8d9b0c047f8cfa05a5451d20be00025d334b82 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Thu, 26 Feb 2026 16:43:54 +0000 Subject: [PATCH 027/119] make offchain reception a builtin feature --- .../aztec-nr/aztec/src/macros/aztec.nr | 40 ++++- .../aztec/src/messages/discovery/mod.nr | 1 + .../src/messages/discovery/offchain_inbox.nr | 105 +++++++++++++ .../offchain_payment_contract/src/main.nr | 138 +----------------- 4 files changed, 147 insertions(+), 137 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index ba37b82c06da..79d912307833 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -130,7 +130,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { let hook = config.discover_offchain_messages.unwrap(); quote { Option::some($hook) } } else { - quote { Option::>::none() } + quote { Option::some(aztec::messages::discovery::offchain_inbox::offchain_sync_inbox) } }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { @@ -142,6 +142,12 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote {} }; + let offchain_enqueue_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { offchain_enqueue }) { + generate_offchain_enqueue() + } else { + quote {} + }; + let process_message_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { process_message }) { generate_process_message(process_custom_message_option) } else { @@ -159,6 +165,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { $public_dispatch $sync_state_fn_and_abi_export $process_message_fn_and_abi_export + $offchain_enqueue_fn_and_abi_export } } @@ -418,6 +425,37 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q } } +/// Generates the `offchain_enqueue` utility function that adds a message to the inbox. +comptime fn generate_offchain_enqueue() -> Quoted { + quote { + pub struct offchain_enqueue_parameters { + pub ciphertext: BoundedVec, + pub recipient: aztec::protocol::address::AztecAddress, + pub tx_hash: Field, + } + + #[abi(functions)] + pub struct offchain_enqueue_abi { + parameters: offchain_enqueue_parameters, + } + + #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] + unconstrained fn offchain_enqueue( + ciphertext: BoundedVec, + recipient: aztec::protocol::address::AztecAddress, + tx_hash: Field, + ) { + let address = aztec::context::UtilityContext::new().this_address(); + aztec::messages::discovery::offchain_inbox::enqueue_offchain_message( + address, + ciphertext, + recipient, + tx_hash, + ); + } + } +} + /// Checks that all functions in the module have a context macro applied. /// /// Non-macroified functions are not allowed in contracts. They must all be one of diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index b10a4b3632ed..371b58844c94 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -1,6 +1,7 @@ use crate::protocol::{address::AztecAddress, logging::{debug_log, debug_log_format}}; pub mod nonce_discovery; +pub mod offchain_inbox; pub mod partial_notes; pub mod private_events; pub mod private_notes; diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr new file mode 100644 index 000000000000..26debd1e00c2 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr @@ -0,0 +1,105 @@ +use crate::{ + capsules::CapsuleArray, + context::UtilityContext, + messages::{ + encoding::MESSAGE_CIPHERTEXT_LEN, + processing::{MessageContext, MessageContextResponse, OffchainMessage, resolve_message_contexts}, + }, + protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, +}; + +pub global OFFCHAIN_INBOX_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_INBOX_SLOT".as_bytes()); +pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = + sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes()); +pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = + sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes()); +pub global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); +pub global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 86400; + +#[derive(Serialize, Deserialize)] +pub struct PendingOffchainMsg { + pub ciphertext: BoundedVec, + pub recipient: AztecAddress, + // Use 0 as a sentinel for tx-less messages. + pub tx_hash: Field, + pub enqueued_ts: u64, +} + +/// Enqueue an offchain-delivered message into the contract-owned inbox. +pub unconstrained fn enqueue_offchain_message( + contract_address: AztecAddress, + ciphertext: BoundedVec, + recipient: AztecAddress, + tx_hash: Field, +) { + CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT).push( + PendingOffchainMsg { ciphertext, recipient, tx_hash, enqueued_ts: UtilityContext::new().timestamp() }, + ); +} + +/// Returns offchain-delivered messages to process during sync. +/// +/// Messages remain in the inbox and are reprocessed on each sync until they expire. +pub unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray { + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); + let ready: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); + + // Clear any stale ready messages from a previous run. + ready.for_each(|i, _| { ready.remove(i); }); + + // Clear any stale requests from a previous run. + requests.for_each(|i, _| { requests.remove(i); }); + + // Build a request list aligned with the inbox indices. + let mut i = 0; + let inbox_len = inbox.len(); + while i < inbox_len { + let msg = inbox.get(i); + requests.push(msg.tx_hash); + i += 1; + } + + // Ask PXE to resolve contexts for all requested tx hashes. + resolve_message_contexts( + address, + OFFCHAIN_CONTEXT_REQUESTS_SLOT, + OFFCHAIN_CONTEXT_RESPONSES_SLOT, + ); + + let responses: CapsuleArray> = + CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); + assert_eq(responses.len(), inbox_len); + + let now = UtilityContext::new().timestamp(); + + let mut j = inbox_len; + while j > 0 { + j -= 1; + let maybe_ctx = responses.get(j); + // Clear response entry as we go so the array is empty for the next run. + responses.remove(j); + + let msg = inbox.get(j); + if now > msg.enqueued_ts + OFFCHAIN_MESSAGE_TTL_SECONDS { + inbox.remove(j); + continue; + } + if maybe_ctx.is_none() { + // Tx not yet available; keep the message in the inbox. + continue; + } + + let ctx = maybe_ctx.unwrap(); + let message_context = MessageContext { + tx_hash: ctx.tx_hash, + unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx, + first_nullifier_in_tx: ctx.first_nullifier_in_tx, + recipient: msg.recipient, + }; + + ready.push(OffchainMessage { message_ciphertext: msg.ciphertext, message_context }); + } + + ready +} diff --git a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr index a7f140827dbf..d896eff05ad2 100644 --- a/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test/offchain_payment_contract/src/main.nr @@ -1,115 +1,14 @@ -use aztec::{ - capsules::CapsuleArray, - context::UtilityContext, - messages::{ - encoding::MESSAGE_CIPHERTEXT_LEN, - processing::{ - MessageContext, MessageContextResponse, OffchainMessage, resolve_message_contexts, - }, - }, - protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, -}; - -pub global OFFCHAIN_INBOX_SLOT: Field = - sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_INBOX_SLOT".as_bytes()); -pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field( - "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes(), -); -pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field( - "OFFCHAIN_PAYMENT::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes(), -); -pub global OFFCHAIN_READY_MESSAGES_SLOT: Field = - sha256_to_field("OFFCHAIN_PAYMENT::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); -pub global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 86400; - -#[derive(Serialize, Deserialize)] -pub struct PendingOffchainMsg { - pub ciphertext: BoundedVec, - pub recipient: AztecAddress, - // Use 0 as a sentinel for tx-less messages. - pub tx_hash: Field, - pub enqueued_ts: u64, -} - -unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray { - let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); - let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); - let ready: CapsuleArray = - CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); - - // Clear any stale ready messages from a previous run. - ready.for_each(|i, _| { ready.remove(i); }); - - // Clear any stale requests from a previous run. - requests.for_each(|i, _| { requests.remove(i); }); - - // Build a request list aligned with the inbox indices. - let mut i = 0; - let inbox_len = inbox.len(); - while i < inbox_len { - let msg = inbox.get(i); - requests.push(msg.tx_hash); - i += 1; - } - - // Ask PXE to resolve contexts for all requested tx hashes. - resolve_message_contexts( - address, - OFFCHAIN_CONTEXT_REQUESTS_SLOT, - OFFCHAIN_CONTEXT_RESPONSES_SLOT, - ); - - let responses: CapsuleArray> = - CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); - assert_eq(responses.len(), inbox_len); - - let now = UtilityContext::new().timestamp(); - - let mut j = inbox_len; - while j > 0 { - j -= 1; - let maybe_ctx = responses.get(j); - // Clear response entry as we go so the array is empty for the next run. - responses.remove(j); - - let msg = inbox.get(j); - if now > msg.enqueued_ts + OFFCHAIN_MESSAGE_TTL_SECONDS { - inbox.remove(j); - continue; - } - if maybe_ctx.is_none() { - // Tx not yet available; keep the message in the inbox. - continue; - } - - let ctx = maybe_ctx.unwrap(); - let message_context = MessageContext { - tx_hash: ctx.tx_hash, - unique_note_hashes_in_tx: ctx.unique_note_hashes_in_tx, - first_nullifier_in_tx: ctx.first_nullifier_in_tx, - recipient: msg.recipient, - }; - - ready.push(OffchainMessage { message_ciphertext: msg.ciphertext, message_context }); - } - - ready -} - use aztec::macros::aztec; -#[aztec(::aztec::macros::AztecConfig::new().discover_offchain_messages(crate::offchain_sync_inbox))] +#[aztec] contract OffchainPayment { use aztec::{ - capsules::CapsuleArray, - context::UtilityContext, macros::{functions::external, storage::storage}, - messages::{encoding::MESSAGE_CIPHERTEXT_LEN, message_delivery::MessageDelivery}, + messages::message_delivery::MessageDelivery, protocol::address::AztecAddress, state_vars::Owned, }; - use crate::{OFFCHAIN_INBOX_SLOT, PendingOffchainMsg}; use balance_set::BalanceSet; #[storage] @@ -137,37 +36,4 @@ contract OffchainPayment { unconstrained fn get_balance(owner: AztecAddress) -> u128 { self.storage.balances.at(owner).balance_of() } - - #[external("utility")] - unconstrained fn get_inbox_len() -> u32 { - let addr = UtilityContext::new().this_address(); - let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); - inbox.len() - } - - #[external("utility")] - unconstrained fn get_inbox_tx_hash(index: u32) -> Field { - let addr = UtilityContext::new().this_address(); - let inbox: CapsuleArray = CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT); - let msg = inbox.get(index); - msg.tx_hash - } - - // Enqueue an offchain-delivered message into the contract-owned inbox. - #[external("utility")] - unconstrained fn offchain_enqueue( - ciphertext: BoundedVec, - recipient: AztecAddress, - tx_hash: Field, - ) { - let addr = UtilityContext::new().this_address(); - CapsuleArray::at(addr, OFFCHAIN_INBOX_SLOT).push( - PendingOffchainMsg { - ciphertext, - recipient, - tx_hash, - enqueued_ts: UtilityContext::new().timestamp(), - }, - ); - } } From ae40fd1e996ec3d75a8f63460892996abb6d0262 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 13:53:03 +0000 Subject: [PATCH 028/119] move DiscoverOffchainMessages to a more reasonable location --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 2 +- .../aztec-nr/aztec/src/messages/discovery/mod.nr | 8 ++++++-- .../aztec-nr/aztec/src/messages/processing/mod.nr | 4 ---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 79d912307833..fb7ae941082b 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -13,7 +13,7 @@ use crate::{ module_has_storage, }, }, - messages::{discovery::CustomMessageHandler, processing::DiscoverOffchainMessages}, + messages::discovery::{CustomMessageHandler, DiscoverOffchainMessages}, }; /// Configuration for the [`aztec`] macro. diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 371b58844c94..59dfe80d2d2d 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -14,8 +14,8 @@ use crate::{ encoding::MAX_MESSAGE_CONTENT_LEN, logs::note::MAX_NOTE_PACKED_LEN, processing::{ - DiscoverOffchainMessages, get_private_logs, MessageContext, OffchainMessage, - pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events, + get_private_logs, MessageContext, OffchainMessage, pending_tagged_log::PendingTaggedLog, + validate_and_store_enqueued_notes_and_events, }, }, utils::array, @@ -80,6 +80,10 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); +/// A handler that returns offchain-delivered messages to process during sync. +pub type DiscoverOffchainMessages = unconstrained fn[Env]( +/* contract_address */AztecAddress) -> CapsuleArray; + /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private /// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. /// This is the mechanism via which a contract updates its knowledge of its private state. diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index 8e118a2faaeb..425fcd5a801f 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -54,10 +54,6 @@ pub struct OffchainMessage { pub message_context: MessageContext, } -/// A handler that returns offchain-delivered messages to process during sync. -pub type DiscoverOffchainMessages = unconstrained fn[Env]( -/* contract_address */AztecAddress) -> CapsuleArray; - /// Searches for private logs emitted by `contract_address` that might contain messages for one of the local accounts, /// and stores them in a `CapsuleArray` which is then returned. pub(crate) unconstrained fn get_private_logs(contract_address: AztecAddress) -> CapsuleArray { From d6e55cfcd70a280823b18c04bc2a4c75a28bb9b2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 14:14:33 +0000 Subject: [PATCH 029/119] document discover_offchain_messages --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index fb7ae941082b..762daa40b35a 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -56,9 +56,16 @@ impl AztecConfig { } } - /// Sets a handler that supplies offchain-delivered messages during `sync_state`. + /// Sets a handler to customize how offchain-delivered messages are discovered and managed by the contract. /// - /// `hook` must be a function that conforms to the [`DiscoverOffchainMessages`] type signature. + /// This is an advanced feature that requires knowledge about how offchain messages are stored, how to ensure + /// reprocessing under reorgs, etc. If you are unsure about how you could use this, it probably means you + /// should not. + /// + /// By default, the lifecycle of offchain messages is handled by + /// [`crate::messages::discovery::offchain_inbox::offchain_sync_inbox`]. + /// + /// This config allows for the default behavior to be overridden. pub comptime fn discover_offchain_messages(self: Self, hook: DiscoverOffchainMessages<()>) -> Self { Self { custom_message_handler: self.custom_message_handler, discover_offchain_messages: Option::some(hook) } } From c9965392d0e18fad7cd597a97369c67471af0e49 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 14:21:20 +0000 Subject: [PATCH 030/119] rename to offchain_message_handler --- .../aztec-nr/aztec/src/macros/aztec.nr | 25 ++++++++----------- .../aztec/src/messages/discovery/mod.nr | 8 +++--- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 762daa40b35a..6123144256ca 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -13,7 +13,7 @@ use crate::{ module_has_storage, }, }, - messages::discovery::{CustomMessageHandler, DiscoverOffchainMessages}, + messages::discovery::{CustomMessageHandler, OffchainMessageHandler}, }; /// Configuration for the [`aztec`] macro. @@ -30,7 +30,7 @@ use crate::{ /// ``` pub struct AztecConfig { custom_message_handler: Option>, - discover_offchain_messages: Option>, + offchain_message_handler: Option>, } impl AztecConfig { @@ -39,7 +39,7 @@ impl AztecConfig { /// Calling `new` is equivalent to invoking the [`aztec`] macro with no parameters. The different methods /// (e.g. [`AztecConfig::custom_message_handler`]) can then be used to change the default behavior. pub comptime fn new() -> Self { - Self { custom_message_handler: Option::none(), discover_offchain_messages: Option::none() } + Self { custom_message_handler: Option::none(), offchain_message_handler: Option::none() } } /// Sets a handler for custom messages. @@ -50,10 +50,7 @@ impl AztecConfig { /// `handler` must be a function that conforms to the /// [`crate::messages::discovery::CustomMessageHandler`] type signature. pub comptime fn custom_message_handler(self: Self, handler: CustomMessageHandler<()>) -> Self { - Self { - custom_message_handler: Option::some(handler), - discover_offchain_messages: self.discover_offchain_messages, - } + Self { custom_message_handler: Option::some(handler), offchain_message_handler: self.offchain_message_handler } } /// Sets a handler to customize how offchain-delivered messages are discovered and managed by the contract. @@ -66,8 +63,8 @@ impl AztecConfig { /// [`crate::messages::discovery::offchain_inbox::offchain_sync_inbox`]. /// /// This config allows for the default behavior to be overridden. - pub comptime fn discover_offchain_messages(self: Self, hook: DiscoverOffchainMessages<()>) -> Self { - Self { custom_message_handler: self.custom_message_handler, discover_offchain_messages: Option::some(hook) } + pub comptime fn offchain_message_handler(self: Self, hook: OffchainMessageHandler<()>) -> Self { + Self { custom_message_handler: self.custom_message_handler, offchain_message_handler: Option::some(hook) } } } @@ -133,8 +130,8 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote { Option::>::none() } }; - let discover_offchain_messages_option = if config.discover_offchain_messages.is_some() { - let hook = config.discover_offchain_messages.unwrap(); + let offchain_message_handler_option = if config.offchain_message_handler.is_some() { + let hook = config.offchain_message_handler.unwrap(); quote { Option::some($hook) } } else { quote { Option::some(aztec::messages::discovery::offchain_inbox::offchain_sync_inbox) } @@ -143,7 +140,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { generate_sync_state( process_custom_message_option, - discover_offchain_messages_option, + offchain_message_handler_option, ) } else { quote {} @@ -374,7 +371,7 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - /// Generates the `sync_state` utility function that performs message discovery. comptime fn generate_sync_state( process_custom_message_option: Quoted, - discover_offchain_messages_option: Quoted, + offchain_message_handler_option: Quoted, ) -> Quoted { quote { pub struct sync_state_parameters {} @@ -391,7 +388,7 @@ comptime fn generate_sync_state( address, _compute_note_hash_and_nullifier, $process_custom_message_option, - $discover_offchain_messages_option, + $offchain_message_handler_option, ); } } diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 59dfe80d2d2d..e7a232cdfbac 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -81,7 +81,7 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* message_context */ MessageContext); /// A handler that returns offchain-delivered messages to process during sync. -pub type DiscoverOffchainMessages = unconstrained fn[Env]( +pub type OffchainMessageHandler = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private @@ -97,7 +97,7 @@ pub unconstrained fn do_sync_state, process_custom_message: Option>, - discover_offchain_messages: Option>, + offchain_message_handler: Option>, ) { debug_log("Performing state synchronization"); @@ -124,8 +124,8 @@ pub unconstrained fn do_sync_state = fetch(contract_address); msgs.for_each(|i, msg| { process_message_ciphertext( From cec23454fd6f830ef7473f8b0c8a603cc5d86c5f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 14:27:50 +0000 Subject: [PATCH 031/119] hook -> handler --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 6123144256ca..a84de982fc96 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -63,8 +63,8 @@ impl AztecConfig { /// [`crate::messages::discovery::offchain_inbox::offchain_sync_inbox`]. /// /// This config allows for the default behavior to be overridden. - pub comptime fn offchain_message_handler(self: Self, hook: OffchainMessageHandler<()>) -> Self { - Self { custom_message_handler: self.custom_message_handler, offchain_message_handler: Option::some(hook) } + pub comptime fn offchain_message_handler(self: Self, handler: OffchainMessageHandler<()>) -> Self { + Self { custom_message_handler: self.custom_message_handler, offchain_message_handler: Option::some(handler) } } } @@ -131,8 +131,8 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { }; let offchain_message_handler_option = if config.offchain_message_handler.is_some() { - let hook = config.offchain_message_handler.unwrap(); - quote { Option::some($hook) } + let handler = config.offchain_message_handler.unwrap(); + quote { Option::some($handler) } } else { quote { Option::some(aztec::messages::discovery::offchain_inbox::offchain_sync_inbox) } }; From dfef1bdda00e4cdf13c9e75940d73e30ffb624d9 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 16:30:00 +0000 Subject: [PATCH 032/119] enqueue -> receive --- .../aztec-nr/aztec/src/macros/aztec.nr | 20 +++++++++---------- .../src/messages/discovery/offchain_inbox.nr | 4 ++-- .../src/e2e_offchain_payment_qr.test.ts | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index a84de982fc96..a2c4aa01673a 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -146,8 +146,8 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote {} }; - let offchain_enqueue_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { offchain_enqueue }) { - generate_offchain_enqueue() + let offchain_receive_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { offchain_receive }) { + generate_offchain_receive() } else { quote {} }; @@ -169,7 +169,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { $public_dispatch $sync_state_fn_and_abi_export $process_message_fn_and_abi_export - $offchain_enqueue_fn_and_abi_export + $offchain_receive_fn_and_abi_export } } @@ -429,28 +429,28 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q } } -/// Generates the `offchain_enqueue` utility function that adds a message to the inbox. -comptime fn generate_offchain_enqueue() -> Quoted { +/// Generates the `offchain_receive` utility function that adds a message to the inbox. +comptime fn generate_offchain_receive() -> Quoted { quote { - pub struct offchain_enqueue_parameters { + pub struct offchain_receive_parameters { pub ciphertext: BoundedVec, pub recipient: aztec::protocol::address::AztecAddress, pub tx_hash: Field, } #[abi(functions)] - pub struct offchain_enqueue_abi { - parameters: offchain_enqueue_parameters, + pub struct offchain_receive_abi { + parameters: offchain_receive_parameters, } #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] - unconstrained fn offchain_enqueue( + unconstrained fn offchain_receive( ciphertext: BoundedVec, recipient: aztec::protocol::address::AztecAddress, tx_hash: Field, ) { let address = aztec::context::UtilityContext::new().this_address(); - aztec::messages::discovery::offchain_inbox::enqueue_offchain_message( + aztec::messages::discovery::offchain_inbox::receive_offchain_message( address, ciphertext, recipient, diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr index 26debd1e00c2..19e4c37dfb12 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr @@ -25,8 +25,8 @@ pub struct PendingOffchainMsg { pub enqueued_ts: u64, } -/// Enqueue an offchain-delivered message into the contract-owned inbox. -pub unconstrained fn enqueue_offchain_message( +/// Receive an offchain-delivered message into the contract-owned inbox. +pub unconstrained fn receive_offchain_message( contract_address: AztecAddress, ciphertext: BoundedVec, recipient: AztecAddress, diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts index a4fd04eb8deb..55f6ed0cfbbc 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts @@ -82,7 +82,7 @@ describe('e2e_offchain_payment_qr', () => { const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); - await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); + await contract.methods.offchain_receive(ciphertext, bob, txHash.hash).simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. await forceEmptyBlock(); @@ -114,9 +114,9 @@ describe('e2e_offchain_payment_qr', () => { const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); // Deliver the offchain message for eventual processing - await contract.methods.offchain_enqueue(ciphertext, bob, txHash.hash).simulate({ from: bob }); + await contract.methods.offchain_receive(ciphertext, bob, txHash.hash).simulate({ from: bob }); - // TODO: revisit this. The call to offchain_enqueue is a utility and as such it causes the contract to sync, which, + // TODO: revisit this. The call to offchain_receive is a utility and as such it causes the contract to sync, which, // in combination with our caching policies, means subsequent utility calls won't trigger a re-sync. // Given we're hooking the offchain sync process to the general sync process, this means we won't process any new // offchain messages until at least one block passes. From fd717403f445ba56d66b6ea53c88a6d22fa116a8 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 16:43:45 +0000 Subject: [PATCH 033/119] nicer doc for offchain_receive --- .../aztec-nr/aztec/src/macros/aztec.nr | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index a2c4aa01673a..7e4e526ee706 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -429,7 +429,7 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q } } -/// Generates the `offchain_receive` utility function that adds a message to the inbox. +/// Generates the `offchain_receive` utility function that adds a message to the offchain message inbox. comptime fn generate_offchain_receive() -> Quoted { quote { pub struct offchain_receive_parameters { @@ -443,6 +443,22 @@ comptime fn generate_offchain_receive() -> Quoted { parameters: offchain_receive_parameters, } + /// Delivers an offchain message to this contract's offchain inbox for processing during the next sync. + /// + /// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender + /// transmits the encrypted payload to the recipient through an external channel (e.g. a URL accessible by the + /// recipient). The recipient then calls this function to hand the message to the contract so it can be processed + /// through the same mechanisms as onchain messages. + /// + /// Messages are processed when their originating transaction is found onchain (providing the context needed to + /// validate resulting notes and events). + /// + /// Messages are kept in the inbox until their originating until the originating transaction can be assumed to + /// have expired. This is necessary to make the mechanism resilient to reorgs. + /// + /// Processing order is not guaranteed. + /// + /// This function is automatically injected by the `#[aztec]` macro. #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] unconstrained fn offchain_receive( ciphertext: BoundedVec, From d0e70988f8fe43f61433511216bb34f44728c90d Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 16:58:47 +0000 Subject: [PATCH 034/119] document OffchainMessageHandler --- .../aztec-nr/aztec/src/messages/discovery/mod.nr | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index e7a232cdfbac..2996dc9e0ab8 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -80,7 +80,16 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); -/// A handler that returns offchain-delivered messages to process during sync. +/// A function that manages offchain-delivered messages for processing during sync. +/// +/// Offchain messages are transaction effects that are not broadcasted via onchain logs. They are instead delivered to +/// the recipient through an external channel (e.g. a URL) and stored locally via the auto-generated `offchain_receive` +/// utility function (injected by [`crate::macros::aztec`]). This handler is responsible for deciding when each message +/// is ready for processing, when it can be safely disposed of, etc. +/// +/// The default implementation is [`offchain_inbox::offchain_sync_inbox`], which manages an inbox with max TX TTL-based +/// expiry and automatic context resolution. It can be overridden via +/// [`crate::macros::AztecConfig::offchain_message_handler`]. pub type OffchainMessageHandler = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; From 0f0b27cfc9251d3b8581a34d1ccef3b3c71ca7e9 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 17:05:45 +0000 Subject: [PATCH 035/119] offchain_inbox -> offchain --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 6 +++--- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 4 ++-- .../messages/discovery/{offchain_inbox.nr => offchain.nr} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename noir-projects/aztec-nr/aztec/src/messages/discovery/{offchain_inbox.nr => offchain.nr} (100%) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 7e4e526ee706..b4c380b1c17d 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -60,7 +60,7 @@ impl AztecConfig { /// should not. /// /// By default, the lifecycle of offchain messages is handled by - /// [`crate::messages::discovery::offchain_inbox::offchain_sync_inbox`]. + /// [`crate::messages::discovery::offchain::offchain_sync_inbox`]. /// /// This config allows for the default behavior to be overridden. pub comptime fn offchain_message_handler(self: Self, handler: OffchainMessageHandler<()>) -> Self { @@ -134,7 +134,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { let handler = config.offchain_message_handler.unwrap(); quote { Option::some($handler) } } else { - quote { Option::some(aztec::messages::discovery::offchain_inbox::offchain_sync_inbox) } + quote { Option::some(aztec::messages::discovery::offchain::offchain_sync_inbox) } }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { @@ -466,7 +466,7 @@ comptime fn generate_offchain_receive() -> Quoted { tx_hash: Field, ) { let address = aztec::context::UtilityContext::new().this_address(); - aztec::messages::discovery::offchain_inbox::receive_offchain_message( + aztec::messages::discovery::offchain::receive_offchain_message( address, ciphertext, recipient, diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 2996dc9e0ab8..e46edaa83058 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -1,7 +1,7 @@ use crate::protocol::{address::AztecAddress, logging::{debug_log, debug_log_format}}; pub mod nonce_discovery; -pub mod offchain_inbox; +pub mod offchain; pub mod partial_notes; pub mod private_events; pub mod private_notes; @@ -87,7 +87,7 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /// utility function (injected by [`crate::macros::aztec`]). This handler is responsible for deciding when each message /// is ready for processing, when it can be safely disposed of, etc. /// -/// The default implementation is [`offchain_inbox::offchain_sync_inbox`], which manages an inbox with max TX TTL-based +/// The default implementation is [`offchain::offchain_sync_inbox`], which manages an inbox with max TX TTL-based /// expiry and automatic context resolution. It can be overridden via /// [`crate::macros::AztecConfig::offchain_message_handler`]. pub type OffchainMessageHandler = unconstrained fn[Env]( diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr similarity index 100% rename from noir-projects/aztec-nr/aztec/src/messages/discovery/offchain_inbox.nr rename to noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr From e41e8474e3a699254293f295aac0d75bda9badcd Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 17:12:01 +0000 Subject: [PATCH 036/119] offchain_sync_inbox -> default_handler --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 4 ++-- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 2 +- .../aztec-nr/aztec/src/messages/discovery/offchain.nr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index b4c380b1c17d..ac0a72d8cecf 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -60,7 +60,7 @@ impl AztecConfig { /// should not. /// /// By default, the lifecycle of offchain messages is handled by - /// [`crate::messages::discovery::offchain::offchain_sync_inbox`]. + /// [`crate::messages::discovery::offchain::default_handler`]. /// /// This config allows for the default behavior to be overridden. pub comptime fn offchain_message_handler(self: Self, handler: OffchainMessageHandler<()>) -> Self { @@ -134,7 +134,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { let handler = config.offchain_message_handler.unwrap(); quote { Option::some($handler) } } else { - quote { Option::some(aztec::messages::discovery::offchain::offchain_sync_inbox) } + quote { Option::some(aztec::messages::discovery::offchain::default_handler) } }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index e46edaa83058..a993fac04172 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -87,7 +87,7 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /// utility function (injected by [`crate::macros::aztec`]). This handler is responsible for deciding when each message /// is ready for processing, when it can be safely disposed of, etc. /// -/// The default implementation is [`offchain::offchain_sync_inbox`], which manages an inbox with max TX TTL-based +/// The default implementation is [`offchain::default_handler`], which manages an inbox with max TX TTL-based /// expiry and automatic context resolution. It can be overridden via /// [`crate::macros::AztecConfig::offchain_message_handler`]. pub type OffchainMessageHandler = unconstrained fn[Env]( diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 19e4c37dfb12..9b5e44e74913 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -40,7 +40,7 @@ pub unconstrained fn receive_offchain_message( /// Returns offchain-delivered messages to process during sync. /// /// Messages remain in the inbox and are reprocessed on each sync until they expire. -pub unconstrained fn offchain_sync_inbox(address: AztecAddress) -> CapsuleArray { +pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); let ready: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); From 642a023722faa3b0e1399e1148b4187a610b78d5 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 17:22:56 +0000 Subject: [PATCH 037/119] receive_offchain_message -> receive --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 2 +- noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index ac0a72d8cecf..854e14fb0afa 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -466,7 +466,7 @@ comptime fn generate_offchain_receive() -> Quoted { tx_hash: Field, ) { let address = aztec::context::UtilityContext::new().this_address(); - aztec::messages::discovery::offchain::receive_offchain_message( + aztec::messages::discovery::offchain::receive( address, ciphertext, recipient, diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 9b5e44e74913..c3f5695cac82 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -26,7 +26,7 @@ pub struct PendingOffchainMsg { } /// Receive an offchain-delivered message into the contract-owned inbox. -pub unconstrained fn receive_offchain_message( +pub unconstrained fn receive( contract_address: AztecAddress, ciphertext: BoundedVec, recipient: AztecAddress, From 485854d8828a71f14759c4121160f207cb0dc17d Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 17:30:18 +0000 Subject: [PATCH 038/119] better docs --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 15 ++------------- .../aztec/src/messages/discovery/offchain.nr | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 854e14fb0afa..902dc9d24711 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -443,21 +443,10 @@ comptime fn generate_offchain_receive() -> Quoted { parameters: offchain_receive_parameters, } - /// Delivers an offchain message to this contract's offchain inbox for processing during the next sync. + /// Delivers an offchain message to this contract's offchain inbox for subsequent processing. /// - /// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender - /// transmits the encrypted payload to the recipient through an external channel (e.g. a URL accessible by the - /// recipient). The recipient then calls this function to hand the message to the contract so it can be processed - /// through the same mechanisms as onchain messages. + /// For more details, see [`aztec::messages::discovery::offchain::receive`]. /// - /// Messages are processed when their originating transaction is found onchain (providing the context needed to - /// validate resulting notes and events). - /// - /// Messages are kept in the inbox until their originating until the originating transaction can be assumed to - /// have expired. This is necessary to make the mechanism resilient to reorgs. - /// - /// Processing order is not guaranteed. - /// /// This function is automatically injected by the `#[aztec]` macro. #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] unconstrained fn offchain_receive( diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index c3f5695cac82..3e21a6ec9cc0 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -25,7 +25,20 @@ pub struct PendingOffchainMsg { pub enqueued_ts: u64, } -/// Receive an offchain-delivered message into the contract-owned inbox. +/// Delivers an offchain message to the given contract's offchain inbox for subsequent processing. +/// +/// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender shares the +/// message to the recipient through an external channel (e.g. a URL accessible by the recipient). The recipient then +/// calls this function to hand the message to the contract so it can be processed through the same mechanisms as +/// onchain messages. +/// +/// Messages are processed when their originating transaction is found onchain (providing the context needed to +/// validate resulting notes and events). +/// +/// Messages are kept in the inbox until the originating transaction can be assumed to have expired. This is necessary +/// to make the mechanism resilient to reorgs. +/// +/// Processing order is not guaranteed. pub unconstrained fn receive( contract_address: AztecAddress, ciphertext: BoundedVec, From 49bf4cf316588bc465dd20bedb6197b3f20ab3b5 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Fri, 27 Feb 2026 18:08:49 +0000 Subject: [PATCH 039/119] more docs, and visibility restriction --- .../aztec/src/messages/discovery/mod.nr | 1 - .../aztec/src/messages/discovery/offchain.nr | 53 +++++++++++++------ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index a993fac04172..a9fc5e882bb3 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -132,7 +132,6 @@ pub unconstrained fn do_sync_state = fetch(contract_address); diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 3e21a6ec9cc0..be4be2130274 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -8,21 +8,41 @@ use crate::{ protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, }; -pub global OFFCHAIN_INBOX_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_INBOX_SLOT".as_bytes()); -pub global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = - sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes()); -pub global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = - sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes()); -pub global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); -pub global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 86400; +/// Base capsule slot for the persistent inbox of [`PendingOffchainMsg`] entries. +/// +/// This is the slot where we accumulate messages received through [`receive`]. +global OFFCHAIN_INBOX_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_INBOX_SLOT".as_bytes()); + +/// Ephemeral capsule slot used by [`default_handler`] to pass tx hash resolution requests to PXE. +global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes()); + +/// Ephemeral capsule slot used by [`default_handler`] to read tx context responses from PXE. +global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes()); + +/// Ephemeral capsule slot used by [`default_handler`] to collect messages ready for processing. +global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); + +/// Minimum time that a message is kept in the inbox before eviction: 24 hours. After 24 hours, the protocol guarantees +/// that the original TX that emitted the message can no longer be included in a block. This gives us a bounded time +/// window within which we need to keep messages around in case we need to reprocess them after reorgs. +global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 24 * 3600; +/// An offchain message awaiting processing (or re-processing) in the inbox. +/// +/// Messages remain in the inbox until they expire after [`OFFCHAIN_MESSAGE_TTL_SECONDS`], even if they have already +/// been processed. This is necessary to handle reorgs: a processed message may need to be re-processed if the +/// transaction that provided its context is reverted. On each sync, resolved messages are promoted to +/// [`OffchainMessage`] for processing. #[derive(Serialize, Deserialize)] -pub struct PendingOffchainMsg { - pub ciphertext: BoundedVec, - pub recipient: AztecAddress, - // Use 0 as a sentinel for tx-less messages. - pub tx_hash: Field, - pub enqueued_ts: u64, +struct PendingOffchainMsg { + /// The encrypted message payload. + ciphertext: BoundedVec, + /// The intended recipient of the message. + recipient: AztecAddress, + /// The hash of the transaction that produced this message. A value of 0 indicates a tx-less message. + tx_hash: Field, + /// Timestamp at which the message was received, used for TTL-based eviction. + received_at: u64, } /// Delivers an offchain message to the given contract's offchain inbox for subsequent processing. @@ -46,13 +66,14 @@ pub unconstrained fn receive( tx_hash: Field, ) { CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT).push( - PendingOffchainMsg { ciphertext, recipient, tx_hash, enqueued_ts: UtilityContext::new().timestamp() }, + PendingOffchainMsg { ciphertext, recipient, tx_hash, received_at: UtilityContext::new().timestamp() }, ); } /// Returns offchain-delivered messages to process during sync. /// -/// Messages remain in the inbox and are reprocessed on each sync until they expire. +/// Messages remain in the inbox and are reprocessed on each sync until their originating transaction is no longer at +/// risk of being dropped by a reorg. pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); @@ -94,7 +115,7 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray msg.enqueued_ts + OFFCHAIN_MESSAGE_TTL_SECONDS { + if now > msg.received_at + OFFCHAIN_MESSAGE_TTL_SECONDS { inbox.remove(j); continue; } From f40df639ff4002150580bb3f957cbdd7004e6e22 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 09:34:28 +0000 Subject: [PATCH 040/119] more cleanup of offchain inbox handler --- .../aztec/src/messages/discovery/offchain.nr | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index be4be2130274..f9630ea22ac6 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -76,21 +76,24 @@ pub unconstrained fn receive( /// risk of being dropped by a reorg. pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); - let requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); - let ready: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); + let context_resolution_requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); + let resolved_contexts: CapsuleArray> = + CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); + let ready_to_process: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); // Clear any stale ready messages from a previous run. - ready.for_each(|i, _| { ready.remove(i); }); + ready_to_process.for_each(|i, _| { ready_to_process.remove(i); }); - // Clear any stale requests from a previous run. - requests.for_each(|i, _| { requests.remove(i); }); + // Clear any stale context resolution requests/responses from a previous run. + context_resolution_requests.for_each(|i, _| { context_resolution_requests.remove(i); }); + resolved_contexts.for_each(|i, _| { resolved_contexts.remove(i); }); // Build a request list aligned with the inbox indices. let mut i = 0; let inbox_len = inbox.len(); while i < inbox_len { let msg = inbox.get(i); - requests.push(msg.tx_hash); + context_resolution_requests.push(msg.tx_hash); i += 1; } @@ -101,26 +104,24 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray> = - CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); - assert_eq(responses.len(), inbox_len); + assert_eq(resolved_contexts.len(), inbox_len); let now = UtilityContext::new().timestamp(); let mut j = inbox_len; while j > 0 { j -= 1; - let maybe_ctx = responses.get(j); - // Clear response entry as we go so the array is empty for the next run. - responses.remove(j); - + let maybe_ctx = resolved_contexts.get(j); let msg = inbox.get(j); + + // After max TTL it is safe to remove the message from the inbox if now > msg.received_at + OFFCHAIN_MESSAGE_TTL_SECONDS { inbox.remove(j); continue; } + + // Tx not yet available; keep the message in the inbox. if maybe_ctx.is_none() { - // Tx not yet available; keep the message in the inbox. continue; } @@ -132,8 +133,8 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray Date: Mon, 2 Mar 2026 09:46:16 +0000 Subject: [PATCH 041/119] cleanup --- .../aztec/src/messages/discovery/offchain.nr | 1 + .../processing/message_context_response.nr | 10 ---------- .../aztec/src/messages/processing/mod.nr | 16 +++++++++++++--- 3 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 noir-projects/aztec-nr/aztec/src/messages/processing/message_context_response.nr diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index f9630ea22ac6..1a280976277b 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -125,6 +125,7 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray, - pub first_nullifier_in_tx: Field, -} diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index 425fcd5a801f..cfbfb91f26b0 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -2,8 +2,13 @@ pub(crate) mod event_validation_request; mod message_context; pub use message_context::MessageContext; -mod message_context_response; -pub use message_context_response::MessageContextResponse; +/// Response containing the transaction context needed to process an offchain-delivered message. +#[derive(Deserialize, Eq)] +pub struct MessageContextResponse { + pub tx_hash: Field, + pub unique_note_hashes_in_tx: BoundedVec, + pub first_nullifier_in_tx: Field, +} pub(crate) mod note_validation_request; pub(crate) mod log_retrieval_request; @@ -24,7 +29,12 @@ use crate::{ }, oracle, }; -use crate::protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}; +use crate::protocol::{ + address::AztecAddress, + constants::MAX_NOTE_HASHES_PER_TX, + hash::sha256_to_field, + traits::{Deserialize, Serialize}, +}; use event_validation_request::EventValidationRequest; // Base slot for the pending tagged log array to which the fetch_tagged_logs oracle inserts found private logs. From db989535b19632b0b4a401650c27c064f85eff20 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 09:54:12 +0000 Subject: [PATCH 042/119] doc comments on MessageTxContext --- .../aztec/src/messages/discovery/offchain.nr | 4 ++-- .../aztec/src/messages/processing/mod.nr | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 1a280976277b..6f1875df17cc 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -3,7 +3,7 @@ use crate::{ context::UtilityContext, messages::{ encoding::MESSAGE_CIPHERTEXT_LEN, - processing::{MessageContext, MessageContextResponse, OffchainMessage, resolve_message_contexts}, + processing::{MessageContext, MessageTxContext, OffchainMessage, resolve_message_contexts}, }, protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, }; @@ -77,7 +77,7 @@ pub unconstrained fn receive( pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let context_resolution_requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); - let resolved_contexts: CapsuleArray> = + let resolved_contexts: CapsuleArray> = CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); let ready_to_process: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index cfbfb91f26b0..bc0dce6e0e29 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -2,13 +2,6 @@ pub(crate) mod event_validation_request; mod message_context; pub use message_context::MessageContext; -/// Response containing the transaction context needed to process an offchain-delivered message. -#[derive(Deserialize, Eq)] -pub struct MessageContextResponse { - pub tx_hash: Field, - pub unique_note_hashes_in_tx: BoundedVec, - pub first_nullifier_in_tx: Field, -} pub(crate) mod note_validation_request; pub(crate) mod log_retrieval_request; @@ -57,6 +50,18 @@ global LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT: Field = sha256_to_field( "AZTEC_NR::LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT".as_bytes(), ); +/// Transaction context needed to process a message. +/// +/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. This type is used when processing +/// offchain messages. MessageTxContext's are resolved via a PXE oracle call, but and then it's the contract who pairs +/// that data up with a recipient to build a `MessageContext`. +#[derive(Deserialize, Eq)] +pub(crate) struct MessageTxContext { + pub tx_hash: Field, + pub unique_note_hashes_in_tx: BoundedVec, + pub first_nullifier_in_tx: Field, +} + /// An offchain-delivered message ready for processing during sync. #[derive(Serialize, Deserialize)] pub struct OffchainMessage { From eca50127f64571f127ca8ed1fd557806d65d6c8b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 10:00:03 +0000 Subject: [PATCH 043/119] move MessageTxContext --- .../messages/processing/message_context.nr | 12 +++++++++++ .../aztec/src/messages/processing/mod.nr | 20 ++----------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr index 253a75f2a899..1e6eb9b59dd1 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr @@ -13,6 +13,18 @@ pub struct MessageContext { pub recipient: AztecAddress, } +/// Transaction context needed to process a message. +/// +/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. This type is used when processing +/// offchain messages. MessageTxContext's are resolved via a PXE oracle call, and then it's the contract who pairs +/// that data up with a recipient to build a `MessageContext`. +#[derive(Deserialize, Eq)] +pub(crate) struct MessageTxContext { + pub tx_hash: Field, + pub unique_note_hashes_in_tx: BoundedVec, + pub first_nullifier_in_tx: Field, +} + mod test { use crate::messages::processing::MessageContext; use crate::protocol::{address::AztecAddress, traits::{Deserialize, FromField}}; diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index bc0dce6e0e29..a0d290577f1c 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -2,6 +2,7 @@ pub(crate) mod event_validation_request; mod message_context; pub use message_context::MessageContext; +pub(crate) use message_context::MessageTxContext; pub(crate) mod note_validation_request; pub(crate) mod log_retrieval_request; @@ -22,12 +23,7 @@ use crate::{ }, oracle, }; -use crate::protocol::{ - address::AztecAddress, - constants::MAX_NOTE_HASHES_PER_TX, - hash::sha256_to_field, - traits::{Deserialize, Serialize}, -}; +use crate::protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}; use event_validation_request::EventValidationRequest; // Base slot for the pending tagged log array to which the fetch_tagged_logs oracle inserts found private logs. @@ -50,18 +46,6 @@ global LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT: Field = sha256_to_field( "AZTEC_NR::LOG_RETRIEVAL_RESPONSES_ARRAY_BASE_SLOT".as_bytes(), ); -/// Transaction context needed to process a message. -/// -/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. This type is used when processing -/// offchain messages. MessageTxContext's are resolved via a PXE oracle call, but and then it's the contract who pairs -/// that data up with a recipient to build a `MessageContext`. -#[derive(Deserialize, Eq)] -pub(crate) struct MessageTxContext { - pub tx_hash: Field, - pub unique_note_hashes_in_tx: BoundedVec, - pub first_nullifier_in_tx: Field, -} - /// An offchain-delivered message ready for processing during sync. #[derive(Serialize, Deserialize)] pub struct OffchainMessage { From ab48b796e7edf95c7f34271c34673825c1ae03ce Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 10:34:48 +0000 Subject: [PATCH 044/119] rename MessageContextResponse on TS side --- ...{message_context_response.ts => message_tx_context.ts} | 6 +++--- .../oracle/utility_execution_oracle.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) rename yarn-project/pxe/src/contract_function_simulator/noir-structs/{message_context_response.ts => message_tx_context.ts} (90%) diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.ts similarity index 90% rename from yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts rename to yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.ts index 2a5a7671b40e..a379e0219f76 100644 --- a/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_context_response.ts +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.ts @@ -7,7 +7,7 @@ import type { TxHash } from '@aztec/stdlib/tx'; * Intermediate struct used to return resolved message contexts from PXE. The * `utilityResolveMessageContexts` oracle stores values of this type in a CapsuleArray. */ -export class MessageContextResponse { +export class MessageTxContext { constructor( public txHash: TxHash, public uniqueNoteHashesInTx: Fr[], @@ -28,11 +28,11 @@ export class MessageContextResponse { return range(serializationLen).map(_ => Fr.zero()); } - static toSerializedOption(response: MessageContextResponse | null): Fr[] { + static toSerializedOption(response: MessageTxContext | null): Fr[] { if (response) { return [new Fr(1), ...response.toFields()]; } else { - return [new Fr(0), ...MessageContextResponse.toEmptyFields()]; + return [new Fr(0), ...MessageTxContext.toEmptyFields()]; } } } diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index eb62b3b01d1a..d98952ed3f03 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -37,7 +37,7 @@ import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_ import { EventValidationRequest } from '../noir-structs/event_validation_request.js'; import { LogRetrievalRequest } from '../noir-structs/log_retrieval_request.js'; import { LogRetrievalResponse } from '../noir-structs/log_retrieval_response.js'; -import { MessageContextResponse } from '../noir-structs/message_context_response.js'; +import { MessageTxContext } from '../noir-structs/message_tx_context.js'; import { NoteValidationRequest } from '../noir-structs/note_validation_request.js'; import { UtilityContext } from '../noir-structs/utility_context.js'; import { pickNotes } from '../pick_notes.js'; @@ -585,18 +585,18 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra return null; } - return new MessageContextResponse(data.txHash, data.noteHashes, data.nullifiers[0]); + return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]); }), ); // Requests are cleared once we're done. await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); - // Store Option in the response capsule array. + // Store Option in the response capsule array. await this.capsuleStore.setCapsuleArray( contractAddress, messageContextResponsesArrayBaseSlot, - maybeMessageContexts.map(MessageContextResponse.toSerializedOption), + maybeMessageContexts.map(MessageTxContext.toSerializedOption), this.jobId, ); } From e92d6f71d0452808b076092a0628be03dd22ab8e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 10:41:43 +0000 Subject: [PATCH 045/119] add tx context serialization tests on ts side --- .../noir-structs/message_tx_context.test.ts | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.test.ts diff --git a/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.test.ts b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.test.ts new file mode 100644 index 000000000000..72e8f5c01c9a --- /dev/null +++ b/yarn-project/pxe/src/contract_function_simulator/noir-structs/message_tx_context.test.ts @@ -0,0 +1,172 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import { TxHash } from '@aztec/stdlib/tx'; + +import { MessageTxContext } from './message_tx_context.js'; + +describe('MessageTxContext', () => { + it('serialization of some matches snapshot', () => { + const txHash = new TxHash(new Fr(123)); + const uniqueNoteHashes = [new Fr(4n), new Fr(5n)]; + const firstNullifier = new Fr(6n); + const ctx = new MessageTxContext(txHash, uniqueNoteHashes, firstNullifier); + const serialized = MessageTxContext.toSerializedOption(ctx); + expect(serialized.map(f => f.toString())).toMatchInlineSnapshot( + ` + [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000000000000000000000000000000000000000007b", + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000006", + ] + `, + ); + }); + it('serialization of none matches snapshot', () => { + const serialized = MessageTxContext.toSerializedOption(null); + expect(serialized.map(f => f.toString())).toMatchInlineSnapshot( + ` + [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + ] + `, + ); + }); + it('serialization length of empty matches', () => { + const txHash = new TxHash(new Fr(123)); + const uniqueNoteHashes = [new Fr(4n), new Fr(5n)]; + const firstNullifier = new Fr(6n); + const ctx = new MessageTxContext(txHash, uniqueNoteHashes, firstNullifier); + expect(ctx.toFields().length).toEqual(MessageTxContext.toEmptyFields().length); + }); +}); From 36facfaebdf00214ee1083665718217555ac2cdf Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 10:55:22 +0000 Subject: [PATCH 046/119] consider anchor block --- .../oracle/utility_execution_oracle.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index d98952ed3f03..599254f95ed3 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -564,7 +564,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra ); // TODO: optimize, we might be hitting the node to get the same txHash repeatedly - // TODO: we should be querying at the anchor block + const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber(); const maybeMessageContexts = await Promise.all( requestCapsules.map(async fields => { // TODO: clarify that txHash=0 means no txHash @@ -576,7 +576,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra const txHash = TxHash.fromField(txHashField); const txEffect = await this.aztecNode.getTxEffect(txHash); - if (!txEffect) { + if (!txEffect || txEffect.l2BlockNumber > anchorBlockNumber) { return null; } @@ -589,10 +589,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra }), ); - // Requests are cleared once we're done. + // Clear request capsule await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); - // Store Option in the response capsule array. + // Leave response in response capsule array. await this.capsuleStore.setCapsuleArray( contractAddress, messageContextResponsesArrayBaseSlot, From 8882923e8dacf775c0ed480b6f157163d0f0cfef Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 12:00:50 +0000 Subject: [PATCH 047/119] extract MessageContextService --- .../contract_function_simulator.ts | 6 +++ .../src/contract_function_simulator/index.ts | 1 + .../oracle/oracle_version_is_checked.test.ts | 4 ++ .../oracle/private_execution.test.ts | 4 ++ .../oracle/private_execution_oracle.ts | 1 + .../oracle/utility_execution.test.ts | 5 +++ .../oracle/utility_execution_oracle.ts | 32 ++++----------- .../src/messages/message_context_service.ts | 41 +++++++++++++++++++ yarn-project/pxe/src/pxe.ts | 6 +++ .../oracle/txe_oracle_top_level_context.ts | 2 + yarn-project/txe/src/state_machine/index.ts | 6 ++- yarn-project/txe/src/txe_session.ts | 3 ++ 12 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 yarn-project/pxe/src/messages/message_context_service.ts diff --git a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts index b23535571a58..c9e898ca7f8c 100644 --- a/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts +++ b/yarn-project/pxe/src/contract_function_simulator/contract_function_simulator.ts @@ -89,6 +89,7 @@ import { import type { AccessScopes } from '../access_scopes.js'; import type { ContractSyncService } from '../contract_sync/contract_sync_service.js'; +import type { MessageContextService } from '../messages/message_context_service.js'; import type { AddressStore } from '../storage/address_store/address_store.js'; import type { CapsuleStore } from '../storage/capsule_store/capsule_store.js'; import type { ContractStore } from '../storage/contract_store/contract_store.js'; @@ -138,6 +139,7 @@ export type ContractFunctionSimulatorArgs = { privateEventStore: PrivateEventStore; simulator: CircuitSimulator; contractSyncService: ContractSyncService; + messageContextService: MessageContextService; }; /** @@ -157,6 +159,7 @@ export class ContractFunctionSimulator { private readonly privateEventStore: PrivateEventStore; private readonly simulator: CircuitSimulator; private readonly contractSyncService: ContractSyncService; + private readonly messageContextService: MessageContextService; constructor(args: ContractFunctionSimulatorArgs) { this.contractStore = args.contractStore; @@ -171,6 +174,7 @@ export class ContractFunctionSimulator { this.privateEventStore = args.privateEventStore; this.simulator = args.simulator; this.contractSyncService = args.contractSyncService; + this.messageContextService = args.messageContextService; this.log = createLogger('simulator'); } @@ -241,6 +245,7 @@ export class ContractFunctionSimulator { senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.messageContextService, contractSyncService: this.contractSyncService, jobId, totalPublicCalldataCount: 0, @@ -335,6 +340,7 @@ export class ContractFunctionSimulator { senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.messageContextService, jobId, scopes, }); diff --git a/yarn-project/pxe/src/contract_function_simulator/index.ts b/yarn-project/pxe/src/contract_function_simulator/index.ts index 7ac8010eec78..4a7c7b2b357d 100644 --- a/yarn-project/pxe/src/contract_function_simulator/index.ts +++ b/yarn-project/pxe/src/contract_function_simulator/index.ts @@ -4,6 +4,7 @@ export { HashedValuesCache } from './hashed_values_cache.js'; export { pickNotes } from './pick_notes.js'; export type { NoteData, IMiscOracle, IUtilityExecutionOracle, IPrivateExecutionOracle } from './oracle/interfaces.js'; export { MessageLoadOracleInputs } from './oracle/message_load_oracle_inputs.js'; +export { MessageContextService } from '../messages/message_context_service.js'; export { UtilityExecutionOracle } from './oracle/utility_execution_oracle.js'; export { PrivateExecutionOracle } from './oracle/private_execution_oracle.js'; export { Oracle } from './oracle/oracle.js'; diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts index 9a6ce8b9bc36..8878ad6b4ca0 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle_version_is_checked.test.ts @@ -13,6 +13,7 @@ import { jest } from '@jest/globals'; import { mock } from 'jest-mock-extended'; import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js'; +import type { MessageContextService } from '../../messages/message_context_service.js'; import type { AddressStore } from '../../storage/address_store/address_store.js'; import type { CapsuleStore } from '../../storage/capsule_store/capsule_store.js'; import type { ContractStore } from '../../storage/contract_store/contract_store.js'; @@ -38,6 +39,7 @@ describe('Oracle Version Check test suite', () => { let capsuleStore: ReturnType>; let privateEventStore: ReturnType>; let contractSyncService: ReturnType>; + let messageContextService: ReturnType>; let acirSimulator: ContractFunctionSimulator; let contractAddress: AztecAddress; let anchorBlockHeader: BlockHeader; @@ -57,6 +59,7 @@ describe('Oracle Version Check test suite', () => { capsuleStore = mock(); privateEventStore = mock(); contractSyncService = mock(); + messageContextService = mock(); utilityAssertCompatibleOracleVersionSpy = jest.spyOn( UtilityExecutionOracle.prototype, 'utilityAssertCompatibleOracleVersion', @@ -103,6 +106,7 @@ describe('Oracle Version Check test suite', () => { privateEventStore, simulator, contractSyncService, + messageContextService, }); }); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts index 02e5f378c460..911f985a73b4 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts @@ -67,6 +67,7 @@ import { toFunctionSelector } from 'viem'; import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js'; import { syncState } from '../../contract_sync/helpers.js'; +import type { MessageContextService } from '../../messages/message_context_service.js'; import type { AddressStore } from '../../storage/address_store/address_store.js'; import type { CapsuleStore } from '../../storage/capsule_store/capsule_store.js'; import type { ContractStore } from '../../storage/contract_store/contract_store.js'; @@ -124,6 +125,7 @@ describe('Private Execution test suite', () => { let capsuleStore: MockProxy; let privateEventStore: MockProxy; let contractSyncService: MockProxy; + let messageContextService: MockProxy; let acirSimulator: ContractFunctionSimulator; let anchorBlockHeader = BlockHeader.empty(); let logger: Logger; @@ -324,6 +326,7 @@ describe('Private Execution test suite', () => { privateEventStore = mock(); senderAddressBookStore = mock(); contractSyncService = mock(); + messageContextService = mock(); // Configure mock to actually perform sync_state calls (needed for nested call tests) contractSyncService.ensureContractSynced.mockImplementation( async (contractAddress, functionToInvokeAfterSync, utilityExecutor, anchorBlockHeader, jobId) => { @@ -491,6 +494,7 @@ describe('Private Execution test suite', () => { privateEventStore, simulator, contractSyncService, + messageContextService, }); }); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts index c6d33cc7244e..0067c93c83e9 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution_oracle.ts @@ -569,6 +569,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.messageContextService, contractSyncService: this.contractSyncService, jobId: this.jobId, totalPublicCalldataCount: this.totalPublicCalldataCount, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts index fd6cb7ef4a2a..7486cbeaf50a 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts @@ -18,6 +18,7 @@ import { mock } from 'jest-mock-extended'; import type { _MockProxy } from 'jest-mock-extended/lib/Mock.js'; import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js'; +import { MessageContextService } from '../../messages/message_context_service.js'; import type { AddressStore } from '../../storage/address_store/address_store.js'; import type { CapsuleStore } from '../../storage/capsule_store/capsule_store.js'; import type { ContractStore } from '../../storage/contract_store/contract_store.js'; @@ -43,6 +44,7 @@ describe('Utility Execution test suite', () => { let capsuleStore: ReturnType>; let privateEventStore: ReturnType>; let contractSyncService: ReturnType>; + let messageContextService: MessageContextService; let acirSimulator: ContractFunctionSimulator; let owner: AztecAddress; let ownerCompleteAddress: CompleteAddress; @@ -65,6 +67,7 @@ describe('Utility Execution test suite', () => { capsuleStore = mock(); privateEventStore = mock(); contractSyncService = mock(); + messageContextService = new MessageContextService(aztecNode); const capsuleArrays = new Map(); anchorBlockHeader = BlockHeader.random(); senderTaggingStore.getLastFinalizedIndex.mockResolvedValue(undefined); @@ -103,6 +106,7 @@ describe('Utility Execution test suite', () => { privateEventStore, simulator, contractSyncService, + messageContextService, }); const ownerPartialAddress = Fr.random(); @@ -219,6 +223,7 @@ describe('Utility Execution test suite', () => { senderAddressBookStore, capsuleStore, privateEventStore, + messageContextService, jobId: 'test-job-id', scopes: 'ALL_SCOPES', }); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index 599254f95ed3..6c00484ecfda 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -19,12 +19,12 @@ import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging'; import type { NoteStatus } from '@aztec/stdlib/note'; import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees'; import type { BlockHeader, Capsule } from '@aztec/stdlib/tx'; -import { TxHash } from '@aztec/stdlib/tx'; import type { AccessScopes } from '../../access_scopes.js'; import { createContractLogger, logContractMessage } from '../../contract_logging.js'; import { EventService } from '../../events/event_service.js'; import { LogService } from '../../logs/log_service.js'; +import { MessageContextService } from '../../messages/message_context_service.js'; import { NoteService } from '../../notes/note_service.js'; import { ORACLE_VERSION } from '../../oracle_version.js'; import type { AddressStore } from '../../storage/address_store/address_store.js'; @@ -60,6 +60,7 @@ export type UtilityExecutionOracleArgs = { senderAddressBookStore: SenderAddressBookStore; capsuleStore: CapsuleStore; privateEventStore: PrivateEventStore; + messageContextService: MessageContextService; jobId: string; log?: ReturnType; scopes: AccessScopes; @@ -87,6 +88,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra protected readonly senderAddressBookStore: SenderAddressBookStore; protected readonly capsuleStore: CapsuleStore; protected readonly privateEventStore: PrivateEventStore; + protected readonly messageContextService: MessageContextService; protected readonly jobId: string; protected log: ReturnType; protected readonly scopes: AccessScopes; @@ -105,6 +107,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra this.senderAddressBookStore = args.senderAddressBookStore; this.capsuleStore = args.capsuleStore; this.privateEventStore = args.privateEventStore; + this.messageContextService = args.messageContextService; this.jobId = args.jobId; this.log = args.log ?? createLogger('simulator:client_view_context'); this.scopes = args.scopes; @@ -563,31 +566,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra this.jobId, ); - // TODO: optimize, we might be hitting the node to get the same txHash repeatedly - const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber(); - const maybeMessageContexts = await Promise.all( - requestCapsules.map(async fields => { - // TODO: clarify that txHash=0 means no txHash - // TODO: think how to handle no tx case - const txHashField = fields[0] ?? Fr.ZERO; - if (txHashField.isZero()) { - return null; - } - - const txHash = TxHash.fromField(txHashField); - const txEffect = await this.aztecNode.getTxEffect(txHash); - if (!txEffect || txEffect.l2BlockNumber > anchorBlockNumber) { - return null; - } + const txHashes = requestCapsules.map(fields => fields[0] ?? Fr.ZERO); - const data = txEffect.data; - if (data.nullifiers.length === 0) { - return null; - } - - return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]); - }), - ); + const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber(); + const maybeMessageContexts = await this.messageContextService.resolveMessageContexts(txHashes, anchorBlockNumber); // Clear request capsule await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); diff --git a/yarn-project/pxe/src/messages/message_context_service.ts b/yarn-project/pxe/src/messages/message_context_service.ts new file mode 100644 index 000000000000..3ee5bcbdaeea --- /dev/null +++ b/yarn-project/pxe/src/messages/message_context_service.ts @@ -0,0 +1,41 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import type { AztecNode } from '@aztec/stdlib/interfaces/server'; +import { TxHash } from '@aztec/stdlib/tx'; + +import { MessageTxContext } from '../contract_function_simulator/noir-structs/message_tx_context.js'; + +/** Resolves transaction hashes into the context needed to process messages. */ +export class MessageContextService { + constructor(private readonly aztecNode: AztecNode) {} + + /** + * Resolves a list of tx hashes into their message contexts. + * + * For each tx hash, looks up the corresponding tx effect and extracts the note hashes and first nullifier needed to + * process messages that originated from that transaction. Returns `null` for tx hashes that are zero, not yet + * available, or in blocks beyond the anchor block. + */ + resolveMessageContexts(txHashes: Fr[], anchorBlockNumber: number): Promise<(MessageTxContext | null)[]> { + // TODO: optimize, we might be hitting the node to get the same txHash repeatedly + return Promise.all( + txHashes.map(async txHashField => { + if (txHashField.isZero()) { + return null; + } + + const txHash = TxHash.fromField(txHashField); + const txEffect = await this.aztecNode.getTxEffect(txHash); + if (!txEffect || txEffect.l2BlockNumber > anchorBlockNumber) { + return null; + } + + const data = txEffect.data; + if (data.nullifiers.length === 0) { + return null; + } + + return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]); + }), + ); + } +} diff --git a/yarn-project/pxe/src/pxe.ts b/yarn-project/pxe/src/pxe.ts index 9181add33b6e..4477c343e9eb 100644 --- a/yarn-project/pxe/src/pxe.ts +++ b/yarn-project/pxe/src/pxe.ts @@ -68,6 +68,7 @@ import { PXEDebugUtils } from './debug/pxe_debug_utils.js'; import { enrichPublicSimulationError, enrichSimulationError } from './error_enriching.js'; import { PrivateEventFilterValidator } from './events/private_event_filter_validator.js'; import { JobCoordinator } from './job_coordinator/job_coordinator.js'; +import { MessageContextService } from './messages/message_context_service.js'; import { PrivateKernelExecutionProver, type PrivateKernelExecutionProverConfig, @@ -157,6 +158,7 @@ export class PXE { private addressStore: AddressStore, private privateEventStore: PrivateEventStore, private contractSyncService: ContractSyncService, + private messageContextService: MessageContextService, private simulator: CircuitSimulator, private proverEnabled: boolean, private proofCreator: PrivateKernelProver, @@ -212,6 +214,8 @@ export class PXE { noteStore, createLogger('pxe:contract_sync', bindings), ); + const messageContextService = new MessageContextService(node); + const synchronizer = new BlockSynchronizer( node, store, @@ -252,6 +256,7 @@ export class PXE { addressStore, privateEventStore, contractSyncService, + messageContextService, simulator, proverEnabled, proofCreator, @@ -293,6 +298,7 @@ export class PXE { privateEventStore: this.privateEventStore, simulator: this.simulator, contractSyncService: this.contractSyncService, + messageContextService: this.messageContextService, }); } diff --git a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts index 45be8bbcf95c..32de1d689550 100644 --- a/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts +++ b/yarn-project/txe/src/oracle/txe_oracle_top_level_context.ts @@ -387,6 +387,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl // contract would perform, including setting senderForTags. senderForTags: from, simulator, + messageContextService: this.stateMachine.messageContextService, }); // Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks. @@ -743,6 +744,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.stateMachine.messageContextService, jobId, scopes, }); diff --git a/yarn-project/txe/src/state_machine/index.ts b/yarn-project/txe/src/state_machine/index.ts index cf7884c24ff4..5976e9f346a6 100644 --- a/yarn-project/txe/src/state_machine/index.ts +++ b/yarn-project/txe/src/state_machine/index.ts @@ -4,6 +4,7 @@ import { CheckpointNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import { createLogger } from '@aztec/foundation/log'; import { type AnchorBlockStore, type ContractStore, ContractSyncService, type NoteStore } from '@aztec/pxe/server'; +import { MessageContextService } from '@aztec/pxe/simulator'; import { L2Block } from '@aztec/stdlib/block'; import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import type { AztecNode } from '@aztec/stdlib/interfaces/client'; @@ -26,6 +27,7 @@ export class TXEStateMachine { public archiver: TXEArchiver, public anchorBlockStore: AnchorBlockStore, public contractSyncService: ContractSyncService, + public messageContextService: MessageContextService, ) {} public static async create( @@ -68,7 +70,9 @@ export class TXEStateMachine { createLogger('txe:contract_sync'), ); - return new this(node, synchronizer, archiver, anchorBlockStore, contractSyncService); + const messageContextService = new MessageContextService(node); + + return new this(node, synchronizer, archiver, anchorBlockStore, contractSyncService, messageContextService); } public async handleL2Block(block: L2Block) { diff --git a/yarn-project/txe/src/txe_session.ts b/yarn-project/txe/src/txe_session.ts index 5c7b87ea4feb..1dc14f7d5f21 100644 --- a/yarn-project/txe/src/txe_session.ts +++ b/yarn-project/txe/src/txe_session.ts @@ -369,6 +369,7 @@ export class TXESession implements TXESessionStateHandler { contractSyncService: this.stateMachine.contractSyncService, jobId: this.currentJobId, scopes: 'ALL_SCOPES', + messageContextService: this.stateMachine.messageContextService, }); // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary @@ -437,6 +438,7 @@ export class TXESession implements TXESessionStateHandler { senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.stateMachine.messageContextService, jobId: this.currentJobId, scopes: 'ALL_SCOPES', }); @@ -528,6 +530,7 @@ export class TXESession implements TXESessionStateHandler { senderAddressBookStore: this.senderAddressBookStore, capsuleStore: this.capsuleStore, privateEventStore: this.privateEventStore, + messageContextService: this.stateMachine.messageContextService, jobId: this.currentJobId, scopes, }); From 463709ebfdcdb7dab02ab13d0d9e19576447843f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 13:52:18 +0000 Subject: [PATCH 048/119] tests for oracle --- .../oracle/utility_execution.test.ts | 78 +++++++++++++++++++ .../oracle/utility_execution_oracle.ts | 7 +- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts index 7486cbeaf50a..9d208e30d23b 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts @@ -28,6 +28,7 @@ import type { RecipientTaggingStore } from '../../storage/tagging_store/recipien import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_address_book_store.js'; import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js'; import { ContractFunctionSimulator } from '../contract_function_simulator.js'; +import { MessageTxContext } from '../noir-structs/message_tx_context.js'; import { UtilityExecutionOracle } from './utility_execution_oracle.js'; describe('Utility Execution test suite', () => { @@ -236,5 +237,82 @@ describe('Utility Execution test suite', () => { ); }); }); + + describe('utilityResolveMessageContexts', () => { + const requestSlot = Fr.random(); + const responseSlot = Fr.random(); + + it('throws when contractAddress does not match', async () => { + const wrongAddress = await AztecAddress.random(); + await expect( + utilityExecutionOracle.utilityResolveMessageContexts(wrongAddress, requestSlot, responseSlot), + ).rejects.toThrow(`Got a message context request from ${wrongAddress}, expected ${contractAddress}`); + }); + + it('returns null for zero tx hashes', async () => { + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[Fr.ZERO]]); + + await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); + + const response = capsuleStore.setCapsuleArray.mock.calls.find( + call => call[0].equals(contractAddress) && call[1].equals(responseSlot), + ); + expect(response).toBeDefined(); + const responseFields = response![2][0]; + expect(responseFields).toEqual(MessageTxContext.toSerializedOption(null)); + expect(aztecNode.getTxEffect).not.toHaveBeenCalled(); + }); + + it('resolves a valid tx hash into a MessageTxContext', async () => { + const txHash = TxHash.random(); + const noteHash = Fr.random(); + const firstNullifier = Fr.random(); + + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[txHash.hash]]); + aztecNode.getTxEffect.mockResolvedValueOnce({ + l2BlockNumber: BlockNumber(syncedBlockNumber - 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash, noteHashes: [noteHash], nullifiers: [firstNullifier] }, + } as any); + + await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); + + const response = capsuleStore.setCapsuleArray.mock.calls.find( + call => call[0].equals(contractAddress) && call[1].equals(responseSlot), + ); + expect(response).toBeDefined(); + const responseFields = response![2][0]; + const expected = MessageTxContext.toSerializedOption(new MessageTxContext(txHash, [noteHash], firstNullifier)); + expect(responseFields).toEqual(expected); + }); + + it('returns null for tx effects beyond anchor block', async () => { + const txHash = TxHash.random(); + + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[txHash.hash]]); + aztecNode.getTxEffect.mockResolvedValueOnce({ + l2BlockNumber: BlockNumber(syncedBlockNumber + 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash, noteHashes: [], nullifiers: [Fr.random()] }, + } as any); + + await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); + + const response = capsuleStore.setCapsuleArray.mock.calls.find( + call => call[0].equals(contractAddress) && call[1].equals(responseSlot), + ); + expect(response).toBeDefined(); + const responseFields = response![2][0]; + expect(responseFields).toEqual(MessageTxContext.toSerializedOption(null)); + }); + + it('clears the request capsule after processing', async () => { + capsuleStore.readCapsuleArray.mockResolvedValueOnce([]); + await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); + expect(capsuleStore.setCapsuleArray).toHaveBeenCalledWith(contractAddress, requestSlot, [], 'test-job-id'); + }); + }); }); }); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index 6c00484ecfda..cbdba0ebcbec 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -567,9 +567,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra ); const txHashes = requestCapsules.map(fields => fields[0] ?? Fr.ZERO); - - const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber(); - const maybeMessageContexts = await this.messageContextService.resolveMessageContexts(txHashes, anchorBlockNumber); + const maybeMessageContexts = await this.messageContextService.resolveMessageContexts( + txHashes, + this.anchorBlockHeader.getBlockNumber(), + ); // Clear request capsule await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); From e9e94572179b521d0a1553a19d9a0b026736b03c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 17:28:33 +0000 Subject: [PATCH 049/119] add MessageContextService unit tests --- .../messages/message_context_service.test.ts | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 yarn-project/pxe/src/messages/message_context_service.test.ts diff --git a/yarn-project/pxe/src/messages/message_context_service.test.ts b/yarn-project/pxe/src/messages/message_context_service.test.ts new file mode 100644 index 000000000000..7815ebbd21e7 --- /dev/null +++ b/yarn-project/pxe/src/messages/message_context_service.test.ts @@ -0,0 +1,142 @@ +import { BlockNumber } from '@aztec/foundation/branded-types'; +import { Fr } from '@aztec/foundation/curves/bn254'; +import { BlockHash } from '@aztec/stdlib/block'; +import type { AztecNode } from '@aztec/stdlib/interfaces/server'; +import { TxHash } from '@aztec/stdlib/tx'; + +import { mock } from 'jest-mock-extended'; + +import { MessageTxContext } from '../contract_function_simulator/noir-structs/message_tx_context.js'; +import { MessageContextService } from './message_context_service.js'; + +describe('MessageContextService', () => { + let aztecNode: ReturnType>; + let service: MessageContextService; + const anchorBlockNumber = 100; + + beforeEach(() => { + aztecNode = mock(); + service = new MessageContextService(aztecNode); + }); + + it('returns null for zero tx hash', async () => { + const results = await service.resolveMessageContexts([Fr.ZERO], anchorBlockNumber); + + expect(results).toEqual([null]); + expect(aztecNode.getTxEffect).not.toHaveBeenCalled(); + }); + + it('returns null when tx effect is not found', async () => { + const txHash = TxHash.random(); + aztecNode.getTxEffect.mockResolvedValueOnce(undefined); + + const results = await service.resolveMessageContexts([txHash.hash], anchorBlockNumber); + + expect(results).toEqual([null]); + }); + + it('returns null when tx effect is beyond anchor block', async () => { + const txHash = TxHash.random(); + aztecNode.getTxEffect.mockResolvedValueOnce({ + l2BlockNumber: BlockNumber(anchorBlockNumber + 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash, noteHashes: [Fr.random()], nullifiers: [Fr.random()] }, + } as any); + + const results = await service.resolveMessageContexts([txHash.hash], anchorBlockNumber); + + expect(results).toEqual([null]); + }); + + it('returns null when tx effect has no nullifiers', async () => { + const txHash = TxHash.random(); + aztecNode.getTxEffect.mockResolvedValueOnce({ + l2BlockNumber: BlockNumber(anchorBlockNumber - 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash, noteHashes: [Fr.random()], nullifiers: [] }, + } as any); + + const results = await service.resolveMessageContexts([txHash.hash], anchorBlockNumber); + + expect(results).toEqual([null]); + }); + + it('resolves a valid tx hash into a MessageTxContext', async () => { + const txHash = TxHash.random(); + const noteHashes = [Fr.random(), Fr.random()]; + const firstNullifier = Fr.random(); + + aztecNode.getTxEffect.mockResolvedValueOnce({ + l2BlockNumber: BlockNumber(anchorBlockNumber - 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash, noteHashes, nullifiers: [firstNullifier, Fr.random()] }, + } as any); + + const results = await service.resolveMessageContexts([txHash.hash], anchorBlockNumber); + + expect(results).toEqual([new MessageTxContext(txHash, noteHashes, firstNullifier)]); + }); + + it('resolves tx hashes in different situations', async () => { + const validTxHash = TxHash.random(); + const validNoteHashes = [Fr.random()]; + const validNullifier = Fr.random(); + + const notFoundTxHash = TxHash.random(); + const futureTxHash = TxHash.random(); + const noNullifiersTxHash = TxHash.random(); + + aztecNode.getTxEffect.mockImplementation((hash: TxHash) => { + if (hash.equals(validTxHash)) { + return { + l2BlockNumber: BlockNumber(anchorBlockNumber), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash: validTxHash, noteHashes: validNoteHashes, nullifiers: [validNullifier] }, + } as any; + } + if (hash.equals(futureTxHash)) { + return { + l2BlockNumber: BlockNumber(anchorBlockNumber + 5), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash: futureTxHash, noteHashes: [], nullifiers: [Fr.random()] }, + } as any; + } + if (hash.equals(noNullifiersTxHash)) { + return { + l2BlockNumber: BlockNumber(anchorBlockNumber - 1), + l2BlockHash: BlockHash.random(), + txIndexInBlock: 0, + data: { txHash: noNullifiersTxHash, noteHashes: [], nullifiers: [] }, + } as any; + } + return undefined; // notFoundTxHash + }); + + const results = await service.resolveMessageContexts( + [ + Fr.ZERO, // zero → null + validTxHash.hash, // valid → MessageTxContext + notFoundTxHash.hash, // not found → null + futureTxHash.hash, // beyond anchor → null + noNullifiersTxHash.hash, // no nullifiers → null + ], + anchorBlockNumber, + ); + + expect(results).toEqual([ + null, + new MessageTxContext(validTxHash, validNoteHashes, validNullifier), + null, + null, + null, + ]); + + // Zero hash should not trigger getTxEffect + expect(aztecNode.getTxEffect).toHaveBeenCalledTimes(4); + }); +}); From 2e1879876b917a93c1db7c3f9ce194f4264bab94 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 17:32:04 +0000 Subject: [PATCH 050/119] rename --- ...offchain_payment_qr.test.ts => e2e_offchain_payment.test.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename yarn-project/end-to-end/src/{e2e_offchain_payment_qr.test.ts => e2e_offchain_payment.test.ts} (99%) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts similarity index 99% rename from yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts rename to yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 55f6ed0cfbbc..1650d0169f8e 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment_qr.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -17,7 +17,7 @@ import { proveInteraction } from './test-wallet/utils.js'; const TIMEOUT = 120_000; -describe('e2e_offchain_payment_qr', () => { +describe('e2e_offchain_payment', () => { let contract: OffchainPaymentContract; let aztecNode: AztecNode; let aztecNodeAdmin: AztecNodeAdmin; From f71c4abb1c8eac0648688e9254c3e200b8a039e4 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 2 Mar 2026 18:11:59 +0000 Subject: [PATCH 051/119] fix private oracle tests --- .../contract_function_simulator/oracle/private_execution.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts index 911f985a73b4..edf5e87ea101 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/private_execution.test.ts @@ -327,6 +327,7 @@ describe('Private Execution test suite', () => { senderAddressBookStore = mock(); contractSyncService = mock(); messageContextService = mock(); + messageContextService.resolveMessageContexts.mockResolvedValue([]); // Configure mock to actually perform sync_state calls (needed for nested call tests) contractSyncService.ensureContractSynced.mockImplementation( async (contractAddress, functionToInvokeAfterSync, utilityExecutor, anchorBlockHeader, jobId) => { From b38b65d8450666ba455dd9eb59b0e62f36dc4782 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 11:20:55 +0000 Subject: [PATCH 052/119] merge with merge-train --- .../src/contract/base_contract_interaction.ts | 4 +- .../aztec.js/src/contract/batch_call.ts | 10 ++- .../aztec.js/src/contract/contract.test.ts | 6 +- .../contract/contract_function_interaction.ts | 28 +++---- .../aztec.js/src/contract/deploy_method.ts | 59 ++++++++------ .../src/contract/interaction_options.ts | 62 ++++++++++++--- yarn-project/aztec.js/src/utils/authwit.ts | 46 ++++++----- .../aztec.js/src/wallet/wallet.test.ts | 17 ++-- yarn-project/aztec.js/src/wallet/wallet.ts | 12 ++- yarn-project/aztec/src/examples/token.ts | 10 ++- .../aztec/src/local-network/banana_fpc.ts | 2 +- yarn-project/bot/src/amm_bot.ts | 35 +++++---- yarn-project/bot/src/bot.ts | 3 +- yarn-project/bot/src/cross_chain_bot.ts | 3 +- yarn-project/bot/src/factory.ts | 29 ++++--- yarn-project/bot/src/utils.ts | 6 +- .../cli-wallet/src/cmds/create_account.ts | 18 +++-- yarn-project/cli-wallet/src/cmds/deploy.ts | 4 +- .../cli-wallet/src/cmds/deploy_account.ts | 18 +++-- yarn-project/cli-wallet/src/cmds/send.ts | 4 +- yarn-project/cli-wallet/src/cmds/simulate.ts | 2 +- .../client_flows/account_deployments.test.ts | 4 +- .../src/bench/client_flows/amm.test.ts | 2 +- .../src/bench/client_flows/bridging.test.ts | 2 +- .../client_flows/client_flows_benchmark.ts | 62 ++++++++------- .../bench/client_flows/deployments.test.ts | 4 +- .../bench/client_flows/storage_proof.test.ts | 4 +- .../src/bench/client_flows/transfers.test.ts | 4 +- .../src/bench/node_rpc_perf.test.ts | 4 +- .../src/bench/tx_stats_bench.test.ts | 4 +- yarn-project/end-to-end/src/bench/utils.ts | 9 ++- .../src/composed/docs_examples.test.ts | 6 +- .../e2e_local_network_example.test.ts | 24 +++--- .../src/composed/e2e_persistence.test.ts | 73 +++++++++++------ .../e2e_token_bridge_tutorial_test.test.ts | 16 ++-- .../src/composed/ha/e2e_ha_full.test.ts | 6 +- ...e2e_multi_validator_node_key_store.test.ts | 5 +- .../end-to-end/src/devnet/e2e_smoke.test.ts | 8 +- .../end-to-end/src/e2e_2_pxes.test.ts | 4 +- .../src/e2e_account_contracts.test.ts | 2 +- yarn-project/end-to-end/src/e2e_amm.test.ts | 69 +++++++++------- .../end-to-end/src/e2e_authwit.test.ts | 4 +- .../end-to-end/src/e2e_avm_simulator.test.ts | 32 +++++--- .../blacklist_token_contract_test.ts | 32 ++++---- .../e2e_blacklist_token_contract/burn.test.ts | 70 +++++++++++++---- .../minting.test.ts | 10 ++- .../shielding.test.ts | 39 +++++++--- .../transfer_private.test.ts | 66 +++++++++++++--- .../transfer_public.test.ts | 78 +++++++++++++++---- .../unshielding.test.ts | 30 +++++-- .../end-to-end/src/e2e_block_building.test.ts | 66 ++++++++-------- .../end-to-end/src/e2e_card_game.test.ts | 34 ++++---- .../src/e2e_contract_updates.test.ts | 4 +- .../l1_to_l2.test.ts | 4 +- .../l2_to_l1.test.ts | 10 +-- .../src/e2e_crowdfunding_and_claim.test.ts | 34 ++++---- .../contract_class_registration.test.ts | 12 +-- .../e2e_deploy_contract/deploy_method.test.ts | 19 +++-- .../src/e2e_deploy_contract/legacy.test.ts | 8 +- .../private_initialization.test.ts | 2 +- .../end-to-end/src/e2e_double_spend.test.ts | 2 +- .../epochs_invalidate_block.parallel.test.ts | 2 +- .../e2e_epochs/epochs_mbps.parallel.test.ts | 4 +- .../epochs_proof_public_cross_chain.test.ts | 6 +- .../src/e2e_escrow_contract.test.ts | 6 +- .../end-to-end/src/e2e_event_logs.test.ts | 23 +++--- .../end-to-end/src/e2e_event_only.test.ts | 6 +- .../src/e2e_expiration_timestamp.test.ts | 2 +- .../src/e2e_fees/account_init.test.ts | 13 ++-- .../end-to-end/src/e2e_fees/failures.test.ts | 6 +- .../src/e2e_fees/fee_juice_payments.test.ts | 28 ++++--- .../src/e2e_fees/fee_settings.test.ts | 2 +- .../end-to-end/src/e2e_fees/fees_test.ts | 12 ++- .../src/e2e_fees/gas_estimation.test.ts | 14 ++-- .../src/e2e_fees/private_payments.test.ts | 20 ++--- .../src/e2e_fees/public_payments.test.ts | 14 ++-- .../src/e2e_fees/sponsored_payments.test.ts | 16 ++-- .../src/e2e_kernelless_simulation.test.ts | 22 +++--- yarn-project/end-to-end/src/e2e_keys.test.ts | 4 +- .../src/e2e_lending_contract.test.ts | 16 +++- .../end-to-end/src/e2e_mempool_limit.test.ts | 4 +- .../e2e_multi_validator_node.test.ts | 4 +- .../end-to-end/src/e2e_multiple_blobs.test.ts | 6 +- .../src/e2e_nested_contract/importer.test.ts | 4 +- .../manual_private_enqueue.test.ts | 4 +- .../e2e_nested_contract/manual_public.test.ts | 2 +- .../nested_contract_test.ts | 10 ++- yarn-project/end-to-end/src/e2e_nft.test.ts | 20 +++-- .../end-to-end/src/e2e_note_getter.test.ts | 21 +++-- .../src/e2e_offchain_effect.test.ts | 8 +- .../end-to-end/src/e2e_orderbook.test.ts | 34 +++++--- .../end-to-end/src/e2e_ordering.test.ts | 4 +- .../end-to-end/src/e2e_p2p/add_rollup.test.ts | 4 +- .../end-to-end/src/e2e_p2p/p2p_network.ts | 5 +- yarn-project/end-to-end/src/e2e_p2p/shared.ts | 2 +- .../e2e_pending_note_hashes_contract.test.ts | 2 +- .../end-to-end/src/e2e_phase_check.test.ts | 2 +- .../src/e2e_private_voting_contract.test.ts | 2 +- .../end-to-end/src/e2e_prover/client.test.ts | 6 +- .../end-to-end/src/e2e_prover/full.test.ts | 10 ++- .../end-to-end/src/e2e_pruned_blocks.test.ts | 6 +- .../e2e_public_testnet_transfer.test.ts | 10 ++- .../src/e2e_scope_isolation.test.ts | 2 +- .../gov_proposal.parallel.test.ts | 11 +-- .../end-to-end/src/e2e_simple.test.ts | 2 +- .../end-to-end/src/e2e_state_vars.test.ts | 48 ++++++------ .../end-to-end/src/e2e_static_calls.test.ts | 4 +- .../e2e_storage_proof.test.ts | 4 +- .../end-to-end/src/e2e_synching.test.ts | 40 ++++++---- .../src/e2e_token_contract/burn.test.ts | 52 +++++++++---- .../private_transfer_recursion.test.ts | 10 ++- .../reading_constants.test.ts | 14 +++- .../e2e_token_contract/token_contract_test.ts | 22 ++++-- .../src/e2e_token_contract/transfer.test.ts | 12 +-- .../transfer_in_private.test.ts | 26 +++++-- .../transfer_in_public.test.ts | 34 ++++---- .../transfer_to_private.test.ts | 8 +- .../transfer_to_public.test.ts | 24 ++++-- .../src/fixtures/e2e_prover_test.ts | 16 ++-- yarn-project/end-to-end/src/fixtures/setup.ts | 6 +- .../end-to-end/src/fixtures/token_utils.ts | 6 +- .../writing_an_account_contract.test.ts | 4 +- .../src/shared/cross_chain_test_harness.ts | 22 +++--- .../src/shared/submit-transactions.ts | 5 +- .../end-to-end/src/shared/uniswap_l1_l2.ts | 10 ++- .../src/simulators/lending_simulator.ts | 6 +- .../end-to-end/src/spartan/1tps.test.ts | 21 +++-- .../end-to-end/src/spartan/4epochs.test.ts | 35 +++++---- .../end-to-end/src/spartan/n_tps.test.ts | 4 +- .../src/spartan/n_tps_prove.test.ts | 4 +- .../src/spartan/reqresp_effectiveness.test.ts | 3 +- .../src/spartan/setup_test_wallets.ts | 10 +-- .../end-to-end/src/spartan/transfer.test.ts | 29 ++++--- yarn-project/stdlib/src/tx/simulated_tx.ts | 9 ++- .../wallet-sdk/src/base-wallet/base_wallet.ts | 5 +- 135 files changed, 1366 insertions(+), 774 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index 8e47f1eb7cfe..bfa319bc77df 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -41,8 +41,8 @@ export abstract class BaseContractInteraction { * the AztecAddress of the sender, optional fee configuration, and optional wait settings * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ - // Overload for when wait is not specified at all - returns TReturn - public send(options: SendInteractionOptionsWithoutWait): Promise; + // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } + public send(options: SendInteractionOptionsWithoutWait): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index cd112be6f59b..1ec44f3f3d25 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -108,7 +108,10 @@ export class BatchCall extends BaseContractInteraction { const wrappedResult = batchResults[i]; if (wrappedResult.name === 'executeUtility') { const rawReturnValues = (wrappedResult.result as UtilityExecutionResult).result; - results[resultIndex] = rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : []; + results[resultIndex] = { + result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], + offchainEffects: [], + }; } } @@ -127,7 +130,10 @@ export class BatchCall extends BaseContractInteraction { ? simulatedTx.getPrivateReturnValues()?.nested?.[resultIndex].values : simulatedTx.getPublicReturnValues()?.[resultIndex].values; - results[callIndex] = rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : []; + results[callIndex] = { + result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], + offchainEffects: simulatedTx.offchainEffects, + }; }); } } diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 28005f6ecbfc..7f815be7c056 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -136,7 +136,7 @@ describe('Contract Class', () => { wallet.simulateTx.mockResolvedValue(mockTxSimulationResult); account.createTxExecutionRequest.mockResolvedValue(mockTxRequest); wallet.registerContract.mockResolvedValue(contractInstance); - wallet.sendTx.mockResolvedValue(mockTxReceipt); + wallet.sendTx.mockResolvedValue({ receipt: mockTxReceipt, offchainEffects: [] }); wallet.executeUtility.mockResolvedValue(mockUtilityResultValue); }); @@ -144,7 +144,7 @@ describe('Contract Class', () => { const fooContract = Contract.at(contractAddress, defaultArtifact, wallet); const param0 = 12; const param1 = 345n; - const receipt = await fooContract.methods.bar(param0, param1).send({ from: account.getAddress() }); + const { receipt } = await fooContract.methods.bar(param0, param1).send({ from: account.getAddress() }); expect(receipt).toBe(mockTxReceipt); expect(wallet.sendTx).toHaveBeenCalledTimes(1); @@ -152,7 +152,7 @@ describe('Contract Class', () => { it('should call view on a utility function', async () => { const fooContract = Contract.at(contractAddress, defaultArtifact, wallet); - const result = await fooContract.methods.qux(123n).simulate({ from: account.getAddress() }); + const { result } = await fooContract.methods.qux(123n).simulate({ from: account.getAddress() }); expect(wallet.executeUtility).toHaveBeenCalledTimes(1); expect(wallet.executeUtility).toHaveBeenCalledWith( expect.objectContaining({ name: 'qux', to: contractAddress }), diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index a087d85f0525..909d599891e3 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -8,7 +8,7 @@ import { } from '@aztec/stdlib/abi'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import { type Capsule, type HashedValues, type TxProfileResult, collectOffchainEffects } from '@aztec/stdlib/tx'; +import type { Capsule, HashedValues, TxProfileResult } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; @@ -18,6 +18,8 @@ import { type ProfileInteractionOptions, type RequestInteractionOptions, type SimulateInteractionOptions, + type SimulateWithGasEstimationOptions, + type SimulateWithMetadataOptions, type SimulationReturn, toProfileOptions, toSimulateOptions, @@ -97,17 +99,15 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * function or a rich object containing extra metadata, such as estimated gas costs (if requested via options), * execution statistics and emitted offchain effects */ - public async simulate( - options: T, - ): Promise['estimateGas']>>; + public async simulate(options: SimulateWithMetadataOptions): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate( - options: T, - ): Promise>; + public async simulate(options: SimulateWithGasEstimationOptions): Promise>; + // eslint-disable-next-line jsdoc/require-jsdoc + public async simulate(options?: SimulateInteractionOptions): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc public async simulate( - options: SimulateInteractionOptions, - ): Promise> { + options: SimulateInteractionOptions = {} as SimulateInteractionOptions, + ): Promise> { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const call = await this.getFunctionCall(); @@ -122,11 +122,11 @@ export class ContractFunctionInteraction extends BaseContractInteraction { if (options.includeMetadata) { return { stats: utilityResult.stats, + offchainEffects: [], result: returnValue, }; - } else { - return returnValue; } + return { result: returnValue, offchainEffects: [] }; } const executionPayload = await this.request(options); @@ -148,6 +148,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } const returnValue = rawReturnValues ? decodeFromAbi(this.functionDao.returnTypes, rawReturnValues) : []; + const offchainEffects = simulatedTx.offchainEffects; if (options.includeMetadata || options.fee?.estimateGas) { const { gasLimits, teardownGasLimits } = getGasLimits(simulatedTx, options.fee?.estimatedGasPadding); @@ -156,13 +157,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction { ); return { stats: simulatedTx.stats, - offchainEffects: collectOffchainEffects(simulatedTx.privateExecutionResult), + offchainEffects, result: returnValue, estimatedGas: { gasLimits, teardownGasLimits }, }; - } else { - return returnValue; } + return { result: returnValue, offchainEffects }; } /** diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index c91e54b34dbd..dfb55d006989 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -9,14 +9,7 @@ import { getContractInstanceFromInstantiationParams, } from '@aztec/stdlib/contract'; import type { PublicKeys } from '@aztec/stdlib/keys'; -import { - type Capsule, - HashedValues, - TxHash, - type TxProfileResult, - type TxReceipt, - collectOffchainEffects, -} from '@aztec/stdlib/tx'; +import { type Capsule, HashedValues, type OffchainEffect, type TxProfileResult, type TxReceipt } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import { publishContractClass } from '../deployment/publish_class.js'; @@ -34,6 +27,7 @@ import { type SendInteractionOptionsWithoutWait, type SimulationInteractionFeeOptions, type SimulationReturn, + type TxSendResultImmediate, toProfileOptions, toSendOptions, toSimulateOptions, @@ -130,6 +124,12 @@ export type DeployTxReceipt = TxR instance: ContractInstanceWithAddress; }; +/** Wait options that request a full receipt instead of just the contract instance. */ +type WaitWithReturnReceipt = { + /** Request the full receipt instead of just the contract instance. */ + returnReceipt: true; +}; + /** * Represents the result type of deploying a contract. * - If wait is NO_WAIT, returns TxHash immediately. @@ -137,13 +137,20 @@ export type DeployTxReceipt = TxR * - Otherwise (undefined or DeployWaitOptions without returnReceipt), returns TContract after waiting. */ export type DeployReturn = W extends NoWait - ? TxHash - : W extends { - // eslint-disable-next-line jsdoc/require-jsdoc - returnReceipt: true; + ? TxSendResultImmediate + : W extends WaitWithReturnReceipt + ? { + /** The deploy transaction receipt. */ + receipt: DeployTxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; } - ? DeployTxReceipt - : TContract; + : { + /** The deployed contract instance. */ + contract: TContract; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; + }; /** * Contract interaction for deployment. @@ -343,24 +350,30 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send(options: DeployOptionsWithoutWait): Promise; - // Generic overload for explicit wait values + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class + public override send(options: DeployOptionsWithoutWait): Promise>; + + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, ): Promise>; + // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override async send(options: DeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); const sendOptions = this.convertDeployOptionsToSendOptions(options); if (options.wait === NO_WAIT) { - const txHash = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); - this.log.debug(`Sent deployment tx ${txHash.hash} of ${this.artifact.name} contract`); - return txHash; + const result = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); + this.log.debug(`Sent deployment tx ${result.txHash.hash} of ${this.artifact.name} contract`); + return result; } - const receipt = await this.wallet.sendTx(executionPayload, sendOptions as SendOptions); + const { receipt, offchainEffects } = await this.wallet.sendTx( + executionPayload, + sendOptions as SendOptions, + ); this.log.debug(`Deployed ${this.artifact.name} contract in tx ${receipt.txHash}`); // Attach contract instance @@ -369,10 +382,10 @@ export class DeployMethod extends // Return full receipt if requested, otherwise just the contract if (options.wait && typeof options.wait === 'object' && options.wait.returnReceipt) { - return { ...receipt, contract, instance }; + return { receipt: { ...receipt, contract, instance }, offchainEffects }; } - return contract; + return { contract, offchainEffects }; } /** @@ -414,7 +427,7 @@ export class DeployMethod extends ); return { stats: simulatedTx.stats!, - offchainEffects: collectOffchainEffects(simulatedTx.privateExecutionResult), + offchainEffects: simulatedTx.offchainEffects, result: undefined, estimatedGas: { gasLimits, teardownGasLimits }, }; diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index b3655f825fa2..c8c98436626c 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -116,11 +116,26 @@ export type SimulateInteractionOptions = Omit & { skipTxValidation?: boolean; /** Whether to ensure the fee payer is not empty and has enough balance to pay for the fee. */ skipFeeEnforcement?: boolean; - /** Whether to include metadata such as offchain effects and performance statistics (e.g. timing information of the different circuits and oracles) in - * the simulation result, instead of just the return value of the function */ + /** Whether to include metadata such as performance statistics (e.g. timing information of the different circuits and oracles) and gas estimation + * in the simulation result, in addition to the return value and offchain effects */ includeMetadata?: boolean; }; +/** Simulation options that request full metadata (stats, gas estimation) in the result. */ +export type SimulateWithMetadataOptions = SimulateInteractionOptions & { + /** Request metadata in the simulation result. */ + includeMetadata: true; +}; + +/** Simulation options that request gas estimation in the result. */ +export type SimulateWithGasEstimationOptions = SimulateInteractionOptions & { + /** Fee options with gas estimation enabled. */ + fee: { + /** Request gas estimation in the simulation result. */ + estimateGas: true; + }; +}; + /** * Represents the options for profiling an interaction. */ @@ -133,9 +148,8 @@ export type ProfileInteractionOptions = SimulateInteractionOptions & { /** * Represents the result type of a simulation. - * By default, it will just be the return value of the simulated function - * If `includeMetadata` is set to true in `SimulateInteractionOptions` on the input of `simulate(...)`, - * it will provide extra information. + * Always includes the return value and offchain effects. + * When `includeMetadata` is set to true, also includes stats and gas estimation. */ export type SimulationReturn = T extends true ? { @@ -143,19 +157,47 @@ export type SimulationReturn = T extends true stats: SimulationStats; /** Offchain effects generated during the simulation */ offchainEffects: OffchainEffect[]; - /** Return value of the function */ + /** Return value of the function */ result: any; /** Gas estimation results */ estimatedGas: Pick; } - : any; + : { + /** Return value of the function */ + result: any; + /** Offchain effects generated during the simulation */ + offchainEffects: OffchainEffect[]; + }; + +/** Result of sendTx when not waiting for mining. */ +export type TxSendResultImmediate = { + /** The hash of the sent transaction. */ + txHash: TxHash; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; + +/** Result of sendTx when waiting for mining. */ +export type TxSendResultMined = { + /** The transaction receipt. */ + receipt: TxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; /** * Represents the result type of sending a transaction. - * If `wait` is NO_WAIT, returns TxHash immediately without waiting. - * If `wait` is undefined or WaitOpts, returns TReturn (defaults to TxReceipt) after waiting. + * If `wait` is NO_WAIT, returns TxSendResultImmediate. + * Otherwise returns TxSendResultMined. */ -export type SendReturn = T extends NoWait ? TxHash : TReturn; +export type SendReturn = T extends NoWait + ? TxSendResultImmediate + : { + /** The transaction receipt. */ + receipt: TReturn; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; + }; /** * Transforms and cleans up the higher level SendInteractionOptions defined by the interaction into diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index d0d8b9f1c088..c1ff57e9f454 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -5,7 +5,7 @@ import { type ABIParameterVisibility, type FunctionAbi, type FunctionCall, Funct import { AuthWitness, computeInnerAuthWitHash, computeOuterAuthWitHash } from '@aztec/stdlib/auth-witness'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { computeVarArgsHash } from '@aztec/stdlib/hash'; -import type { TxHash, TxProfileResult, TxReceipt } from '@aztec/stdlib/tx'; +import type { TxProfileResult } from '@aztec/stdlib/tx'; import { ContractFunctionInteraction } from '../contract/contract_function_interaction.js'; import type { @@ -15,6 +15,7 @@ import type { SendInteractionOptionsWithoutWait, SendReturn, SimulateInteractionOptions, + SimulateWithMetadataOptions, SimulationReturn, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -189,10 +190,12 @@ export async function lookupValidity( errorTypes: {}, } as FunctionAbi; try { - results.isValidInPrivate = (await new ContractFunctionInteraction(wallet, onBehalfOf, lookupValidityAbi, [ - consumer, - innerHash, - ]).simulate({ from: onBehalfOf, authWitnesses: [witness] })) as boolean; + results.isValidInPrivate = ( + await new ContractFunctionInteraction(wallet, onBehalfOf, lookupValidityAbi, [consumer, innerHash]).simulate({ + from: onBehalfOf, + authWitnesses: [witness], + }) + ).result as boolean; // TODO: Narrow down the error to make sure simulation failed due to an invalid authwit // eslint-disable-next-line no-empty } catch {} @@ -219,12 +222,12 @@ export async function lookupValidity( returnTypes: [{ kind: 'boolean' }], errorTypes: {}, } as FunctionAbi; - results.isValidInPublic = (await new ContractFunctionInteraction( - wallet, - ProtocolContractAddress.AuthRegistry, - isConsumableAbi, - [onBehalfOf, messageHash], - ).simulate({ from: onBehalfOf })) as boolean; + results.isValidInPublic = ( + await new ContractFunctionInteraction(wallet, ProtocolContractAddress.AuthRegistry, isConsumableAbi, [ + onBehalfOf, + messageHash, + ]).simulate({ from: onBehalfOf }) + ).result as boolean; return results; } @@ -262,14 +265,17 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing additional configuration for the transaction. * @returns The result of the transaction as returned by the contract function. */ - public override simulate( - options: Omit, - ): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) + public override simulate(options: Omit): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) + // eslint-disable-next-line jsdoc/require-jsdoc + public override simulate(options?: Omit): Promise>; + // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) // eslint-disable-next-line jsdoc/require-jsdoc public override simulate( - options: Omit = {}, - ): Promise> { - return super.simulate({ ...options, from: this.from }); + options: Omit = {} as Omit, + ): Promise> { + return super.simulate({ ...options, from: this.from } as SimulateInteractionOptions); } /** @@ -290,8 +296,8 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing 'fee' options information * @returns A TxReceipt (if wait is true/undefined) or TxHash (if wait is false) */ - // Overload for when wait is not specified at all - returns TxReceipt - public override send(options?: Omit): Promise; + // Overload for when wait is not specified at all + public override send(options?: Omit): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public override send( @@ -300,7 +306,7 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac // eslint-disable-next-line jsdoc/require-jsdoc public override send( options?: Omit, 'from'>, - ): Promise { + ): Promise> { return super.send({ ...options, from: this.from }); } diff --git a/yarn-project/aztec.js/src/wallet/wallet.test.ts b/yarn-project/aztec.js/src/wallet/wallet.test.ts index 298f279b07b3..1149e418a795 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.test.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.test.ts @@ -11,6 +11,7 @@ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract'; import { PublicKeys } from '@aztec/stdlib/keys'; import { ExecutionPayload, + type OffchainEffect, TxHash, TxProfileResult, TxReceipt, @@ -209,12 +210,14 @@ describe('WalletSchema', () => { const resultWithWait = await context.client.sendTx(exec, { from: await AztecAddress.random(), }); - expect(resultWithWait).toBeInstanceOf(TxReceipt); + expect(resultWithWait.receipt).toBeInstanceOf(TxReceipt); + expect(resultWithWait.offchainEffects).toEqual([]); const resultWithoutWait = await context.client.sendTx(exec, { from: await AztecAddress.random(), wait: NO_WAIT, }); - expect(resultWithoutWait).toBeInstanceOf(TxHash); + expect(resultWithoutWait.txHash).toBeInstanceOf(TxHash); + expect(resultWithoutWait.offchainEffects).toEqual([]); }); it('createAuthWit', async () => { @@ -363,7 +366,7 @@ describe('WalletSchema', () => { expect(results[8]).toEqual({ name: 'simulateTx', result: expect.any(TxSimulationResult) }); expect(results[9]).toEqual({ name: 'executeUtility', result: expect.any(UtilityExecutionResult) }); expect(results[10]).toEqual({ name: 'profileTx', result: expect.any(TxProfileResult) }); - expect(results[11]).toEqual({ name: 'sendTx', result: expect.any(TxReceipt) }); + expect(results[11]).toEqual({ name: 'sendTx', result: { receipt: expect.any(TxReceipt), offchainEffects: [] } }); expect(results[12]).toEqual({ name: 'createAuthWit', result: expect.any(AuthWitness) }); }); }); @@ -456,9 +459,13 @@ class MockWallet implements Wallet { opts: SendOptions, ): Promise> { if (opts.wait === NO_WAIT) { - return Promise.resolve(TxHash.random()) as Promise>; + return Promise.resolve({ txHash: TxHash.random(), offchainEffects: [] as OffchainEffect[] }) as Promise< + SendReturn + >; } - return Promise.resolve(TxReceipt.empty()) as Promise>; + return Promise.resolve({ receipt: TxReceipt.empty(), offchainEffects: [] as OffchainEffect[] }) as Promise< + SendReturn + >; } createAuthWit(_from: AztecAddress, _messageHashOrIntent: any): Promise { diff --git a/yarn-project/aztec.js/src/wallet/wallet.ts b/yarn-project/aztec.js/src/wallet/wallet.ts index f4c3c296ee5b..a08ca74ec867 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.ts @@ -492,6 +492,11 @@ export const WalletCapabilitiesSchema = z.object({ expiresAt: optional(z.number()), }); +const OffchainEffectSchema = z.object({ + data: z.array(schemas.Fr), + contractAddress: schemas.AztecAddress, +}); + /** * Record of all wallet method schemas (excluding batch). * This is the single source of truth for method schemas - batch schemas are derived from this. @@ -535,7 +540,12 @@ const WalletMethodSchemas = { sendTx: z .function() .args(ExecutionPayloadSchema, SendOptionsSchema) - .returns(z.union([TxHash.schema, TxReceipt.schema])), + .returns( + z.union([ + z.object({ txHash: TxHash.schema, offchainEffects: z.array(OffchainEffectSchema) }), + z.object({ receipt: TxReceipt.schema, offchainEffects: z.array(OffchainEffectSchema) }), + ]), + ), createAuthWit: z.function().args(schemas.AztecAddress, MessageHashOrIntentSchema).returns(AuthWitness.schema), requestCapabilities: z.function().args(AppCapabilitiesSchema).returns(WalletCapabilitiesSchema), }; diff --git a/yarn-project/aztec/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts index 2a99d350c314..1e7e2077910d 100644 --- a/yarn-project/aztec/src/examples/token.ts +++ b/yarn-project/aztec/src/examples/token.ts @@ -32,7 +32,9 @@ async function main() { logger.info(`Fetched Alice and Bob accounts: ${alice.toString()}, ${bob.toString()}`); logger.info('Deploying Token...'); - const token = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({ from: alice }); + const { contract: token } = await TokenContract.deploy(wallet, alice, 'TokenName', 'TokenSymbol', 18).send({ + from: alice, + }); logger.info('Token deployed'); // Mint tokens to Alice @@ -41,7 +43,7 @@ async function main() { logger.info(`${ALICE_MINT_BALANCE} tokens were successfully minted by Alice and transferred to private`); - const balanceAfterMint = await token.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: balanceAfterMint } = await token.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Tokens successfully minted. New Alice's balance: ${balanceAfterMint}`); // We will now transfer tokens from Alice to Bob @@ -49,10 +51,10 @@ async function main() { await token.methods.transfer(bob, TRANSFER_AMOUNT).send({ from: alice }); // Check the new balances - const aliceBalance = await token.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceBalance } = await token.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance ${aliceBalance}`); - const bobBalance = await token.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobBalance } = await token.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance ${bobBalance}`); } diff --git a/yarn-project/aztec/src/local-network/banana_fpc.ts b/yarn-project/aztec/src/local-network/banana_fpc.ts index 5ccb73d8746f..e5363e0eda73 100644 --- a/yarn-project/aztec/src/local-network/banana_fpc.ts +++ b/yarn-project/aztec/src/local-network/banana_fpc.ts @@ -48,7 +48,7 @@ export async function getBananaFPCAddress(initialAccounts: InitialAccountData[]) export async function setupBananaFPC(initialAccounts: InitialAccountData[], wallet: Wallet, log: LogFn) { const bananaCoinAddress = await getBananaCoinAddress(initialAccounts); const admin = getBananaAdmin(initialAccounts); - const [bananaCoin, fpc] = await Promise.all([ + const [{ contract: bananaCoin }, { contract: fpc }] = await Promise.all([ TokenContract.deploy(wallet, admin, bananaCoinArgs.name, bananaCoinArgs.symbol, bananaCoinArgs.decimal).send({ from: admin, contractAddressSalt: BANANA_COIN_SALT, diff --git a/yarn-project/bot/src/amm_bot.ts b/yarn-project/bot/src/amm_bot.ts index 362470479518..21461c775d90 100644 --- a/yarn-project/bot/src/amm_bot.ts +++ b/yarn-project/bot/src/amm_bot.ts @@ -71,12 +71,14 @@ export class AmmBot extends BaseBot { .getFunctionCall(), }); - const amountOutMin = await amm.methods - .get_amount_out_for_exact_in( - await tokenIn.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }), - await tokenOut.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }), - amountIn, - ) + const { result: tokenInBalance } = await tokenIn.methods + .balance_of_public(amm.address) + .simulate({ from: this.defaultAccountAddress }); + const { result: tokenOutBalance } = await tokenOut.methods + .balance_of_public(amm.address) + .simulate({ from: this.defaultAccountAddress }); + const { result: amountOutMin } = await amm.methods + .get_amount_out_for_exact_in(tokenInBalance, tokenOutBalance, amountIn) .simulate({ from: this.defaultAccountAddress }); const swapExactTokensInteraction = amm.methods @@ -89,7 +91,8 @@ export class AmmBot extends BaseBot { this.log.verbose(`Sending transaction`, logCtx); this.log.info(`Tx. Balances: ${jsonStringify(balances)}`, { ...logCtx, balances }); - return swapExactTokensInteraction.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await swapExactTokensInteraction.send({ ...opts, wait: NO_WAIT }); + return txHash; } protected override async onTxMined(receipt: TxReceipt, logCtx: object): Promise { @@ -110,15 +113,17 @@ export class AmmBot extends BaseBot { } private async getPublicBalanceFor(address: AztecAddress, from?: AztecAddress): Promise { - return { - token0: await this.token0.methods.balance_of_public(address).simulate({ from: from ?? address }), - token1: await this.token1.methods.balance_of_public(address).simulate({ from: from ?? address }), - }; + const { result: token0 } = await this.token0.methods.balance_of_public(address).simulate({ from: from ?? address }); + const { result: token1 } = await this.token1.methods.balance_of_public(address).simulate({ from: from ?? address }); + return { token0, token1 }; } private async getPrivateBalanceFor(address: AztecAddress, from?: AztecAddress): Promise { - return { - token0: await this.token0.methods.balance_of_private(address).simulate({ from: from ?? address }), - token1: await this.token1.methods.balance_of_private(address).simulate({ from: from ?? address }), - }; + const { result: token0 } = await this.token0.methods + .balance_of_private(address) + .simulate({ from: from ?? address }); + const { result: token1 } = await this.token1.methods + .balance_of_private(address) + .simulate({ from: from ?? address }); + return { token0, token1 }; } } diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index dce40d2194ad..c2128b2a219e 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -76,7 +76,8 @@ export class Bot extends BaseBot { await batch.simulate({ from: this.defaultAccountAddress }); this.log.verbose(`Sending transaction`, logCtx); - return batch.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await batch.send({ ...opts, wait: NO_WAIT }); + return txHash; } public async getBalances() { diff --git a/yarn-project/bot/src/cross_chain_bot.ts b/yarn-project/bot/src/cross_chain_bot.ts index 0165b5a778a8..ea4a6c365c29 100644 --- a/yarn-project/bot/src/cross_chain_bot.ts +++ b/yarn-project/bot/src/cross_chain_bot.ts @@ -140,7 +140,8 @@ export class CrossChainBot extends BaseBot { const opts = await this.getSendMethodOpts(batch); this.log.verbose(`Sending cross-chain batch with ${calls.length} calls`, logCtx); - return batch.send({ ...opts, wait: NO_WAIT }); + const { txHash } = await batch.send({ ...opts, wait: NO_WAIT }); + return txHash; } protected override async onTxMined(receipt: TxReceipt, logCtx: object): Promise { diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 970dd845ec69..0d7aa408e476 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -231,7 +231,7 @@ export class BotFactory { const gasSettings = GasSettings.default({ maxFeesPerGas }); await this.withNoMinTxsPerBlock(async () => { - const txHash = await deployMethod.send({ + const { txHash } = await deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod }, wait: NO_WAIT, @@ -302,7 +302,7 @@ export class BotFactory { await deploy.register(); } else { this.log.info(`Deploying token contract at ${address.toString()}`); - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`); await this.withNoMinTxsPerBlock(async () => { await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); @@ -343,7 +343,7 @@ export class BotFactory { const amm = AMMContract.at(instance.address, this.wallet); this.log.info(`AMM deployed at ${amm.address}`); - const minterReceipt = await lpToken.methods + const { receipt: minterReceipt } = await lpToken.methods .set_minter(amm.address, true) .send({ from: deployer, wait: { timeout: this.config.txMinedWaitSeconds } }); this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`); @@ -362,9 +362,18 @@ export class BotFactory { ): Promise { const getPrivateBalances = () => Promise.all([ - token0.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), - token1.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), - lpToken.methods.balance_of_private(liquidityProvider).simulate({ from: liquidityProvider }), + token0.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), + token1.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), + lpToken.methods + .balance_of_private(liquidityProvider) + .simulate({ from: liquidityProvider }) + .then(r => r.result), ]); const authwitNonce = Fr.random(); @@ -405,14 +414,14 @@ export class BotFactory { .getFunctionCall(), }); - const mintReceipt = await new BatchCall(this.wallet, [ + const { receipt: mintReceipt } = await new BatchCall(this.wallet, [ token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE), token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE), ]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } }); this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`); - const addLiquidityReceipt = await amm.methods + const { receipt: addLiquidityReceipt } = await amm.methods .add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce) .send({ from: liquidityProvider, @@ -443,7 +452,7 @@ export class BotFactory { } else { this.log.info(`Deploying contract ${name} at ${address.toString()}`); await this.withNoMinTxsPerBlock(async () => { - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`); return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); }); @@ -488,7 +497,7 @@ export class BotFactory { // PrivateToken's mint accesses contract-level private storage vars (admin, total_supply). const additionalScopes = isStandardToken ? undefined : [token.address]; await this.withNoMinTxsPerBlock(async () => { - const txHash = await new BatchCall(token.wallet, calls).send({ from: minter, additionalScopes, wait: NO_WAIT }); + const { txHash } = await new BatchCall(token.wallet, calls).send({ from: minter, additionalScopes, wait: NO_WAIT }); this.log.info(`Sent token mint tx with hash ${txHash.toString()}`); return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); }); diff --git a/yarn-project/bot/src/utils.ts b/yarn-project/bot/src/utils.ts index 1bd0b7f6b743..807d34be95ab 100644 --- a/yarn-project/bot/src/utils.ts +++ b/yarn-project/bot/src/utils.ts @@ -15,8 +15,8 @@ export async function getBalances( who: AztecAddress, from?: AztecAddress, ): Promise<{ privateBalance: bigint; publicBalance: bigint }> { - const privateBalance = await token.methods.balance_of_private(who).simulate({ from: from ?? who }); - const publicBalance = await token.methods.balance_of_public(who).simulate({ from: from ?? who }); + const { result: privateBalance } = await token.methods.balance_of_private(who).simulate({ from: from ?? who }); + const { result: publicBalance } = await token.methods.balance_of_public(who).simulate({ from: from ?? who }); return { privateBalance, publicBalance }; } @@ -25,7 +25,7 @@ export async function getPrivateBalance( who: AztecAddress, from?: AztecAddress, ): Promise { - const privateBalance = await token.methods.get_balance(who).simulate({ from: from ?? who }); + const { result: privateBalance } = await token.methods.get_balance(who).simulate({ from: from ?? who }); return privateBalance; } diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index 64b730b95aa1..1279cf8b0f54 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -109,7 +109,7 @@ export async function createAccount( if (!json) { log(`\nWaiting for account contract deployment...`); } - const result = await deployMethod.send({ + const sendOpts = { ...deployAccountOpts, fee: deployAccountOpts.fee ? { @@ -117,18 +117,20 @@ export async function createAccount( gasSettings: estimatedGas, } : undefined, - wait: wait ? { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true } : NO_WAIT, - }); - const isReceipt = (data: TxReceipt | TxHash): data is TxReceipt => 'txHash' in data; - if (isReceipt(result)) { - txReceipt = result; - txHash = result.txHash; + }; + if (wait) { + const { receipt } = await deployMethod.send({ + ...sendOpts, + wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true }, + }); + txReceipt = receipt; + txHash = receipt.txHash; out.txReceipt = { status: txReceipt.status, transactionFee: txReceipt.transactionFee, }; } else { - txHash = result; + ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT })); } debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`); out.txHash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/deploy.ts b/yarn-project/cli-wallet/src/cmds/deploy.ts index 0de9615aa276..d4ddc6a3c968 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy.ts @@ -98,7 +98,7 @@ export async function deploy( const instance = await deploy.getInstance(); if (wait) { - const receipt = await deploy.send({ ...deployOpts, wait: { timeout, returnReceipt: true } }); + const { receipt } = await deploy.send({ ...deployOpts, wait: { timeout, returnReceipt: true } }); const txHash = receipt.txHash; debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`); out.hash = txHash; @@ -121,7 +121,7 @@ export async function deploy( }; } } else { - const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT }); + const { txHash } = await deploy.send({ ...deployOpts, wait: NO_WAIT }); debugLogger.debug(`Deploy tx sent with hash ${txHash.toString()}`); out.hash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/deploy_account.ts b/yarn-project/cli-wallet/src/cmds/deploy_account.ts index f18037e7cbb6..7c3c6e020023 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy_account.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy_account.ts @@ -89,7 +89,7 @@ export async function deployAccount( if (!json) { log(`\nWaiting for account contract deployment...`); } - const result = await deployMethod.send({ + const sendOpts = { ...deployAccountOpts, fee: deployAccountOpts.fee ? { @@ -97,18 +97,20 @@ export async function deployAccount( gasSettings: estimatedGas, } : undefined, - wait: wait ? { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true } : NO_WAIT, - }); - const isReceipt = (data: TxReceipt | TxHash): data is TxReceipt => 'txHash' in data; - if (isReceipt(result)) { - txReceipt = result; - txHash = result.txHash; + }; + if (wait) { + const { receipt } = await deployMethod.send({ + ...sendOpts, + wait: { timeout: DEFAULT_TX_TIMEOUT_S, returnReceipt: true }, + }); + txReceipt = receipt; + txHash = receipt.txHash; out.txReceipt = { status: txReceipt.status, transactionFee: txReceipt.transactionFee, }; } else { - txHash = result; + ({ txHash } = await deployMethod.send({ ...sendOpts, wait: NO_WAIT })); } debugLogger.debug(`Account contract tx sent with hash ${txHash.toString()}`); out.txHash = txHash; diff --git a/yarn-project/cli-wallet/src/cmds/send.ts b/yarn-project/cli-wallet/src/cmds/send.ts index 4059db13a93b..30129ef78e0d 100644 --- a/yarn-project/cli-wallet/src/cmds/send.ts +++ b/yarn-project/cli-wallet/src/cmds/send.ts @@ -52,7 +52,7 @@ export async function send( if (wait) { try { - const receipt = await call.send({ + const { receipt } = await call.send({ ...sendOptions, fee: { ...sendOptions.fee, gasSettings: estimatedGas }, wait: { timeout: DEFAULT_TX_TIMEOUT_S }, @@ -74,7 +74,7 @@ export async function send( throw err; } } else { - const txHash = await call.send({ + const { txHash } = await call.send({ ...sendOptions, fee: { ...sendOptions.fee, gasSettings: estimatedGas }, wait: NO_WAIT, diff --git a/yarn-project/cli-wallet/src/cmds/simulate.ts b/yarn-project/cli-wallet/src/cmds/simulate.ts index 2443428df708..aec161b4ab33 100644 --- a/yarn-project/cli-wallet/src/cmds/simulate.ts +++ b/yarn-project/cli-wallet/src/cmds/simulate.ts @@ -38,7 +38,7 @@ export async function simulate( }); if (verbose) { await printAuthorizations( - simulationResult.offchainEffects!, + simulationResult.offchainEffects, async (address: AztecAddress) => { const metadata = await wallet.getContractMetadata(address); if (!metadata.instance) { diff --git a/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts b/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts index db57a7b52d9b..e38eb09f7c9f 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/account_deployments.test.ts @@ -91,8 +91,8 @@ describe('Deployment benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); - expect(tx.transactionFee!).toBeGreaterThan(0n); + const { receipt } = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); + expect(receipt.transactionFee!).toBeGreaterThan(0n); } }); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts index 22ca27be3eee..44a5652b484f 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/amm.test.ts @@ -199,7 +199,7 @@ describe('AMM benchmark', () => { ); if (process.env.SANITY_CHECKS) { - const tx = await addLiquidityInteraction.send({ from: benchysAddress }); + const { receipt: tx } = await addLiquidityInteraction.send({ from: benchysAddress }); expect(tx.transactionFee!).toBeGreaterThan(0n); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts b/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts index 0800c709ea82..27aa73ef8e39 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/bridging.test.ts @@ -106,7 +106,7 @@ describe('Bridging benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await claimInteraction.send(options); + const { receipt: tx } = await claimInteraction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); // 4. Check the balance diff --git a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts index 689cfc2dcfef..96c1c7741364 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts @@ -162,11 +162,15 @@ export class ClientFlowsBenchmark { /** Admin mints bananaCoin tokens privately to the target address and redeems them. */ async mintPrivateBananas(amount: bigint, address: AztecAddress) { - const balanceBefore = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceBefore } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); await mintTokensToPrivate(this.bananaCoin, this.adminAddress, address, amount); - const balanceAfter = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceAfter } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); expect(balanceAfter).toEqual(balanceBefore + amount); } @@ -242,13 +246,12 @@ export class ClientFlowsBenchmark { async applyDeployBananaToken() { this.logger.info('Applying banana token deployment'); - const { contract: bananaCoin, instance: bananaCoinInstance } = await BananaCoin.deploy( - this.adminWallet, - this.adminAddress, - 'BC', - 'BC', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: bananaCoin, instance: bananaCoinInstance }, + } = await BananaCoin.deploy(this.adminWallet, this.adminAddress, 'BC', 'BC', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`); this.bananaCoin = bananaCoin; this.bananaCoinInstance = bananaCoinInstance; @@ -256,13 +259,12 @@ export class ClientFlowsBenchmark { async applyDeployCandyBarToken() { this.logger.info('Applying candy bar token deployment'); - const { contract: candyBarCoin, instance: candyBarCoinInstance } = await TokenContract.deploy( - this.adminWallet, - this.adminAddress, - 'CBC', - 'CBC', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: candyBarCoin, instance: candyBarCoinInstance }, + } = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'CBC', 'CBC', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`CandyBarCoin deployed at ${candyBarCoin.address}`); this.candyBarCoin = candyBarCoin; this.candyBarCoinInstance = candyBarCoinInstance; @@ -274,11 +276,12 @@ export class ClientFlowsBenchmark { expect((await this.context.wallet.getContractMetadata(feeJuiceContract.address)).isContractPublished).toBe(true); const bananaCoin = this.bananaCoin; - const { contract: bananaFPC, instance: bananaFPCInstance } = await FPCContract.deploy( - this.adminWallet, - bananaCoin.address, - this.adminAddress, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); + const { + receipt: { contract: bananaFPC, instance: bananaFPCInstance }, + } = await FPCContract.deploy(this.adminWallet, bananaCoin.address, this.adminAddress).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); this.logger.info(`BananaPay deployed at ${bananaFPC.address}`); @@ -341,14 +344,15 @@ export class ClientFlowsBenchmark { public async applyDeployAmm() { this.logger.info('Applying AMM deployment'); - const { contract: liquidityToken, instance: liquidityTokenInstance } = await TokenContract.deploy( - this.adminWallet, - this.adminAddress, - 'LPT', - 'LPT', - 18n, - ).send({ from: this.adminAddress, wait: { returnReceipt: true } }); - const { contract: amm, instance: ammInstance } = await AMMContract.deploy( + const { + receipt: { contract: liquidityToken, instance: liquidityTokenInstance }, + } = await TokenContract.deploy(this.adminWallet, this.adminAddress, 'LPT', 'LPT', 18n).send({ + from: this.adminAddress, + wait: { returnReceipt: true }, + }); + const { + receipt: { contract: amm, instance: ammInstance }, + } = await AMMContract.deploy( this.adminWallet, this.bananaCoin.address, this.candyBarCoin.address, diff --git a/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts b/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts index 124e0b0cbbae..f75f4d011751 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/deployments.test.ts @@ -86,8 +86,8 @@ describe('Deployment benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); - expect(tx.transactionFee!).toBeGreaterThan(0n); + const { receipt } = await deploymentInteraction.send({ ...options, wait: { returnReceipt: true } }); + expect(receipt.transactionFee!).toBeGreaterThan(0n); } }); }); diff --git a/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts b/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts index 00d431a4bc40..c46f17ccf444 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/storage_proof.test.ts @@ -34,7 +34,7 @@ describe('Storage proof benchmark', () => { await t.applyFPCSetup(); await t.applyDeploySponsoredFPC(); - const deployed = await StorageProofTestContract.deploy(t.adminWallet).send({ + const { receipt: deployed } = await StorageProofTestContract.deploy(t.adminWallet).send({ from: t.adminAddress, wait: { returnReceipt: true }, }); @@ -106,7 +106,7 @@ describe('Storage proof benchmark', () => { ); if (process.env.SANITY_CHECKS) { - const tx = await interaction.send(options); + const { receipt: tx } = await interaction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); expect(tx.hasExecutionSucceeded()).toBe(true); } diff --git a/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts b/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts index 77c8149072b7..df7222e067db 100644 --- a/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts +++ b/yarn-project/end-to-end/src/bench/client_flows/transfers.test.ts @@ -151,7 +151,7 @@ describe('Transfer benchmark', () => { if (process.env.SANITY_CHECKS) { // Ensure we paid a fee - const tx = await transferInteraction.send(options); + const { receipt: tx } = await transferInteraction.send(options); expect(tx.transactionFee!).toBeGreaterThan(0n); // Sanity checks @@ -179,7 +179,7 @@ describe('Transfer benchmark', () => { */ expect(txEffects!.data.noteHashes.length).toBe(2 + (benchmarkingPaymentMethod === 'private_fpc' ? 2 : 0)); - const senderBalance = await asset.methods + const { result: senderBalance } = await asset.methods .balance_of_private(benchysAddress) .simulate({ from: benchysAddress }); expect(senderBalance).toEqual(expectedChange); diff --git a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts index a0362bf60e56..7f7cac22f729 100644 --- a/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts +++ b/yarn-project/end-to-end/src/bench/node_rpc_perf.test.ts @@ -152,9 +152,9 @@ describe('e2e_node_rpc_perf', () => { })); logger.info('Deploying token contract...'); - tokenContract = await TokenContract.deploy(wallet, ownerAddress, 'TestToken', 'TST', 18n).send({ + ({ contract: tokenContract } = await TokenContract.deploy(wallet, ownerAddress, 'TestToken', 'TST', 18n).send({ from: ownerAddress, - }); + })); contractAddress = tokenContract.address; logger.info(`Token contract deployed at ${contractAddress}`); diff --git a/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts b/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts index 54d51c3ce6da..e69ffed1efc2 100644 --- a/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts +++ b/yarn-project/end-to-end/src/bench/tx_stats_bench.test.ts @@ -65,12 +65,12 @@ describe('transaction benchmarks', () => { } = t); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/bench/utils.ts b/yarn-project/end-to-end/src/bench/utils.ts index 370baefc8b6c..7daa924782df 100644 --- a/yarn-project/end-to-end/src/bench/utils.ts +++ b/yarn-project/end-to-end/src/bench/utils.ts @@ -25,7 +25,7 @@ export async function benchmarkSetup( ) { const context = await setup(1, { ...opts, telemetryConfig: { benchmark: true } }); const defaultAccountAddress = context.accounts[0]; - const contract = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress }); + const { contract } = await BenchmarkingContract.deploy(context.wallet).send({ from: defaultAccountAddress }); context.logger.info(`Deployed benchmarking contract at ${contract.address}`); const sequencer = (context.aztecNode as AztecNodeService).getSequencer()!; const telemetry = context.telemetryClient as BenchmarkTelemetryClient; @@ -149,7 +149,12 @@ export async function sendTxs( context.logger.info(`Creating ${txCount} txs`); const [from] = context.accounts; context.logger.info(`Sending ${txCount} txs`); - return Promise.all(calls.map(call => call.send({ from, wait: NO_WAIT }))); + return Promise.all( + calls.map(async call => { + const { txHash } = await call.send({ from, wait: NO_WAIT }); + return txHash; + }), + ); } export async function waitTxs(txs: TxHash[], context: EndToEndContext, txWaitOpts?: WaitOpts) { diff --git a/yarn-project/end-to-end/src/composed/docs_examples.test.ts b/yarn-project/end-to-end/src/composed/docs_examples.test.ts index 3d0f4d548cd7..2532f7afde43 100644 --- a/yarn-project/end-to-end/src/composed/docs_examples.test.ts +++ b/yarn-project/end-to-end/src/composed/docs_examples.test.ts @@ -36,7 +36,7 @@ describe('docs_examples', () => { const newAccountAddress = newAccountManager.address; const defaultAccountAddress = prefundedAccount.address; - const deployedContract = await TokenContract.deploy( + const { contract: deployedContract } = await TokenContract.deploy( wallet, // wallet instance defaultAccountAddress, // account 'TokenName', // constructor arg1 @@ -49,7 +49,9 @@ describe('docs_examples', () => { await contract.methods.mint_to_public(newAccountAddress, 1).send({ from: defaultAccountAddress }); // docs:start:simulate_function - const balance = await contract.methods.balance_of_public(newAccountAddress).simulate({ from: newAccountAddress }); + const { result: balance } = await contract.methods + .balance_of_public(newAccountAddress) + .simulate({ from: newAccountAddress }); expect(balance).toEqual(1n); // docs:end:simulate_function }); diff --git a/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts b/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts index 951c3e0152c4..7bd8d64e0fad 100644 --- a/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_local_network_example.test.ts @@ -71,10 +71,10 @@ describe('e2e_local_network_example', () => { ////////////// QUERYING THE TOKEN BALANCE FOR EACH ACCOUNT ////////////// - let aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + let { result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance ${aliceBalance}`); - let bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + let { result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply); @@ -88,10 +88,10 @@ describe('e2e_local_network_example', () => { await tokenContract.methods.transfer(bob, transferQuantity).send({ from: alice }); // Check the new balances - aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + ({ result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice })); logger.info(`Alice's balance ${aliceBalance}`); - bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + ({ result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob })); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply - transferQuantity); @@ -108,10 +108,10 @@ describe('e2e_local_network_example', () => { await mintTokensToPrivate(tokenContract, bob, bob, mintQuantity); // Check the new balances - aliceBalance = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice }); + ({ result: aliceBalance } = await tokenContract.methods.balance_of_private(alice).simulate({ from: alice })); logger.info(`Alice's balance ${aliceBalance}`); - bobBalance = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob }); + ({ result: bobBalance } = await tokenContract.methods.balance_of_private(bob).simulate({ from: bob })); logger.info(`Bob's balance ${bobBalance}`); expect(aliceBalance).toBe(initialSupply - transferQuantity); @@ -188,7 +188,7 @@ describe('e2e_local_network_example', () => { const maxFeesPerGas = (await node.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PrivateFeePaymentMethod(bananaFPCAddress, alice, wallet, gasSettings); - const receiptForAlice = await bananaCoin.methods + const { receipt: receiptForAlice } = await bananaCoin.methods .transfer(bob, amountTransferToBob) .send({ from: alice, fee: { paymentMethod } }); // docs:end:private_fpc_payment @@ -196,11 +196,11 @@ describe('e2e_local_network_example', () => { logger.info(`Transaction fee: ${transactionFee}`); // Check the balances - const aliceBalance = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceBalance } = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's balance: ${aliceBalance}`); expect(aliceBalance).toEqual(mintAmount - transactionFee - amountTransferToBob); - const bobBalance = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobBalance } = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's balance: ${bobBalance}`); expect(bobBalance).toEqual(amountTransferToBob); @@ -214,16 +214,16 @@ describe('e2e_local_network_example', () => { // const sponsoredPaymentMethod = await SponsoredFeePaymentMethod.new(pxe); const initialFPCFeeJuice = await getFeeJuiceBalance(sponsoredFPC, node); - const receiptForBob = await bananaCoin.methods + const { receipt: receiptForBob } = await bananaCoin.methods .transfer(alice, amountTransferToAlice) .send({ from: bob, fee: { paymentMethod: sponsoredPaymentMethod } }); // docs:end:sponsored_fpc_payment // Check the balances - const aliceNewBalance = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); + const { result: aliceNewBalance } = await bananaCoin.methods.balance_of_private(alice).simulate({ from: alice }); logger.info(`Alice's new balance: ${aliceNewBalance}`); expect(aliceNewBalance).toEqual(aliceBalance + amountTransferToAlice); - const bobNewBalance = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); + const { result: bobNewBalance } = await bananaCoin.methods.balance_of_private(bob).simulate({ from: bob }); logger.info(`Bob's new balance: ${bobNewBalance}`); expect(bobNewBalance).toEqual(bobBalance - amountTransferToAlice); diff --git a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts index 40a53ad5f254..3f60809d3f8f 100644 --- a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts @@ -68,7 +68,9 @@ describe('Aztec persistence', () => { owner = initialFundedAccounts[0]; ownerAddress = owner.address; - const { contract, instance } = await TokenBlacklistContract.deploy(wallet, ownerAddress).send({ + const { + receipt: { contract, instance }, + } = await TokenBlacklistContract.deploy(wallet, ownerAddress).send({ from: ownerAddress, wait: { returnReceipt: true }, }); @@ -84,7 +86,7 @@ describe('Aztec persistence', () => { const secret = Fr.random(); - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(1000n, await computeSecretHash(secret)) .send({ from: ownerAddress }); @@ -139,19 +141,29 @@ describe('Aztec persistence', () => { it('correctly restores private notes', async () => { // test for >0 instead of exact value so test isn't dependent on run order await expect( - contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), ).resolves.toBeGreaterThan(0n); }); it('correctly restores public storage', async () => { - await expect(contract.methods.total_supply().simulate({ from: ownerAddress })).resolves.toBeGreaterThan(0n); + await expect( + contract.methods + .total_supply() + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toBeGreaterThan(0n); }); it('tracks new notes for the owner', async () => { - const balance = await contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }); + const { result: balance } = await contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }); const secret = Fr.random(); - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(1000n, await computeSecretHash(secret)) .send({ from: ownerAddress }); await addPendingShieldNoteToPXE( @@ -165,9 +177,12 @@ describe('Aztec persistence', () => { await contract.methods.redeem_shield(ownerAddress, 1000n, secret).send({ from: ownerAddress }); - await expect(contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - balance + 1000n, - ); + await expect( + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(balance + 1000n); }); it('allows spending of private notes', async () => { @@ -175,13 +190,13 @@ describe('Aztec persistence', () => { const otherAccount = await context.wallet.createSchnorrAccount(account.secret, account.salt); const otherAddress = otherAccount.address; - const initialOwnerBalance = await contract.methods + const { result: initialOwnerBalance } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); await contract.methods.transfer(ownerAddress, otherAddress, 500n, 0).send({ from: ownerAddress }); - const [ownerBalance, targetBalance] = await Promise.all([ + const [{ result: ownerBalance }, { result: targetBalance }] = await Promise.all([ contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), contract.methods.balance_of_private(otherAddress).simulate({ from: otherAddress }), ]); @@ -224,16 +239,24 @@ describe('Aztec persistence', () => { it("pxe does not have owner's private notes", async () => { await context.wallet.registerContract(contractInstance, TokenBlacklistContract.artifact); const contract = TokenBlacklistContract.at(contractAddress, wallet); - await expect(contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - 0n, - ); + await expect( + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(0n); }); it('has access to public storage', async () => { await context.wallet.registerContract(contractInstance, TokenBlacklistContract.artifact); const contract = TokenBlacklistContract.at(contractAddress, wallet); - await expect(contract.methods.total_supply().simulate({ from: ownerAddress })).resolves.toBeGreaterThan(0n); + await expect( + contract.methods + .total_supply() + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toBeGreaterThan(0n); }); it('pxe restores notes after registering the owner', async () => { @@ -245,7 +268,10 @@ describe('Aztec persistence', () => { // check that notes total more than 0 so that this test isn't dependent on run order await expect( - contract.methods.balance_of_private(ownerAddress).simulate({ from: ownerAddress }), + contract.methods + .balance_of_private(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), ).resolves.toBeGreaterThan(0n); }); }); @@ -274,7 +300,7 @@ describe('Aztec persistence', () => { // mint some tokens with a secret we know and redeem later on a separate PXE secret = Fr.random(); mintAmount = 1000n; - const mintTxReceipt = await contract.methods + const { receipt: mintTxReceipt } = await contract.methods .mint_private(mintAmount, await computeSecretHash(secret)) .send({ from: ownerAddress }); mintTxHash = mintTxReceipt.txHash; @@ -301,9 +327,12 @@ describe('Aztec persistence', () => { }); it("restores owner's public balance", async () => { - await expect(contract.methods.balance_of_public(ownerAddress).simulate({ from: ownerAddress })).resolves.toEqual( - revealedAmount, - ); + await expect( + contract.methods + .balance_of_public(ownerAddress) + .simulate({ from: ownerAddress }) + .then(r => r.result), + ).resolves.toEqual(revealedAmount); }); it('allows consuming transparent note created on another PXE', async () => { @@ -317,12 +346,12 @@ describe('Aztec persistence', () => { aztecNode, ); - const balanceBeforeRedeem = await contract.methods + const { result: balanceBeforeRedeem } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); await contract.methods.redeem_shield(ownerAddress, mintAmount, secret).send({ from: ownerAddress }); - const balanceAfterRedeem = await contract.methods + const { result: balanceAfterRedeem } = await contract.methods .balance_of_private(ownerAddress) .simulate({ from: ownerAddress }); diff --git a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts index 923b14ea06f8..3a4b9932e270 100644 --- a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts @@ -100,7 +100,13 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { // Deploy L2 token contract // docs:start:deploy-l2-token - const l2TokenContract = await TokenContract.deploy(wallet, ownerAztecAddress, 'L2 Token', 'L2', 18).send({ + const { contract: l2TokenContract } = await TokenContract.deploy( + wallet, + ownerAztecAddress, + 'L2 Token', + 'L2', + 18, + ).send({ from: ownerAztecAddress, }); logger.info(`L2 token contract deployed at ${l2TokenContract.address}`); @@ -130,7 +136,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { // docs:end:deploy-portal // Deploy L2 bridge contract // docs:start:deploy-l2-bridge - const l2BridgeContract = await TokenBridgeContract.deploy( + const { contract: l2BridgeContract } = await TokenBridgeContract.deploy( wallet, l2TokenContract.address, l1PortalContractAddress, @@ -175,7 +181,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { await l2BridgeContract.methods .claim_public(ownerAztecAddress, MINT_AMOUNT, claim.claimSecret, claim.messageLeafIndex) .send({ from: ownerAztecAddress }); - const balance = await l2TokenContract.methods + const { result: balance } = await l2TokenContract.methods .balance_of_public(ownerAztecAddress) .simulate({ from: ownerAztecAddress }); logger.info(`Public L2 balance of ${ownerAztecAddress} is ${balance}`); @@ -206,12 +212,12 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { l2BridgeContract.address, EthAddress.ZERO, ); - const l2TxReceipt = await l2BridgeContract.methods + const { receipt: l2TxReceipt } = await l2BridgeContract.methods .exit_to_l1_public(EthAddress.fromString(ownerEthAddress), withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ from: ownerAztecAddress }); await waitForProven(node, l2TxReceipt, { provenTimeout: 500 }); - const newL2Balance = await l2TokenContract.methods + const { result: newL2Balance } = await l2TokenContract.methods .balance_of_public(ownerAztecAddress) .simulate({ from: ownerAztecAddress }); logger.info(`New L2 balance of ${ownerAztecAddress} is ${newL2Balance}`); diff --git a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts index 286d4f5c2271..4256ab0ca551 100644 --- a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts +++ b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts @@ -321,7 +321,7 @@ describe('HA Full Setup', () => { const sender = ownerAddress; logger.info(`Deploying contract from ${sender}`); - const receipt = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, @@ -442,7 +442,7 @@ describe('HA Full Setup', () => { // Send a transaction to trigger block building which will also trigger voting logger.info('Sending transaction to trigger block building...'); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const receipt = await deployer.deploy(ownerAddress, ownerAddress, 42).send({ + const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, 42).send({ from: ownerAddress, contractAddressSalt: Fr.random(), wait: { returnReceipt: true }, @@ -642,7 +642,7 @@ describe('HA Full Setup', () => { logger.info(`Active nodes: ${haNodeServices.length - killedNodes.length}/${NODE_COUNT}`); const deployer = new ContractDeployer(StatefulTestContractArtifact, wallet); - const receipt = await deployer.deploy(ownerAddress, ownerAddress, i + 100).send({ + const { receipt } = await deployer.deploy(ownerAddress, ownerAddress, i + 100).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(i + 100)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts index ce44c4ecd9c4..ff0bf8f94e9b 100644 --- a/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts +++ b/yarn-project/end-to-end/src/composed/web3signer/e2e_multi_validator_node_key_store.test.ts @@ -400,7 +400,10 @@ describe('e2e_multi_validator_node', () => { }); const settledTransactions = await Promise.all( - sentTransactionPromises.map(async sentTransactionPromise => waitForTx(aztecNode, await sentTransactionPromise)), + sentTransactionPromises.map(async sentTransactionPromise => { + const { txHash } = await sentTransactionPromise; + return waitForTx(aztecNode, txHash); + }), ); await Promise.all( diff --git a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts index 2ce5cf1fef9d..6b985b3a0242 100644 --- a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -138,7 +138,7 @@ describe('End-to-end tests for devnet', () => { const l2AccountDeployMethod = await l2AccountManager.getDeployMethod(); - const txReceipt = await l2AccountDeployMethod.send({ + const { receipt: txReceipt } = await l2AccountDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod: new FeeJuicePaymentMethodWithClaim(l2AccountAddress, { @@ -172,7 +172,9 @@ describe('End-to-end tests for devnet', () => { expect(txReceipt.isMined() && txReceipt.hasExecutionSucceeded()).toBe(true); const feeJuice = FeeJuiceContract.at((await node.getNodeInfo()).protocolContractAddresses.feeJuice, wallet); - const balance = await feeJuice.methods.balance_of_public(l2AccountAddress).simulate({ from: l2AccountAddress }); + const { result: balance } = await feeJuice.methods + .balance_of_public(l2AccountAddress) + .simulate({ from: l2AccountAddress }); expect(balance).toEqual(amount - txReceipt.transactionFee!); }); @@ -253,7 +255,7 @@ describe('End-to-end tests for devnet', () => { async function advanceChainWithEmptyBlocks(wallet: TestWallet) { const [fundedAccountAddress] = await registerInitialLocalNetworkAccountsInWallet(wallet); - const test = await TestContract.deploy(wallet).send({ + const { contract: test } = await TestContract.deploy(wallet).send({ from: fundedAccountAddress, universalDeploy: true, skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index e7c6b83ebe8b..fb18c816774d 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -97,7 +97,9 @@ describe('e2e_2_pxes', () => { const deployChildContractViaServerA = async () => { logger.info(`Deploying Child contract...`); - const { instance } = await ChildContract.deploy(walletA).send({ + const { + receipt: { instance }, + } = await ChildContract.deploy(walletA).send({ from: accountAAddress, wait: { returnReceipt: true }, }); diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index 71c8498a1057..185d4ca73cb4 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -64,7 +64,7 @@ const itShouldBehaveLikeAnAccountContract = ( await deployMethod.send({ from: AztecAddress.ZERO }); } - child = await ChildContract.deploy(wallet).send({ from: address }); + ({ contract: child } = await ChildContract.deploy(wallet).send({ from: address })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_amm.test.ts b/yarn-project/end-to-end/src/e2e_amm.test.ts index 79f640236665..b92c5e9dc683 100644 --- a/yarn-project/end-to-end/src/e2e_amm.test.ts +++ b/yarn-project/end-to-end/src/e2e_amm.test.ts @@ -49,9 +49,9 @@ describe('AMM', () => { ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: liquidityToken } = await deployToken(wallet, adminAddress, 0n, logger)); - amm = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ + ({ contract: amm } = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ from: adminAddress, - }); + })); // TODO(#9480): consider deploying the token by some factory when the AMM is deployed, and making the AMM be the // minter there. @@ -81,15 +81,15 @@ describe('AMM', () => { async function getAmmBalances(): Promise { return { - token0: await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), - token1: await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), + token0: (await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, + token1: (await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, }; } async function getWalletBalances(lp: AztecAddress): Promise { return { - token0: await token0.methods.balance_of_private(lp).simulate({ from: lp }), - token1: await token1.methods.balance_of_private(lp).simulate({ from: lp }), + token0: (await token0.methods.balance_of_private(lp).simulate({ from: lp })).result, + token1: (await token1.methods.balance_of_private(lp).simulate({ from: lp })).result, }; } @@ -146,11 +146,13 @@ describe('AMM', () => { // Liquidity tokens should also be minted for the liquidity provider, as well as locked at the zero address. const expectedLiquidityTokens = (INITIAL_AMM_TOTAL_SUPPLY * 99n) / 100n; expect( - await liquidityToken.methods - .balance_of_private(liquidityProviderAddress) - .simulate({ from: liquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(liquidityProviderAddress) + .simulate({ from: liquidityProviderAddress }) + ).result, ).toEqual(expectedLiquidityTokens); - expect(await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).toEqual( + expect((await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( INITIAL_AMM_TOTAL_SUPPLY, ); }); @@ -162,7 +164,8 @@ describe('AMM', () => { const ammBalancesBefore = await getAmmBalances(); const lpBalancesBefore = await getWalletBalances(otherLiquidityProviderAddress); - const liquidityTokenSupplyBefore = await liquidityToken.methods.total_supply().simulate({ from: adminAddress }); + const liquidityTokenSupplyBefore = (await liquidityToken.methods.total_supply().simulate({ from: adminAddress })) + .result; // The pool currently has the same number of tokens for token0 and token1, since that is the ratio the first // liquidity provider used. Our maximum values have a different ratio (6:5 instead of 1:1), so we will end up @@ -214,11 +217,15 @@ describe('AMM', () => { (liquidityTokenSupplyBefore * (ammBalancesBefore.token0 + expectedAmount0)) / ammBalancesBefore.token0; const expectedLiquidityTokens = expectedTotalSupply - INITIAL_AMM_TOTAL_SUPPLY; - expect(await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).toEqual(expectedTotalSupply); + expect((await liquidityToken.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + expectedTotalSupply, + ); expect( - await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result, ).toEqual(expectedLiquidityTokens); }); @@ -239,9 +246,11 @@ describe('AMM', () => { // We compute the expected amount out and set it as the minimum. In a real-life scenario we'd choose a slightly // lower value to account for slippage, but since we're the only actor interacting with the AMM we can afford to // just pass the exact value. Of course any lower value would also suffice. - const amountOutMin = await amm.methods - .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) - .simulate({ from: swapperAddress }); + const amountOutMin = ( + await amm.methods + .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) + .simulate({ from: swapperAddress }) + ).result; const swapExactTokensInteraction = amm.methods .swap_exact_tokens_for_tokens(token0.address, token1.address, amountIn, amountOutMin, nonceForAuthwits) @@ -264,9 +273,11 @@ describe('AMM', () => { // query the contract for how much token0 we'd get if we sent our entire token1 balance, and then request exactly // that amount. This would fail in a real-life scenario since we'd need to account for slippage, but we can do it // in this test environment since there's nobody else interacting with the AMM. - const amountOut = await amm.methods - .get_amount_out_for_exact_in(ammBalancesBefore.token1, ammBalancesBefore.token0, swapperBalancesBefore.token1) - .simulate({ from: swapperAddress }); + const amountOut = ( + await amm.methods + .get_amount_out_for_exact_in(ammBalancesBefore.token1, ammBalancesBefore.token0, swapperBalancesBefore.token1) + .simulate({ from: swapperAddress }) + ).result; const amountInMax = swapperBalancesBefore.token1; // Swaps also transfer tokens into the AMM, so we provide an authwit for the full amount in (any change will be @@ -299,9 +310,11 @@ describe('AMM', () => { it('remove liquidity', async () => { // We now withdraw all of the tokens of one of the liquidity providers by burning their entire liquidity token // balance. - const liquidityTokenBalance = await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }); + const liquidityTokenBalance = ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result; // Because private burning requires first transferring the tokens into the AMM, we again need to provide an // authwit. @@ -328,9 +341,11 @@ describe('AMM', () => { // The liquidity provider should have no remaining liquidity tokens, and should have recovered the value they // originally deposited. expect( - await liquidityToken.methods - .balance_of_private(otherLiquidityProviderAddress) - .simulate({ from: otherLiquidityProviderAddress }), + ( + await liquidityToken.methods + .balance_of_private(otherLiquidityProviderAddress) + .simulate({ from: otherLiquidityProviderAddress }) + ).result, ).toEqual(0n); // We now assert that the liquidity provider ended up with more tokens than they began with. These extra tokens diff --git a/yarn-project/end-to-end/src/e2e_authwit.test.ts b/yarn-project/end-to-end/src/e2e_authwit.test.ts index bc74c8373ad8..a2e470e4d876 100644 --- a/yarn-project/end-to-end/src/e2e_authwit.test.ts +++ b/yarn-project/end-to-end/src/e2e_authwit.test.ts @@ -34,8 +34,8 @@ describe('e2e_authwit_tests', () => { } = await setup(2)); await ensureAccountContractsPublished(wallet, [account1Address, account2Address]); - auth = await AuthWitTestContract.deploy(wallet).send({ from: account1Address }); - authwitProxy = await GenericProxyContract.deploy(wallet).send({ from: account1Address }); + ({ contract: auth } = await AuthWitTestContract.deploy(wallet).send({ from: account1Address })); + ({ contract: authwitProxy } = await GenericProxyContract.deploy(wallet).send({ from: account1Address })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 07782ca8dd71..1cf55068255e 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -36,11 +36,13 @@ describe('e2e_avm_simulator', () => { let secondAvmContract: AvmTestContract; beforeEach(async () => { - ({ contract: avmContract, instance: avmContractInstance } = await AvmTestContract.deploy(wallet).send({ + ({ + receipt: { contract: avmContract, instance: avmContractInstance }, + } = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress, wait: { returnReceipt: true }, })); - secondAvmContract = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: secondAvmContract } = await AvmTestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); describe('Assertions & error enriching', () => { @@ -145,7 +147,7 @@ describe('e2e_avm_simulator', () => { describe('Contract instance', () => { it('Works', async () => { - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .test_get_contract_instance_matches( avmContract.address, avmContractInstance.deployer, @@ -160,17 +162,21 @@ describe('e2e_avm_simulator', () => { describe('Nullifiers', () => { // Nullifier will not yet be siloed by the kernel. it('Emit and check in the same tx', async () => { - const tx = await avmContract.methods.emit_nullifier_and_check(123456).send({ from: defaultAccountAddress }); + const { receipt: tx } = await avmContract.methods + .emit_nullifier_and_check(123456) + .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); // Nullifier will have been siloed by the kernel, but we check against the unsiloed one. it('Emit and check in separate tx', async () => { const nullifier = new Fr(123456); - let tx = await avmContract.methods.new_nullifier(nullifier).send({ from: defaultAccountAddress }); + let { receipt: tx } = await avmContract.methods.new_nullifier(nullifier).send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); - tx = await avmContract.methods.assert_nullifier_exists(nullifier).send({ from: defaultAccountAddress }); + ({ receipt: tx } = await avmContract.methods + .assert_nullifier_exists(nullifier) + .send({ from: defaultAccountAddress })); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); @@ -204,7 +210,9 @@ describe('e2e_avm_simulator', () => { it('Nested CALL instruction to non-existent contract returns failure, but caller can recover', async () => { // The nested call reverts (returns failure), but the caller doesn't HAVE to rethrow. - const tx = await avmContract.methods.nested_call_to_nothing_recovers().send({ from: defaultAccountAddress }); + const { receipt: tx } = await avmContract.methods + .nested_call_to_nothing_recovers() + .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); }); it('Should NOT be able to emit the same unsiloed nullifier from the same contract', async () => { @@ -218,7 +226,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit different unsiloed nullifiers from the same contract', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_different_nullifier_in_nested_call(avmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -226,7 +234,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit the same unsiloed nullifier from two different contracts', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_same_nullifier_in_nested_call(secondAvmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -234,7 +242,7 @@ describe('e2e_avm_simulator', () => { it('Should be able to emit different unsiloed nullifiers from two different contracts', async () => { const nullifier = new Fr(1); - const tx = await avmContract.methods + const { receipt: tx } = await avmContract.methods .create_different_nullifier_in_nested_call(secondAvmContract.address, nullifier) .send({ from: defaultAccountAddress }); expect(tx.executionResult).toEqual(TxExecutionResult.SUCCESS); @@ -246,7 +254,9 @@ describe('e2e_avm_simulator', () => { let avmContract: AvmInitializerTestContract; beforeEach(async () => { - avmContract = await AvmInitializerTestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: avmContract } = await AvmInitializerTestContract.deploy(wallet).send({ + from: defaultAccountAddress, + })); }); describe('Storage', () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts index 22cad7213ca5..5fc0640a8079 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts @@ -107,20 +107,24 @@ export class BlacklistTokenContractTest { await publicDeployAccounts(this.wallet, [this.adminAddress, this.otherAddress, this.blacklistedAddress]); this.logger.verbose(`Deploying TokenContract...`); - this.asset = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({ + ({ contract: this.asset } = await TokenBlacklistContract.deploy(this.wallet, this.adminAddress).send({ from: this.adminAddress, - }); + })); this.logger.verbose(`Token deployed to ${this.asset.address}`); this.logger.verbose(`Deploying bad account...`); - this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.badAccount.address}.`); // Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender // (so their notes are in scope), but msg_sender in the target must differ from the note owner // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender. this.logger.verbose(`Deploying generic proxy...`); - this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`); await this.crossTimestampOfChange(); @@ -133,9 +137,9 @@ export class BlacklistTokenContractTest { [this.adminAddress, this.otherAddress, this.blacklistedAddress], ); - expect(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).toEqual( - new Role().withAdmin().toNoirStruct(), - ); + expect( + (await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result, + ).toEqual(new Role().withAdmin().toNoirStruct()); } async setup() { @@ -189,9 +193,9 @@ export class BlacklistTokenContractTest { await this.crossTimestampOfChange(); - expect(await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).toEqual( - adminMinterRole.toNoirStruct(), - ); + expect( + (await this.asset.methods.get_roles(this.adminAddress).simulate({ from: this.adminAddress })).result, + ).toEqual(adminMinterRole.toNoirStruct()); this.logger.verbose(`Minting ${amount} publicly...`); await asset.methods.mint_public(this.adminAddress, amount).send({ from: this.adminAddress }); @@ -199,7 +203,7 @@ export class BlacklistTokenContractTest { this.logger.verbose(`Minting ${amount} privately...`); const secret = Fr.random(); const secretHash = await computeSecretHash(secret); - const receipt = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress }); + const { receipt } = await asset.methods.mint_private(amount, secretHash).send({ from: this.adminAddress }); await this.addPendingShieldNoteToPXE(asset, this.adminAddress, amount, secretHash, receipt.txHash); await asset.methods.redeem_shield(this.adminAddress, amount, secret).send({ from: this.adminAddress }); @@ -207,20 +211,20 @@ export class BlacklistTokenContractTest { tokenSim.mintPublic(this.adminAddress, amount); - const publicBalance = await asset.methods + const { result: publicBalance } = await asset.methods .balance_of_public(this.adminAddress) .simulate({ from: this.adminAddress }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(this.adminAddress)); tokenSim.mintPrivate(this.adminAddress, amount); - const privateBalance = await asset.methods + const { result: privateBalance } = await asset.methods .balance_of_private(this.adminAddress) .simulate({ from: this.adminAddress }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(this.adminAddress)); - const totalSupply = await asset.methods.total_supply().simulate({ from: this.adminAddress }); + const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: this.adminAddress }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); } diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts index 6028b6173922..9b8b28204e15 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/burn.test.ts @@ -27,7 +27,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('public', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_public(adminAddress, amount, 0).send({ from: adminAddress }); @@ -36,7 +39,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -61,7 +67,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -70,7 +79,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); const authwitNonce = 1; @@ -82,7 +94,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -91,7 +106,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -109,7 +127,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -138,7 +159,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('private', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn(adminAddress, amount, 0).send({ from: adminAddress }); @@ -146,7 +170,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -166,7 +193,10 @@ describe('e2e_blacklist_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.burn(adminAddress, amount, 0).simulate({ from: adminAddress })).rejects.toThrow( @@ -175,7 +205,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.burn(adminAddress, amount, 1).simulate({ from: adminAddress })).rejects.toThrow( @@ -184,7 +217,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -199,7 +235,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('burn on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -218,7 +257,10 @@ describe('e2e_blacklist_token_contract burn', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts index f1386cfa812a..3e704d342355 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/minting.test.ts @@ -86,9 +86,11 @@ describe('e2e_blacklist_token_contract mint', () => { describe('Mint flow', () => { it('mint_private as minter and redeem as recipient', async () => { - const balanceBefore = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balanceBefore } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); - const receipt = await asset.methods.mint_private(amount, secretHash).send({ from: adminAddress }); + const { receipt } = await asset.methods.mint_private(amount, secretHash).send({ from: adminAddress }); txHash = receipt.txHash; await t.addPendingShieldNoteToPXE(asset, adminAddress, amount, secretHash, txHash); @@ -96,7 +98,9 @@ describe('e2e_blacklist_token_contract mint', () => { await asset.methods.redeem_shield(adminAddress, amount, secret).send({ from: adminAddress }); tokenSim.mintPrivate(adminAddress, amount); - const balanceAfter = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balanceAfter } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); expect(balanceAfter).toBe(balanceBefore + amount); }); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts index b09cb1d9b9c2..7ac49c276345 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/shielding.test.ts @@ -31,11 +31,14 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of self', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); - const receipt = await asset.methods.shield(adminAddress, amount, secretHash, 0).send({ from: adminAddress }); + const { receipt } = await asset.methods.shield(adminAddress, amount, secretHash, 0).send({ from: adminAddress }); // Redeem it await t.addPendingShieldNoteToPXE(asset, adminAddress, amount, secretHash, receipt.txHash); @@ -47,7 +50,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -61,7 +67,7 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { ); await validateActionInteraction.send(); - const receipt = await action.send({ from: otherAddress }); + const { receipt } = await action.send({ from: otherAddress }); // Check that replaying the shield should fail! await expect( @@ -79,7 +85,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); @@ -89,7 +98,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); @@ -101,7 +113,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -119,7 +134,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (wrong designated caller)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balancePub = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePub + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -137,7 +155,10 @@ describe('e2e_blacklist_token_contract shield + redeem_shield', () => { }); it('on behalf of other (without approval)', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts index 87fc616cd1e3..88bbbf428fd5 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts @@ -26,7 +26,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const tokenTransferInteraction = asset.methods.transfer(adminAddress, otherAddress, amount, 0); @@ -35,7 +38,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer to self', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); @@ -44,7 +50,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -64,7 +73,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); @@ -74,7 +86,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); @@ -86,8 +101,14 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_private(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_private(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -99,8 +120,18 @@ describe('e2e_blacklist_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow('Assertion failed: Balance too low'); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_private(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_private(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { @@ -111,7 +142,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -130,7 +164,10 @@ describe('e2e_blacklist_token_contract transfer private', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -148,7 +185,12 @@ describe('e2e_blacklist_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); + expect( + await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); }); it('transfer from a blacklisted account', async () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts index c5d23f421bbd..2862a3c735e7 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts @@ -24,7 +24,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_public(adminAddress, otherAddress, amount, 0).send({ from: adminAddress }); @@ -33,7 +36,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer to self', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_public(adminAddress, adminAddress, amount, 0).send({ from: adminAddress }); @@ -42,7 +48,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -68,7 +77,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -79,7 +91,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 - 1n; const authwitNonce = 1; await expect( @@ -92,7 +107,10 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -103,8 +121,14 @@ describe('e2e_blacklist_token_contract transfer public', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -121,13 +145,29 @@ describe('e2e_blacklist_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: otherAddress })).rejects.toThrow(U128_UNDERFLOW_ERROR); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress }); + const balance0 = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); + const balance1 = await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -145,8 +185,18 @@ describe('e2e_blacklist_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: otherAddress })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(otherAddress).simulate({ from: otherAddress })).toEqual(balance1); + expect( + await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result), + ).toEqual(balance0); + expect( + await asset.methods + .balance_of_public(otherAddress) + .simulate({ from: otherAddress }) + .then(r => r.result), + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts index ae10ada9661d..9547b5b992dd 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/unshielding.test.ts @@ -26,7 +26,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of self', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv / 2n; expect(amount).toBeGreaterThan(0n); @@ -36,7 +39,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -56,7 +62,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -66,7 +75,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -78,7 +90,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -93,7 +108,10 @@ describe('e2e_blacklist_token_contract unshielding', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const balancePriv0 = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }) + .then(r => r.result); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 1eb7fe94be37..ce8df2f1085c 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -106,7 +106,7 @@ describe('e2e_block_building', () => { logger.info(` Total txs to send: ${TX_COUNT}`); logger.info(` Expected minimum blocks: ${EXPECTED_BLOCKS}`); - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); logger.info(`Deployed stateful test contract at ${contract.address}`); // Configure sequencer with a small delay per tx and enforce timetable @@ -184,7 +184,7 @@ describe('e2e_block_building', () => { it('assembles a block with multiple txs with public fns', async () => { // First deploy the contract - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); // Assemble N contract deployment txs // We need to create them sequentially since we cannot have parallel calls to a circuit @@ -213,8 +213,8 @@ describe('e2e_block_building', () => { // Skipped since we only use it to manually test number of invocations to world-state. it.skip('builds blocks with multiple public fns after multiple nullifier insertions', async () => { // First deploy the contracts - const contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); - const another = await TestContract.deploy(wallet).send({ from: ownerAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + const { contract: another } = await TestContract.deploy(wallet).send({ from: ownerAddress }); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 16, maxTxsPerBlock: 16 }); @@ -268,23 +268,18 @@ describe('e2e_block_building', () => { const highPriority = new GasFees(100, 100); const lowPriority = new GasFees(1, 1); - const deployTxHash = await deployMethod.send({ - from: ownerAddress, - fee: { gasSettings: { maxPriorityFeesPerGas: highPriority } }, - wait: NO_WAIT, - }); - const callTxHash = await callInteraction.send({ - from: ownerAddress, - fee: { gasSettings: { maxPriorityFeesPerGas: lowPriority } }, - wait: NO_WAIT, - }); - - const [deployTxReceipt, callTxReceipt] = await Promise.all([ - waitForTx(aztecNode, deployTxHash), - waitForTx(aztecNode, callTxHash), + const [deployResult, callResult] = await Promise.all([ + deployMethod.send({ + from: ownerAddress, + fee: { gasSettings: { maxPriorityFeesPerGas: highPriority } }, + }), + callInteraction.send({ + from: ownerAddress, + fee: { gasSettings: { maxPriorityFeesPerGas: lowPriority } }, + }), ]); - expect(deployTxReceipt.blockNumber).toEqual(callTxReceipt.blockNumber); + expect(deployResult.receipt.blockNumber).toEqual(callResult.receipt.blockNumber); }); }); @@ -299,7 +294,7 @@ describe('e2e_block_building', () => { wallet, accounts: [ownerAddress], } = await setup(1)); - contract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: ownerAddress })); logger.info(`Test contract deployed at ${contract.address}`); }); @@ -427,7 +422,7 @@ describe('e2e_block_building', () => { } = await setup(1)); logger.info(`Deploying test contract`); - testContract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: ownerAddress })); }, 60_000); afterAll(() => teardown()); @@ -531,9 +526,11 @@ describe('e2e_block_building', () => { })); logger.info('Deploying token contract'); - const token = await TokenContract.deploy(wallet, ownerAddress, 'TokenName', 'TokenSymbol', 18).send({ - from: ownerAddress, - }); + const { contract: token } = await TokenContract.deploy(wallet, ownerAddress, 'TokenName', 'TokenSymbol', 18).send( + { + from: ownerAddress, + }, + ); logger.info('Updating txs per block to 4'); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 4, maxTxsPerBlock: 4 }); @@ -566,7 +563,7 @@ describe('e2e_block_building', () => { accounts: [ownerAddress], } = context); - const testContract = await TestContract.deploy(wallet).send({ from: ownerAddress }); + const { contract: testContract } = await TestContract.deploy(wallet).send({ from: ownerAddress }); logger.warn(`Test contract deployed at ${testContract.address}`); // We want the sequencer to wait until both txs have arrived (so minTxsPerBlock=2), but agree to build @@ -585,7 +582,8 @@ describe('e2e_block_building', () => { ]); const batches = times(2, makeBatch); - const txHashes = await Promise.all(batches.map(batch => batch.send({ from: ownerAddress, wait: NO_WAIT }))); + const txHashResults = await Promise.all(batches.map(batch => batch.send({ from: ownerAddress, wait: NO_WAIT }))); + const txHashes = txHashResults.map(({ txHash }) => txHash); logger.warn(`Sent two txs to test contract`, { txs: txHashes.map(hash => hash.toString()) }); await Promise.race(txHashes.map(txHash => waitForTx(aztecNode, txHash, { timeout: 60 }))); @@ -617,7 +615,7 @@ describe('e2e_block_building', () => { accounts: [ownerAddress], } = await setup(1)); - contract = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress }); + ({ contract } = await StatefulTestContract.deploy(wallet, ownerAddress, 1).send({ from: ownerAddress })); initialBlockNumber = await aztecNode.getBlockNumber(); logger.info(`Stateful test contract deployed at ${contract.address}`); @@ -641,15 +639,15 @@ describe('e2e_block_building', () => { // Send a tx to the contract that creates a note. This tx will be reorgd but re-included, // since it is being built against a proven block number. logger.info('Sending initial tx'); - const tx1 = await contract.methods.create_note(ownerAddress, 20).send({ from: ownerAddress }); + const { receipt: tx1 } = await contract.methods.create_note(ownerAddress, 20).send({ from: ownerAddress }); expect(tx1.blockNumber).toEqual(initialBlockNumber + 1); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(21n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(21n); // And send a second one, which won't be re-included. logger.info('Sending second tx'); - const tx2 = await contract.methods.create_note(ownerAddress, 30).send({ from: ownerAddress }); + const { receipt: tx2 } = await contract.methods.create_note(ownerAddress, 30).send({ from: ownerAddress }); expect(tx2.blockNumber).toEqual(initialBlockNumber + 2); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(51n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(51n); logger.info('Advancing past the proof submission window'); @@ -684,12 +682,12 @@ describe('e2e_block_building', () => { expect(newTx1Receipt.blockHash).not.toEqual(tx1.blockHash); // PXE should have cleared out the 30-note from tx2, but reapplied the 20-note from tx1 - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(21n); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(21n); // And we should be able to send a new tx on the new chain logger.info('Sending new tx on reorgd chain'); - const tx3 = await contract.methods.create_note(ownerAddress, 10).send({ from: ownerAddress }); - expect(await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).toEqual(31n); + const { receipt: tx3 } = await contract.methods.create_note(ownerAddress, 10).send({ from: ownerAddress }); + expect((await contract.methods.summed_values(ownerAddress).simulate({ from: ownerAddress })).result).toEqual(31n); expect(tx3.blockNumber).toBeGreaterThanOrEqual(newTx1Receipt.blockNumber! + 1); }); }); diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index 56e05017370b..6f387d27e51c 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -104,7 +104,7 @@ describe('e2e_card_game', () => { const deployContract = async () => { logger.debug(`Deploying L2 contract...`); - contract = await CardGameContract.deploy(wallet).send({ from: firstPlayer }); + ({ contract } = await CardGameContract.deploy(wallet).send({ from: firstPlayer })); logger.info(`L2 contract deployed at ${contract.address}`); }; @@ -113,7 +113,9 @@ describe('e2e_card_game', () => { // docs:start:send_tx await contract.methods.buy_pack(seed).send({ from: firstPlayer }); // docs:end:send_tx - const collection = await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }); + const { result: collection } = await contract.methods + .view_collection_cards(firstPlayer, 0) + .simulate({ from: firstPlayer }); const expected = await getPackedCards(0, seed); expect(boundedVecToArray(collection)).toMatchObject(expected); }); @@ -128,7 +130,7 @@ describe('e2e_card_game', () => { contract.methods.buy_pack(seed).send({ from: secondPlayer }), ]); firstPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }), + (await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer })).result, ); }); @@ -143,11 +145,13 @@ describe('e2e_card_game', () => { .send({ from: secondPlayer }), ).rejects.toThrow(`Not all cards were removed`); - const collection = await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }); + const { result: collection } = await contract.methods + .view_collection_cards(firstPlayer, 0) + .simulate({ from: firstPlayer }); expect(boundedVecToArray(collection)).toHaveLength(1); expect(boundedVecToArray(collection)).toMatchObject([firstPlayerCollection[1]]); - expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })) as Game).toMatchObject({ + expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })).result as Game).toMatchObject({ players: [ { address: firstPlayer, @@ -169,9 +173,8 @@ describe('e2e_card_game', () => { it('should start games', async () => { const secondPlayerCollection = boundedVecToArray( - (await contract.methods - .view_collection_cards(secondPlayer, 0) - .simulate({ from: secondPlayer })) as NoirBoundedVec, + (await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer })) + .result as NoirBoundedVec, ); await Promise.all([ @@ -185,7 +188,7 @@ describe('e2e_card_game', () => { await contract.methods.start_game(GAME_ID).send({ from: firstPlayer }); - expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })) as Game).toMatchObject({ + expect((await contract.methods.view_game(GAME_ID).simulate({ from: firstPlayer })).result as Game).toMatchObject({ players: expect.arrayContaining([ { address: firstPlayer, @@ -220,15 +223,15 @@ describe('e2e_card_game', () => { ]); firstPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer }), + (await contract.methods.view_collection_cards(firstPlayer, 0).simulate({ from: firstPlayer })).result, ); secondPlayerCollection = boundedVecToArray( - await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer }), + (await contract.methods.view_collection_cards(secondPlayer, 0).simulate({ from: secondPlayer })).result, ); thirdPlayerCOllection = boundedVecToArray( - await contract.methods.view_collection_cards(thirdPlayer, 0).simulate({ from: thirdPlayer }), + (await contract.methods.view_collection_cards(thirdPlayer, 0).simulate({ from: thirdPlayer })).result, ); }); @@ -240,7 +243,7 @@ describe('e2e_card_game', () => { } async function playGame(playerDecks: { address: AztecAddress; deck: Card[] }[], id = GAME_ID) { - const initialGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })) as Game; + const initialGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })).result as Game; const players = initialGameState.players.map(player => player.address); const cards = players.map( player => playerDecks.find(playerDeckEntry => playerDeckEntry.address.equals(player))!.deck, @@ -254,7 +257,7 @@ describe('e2e_card_game', () => { } } - const finalGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })) as Game; + const finalGameState = (await contract.methods.view_game(id).simulate({ from: firstPlayer })).result as Game; expect(finalGameState.finished).toBe(true); return finalGameState; @@ -285,7 +288,8 @@ describe('e2e_card_game', () => { await contract.methods.claim_cards(GAME_ID, game.rounds_cards.map(cardToField)).send({ from: winner }); const winnerCollection = boundedVecToArray( - (await contract.methods.view_collection_cards(winner, 0).simulate({ from: winner })) as NoirBoundedVec, + (await contract.methods.view_collection_cards(winner, 0).simulate({ from: winner })) + .result as NoirBoundedVec, ); const winnerGameDeck = [winnerCollection[0], winnerCollection[3]]; diff --git a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts index 10bf16f3e987..34af7e7d44da 100644 --- a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts +++ b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts @@ -110,7 +110,9 @@ describe('e2e_contract_updates', () => { } sequencer = maybeSequencer; - ({ contract, instance } = await UpdatableContract.deploy(wallet, constructorArgs[0]).send({ + ({ + receipt: { contract, instance }, + } = await UpdatableContract.deploy(wallet, constructorArgs[0]).send({ from: defaultAccountAddress, contractAddressSalt: salt, wait: { returnReceipt: true }, diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts index e83b195c5f84..e074e5a9239c 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l1_to_l2.test.ts @@ -34,7 +34,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { await t.setup(); ({ logger: log, crossChainTestHarness, wallet, user1Address, aztecNode } = t); - testContract = await TestContract.deploy(wallet).send({ from: user1Address }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: user1Address })); }, 300_000); afterEach(async () => { @@ -219,7 +219,7 @@ describe('e2e_cross_chain_messaging l1_to_l2', () => { // On public, we actually send the tx and check that it reverts due to the missing message. // This advances the block too as a side-effect. Note that we do not rely on a simulation since the cross chain messages // do not get added at the beginning of the block during node_simulatePublicCalls (maybe they should?). - const receipt = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); + const { receipt } = await consume().send({ from: user1Address, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); await t.context.watcher.markAsProven(); } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index 7432bd33d4f3..d747d3ef6666 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -43,7 +43,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { version = BigInt(await rollup.getVersion()); - contract = await TestContract.deploy(wallet).send({ from: user1Address }); + ({ contract } = await TestContract.deploy(wallet).send({ from: user1Address })); }); afterAll(async () => { @@ -60,7 +60,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { // Configure the node to be able to rollup only 1 tx. await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); - const txReceipt = await new BatchCall(wallet, [ + const { receipt: txReceipt } = await new BatchCall(wallet, [ contract.methods.create_l2_to_l1_message_arbitrary_recipient_private(contents[0], recipient), contract.methods.create_l2_to_l1_message_arbitrary_recipient_public(contents[1], recipient), ]).send({ from: user1Address }); @@ -92,7 +92,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { await aztecNodeAdmin.setConfig({ minTxsPerBlock: 2 }); // Send the 2 txs. - const [noMessageReceipt, withMessageReceipt] = await Promise.all([ + const [{ receipt: noMessageReceipt }, { receipt: withMessageReceipt }] = await Promise.all([ contract.methods.emit_nullifier(Fr.random()).send({ from: user1Address }), contract.methods .create_l2_to_l1_message_arbitrary_recipient_private(content, recipient) @@ -119,7 +119,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { const call0 = createBatchCall(wallet, tx0.recipients, tx0.contents); const call1 = createBatchCall(wallet, tx1.recipients, tx1.contents); - const [l2TxReceipt0, l2TxReceipt1] = await Promise.all([ + const [{ receipt: l2TxReceipt0 }, { receipt: l2TxReceipt1 }] = await Promise.all([ call0.send({ from: user1Address }), call1.send({ from: user1Address }), ]); @@ -173,7 +173,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { const call1 = createBatchCall(wallet, tx1.recipients, tx1.contents); const call2 = createBatchCall(wallet, tx2.recipients, tx2.contents); - const [l2TxReceipt0, l2TxReceipt1, l2TxReceipt2] = await Promise.all([ + const [{ receipt: l2TxReceipt0 }, { receipt: l2TxReceipt1 }, { receipt: l2TxReceipt2 }] = await Promise.all([ call0.send({ from: user1Address }), call1.send({ from: user1Address }), call2.send({ from: user1Address }), diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index 5f892e4c2e11..ced063f43993 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -62,22 +62,22 @@ describe('e2e_crowdfunding_and_claim', () => { // We set the deadline to a week from now deadline = (await cheatCodes.eth.timestamp()) + 7 * 24 * 60 * 60; - donationToken = await TokenContract.deploy( + ({ contract: donationToken } = await TokenContract.deploy( wallet, operatorAddress, donationTokenMetadata.name, donationTokenMetadata.symbol, donationTokenMetadata.decimals, - ).send({ from: operatorAddress }); + ).send({ from: operatorAddress })); logger.info(`Donation Token deployed to ${donationToken.address}`); - rewardToken = await TokenContract.deploy( + ({ contract: rewardToken } = await TokenContract.deploy( wallet, operatorAddress, rewardTokenMetadata.name, rewardTokenMetadata.symbol, rewardTokenMetadata.decimals, - ).send({ from: operatorAddress }); + ).send({ from: operatorAddress })); logger.info(`Reward Token deployed to ${rewardToken.address}`); // We deploy the Crowdfunding contract as an escrow contract (i.e. with populated public keys that make it @@ -94,16 +94,20 @@ describe('e2e_crowdfunding_and_claim', () => { ); const crowdfundingInstance = await crowdfundingDeployment.getInstance(); await wallet.registerContract(crowdfundingInstance, CrowdfundingContract.artifact, crowdfundingSecretKey); - crowdfundingContract = await crowdfundingDeployment.send({ + ({ contract: crowdfundingContract } = await crowdfundingDeployment.send({ from: operatorAddress, // The contract constructor initializes private storage vars that need the contract's own nullifier key. additionalScopes: [crowdfundingInstance.address], - }); + })); logger.info(`Crowdfunding contract deployed at ${crowdfundingContract.address}`); - claimContract = await ClaimContract.deploy(wallet, crowdfundingContract.address, rewardToken.address).send({ + ({ contract: claimContract } = await ClaimContract.deploy( + wallet, + crowdfundingContract.address, + rewardToken.address, + ).send({ from: operatorAddress, - }); + })); logger.info(`Claim contract deployed at ${claimContract.address}`); await rewardToken.methods.set_minter(claimContract.address, true).send({ from: operatorAddress }); @@ -133,7 +137,7 @@ describe('e2e_crowdfunding_and_claim', () => { // The donor should have exactly one note const pageIndex = 0; - const notes = await crowdfundingContract.methods + const { result: notes } = await crowdfundingContract.methods .get_donation_notes(donor1Address, pageIndex) .simulate({ from: donor1Address }); expect(notes.len).toEqual(1n); @@ -191,7 +195,7 @@ describe('e2e_crowdfunding_and_claim', () => { // The donor should have exactly one note const pageIndex = 0; - const notes = await crowdfundingContract.methods + const { result: notes } = await crowdfundingContract.methods .get_donation_notes(donorAddress, pageIndex) .simulate({ from: donorAddress }); expect(notes.len).toEqual(1n); @@ -230,11 +234,11 @@ describe('e2e_crowdfunding_and_claim', () => { const otherCrowdfundingInstance = await otherCrowdfundingDeployment.getInstance(); await wallet.registerContract(otherCrowdfundingInstance, CrowdfundingContract.artifact, crowdfundingSecretKey); - otherCrowdfundingContract = await otherCrowdfundingDeployment.send({ + ({ contract: otherCrowdfundingContract } = await otherCrowdfundingDeployment.send({ from: operatorAddress, // The contract constructor initializes private storage vars that need the contract's own nullifier key. additionalScopes: [otherCrowdfundingInstance.address], - }); + })); logger.info(`Crowdfunding contract deployed at ${otherCrowdfundingContract.address}`); } @@ -254,11 +258,11 @@ describe('e2e_crowdfunding_and_claim', () => { // 3) Get the donation note const pageIndex = 0; - const notes = await otherCrowdfundingContract.methods + const { result: notes2 } = await otherCrowdfundingContract.methods .get_donation_notes(donor1Address, pageIndex) .simulate({ from: donor1Address }); - expect(notes.len).toEqual(1n); - const otherContractNote = notes.storage[0]; + expect(notes2.len).toEqual(1n); + const otherContractNote = notes2.storage[0]; // 4) Try to claim rewards using note from other contract await expect( diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index fc5a57f84fe6..d027cd6c4b39 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -37,7 +37,7 @@ describe('e2e_deploy_contract contract class registration', () => { ({ logger, wallet, aztecNode, defaultAccountAddress } = await t.setup()); artifact = StatefulTestContract.artifact; publicationTxReceipt = await publishContractClass(wallet, artifact).then(c => - c.send({ from: defaultAccountAddress }), + c.send({ from: defaultAccountAddress }).then(({ receipt }) => receipt), ); contractClass = await getContractClassFromArtifact(artifact); expect(await aztecNode.getContractClass(contractClass.id)).toBeDefined(); @@ -47,7 +47,7 @@ describe('e2e_deploy_contract contract class registration', () => { describe('publishing a contract class', () => { it('emits public bytecode', async () => { - const publicationTxReceipt = await publishContractClass(wallet, TestContract.artifact).then(c => + const { receipt: publicationTxReceipt } = await publishContractClass(wallet, TestContract.artifact).then(c => c.send({ from: defaultAccountAddress }), ); const logs = await aztecNode.getContractClassLogs({ txHash: publicationTxReceipt.txHash }); @@ -155,7 +155,7 @@ describe('e2e_deploy_contract contract class registration', () => { it('refuses to call a public function with init check if the instance is not initialized', async () => { const whom = await AztecAddress.random(); - const receipt = await contract.methods + const { receipt } = await contract.methods .increment_public_value(whom, 10) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); @@ -195,7 +195,7 @@ describe('e2e_deploy_contract contract class registration', () => { it('refuses to initialize the instance with wrong args via a public function', async () => { const whom = await AztecAddress.random(); - const receipt = await contract.methods + const { receipt } = await contract.methods .public_constructor(whom, 43) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); @@ -228,7 +228,7 @@ describe('e2e_deploy_contract contract class registration', () => { // Register the instance to be deployed in the pxe await wallet.registerContract(instance, artifact); // Set up the contract that calls the deployer (which happens to be the TestContract) and call it - const deployer = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: deployer } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); await deployer.methods.publish_contract_instance(instance.address).send({ from: defaultAccountAddress }); }); @@ -243,7 +243,7 @@ describe('e2e_deploy_contract contract class registration', () => { ).rejects.toThrow(/not deployed/); // This time, don't throw on revert and confirm that the tx is included // despite reverting in app logic because of the call to a non-existent contract - const tx = await instance.methods + const { receipt: tx } = await instance.methods .increment_public_value_no_init_check(whom, 10) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(tx.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index 3d81fda867be..9ce2b7366aea 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -40,7 +40,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; logger.debug(`Deploying stateful test contract`); // docs:start:deploy_basic - const contract = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); + const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); // docs:end:deploy_basic expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); logger.debug(`Calling public method on stateful test contract at ${contract.address.toString()}`); @@ -58,7 +58,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; // docs:start:deploy_universal const opts = { universalDeploy: true, from: defaultAccountAddress }; - const contract = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); + const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); // docs:end:deploy_universal expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); @@ -68,7 +68,7 @@ describe('e2e_deploy_contract deploy method', () => { it('publicly deploys and calls a public function from the constructor', async () => { const owner = defaultAccountAddress; // docs:start:deploy_token - const token = await TokenContract.deploy(wallet, owner, 'TOKEN', 'TKN', 18).send({ + const { contract: token } = await TokenContract.deploy(wallet, owner, 'TOKEN', 'TKN', 18).send({ from: defaultAccountAddress, }); // docs:end:deploy_token @@ -79,7 +79,7 @@ describe('e2e_deploy_contract deploy method', () => { const owner = defaultAccountAddress; logger.debug(`Deploying contract via a public constructor`); // docs:start:deploy_with_opts - const contract = await StatefulTestContract.deployWithOpts( + const { contract } = await StatefulTestContract.deployWithOpts( { wallet, method: 'public_constructor' }, owner, 42, @@ -96,7 +96,7 @@ describe('e2e_deploy_contract deploy method', () => { it('deploys a contract with a default initializer not named constructor', async () => { logger.debug(`Deploying contract with a default initializer named initialize`); const opts = { skipClassPublication: true, skipInstancePublication: true, from: defaultAccountAddress }; - const contract = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); + const { contract } = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); logger.debug(`Calling a function to ensure the contract was properly initialized`); await contract.methods.increment_twice(defaultAccountAddress).send({ from: defaultAccountAddress }); expect(await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).toEqual( @@ -106,10 +106,10 @@ describe('e2e_deploy_contract deploy method', () => { it('publicly deploys a contract with no constructor', async () => { logger.debug(`Deploying contract with no constructor`); - const contract = await NoConstructorContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract } = await NoConstructorContract.deploy(wallet).send({ from: defaultAccountAddress }); const arbitraryValue = 42; logger.debug(`Call a public function to check that it was publicly deployed`); - const receipt = await contract.methods.emit_public(arbitraryValue).send({ from: defaultAccountAddress }); + const { receipt } = await contract.methods.emit_public(arbitraryValue).send({ from: defaultAccountAddress }); const logs = await aztecNode.getPublicLogs({ txHash: receipt.txHash }); expect(logs.logs[0].log.getEmittedFields()).toEqual([new Fr(arbitraryValue)]); }); @@ -162,7 +162,10 @@ describe('e2e_deploy_contract deploy method', () => { const publicCallTxPromise = publicCall.send({ from: defaultAccountAddress, wait: { timeout: 600 } }); logger.debug('Deploying a contract and calling a public function in the same block'); - const [deployTxReceipt, publicCallTxReceipt] = await Promise.all([deployTxPromise, publicCallTxPromise]); + const [{ receipt: deployTxReceipt }, { receipt: publicCallTxReceipt }] = await Promise.all([ + deployTxPromise, + publicCallTxPromise, + ]); expect(deployTxReceipt.blockNumber).toEqual(publicCallTxReceipt.blockNumber); }, 300_000); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts index 55f638be25f6..9d16aeb08bb0 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/legacy.test.ts @@ -36,7 +36,7 @@ describe('e2e_deploy_contract legacy', () => { deployer: defaultAccountAddress, }); const deployer = new ContractDeployer(TestContractArtifact, wallet); - const receipt = await deployer + const { receipt } = await deployer .deploy() .send({ from: defaultAccountAddress, contractAddressSalt: salt, wait: { returnReceipt: true } }); expect(receipt.contract.address).toEqual(deploymentData.address); @@ -65,7 +65,7 @@ describe('e2e_deploy_contract legacy', () => { for (let index = 0; index < 2; index++) { logger.info(`Deploying contract ${index + 1}...`); - const receipt = await deployer + const { receipt } = await deployer .deploy() .send({ from: defaultAccountAddress, contractAddressSalt: Fr.random(), wait: { returnReceipt: true } }); logger.info(`Sending TX to contract ${index + 1}...`); @@ -113,8 +113,8 @@ describe('e2e_deploy_contract legacy', () => { expect(goodTxPromiseResult.status).toBe('fulfilled'); expect(badTxReceiptResult.status).toBe('fulfilled'); // but reverted - const goodTxReceipt = goodTxPromiseResult.status === 'fulfilled' ? goodTxPromiseResult.value : null; - const badTxReceipt = badTxReceiptResult.status === 'fulfilled' ? badTxReceiptResult.value : null; + const goodTxReceipt = goodTxPromiseResult.status === 'fulfilled' ? goodTxPromiseResult.value.receipt : null; + const badTxReceipt = badTxReceiptResult.status === 'fulfilled' ? badTxReceiptResult.value.receipt : null; // Both the good and bad transactions are included expect(goodTxReceipt).toBeDefined(); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index f1af8d9c9adf..4a40eef5e2ec 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -31,7 +31,7 @@ describe('e2e_deploy_contract private initialization', () => { // The function has a noinitcheck flag so it can be called without initialization. it('executes a noinitcheck function in an uninitialized contract', async () => { const contract = await t.registerContract(wallet, TestContract); - const receipt = await contract.methods.emit_nullifier(10).send({ from: defaultAccountAddress }); + const { receipt } = await contract.methods.emit_nullifier(10).send({ from: defaultAccountAddress }); const txEffects = await aztecNode.getTxEffect(receipt.txHash); const expected = await siloNullifier(contract.address, new Fr(10)); diff --git a/yarn-project/end-to-end/src/e2e_double_spend.test.ts b/yarn-project/end-to-end/src/e2e_double_spend.test.ts index 6b4e2053b380..3cc69dec717d 100644 --- a/yarn-project/end-to-end/src/e2e_double_spend.test.ts +++ b/yarn-project/end-to-end/src/e2e_double_spend.test.ts @@ -25,7 +25,7 @@ describe('e2e_double_spend', () => { logger, } = await setup(1)); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); logger.info(`Test contract deployed at ${contract.address}`); }); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts index e4740dde721d..220face468aa 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_invalidate_block.parallel.test.ts @@ -238,7 +238,7 @@ describe('e2e_epochs/epochs_invalidate_block', () => { // Send a few transactions so the sequencer builds multiple blocks in the checkpoint // We'll later check that the first tx at least was picked up and mined logger.warn('Sending multiple transactions to trigger block building'); - const [sentTx] = await timesAsync(8, i => + const [{ txHash: sentTx }] = await timesAsync(8, i => testContract.methods.emit_nullifier(BigInt(i + 1)).send({ from: context.accounts[0], wait: NO_WAIT }), ); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts index 247a56d44ae9..ff94a94fca77 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_mbps.parallel.test.ts @@ -117,8 +117,8 @@ describe('e2e_epochs/epochs_mbps', () => { // Unlike emit_nullifier (which has #[noinitcheck]), cross-chain methods require a deployed contract. if (deployCrossChainContract) { logger.warn(`Deploying cross-chain test contract before stopping initial sequencer`); - crossChainContract = await TestContract.deploy(wallet).send({ from }); - logger.warn(`Cross-chain test contract deployed at ${crossChainContract.address}`); + ({ contract: crossChainContract } = await TestContract.deploy(wallet).send({ from })); + logger.warn(`Cross-chain test contract deployed at ${crossChainContract!.address}`); } // Halt block building in initial aztec node, which was not set up as a validator. diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts index 846cd5f82b96..bc5a6c18eb57 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_proof_public_cross_chain.test.ts @@ -46,7 +46,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { // Deploy a contract that consumes L1 to L2 messages await context.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); logger.warn(`Deploying test contract`); - const testContract = await TestContract.deploy(context.wallet).send({ from: context.accounts[0] }); + const { contract: testContract } = await TestContract.deploy(context.wallet).send({ from: context.accounts[0] }); logger.warn(`Test contract deployed at ${testContract.address}`); // Send an l1 to l2 message to be consumed from the contract @@ -64,7 +64,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { // And we consume the message using the test contract. It's important that we don't wait for the membership witness // to be available, since we want to test the scenario where the message becomes available on the same block the tx lands. logger.warn(`Consuming message ${message.content.toString()} from the contract at ${testContract.address}`); - const txReceipt = await testContract.methods + const { receipt: txReceipt } = await testContract.methods .consume_message_from_arbitrary_sender_public( message.content, secret, @@ -90,7 +90,7 @@ describe('e2e_epochs/epochs_proof_public_cross_chain', () => { expect(provenBlockNumber).toBeGreaterThanOrEqual(txReceipt.blockNumber!); // Should not be able to consume the message again. - const failedReceipt = await testContract.methods + const { receipt: failedReceipt } = await testContract.methods .consume_message_from_arbitrary_sender_public( message.content, secret, diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index a9fd2e501887..7da5c6cd9faa 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -42,11 +42,13 @@ describe('e2e_escrow_contract', () => { const escrowInstance = await escrowDeployment.getInstance(); await wallet.registerContract(escrowInstance, EscrowContract.artifact, escrowSecretKey); // The contract constructor initializes private storage vars that need the contract's own nullifier key. - escrowContract = await escrowDeployment.send({ from: owner, additionalScopes: [escrowInstance.address] }); + ({ contract: escrowContract } = await escrowDeployment.send({ from: owner, additionalScopes: [escrowInstance.address] })); logger.info(`Escrow contract deployed at ${escrowContract.address}`); // Deploy Token contract and mint funds for the escrow contract - token = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ from: owner }); + ({ contract: token } = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ + from: owner, + })); await mintTokensToPrivate(token, owner, escrowContract.address, 100n); diff --git a/yarn-project/end-to-end/src/e2e_event_logs.test.ts b/yarn-project/end-to-end/src/e2e_event_logs.test.ts index 4645fc1dcc77..64d89526709f 100644 --- a/yarn-project/end-to-end/src/e2e_event_logs.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_logs.test.ts @@ -47,7 +47,7 @@ describe('Logs', () => { await ensureAccountContractsPublished(wallet, [account1Address, account2Address]); log.warn(`Deploying test contract`); - testLogContract = await TestLogContract.deploy(wallet).send({ from: account1Address }); + ({ contract: testLogContract } = await TestLogContract.deploy(wallet).send({ from: account1Address })); }); afterAll(() => teardown()); @@ -57,9 +57,12 @@ describe('Logs', () => { const preimages = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple, 5>; const txs = await Promise.all( - preimages.map(preimage => - testLogContract.methods.emit_encrypted_events(account2Address, preimage).send({ from: account1Address }), - ), + preimages.map(async preimage => { + const { receipt } = await testLogContract.methods + .emit_encrypted_events(account2Address, preimage) + .send({ from: account1Address }); + return receipt; + }), ); const firstBlockNumber = Math.min(...txs.map(tx => tx.blockNumber!)); @@ -124,13 +127,13 @@ describe('Logs', () => { const preimage = makeTuple(5, makeTuple.bind(undefined, 4, Fr.random)) as Tuple, 5>; let i = 0; - const firstTx = await testLogContract.methods + const { receipt: firstTx } = await testLogContract.methods .emit_unencrypted_events(preimage[i]) .send({ from: account1Address }); await timesParallel(3, () => testLogContract.methods.emit_unencrypted_events(preimage[++i]).send({ from: account1Address }), ); - const lastTx = await testLogContract.methods + const { receipt: lastTx } = await testLogContract.methods .emit_unencrypted_events(preimage[++i]) .send({ from: account1Address }); @@ -181,7 +184,9 @@ describe('Logs', () => { const c = await AztecAddress.random(); const extra = Fr.random(); - const tx = await testLogContract.methods.emit_nested_event(a, b, c, extra).send({ from: account1Address }); + const { receipt: tx } = await testLogContract.methods + .emit_nested_event(a, b, c, extra) + .send({ from: account1Address }); const collectedEvents = await getPublicEvents( aztecNode, @@ -212,7 +217,7 @@ describe('Logs', () => { const tx1NumLogs = 10; { // Call the private function that emits two encrypted logs per call and recursively nests 4 times - const tx = await testLogContract.methods + const { receipt: tx } = await testLogContract.methods .emit_encrypted_events_nested(account2Address, 4) .send({ from: account1Address }); @@ -231,7 +236,7 @@ describe('Logs', () => { const tx2NumLogs = 6; { // Call the private function that emits two encrypted logs per call and recursively nests 2 times - const tx = await testLogContract.methods + const { receipt: tx } = await testLogContract.methods .emit_encrypted_events_nested(account2Address, 2) .send({ from: account1Address }); diff --git a/yarn-project/end-to-end/src/e2e_event_only.test.ts b/yarn-project/end-to-end/src/e2e_event_only.test.ts index 7dd0c3859451..d2b036f601a0 100644 --- a/yarn-project/end-to-end/src/e2e_event_only.test.ts +++ b/yarn-project/end-to-end/src/e2e_event_only.test.ts @@ -26,14 +26,16 @@ describe('EventOnly', () => { accounts: [defaultAccountAddress], } = await setup(1)); await ensureAccountContractsPublished(wallet, [defaultAccountAddress]); - eventOnlyContract = await EventOnlyContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: eventOnlyContract } = await EventOnlyContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); it('emits and retrieves a private event for a contract with no notes', async () => { const value = Fr.random(); - const tx = await eventOnlyContract.methods.emit_event_for_msg_sender(value).send({ from: defaultAccountAddress }); + const { receipt: tx } = await eventOnlyContract.methods + .emit_event_for_msg_sender(value) + .send({ from: defaultAccountAddress }); const events = await wallet.getPrivateEvents(EventOnlyContract.events.TestEvent, { contractAddress: eventOnlyContract.address, diff --git a/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts b/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts index 78e3be77ee02..18240f671298 100644 --- a/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts +++ b/yarn-project/end-to-end/src/e2e_expiration_timestamp.test.ts @@ -25,7 +25,7 @@ describe('e2e_expiration_timestamp', () => { aztecNode, accounts: [defaultAccountAddress], } = await setup()); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts index 966882ad638a..a02e539963db 100644 --- a/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/account_init.test.ts @@ -89,10 +89,7 @@ describe('e2e_fees account_init', () => { const [bobsInitialGas] = await t.getGasBalanceFn(bobsAddress); expect(bobsInitialGas).toEqual(mintAmount); - const tx = await bobsDeployMethod.send({ - from: AztecAddress.ZERO, - wait: { returnReceipt: true }, - }); + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, wait: { returnReceipt: true } }); expect(tx.transactionFee!).toBeGreaterThan(0n); await expect(t.getGasBalanceFn(bobsAddress)).resolves.toEqual([bobsInitialGas - tx.transactionFee!]); @@ -101,7 +98,7 @@ describe('e2e_fees account_init', () => { it('pays natively in the Fee Juice by bridging funds themselves', async () => { const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(bobsAddress); const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobsAddress, claim); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { returnReceipt: true }, @@ -121,7 +118,7 @@ describe('e2e_fees account_init', () => { const maxFeesPerGas = (await aztecNode.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PrivateFeePaymentMethod(bananaFPC.address, bobsAddress, wallet, gasSettings); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: { returnReceipt: true }, @@ -150,7 +147,7 @@ describe('e2e_fees account_init', () => { const maxFeesPerGas = (await aztecNode.getCurrentMinFees()).mul(1.5); const gasSettings = GasSettings.default({ maxFeesPerGas }); const paymentMethod = new PublicFeePaymentMethod(bananaFPC.address, bobsAddress, wallet, gasSettings); - const tx = await bobsDeployMethod.send({ + const { receipt: tx } = await bobsDeployMethod.send({ from: AztecAddress.ZERO, skipInstancePublication: false, fee: { paymentMethod }, @@ -183,7 +180,7 @@ describe('e2e_fees account_init', () => { await t.mintPrivateBananas(mintedBananas, bobsAddress); const [aliceBalanceBefore] = await t.getGasBalanceFn(aliceAddress); - const tx = await SchnorrAccountContractInterface.deployWithPublicKeys( + const { receipt: tx } = await SchnorrAccountContractInterface.deployWithPublicKeys( bobsPublicKeys, wallet, bobsSigningPubKey.x, diff --git a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts index 0c4c54c5032c..440f1949acf8 100644 --- a/yarn-project/end-to-end/src/e2e_fees/failures.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/failures.test.ts @@ -84,7 +84,7 @@ describe('e2e_fees failures', () => { const currentSequencerRewards = await t.getCoinbaseSequencerRewards(); - const txReceipt = await bananaCoin.methods + const { receipt: txReceipt } = await bananaCoin.methods .transfer_in_public(aliceAddress, sequencerAddress, outrageousPublicAmountAliceDoesNotHave, 0) .send({ from: aliceAddress, @@ -187,7 +187,7 @@ describe('e2e_fees failures', () => { ); // if we skip simulation, it includes the failed TX - const txReceipt = await bananaCoin.methods + const { receipt: txReceipt } = await bananaCoin.methods .transfer_in_public(aliceAddress, sequencerAddress, outrageousPublicAmountAliceDoesNotHave, 0) .send({ from: aliceAddress, @@ -285,7 +285,7 @@ describe('e2e_fees failures', () => { }), ).rejects.toThrow(); - const receipt = await bananaCoin.methods + const { receipt } = await bananaCoin.methods .mint_to_public(aliceAddress, 1n) // random operation .send({ from: aliceAddress, diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts index a3509eb164eb..c8ba437bcdcf 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_juice_payments.test.ts @@ -43,7 +43,9 @@ describe('e2e_fees Fee Juice payments', () => { describe('without initial funds', () => { beforeAll(async () => { - expect(await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress })).toEqual(0n); + expect( + (await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress })).result, + ).toEqual(0n); }); it('fails to simulate a tx', async () => { @@ -63,10 +65,12 @@ describe('e2e_fees Fee Juice payments', () => { it('claims bridged funds and pays with them on the same tx', async () => { const claim = await t.feeJuiceBridgeTestHarness.prepareTokensOnL1(bobAddress); const paymentMethod = new FeeJuicePaymentMethodWithClaim(bobAddress, claim); - const receipt = await feeJuiceContract.methods + const { receipt } = await feeJuiceContract.methods .check_balance(0n) .send({ from: bobAddress, fee: { gasSettings, paymentMethod } }); - const endBalance = await feeJuiceContract.methods.balance_of_public(bobAddress).simulate({ from: bobAddress }); + const { result: endBalance } = await feeJuiceContract.methods + .balance_of_public(bobAddress) + .simulate({ from: bobAddress }); expect(endBalance).toBeGreaterThan(0n); expect(endBalance).toBeLessThan(claim.claimAmount); @@ -76,28 +80,30 @@ describe('e2e_fees Fee Juice payments', () => { describe('with initial funds', () => { it('sends tx with payment in Fee Juice with public calls', async () => { - const initialBalance = await feeJuiceContract.methods + const { result: initialBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const { transactionFee } = await bananaCoin.methods + const { + receipt: { transactionFee }, + } = await bananaCoin.methods .transfer_in_public(aliceAddress, bobAddress, 1n, 0n) .send({ fee: { gasSettings }, from: aliceAddress }); expect(transactionFee).toBeGreaterThan(0n); - const endBalance = await feeJuiceContract.methods + const { result: endBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); expect(endBalance).toBeLessThan(initialBalance); }); it('sends tx fee payment in Fee Juice with no public calls', async () => { - const initialBalance = await feeJuiceContract.methods + const { result: initialBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const { transactionFee } = await bananaCoin.methods - .transfer(bobAddress, 1n) - .send({ fee: { gasSettings }, from: aliceAddress }); + const { + receipt: { transactionFee }, + } = await bananaCoin.methods.transfer(bobAddress, 1n).send({ fee: { gasSettings }, from: aliceAddress }); expect(transactionFee).toBeGreaterThan(0n); - const endBalance = await feeJuiceContract.methods + const { result: endBalance } = await feeJuiceContract.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); expect(endBalance).toBeLessThan(initialBalance); diff --git a/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts b/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts index 5ac0ec444727..83fe2d1b837b 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts @@ -26,7 +26,7 @@ describe('e2e_fees fee settings', () => { await t.setup(); ({ aliceAddress, wallet, gasSettings, cheatCodes, aztecNode } = t); - testContract = await TestContract.deploy(wallet).send({ from: aliceAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: aliceAddress })); gasSettings = { ...gasSettings, maxFeesPerGas: undefined }; }); diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index d810c896ffc1..d43093bc0034 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -157,11 +157,15 @@ export class FeesTest { /** Alice mints bananaCoin tokens privately to the target address and redeems them. */ async mintPrivateBananas(amount: bigint, address: AztecAddress) { - const balanceBefore = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceBefore } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); await mintTokensToPrivate(this.bananaCoin, this.aliceAddress, address, amount); - const balanceAfter = await this.bananaCoin.methods.balance_of_private(address).simulate({ from: address }); + const { result: balanceAfter } = await this.bananaCoin.methods + .balance_of_private(address) + .simulate({ from: address }); expect(balanceAfter).toEqual(balanceBefore + amount); } @@ -223,7 +227,7 @@ export class FeesTest { async applyDeployBananaToken() { this.logger.info('Applying deploy banana token setup'); - const bananaCoin = await BananaCoin.deploy(this.wallet, this.aliceAddress, 'BC', 'BC', 18n).send({ + const { contract: bananaCoin } = await BananaCoin.deploy(this.wallet, this.aliceAddress, 'BC', 'BC', 18n).send({ from: this.aliceAddress, }); this.logger.info(`BananaCoin deployed at ${bananaCoin.address}`); @@ -244,7 +248,7 @@ export class FeesTest { expect((await this.wallet.getContractMetadata(feeJuiceContract.address)).isContractPublished).toBe(true); const bananaCoin = this.bananaCoin; - const bananaFPC = await FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin).send({ + const { contract: bananaFPC } = await FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin).send({ from: this.aliceAddress, }); diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index 417cdb495cd0..47fd12c85569 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -88,9 +88,13 @@ describe('e2e_fees gas_estimation', () => { paymentMethod?: FeePaymentMethod, ) => Promise.all( - [GasSettings.from({ ...gasSettings, ...limits }), gasSettings].map(gasSettings => - makeTransferRequest().send({ from: aliceAddress, fee: { gasSettings, paymentMethod } }), - ), + [GasSettings.from({ ...gasSettings, ...limits }), gasSettings].map(async gasSettings => { + const { receipt } = await makeTransferRequest().send({ + from: aliceAddress, + fee: { gasSettings, paymentMethod }, + }); + return receipt; + }), ); const logGasEstimate = (estimatedGas: Pick) => @@ -194,10 +198,10 @@ describe('e2e_fees gas_estimation', () => { }); logGasEstimate(estimatedGas); - const [withEstimate, withoutEstimate] = (await Promise.all([ + const [{ receipt: withEstimate }, { receipt: withoutEstimate }] = (await Promise.all([ deployMethod().send(deployOpts(estimatedGas)), deployMethod().send(deployOpts()), - ])) as unknown as DeployTxReceipt[]; + ])) as unknown as { receipt: DeployTxReceipt }[]; // Estimation should yield that teardown has no cost, so should send the tx with zero for teardown expect(withEstimate.transactionFee!).toEqual(withoutEstimate.transactionFee!); diff --git a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts index ebb72b1f9b3e..9563d02815da 100644 --- a/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/private_payments.test.ts @@ -155,7 +155,7 @@ describe('e2e_fees private_payment', () => { * increase Alice's private banana balance by feeAmount by finalizing partial note */ const newlyMintedBananas = 10n; - const tx = await bananaCoin.methods.mint_to_private(aliceAddress, newlyMintedBananas).send({ + const { receipt: tx } = await bananaCoin.methods.mint_to_private(aliceAddress, newlyMintedBananas).send({ from: aliceAddress, fee: { paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), @@ -200,12 +200,14 @@ describe('e2e_fees private_payment', () => { * increase Alice's private banana balance by feeAmount by finalizing partial note */ const amountTransferredToPrivate = 1n; - const tx = await bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate).send({ - from: aliceAddress, - fee: { - paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_to_private(aliceAddress, amountTransferredToPrivate) + .send({ + from: aliceAddress, + fee: { + paymentMethod: new PrivateFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), + }, + }); const feeAmount = tx.transactionFee!; @@ -249,7 +251,7 @@ describe('e2e_fees private_payment', () => { * increase sequencer/fee recipient/FPC admin private banana balance by feeAmount by finalizing partial note * increase Alice's private banana balance by feeAmount by finalizing partial note */ - const tx = await new BatchCall(wallet, [ + const { receipt: tx } = await new BatchCall(wallet, [ bananaCoin.methods.transfer(bobAddress, amountTransferredInPrivate), bananaCoin.methods.transfer_to_private(aliceAddress, amountTransferredToPrivate), ]).send({ @@ -283,7 +285,7 @@ describe('e2e_fees private_payment', () => { it('rejects txs that dont have enough balance to cover gas costs', async () => { // deploy a copy of bananaFPC but don't fund it! - const bankruptFPC = await FPCContract.deploy(wallet, bananaCoin.address, aliceAddress).send({ + const { contract: bankruptFPC } = await FPCContract.deploy(wallet, bananaCoin.address, aliceAddress).send({ from: aliceAddress, }); diff --git a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts index 51e9109a90dc..b059938777db 100644 --- a/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/public_payments.test.ts @@ -59,12 +59,14 @@ describe('e2e_fees public_payment', () => { it('pays fees for tx that make public transfer', async () => { const bananasToSendToBob = 10n; - const tx = await bananaCoin.methods.transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0).send({ - from: aliceAddress, - fee: { - paymentMethod: new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0) + .send({ + from: aliceAddress, + fee: { + paymentMethod: new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettings), + }, + }); const feeAmount = tx.transactionFee!; diff --git a/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts b/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts index aac9aaa557ee..ec9726d9d129 100644 --- a/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/sponsored_payments.test.ts @@ -56,13 +56,15 @@ describe('e2e_fees sponsored_public_payment', () => { it('pays fees for tx that makes a public transfer', async () => { // docs:start:sponsored_fpc_simple const bananasToSendToBob = 10n; - const tx = await bananaCoin.methods.transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0).send({ - from: aliceAddress, - fee: { - gasSettings, - paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPC.address), - }, - }); + const { receipt: tx } = await bananaCoin.methods + .transfer_in_public(aliceAddress, bobAddress, bananasToSendToBob, 0) + .send({ + from: aliceAddress, + fee: { + gasSettings, + paymentMethod: new SponsoredFeePaymentMethod(sponsoredFPC.address), + }, + }); // docs:end:sponsored_fpc_simple const feeAmount = tx.transactionFee!; diff --git a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts index d424b78be5f1..d0a9cedf6b12 100644 --- a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts +++ b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts @@ -51,9 +51,9 @@ describe('Kernelless simulation', () => { ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: liquidityToken } = await deployToken(wallet, adminAddress, 0n, logger)); - amm = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ + ({ contract: amm } = await AMMContract.deploy(wallet, token0.address, token1.address, liquidityToken.address).send({ from: adminAddress, - }); + })); await liquidityToken.methods.set_minter(amm.address, true).send({ from: adminAddress }); @@ -75,15 +75,15 @@ describe('Kernelless simulation', () => { async function getWalletBalances(lpAddress: AztecAddress): Promise { return { - token0: await token0.methods.balance_of_private(lpAddress).simulate({ from: lpAddress }), - token1: await token1.methods.balance_of_private(lpAddress).simulate({ from: lpAddress }), + token0: (await token0.methods.balance_of_private(lpAddress).simulate({ from: lpAddress })).result, + token1: (await token1.methods.balance_of_private(lpAddress).simulate({ from: lpAddress })).result, }; } async function getAmmBalances(): Promise { return { - token0: await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), - token1: await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress }), + token0: (await token0.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, + token1: (await token1.methods.balance_of_public(amm.address).simulate({ from: adminAddress })).result, }; } @@ -224,7 +224,7 @@ describe('Kernelless simulation', () => { const nonceForAuthwits = Fr.random(); - const amountOutMin = await amm.methods + const { result: amountOutMin } = await amm.methods .get_amount_out_for_exact_in(ammBalancesBefore.token0, ammBalancesBefore.token1, amountIn) .simulate({ from: swapperAddress }); @@ -268,7 +268,9 @@ describe('Kernelless simulation', () => { let pendingNoteHashesContract: PendingNoteHashesContract; beforeAll(async () => { - pendingNoteHashesContract = await PendingNoteHashesContract.deploy(wallet).send({ from: adminAddress }); + ({ contract: pendingNoteHashesContract } = await PendingNoteHashesContract.deploy(wallet).send({ + from: adminAddress, + })); }); it('squashing produces same gas estimates as with-kernels path', async () => { @@ -306,7 +308,9 @@ describe('Kernelless simulation', () => { let pendingNoteHashesContract: PendingNoteHashesContract; beforeAll(async () => { - pendingNoteHashesContract = await PendingNoteHashesContract.deploy(wallet).send({ from: adminAddress }); + ({ contract: pendingNoteHashesContract } = await PendingNoteHashesContract.deploy(wallet).send({ + from: adminAddress, + })); }); it('verifies settled read requests against the note hash tree', async () => { diff --git a/yarn-project/end-to-end/src/e2e_keys.test.ts b/yarn-project/end-to-end/src/e2e_keys.test.ts index 82ecf79ca366..9e56bb423b87 100644 --- a/yarn-project/end-to-end/src/e2e_keys.test.ts +++ b/yarn-project/end-to-end/src/e2e_keys.test.ts @@ -45,7 +45,7 @@ describe('Keys', () => { initialFundedAccounts, } = await setup(1)); - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); secret = initialFundedAccounts[0].secret; }); @@ -125,7 +125,7 @@ describe('Keys', () => { const expectedOvskApp = await computeAppSecretKey(ovskM, testContract.address, 'ov'); // Get the ovsk_app via the test contract - const ovskAppBigInt = await testContract.methods + const { result: ovskAppBigInt } = await testContract.methods .get_ovsk_app(ovpkMHash) .simulate({ from: defaultAccountAddress }); const ovskApp = new Fr(ovskAppBigInt); diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 5c143383ebb1..22c1964fe1f8 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -39,11 +39,13 @@ describe('e2e_lending_contract', () => { const deployContracts = async () => { logger.info(`Deploying price feed contract...`); - const priceFeedContract = await PriceFeedContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: priceFeedContract } = await PriceFeedContract.deploy(wallet).send({ + from: defaultAccountAddress, + }); logger.info(`Price feed deployed to ${priceFeedContract.address}`); logger.info(`Deploying collateral asset feed contract...`); - const collateralAsset = await TokenContract.deploy( + const { contract: collateralAsset } = await TokenContract.deploy( wallet, defaultAccountAddress, 'TokenName', @@ -53,13 +55,19 @@ describe('e2e_lending_contract', () => { logger.info(`Collateral asset deployed to ${collateralAsset.address}`); logger.info(`Deploying stable coin contract...`); - const stableCoin = await TokenContract.deploy(wallet, defaultAccountAddress, 'TokenName', 'TokenSymbol', 18).send({ + const { contract: stableCoin } = await TokenContract.deploy( + wallet, + defaultAccountAddress, + 'TokenName', + 'TokenSymbol', + 18, + ).send({ from: defaultAccountAddress, }); logger.info(`Stable coin asset deployed to ${stableCoin.address}`); logger.info(`Deploying L2 public contract...`); - const lendingContract = await LendingContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: lendingContract } = await LendingContract.deploy(wallet).send({ from: defaultAccountAddress }); logger.info(`CDP deployed at ${lendingContract.address}`); await collateralAsset.methods.set_minter(lendingContract.address, true).send({ from: defaultAccountAddress }); diff --git a/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts b/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts index 8ce86ab42188..f46ff8a3a165 100644 --- a/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts +++ b/yarn-project/end-to-end/src/e2e_mempool_limit.test.ts @@ -31,9 +31,9 @@ describe('e2e_mempool_limit', () => { throw new Error('Aztec node admin API must be available for this test'); } - token = await TokenContract.deploy(wallet, defaultAccountAddress, 'TEST', 'T', 18).send({ + ({ contract: token } = await TokenContract.deploy(wallet, defaultAccountAddress, 'TEST', 'T', 18).send({ from: defaultAccountAddress, - }); + })); await token.methods.mint_to_public(defaultAccountAddress, 10n ** 18n).send({ from: defaultAccountAddress }); }); diff --git a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts index 35dccd29e152..47ec290401f2 100644 --- a/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_validator/e2e_multi_validator_node.test.ts @@ -114,7 +114,7 @@ describe('e2e_multi_validator_node', () => { const sender = ownerAddress; logger.info(`Deploying contract from ${sender}`); - const tx = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, @@ -177,7 +177,7 @@ describe('e2e_multi_validator_node', () => { logger.info(`Deploying contract from ${sender}`); const deployer = new ContractDeployer(artifact, wallet); - const tx = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: tx } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts b/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts index f447cb54d557..c4da4436f003 100644 --- a/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts +++ b/yarn-project/end-to-end/src/e2e_multiple_blobs.test.ts @@ -36,7 +36,7 @@ describe('e2e_multiple_blobs', () => { } = await setup(1)); aztecNodeAdmin = maybeAztecNodeAdmin!; - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -68,10 +68,10 @@ describe('e2e_multiple_blobs', () => { expect(provenTxs.length).toBe(TX_COUNT); // Send them simultaneously to be picked up by the sequencer - const txHashes = await Promise.all(provenTxs.map(tx => tx.send({ from: defaultAccountAddress, wait: NO_WAIT }))); + const sendResults = await Promise.all(provenTxs.map(tx => tx.send({ from: defaultAccountAddress, wait: NO_WAIT }))); // Wait for all to be mined const receipts = await Promise.all( - txHashes.map(txHash => { + sendResults.map(({ txHash }) => { return waitForTx(aztecNode, txHash); }), ); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts index 27449075dfe9..aab6d6fb59c4 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/importer.test.ts @@ -15,8 +15,8 @@ describe('e2e_nested_contract manual', () => { }); beforeEach(async () => { - importerContract = await ImportTestContract.deploy(wallet).send({ from: defaultAccountAddress }); - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: importerContract } = await ImportTestContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(async () => { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts index ac9512f43421..0e19664a5662 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_private_enqueue.test.ts @@ -19,8 +19,8 @@ describe('e2e_nested_contract manual_enqueue', () => { }); beforeEach(async () => { - parentContract = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress }); - childContract = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: parentContract } = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: childContract } = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(async () => { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts index 3b47116fcb96..a699ea8f1764 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/manual_public.test.ts @@ -48,7 +48,7 @@ describe('e2e_nested_contract manual', () => { parentContract.methods.enqueue_call_to_child(childContract.address, pubSetValueSelector, 40n), ]; - const tx = await new BatchCall(wallet, actions).send({ from: defaultAccountAddress }); + const { receipt: tx } = await new BatchCall(wallet, actions).send({ from: defaultAccountAddress }); const extendedLogs = ( await aztecNode.getPublicLogs({ fromBlock: tx.blockNumber!, diff --git a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts index 5632daf6c133..769db81c1ba0 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract/nested_contract_test.ts @@ -65,9 +65,11 @@ export class NestedContractTest { async applyManual() { this.logger.info('Deploying parent and child contracts'); - const parentContract = await ParentContract.deploy(this.wallet).send({ from: this.defaultAccountAddress }); - const childContract = await ChildContract.deploy(this.wallet).send({ from: this.defaultAccountAddress }); - this.parentContract = parentContract; - this.childContract = childContract; + ({ contract: this.parentContract } = await ParentContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress, + })); + ({ contract: this.childContract } = await ChildContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress, + })); } } diff --git a/yarn-project/end-to-end/src/e2e_nft.test.ts b/yarn-project/end-to-end/src/e2e_nft.test.ts index 35a2e8c7310d..7e7f8da1079a 100644 --- a/yarn-project/end-to-end/src/e2e_nft.test.ts +++ b/yarn-project/end-to-end/src/e2e_nft.test.ts @@ -33,7 +33,9 @@ describe('NFT', () => { ({ teardown, wallet, accounts } = await setup(4)); [adminAddress, minterAddress, user1Address, user2Address] = accounts; - nftContract = await NFTContract.deploy(wallet, adminAddress, 'FROG', 'FRG').send({ from: adminAddress }); + ({ contract: nftContract } = await NFTContract.deploy(wallet, adminAddress, 'FROG', 'FRG').send({ + from: adminAddress, + })); }); afterAll(() => teardown()); @@ -41,13 +43,15 @@ describe('NFT', () => { // NOTE: This test is sequential and each test case depends on the previous one it('sets minter', async () => { await nftContract.methods.set_minter(minterAddress, true).send({ from: adminAddress }); - const isMinterAMinter = await nftContract.methods.is_minter(minterAddress).simulate({ from: minterAddress }); + const { result: isMinterAMinter } = await nftContract.methods + .is_minter(minterAddress) + .simulate({ from: minterAddress }); expect(isMinterAMinter).toBe(true); }); it('minter mints to a user', async () => { await nftContract.methods.mint(user1Address, TOKEN_ID).send({ from: minterAddress }); - const ownerAfterMint = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: ownerAfterMint } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(ownerAfterMint).toEqual(user1Address); }); @@ -57,7 +61,7 @@ describe('NFT', () => { const recipient = user2Address; await nftContract.methods.transfer_to_private(recipient, TOKEN_ID).send({ from: user1Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(publicOwnerAfter).toEqual(AztecAddress.ZERO); }); @@ -74,19 +78,21 @@ describe('NFT', () => { it('transfers to public', async () => { await nftContract.methods.transfer_to_public(user1Address, user2Address, TOKEN_ID, 0).send({ from: user1Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user1Address }); expect(publicOwnerAfter).toEqual(user2Address); }); it('transfers in public', async () => { await nftContract.methods.transfer_in_public(user2Address, user1Address, TOKEN_ID, 0).send({ from: user2Address }); - const publicOwnerAfter = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user2Address }); + const { result: publicOwnerAfter } = await nftContract.methods.owner_of(TOKEN_ID).simulate({ from: user2Address }); expect(publicOwnerAfter).toEqual(user1Address); }); const getPrivateNfts = async (owner: AztecAddress) => { - const [nfts, pageLimitReached] = await nftContract.methods.get_private_nfts(owner, 0).simulate({ from: owner }); + const { + result: [nfts, pageLimitReached], + } = await nftContract.methods.get_private_nfts(owner, 0).simulate({ from: owner }); if (pageLimitReached) { throw new Error('Page limit reached and pagination not implemented in test'); } diff --git a/yarn-project/end-to-end/src/e2e_note_getter.test.ts b/yarn-project/end-to-end/src/e2e_note_getter.test.ts index d8192b71827c..c48c9f291a53 100644 --- a/yarn-project/end-to-end/src/e2e_note_getter.test.ts +++ b/yarn-project/end-to-end/src/e2e_note_getter.test.ts @@ -34,7 +34,7 @@ describe('e2e_note_getter', () => { let contract: NoteGetterContract; beforeAll(async () => { - contract = await NoteGetterContract.deploy(wallet).send({ from: defaultAddress }); + ({ contract } = await NoteGetterContract.deploy(wallet).send({ from: defaultAddress })); }); it('inserts notes from 0-9, then makes multiple queries specifying the total suite of comparators', async () => { @@ -47,7 +47,14 @@ describe('e2e_note_getter', () => { // We insert a note with value 5 twice to better test the comparators await contract.methods.insert_note(5).send({ from: defaultAddress }); - const [returnEq, returnNeq, returnLt, returnGt, returnLte, returnGte] = await Promise.all([ + const [ + { result: returnEq }, + { result: returnNeq }, + { result: returnLt }, + { result: returnGt }, + { result: returnLte }, + { result: returnGte }, + ] = await Promise.all([ contract.methods.read_note_values(defaultAddress, Comparator.EQ, 5).simulate({ from: defaultAddress }), contract.methods.read_note_values(defaultAddress, Comparator.NEQ, 5).simulate({ from: defaultAddress }), contract.methods.read_note_values(defaultAddress, Comparator.LT, 5).simulate({ from: defaultAddress }), @@ -78,7 +85,7 @@ describe('e2e_note_getter', () => { const makeTxHybrid = false; beforeAll(async () => { - contract = await TestContract.deploy(wallet).send({ from: defaultAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAddress })); owner = defaultAddress; }); @@ -93,10 +100,10 @@ describe('e2e_note_getter', () => { }); async function assertNoteIsReturned(storageSlot: number, expectedValue: number, activeOrNullified: boolean) { - const viewNotesResult = await contract.methods + const { result: viewNotesResult } = await contract.methods .call_view_notes(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); - const getNotesResult = await contract.methods + const { result: getNotesResult } = await contract.methods .call_get_notes(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); @@ -155,10 +162,10 @@ describe('e2e_note_getter', () => { await contract.methods.call_destroy_note(owner, storageSlot).send({ from: defaultAddress }); // We now fetch multiple notes, and get both the active and the nullified one. - const viewNotesManyResult = await contract.methods + const { result: viewNotesManyResult } = await contract.methods .call_view_notes_many(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); - const getNotesManyResult = await contract.methods + const { result: getNotesManyResult } = await contract.methods .call_get_notes_many(owner, storageSlot, activeOrNullified) .simulate({ from: defaultAddress }); diff --git a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts index 624433699dd6..c045303bed85 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts @@ -33,8 +33,8 @@ describe('e2e_offchain_effect', () => { accounts: [defaultAccountAddress], aztecNode, } = await setup(1)); - contract1 = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress }); - contract2 = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: contract1 } = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: contract2 } = await OffchainEffectContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -164,7 +164,9 @@ describe('e2e_offchain_effect', () => { .simulate({ from: defaultAccountAddress }); // Get the note value - const noteValue = await contract1.methods.get_note_value(owner).simulate({ from: defaultAccountAddress }); + const { result: noteValue } = await contract1.methods + .get_note_value(owner) + .simulate({ from: defaultAccountAddress }); expect(noteValue).toBe(value); }); }); diff --git a/yarn-project/end-to-end/src/e2e_orderbook.test.ts b/yarn-project/end-to-end/src/e2e_orderbook.test.ts index 1ac98d5ae223..22efa02edd17 100644 --- a/yarn-project/end-to-end/src/e2e_orderbook.test.ts +++ b/yarn-project/end-to-end/src/e2e_orderbook.test.ts @@ -52,9 +52,9 @@ describe('Orderbook', () => { ({ contract: token0 } = await deployToken(wallet, adminAddress, 0n, logger)); ({ contract: token1 } = await deployToken(wallet, adminAddress, 0n, logger)); - orderbook = await OrderbookContract.deploy(wallet, token0.address, token1.address).send({ + ({ contract: orderbook } = await OrderbookContract.deploy(wallet, token0.address, token1.address).send({ from: adminAddress, - }); + })); // Mint tokens to maker and taker await mintTokensToPrivate(token0, adminAddress, makerAddress, bidAmount); @@ -95,7 +95,9 @@ describe('Orderbook', () => { orderId = orderCreatedEvents[0].event.order_id; // Get order from orderbook and verify details - const [order, isFulfilled] = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); + const { + result: [order, isFulfilled], + } = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); expect(order.bid_amount).toEqual(bidAmount); expect(order.ask_amount).toEqual(askAmount); expect(order.bid_token_is_zero).toBeTrue(); @@ -103,10 +105,12 @@ describe('Orderbook', () => { // At this point, bidAmount of token0 should be transferred to the public balance of the orderbook and maker // should have 0. - const orderbookBalances0 = await token0.methods + const { result: orderbookBalances0 } = await token0.methods .balance_of_public(orderbook.address) .simulate({ from: makerAddress }); - const makerBalances0 = await token0.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); + const { result: makerBalances0 } = await token0.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); expect(orderbookBalances0).toEqual(bidAmount); expect(makerBalances0).toEqual(0n); }); @@ -142,10 +146,18 @@ describe('Orderbook', () => { expect(orderFulfilledEvents[0].event.order_id).toEqual(orderId); // Verify balances after order fulfillment - const makerBalances0 = await token0.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); - const makerBalances1 = await token1.methods.balance_of_private(makerAddress).simulate({ from: makerAddress }); - const takerBalances0 = await token0.methods.balance_of_private(takerAddress).simulate({ from: takerAddress }); - const takerBalances1 = await token1.methods.balance_of_private(takerAddress).simulate({ from: takerAddress }); + const { result: makerBalances0 } = await token0.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); + const { result: makerBalances1 } = await token1.methods + .balance_of_private(makerAddress) + .simulate({ from: makerAddress }); + const { result: takerBalances0 } = await token0.methods + .balance_of_private(takerAddress) + .simulate({ from: takerAddress }); + const { result: takerBalances1 } = await token1.methods + .balance_of_private(takerAddress) + .simulate({ from: takerAddress }); // Full maker token 0 balance should be transferred to taker and hence maker should have 0 expect(makerBalances0).toEqual(0n); @@ -157,7 +169,9 @@ describe('Orderbook', () => { expect(takerBalances1).toEqual(0n); // Verify that the order is fulfilled - const [_, isFulfilled] = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); + const { + result: [_, isFulfilled], + } = await orderbook.methods.get_order(orderId).simulate({ from: adminAddress }); expect(isFulfilled).toBeTrue(); }); }); diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index fb310765f04e..56f8df05058a 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -56,8 +56,8 @@ describe('e2e_ordering', () => { let pubSetValueSelector: FunctionSelector; beforeEach(async () => { - parent = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress }); - child = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: parent } = await ParentContract.deploy(wallet).send({ from: defaultAccountAddress })); + ({ contract: child } = await ChildContract.deploy(wallet).send({ from: defaultAccountAddress })); pubSetValueSelector = await child.methods.pub_set_value.selector(); }, TIMEOUT); diff --git a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts index a1964ef8f74b..786b2c9aa7a5 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts @@ -285,7 +285,7 @@ describe('e2e_p2p_add_rollup', () => { const aliceAddress = aliceAccountManager.address; - const testContract = await TestContract.deploy(wallet).send({ from: aliceAddress }); + const { contract: testContract } = await TestContract.deploy(wallet).send({ from: aliceAddress }); const [secret, secretHash] = await generateClaimSecret(); @@ -304,7 +304,7 @@ describe('e2e_p2p_add_rollup', () => { // We poll isL1ToL2MessageSynced endpoint until the message is available await retryUntil(async () => await node.isL1ToL2MessageSynced(msgHash), 'message sync', 10); - const receipt = await testContract.methods + const { receipt } = await testContract.methods .create_l2_to_l1_message_arbitrary_recipient_private(contentOutFromRollup, ethRecipient) .send({ from: aliceAddress }); diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index 6e90af2fad87..83e18d1bc4ea 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -323,8 +323,9 @@ export class P2PNetworkTest { throw new Error('Call setupAccount before deploying spam contract'); } - const spamContract = await SpamContract.deploy(this.wallet).send({ from: this.defaultAccountAddress! }); - this.spamContract = spamContract; + ({ contract: this.spamContract } = await SpamContract.deploy(this.wallet).send({ + from: this.defaultAccountAddress!, + })); } async removeInitialNode() { diff --git a/yarn-project/end-to-end/src/e2e_p2p/shared.ts b/yarn-project/end-to-end/src/e2e_p2p/shared.ts index b4bf13758ef9..656313537ec8 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/shared.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/shared.ts @@ -42,7 +42,7 @@ export const submitComplexTxsTo = async ( const spamCount = 15; for (let i = 0; i < numTxs; i++) { const method = spamContract.methods.spam(seed + BigInt(i * spamCount), spamCount, !!opts.callPublic); - const txHash = await method.send({ from, wait: NO_WAIT }); + const { txHash } = await method.send({ from, wait: NO_WAIT }); logger.info(`Tx sent with hash ${txHash.toString()}`); txs.push(txHash); } diff --git a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts index 60960712c619..e69a176d72e7 100644 --- a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts @@ -75,7 +75,7 @@ describe('e2e_pending_note_hashes_contract', () => { const deployContract = async () => { logger.debug(`Deploying L2 contract...`); - contract = await PendingNoteHashesContract.deploy(wallet).send({ from: owner }); + ({ contract } = await PendingNoteHashesContract.deploy(wallet).send({ from: owner })); logger.info(`L2 contract deployed at ${contract.address}`); return contract; }; diff --git a/yarn-project/end-to-end/src/e2e_phase_check.test.ts b/yarn-project/end-to-end/src/e2e_phase_check.test.ts index 03ba19047c61..581611cbbd71 100644 --- a/yarn-project/end-to-end/src/e2e_phase_check.test.ts +++ b/yarn-project/end-to-end/src/e2e_phase_check.test.ts @@ -35,7 +35,7 @@ describe('Phase check', () => { accounts: [defaultAccountAddress], } = await setup(1, { genesisPublicData: [genesisBalanceEntry] })); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); sponsoredFPC = await SponsoredFPCNoEndSetupContract.deploy(wallet).register({ contractAddressSalt: new Fr(SPONSORED_FPC_SALT), }); diff --git a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts index 86809063a08c..6c94f188985a 100644 --- a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts @@ -25,7 +25,7 @@ describe('e2e_voting_contract', () => { accounts: [owner], } = await setup(1)); - votingContract = await PrivateVotingContract.deploy(wallet, owner).send({ from: owner }); + ({ contract: votingContract } = await PrivateVotingContract.deploy(wallet, owner).send({ from: owner })); logger.info(`Counter contract deployed at ${votingContract.address}`); }); diff --git a/yarn-project/end-to-end/src/e2e_prover/client.test.ts b/yarn-project/end-to-end/src/e2e_prover/client.test.ts index 7ff197d01a71..aef6fcd86af6 100644 --- a/yarn-project/end-to-end/src/e2e_prover/client.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/client.test.ts @@ -71,12 +71,14 @@ describe('client_prover', () => { ); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods + .balance_of_private(sender) + .simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/e2e_prover/full.test.ts b/yarn-project/end-to-end/src/e2e_prover/full.test.ts index eb3dc3e82529..7d70a5b443d2 100644 --- a/yarn-project/end-to-end/src/e2e_prover/full.test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/full.test.ts @@ -87,12 +87,14 @@ describe('full_prover', () => { ); // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods + .balance_of_private(sender) + .simulate({ from: sender }); const privateSendAmount = privateBalance / 10n; expect(privateSendAmount).toBeGreaterThan(0n); const privateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); @@ -180,12 +182,12 @@ describe('full_prover', () => { return; } // Create the two transactions - const privateBalance = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); + const { result: privateBalance } = await provenAsset.methods.balance_of_private(sender).simulate({ from: sender }); const privateSendAmount = privateBalance / 20n; expect(privateSendAmount).toBeGreaterThan(0n); const firstPrivateInteraction = provenAsset.methods.transfer(recipient, privateSendAmount); - const publicBalance = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); + const { result: publicBalance } = await provenAsset.methods.balance_of_public(sender).simulate({ from: sender }); const publicSendAmount = publicBalance / 10n; expect(publicSendAmount).toBeGreaterThan(0n); const publicInteraction = provenAsset.methods.transfer_in_public(sender, recipient, publicSendAmount, 0); diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 50a8db649dd6..2c9b8142105c 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -53,7 +53,7 @@ describe('e2e_pruned_blocks', () => { aztecProofSubmissionEpochs: 1024, // effectively do not reorg })); - token = await TokenContract.deploy(wallet, admin, 'TEST', '$TST', 18).send({ from: admin }); + ({ contract: token } = await TokenContract.deploy(wallet, admin, 'TEST', '$TST', 18).send({ from: admin })); logger.info(`L2 token contract deployed at ${token.address}`); }); @@ -76,7 +76,9 @@ describe('e2e_pruned_blocks', () => { // mint transaction that the node will drop the block corresponding to the first mint, resulting in errors if PXE // tried to access any historical information related to it (which it shouldn't). - const firstMintReceipt = await token.methods.mint_to_private(sender, MINT_AMOUNT / 2n).send({ from: admin }); + const { receipt: firstMintReceipt } = await token.methods + .mint_to_private(sender, MINT_AMOUNT / 2n) + .send({ from: admin }); const firstMintTxEffect = await aztecNode.getTxEffect(firstMintReceipt.txHash); // mint_to_private should create just one new note with the minted amount diff --git a/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts b/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts index 708254faba14..551304e259bd 100644 --- a/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_testnet/e2e_public_testnet_transfer.test.ts @@ -60,7 +60,7 @@ describe(`deploys and transfers a private only token`, () => { ); const tokenInstance = await tokenDeployment.getInstance(); await wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey); - const token = await tokenDeployment.send({ + const { contract: token } = await tokenDeployment.send({ from: deployerAddress, // The contract constructor initializes private storage vars that need the contract's own nullifier key. additionalScopes: [tokenInstance.address], @@ -78,8 +78,12 @@ describe(`deploys and transfers a private only token`, () => { logger.info(`Transfer completed`); - const balanceDeployer = await token.methods.get_balance(deployerAddress).simulate({ from: deployerAddress }); - const balanceRecipient = await token.methods.get_balance(recipientAddress).simulate({ from: recipientAddress }); + const { result: balanceDeployer } = await token.methods + .get_balance(deployerAddress) + .simulate({ from: deployerAddress }); + const { result: balanceRecipient } = await token.methods + .get_balance(recipientAddress) + .simulate({ from: recipientAddress }); logger.info(`Deployer balance: ${balanceDeployer}, Recipient balance: ${balanceRecipient}`); diff --git a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts index c84eeadc62b0..b30a5cefdfe8 100644 --- a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts +++ b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts @@ -21,7 +21,7 @@ describe('e2e scope isolation', () => { ({ teardown, wallet, accounts } = await setup(3)); [alice, bob, charlie] = accounts; - contract = await ScopeTestContract.deploy(wallet).send({ from: alice }); + ({ contract } = await ScopeTestContract.deploy(wallet).send({ from: alice })); // Alice and bob create a note for themselves (used by multiple tests below) await contract.methods.create_note(alice, Number(ALICE_NOTE_VALUE)).send({ from: alice }); diff --git a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts index b223bc086f1f..8795dc257a6f 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.parallel.test.ts @@ -112,7 +112,7 @@ describe('e2e_gov_proposal', () => { // Deploy a test contract to send msgs via the outbox, since this increases // gas cost of a proposal, which has triggered oog errors in the past. - testContract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: testContract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); logger.warn(`Deployed test contract at ${testContract.address}`); await cheatCodes.rollup.advanceToEpoch(EpochNumber(4)); @@ -169,11 +169,12 @@ describe('e2e_gov_proposal', () => { // since we wait for the txs to be mined, and do so `roundDuration` times. // Simultaneously, we should be voting for the proposal in every slot. for (let i = 0; i < roundDuration; i++) { - const txHashes = await timesAsync(TXS_PER_BLOCK, () => - testContract.methods + const txHashes = await timesAsync(TXS_PER_BLOCK, async () => { + const { txHash } = await testContract.methods .create_l2_to_l1_message_arbitrary_recipient_private(Fr.random(), EthAddress.random()) - .send({ from: defaultAccountAddress, wait: NO_WAIT }), - ); + .send({ from: defaultAccountAddress, wait: NO_WAIT }); + return txHash; + }); await Promise.all( txHashes.map((hash, j) => { logger.info(`Waiting for tx ${i}-${j}: ${hash} to be mined`); diff --git a/yarn-project/end-to-end/src/e2e_simple.test.ts b/yarn-project/end-to-end/src/e2e_simple.test.ts index 0fcedb023202..1dbfe5861776 100644 --- a/yarn-project/end-to-end/src/e2e_simple.test.ts +++ b/yarn-project/end-to-end/src/e2e_simple.test.ts @@ -72,7 +72,7 @@ describe('e2e_simple', () => { const deployer = new ContractDeployer(artifact, wallet); const sender = ownerAddress; - const txReceipt = await deployer.deploy(ownerAddress, sender, 1).send({ + const { receipt: txReceipt } = await deployer.deploy(ownerAddress, sender, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(BigInt(1)), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index c5b8d6f0635b..22c8a8b89713 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -33,7 +33,7 @@ describe('e2e_state_vars', () => { wallet, accounts: [defaultAccountAddress], } = await setup(1)); - contract = await StateVarsContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await StateVarsContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); @@ -51,7 +51,7 @@ describe('e2e_state_vars', () => { await contract.methods.initialize_public_immutable(1).send({ from: defaultAccountAddress }); - const read = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); + const { result: read } = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); expect(read).toEqual({ account: defaultAccountAddress, value: read.value }); }); @@ -131,7 +131,7 @@ describe('e2e_state_vars', () => { .simulate({ from: defaultAccountAddress }), ).toEqual(false); // Send the transaction and wait for it to be mined (wait function throws if the tx is not mined) - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .initialize_private(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -168,9 +168,9 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE); }); @@ -180,10 +180,10 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const noteBefore = await contract.methods + const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -193,7 +193,7 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const noteAfter = await contract.methods + const { result: noteAfter } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); @@ -206,7 +206,7 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS + 2n, VALUE + 1n) .send({ from: defaultAccountAddress }); @@ -216,9 +216,9 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE + 1n); }); @@ -228,10 +228,12 @@ describe('e2e_state_vars', () => { .is_private_mutable_initialized(defaultAccountAddress) .simulate({ from: defaultAccountAddress }), ).toEqual(true); - const noteBefore = await contract.methods + const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - const txReceipt = await contract.methods.increase_private_value().send({ from: defaultAccountAddress }); + const { receipt: txReceipt } = await contract.methods + .increase_private_value() + .send({ from: defaultAccountAddress }); const txEffects = await aztecNode.getTxEffect(txReceipt.txHash); @@ -239,9 +241,9 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the nullifier of the previous note expect(txEffects?.data.nullifiers.length).toEqual(2); - const { value } = await contract.methods - .get_private_mutable(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }); + const { + result: { value }, + } = await contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }); expect(value).toEqual(noteBefore.value + 1n); }); }); @@ -260,7 +262,7 @@ describe('e2e_state_vars', () => { expect( await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), ).toEqual(false); - const txReceipt = await contract.methods + const { receipt: txReceipt } = await contract.methods .initialize_private_immutable(RANDOMNESS, VALUE) .send({ from: defaultAccountAddress }); @@ -290,7 +292,9 @@ describe('e2e_state_vars', () => { expect( await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), ).toEqual(true); - const { value } = await contract.methods + const { + result: { value }, + } = await contract.methods .view_private_immutable(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); expect(value).toEqual(VALUE); @@ -310,9 +314,9 @@ describe('e2e_state_vars', () => { beforeAll(async () => { // We use the auth contract here because has a nice, clear, simple implementation of Delayed Public Mutable - authContract = await AuthContract.deploy(wallet, defaultAccountAddress).send({ + ({ contract: authContract } = await AuthContract.deploy(wallet, defaultAccountAddress).send({ from: defaultAccountAddress, - }); + })); if (aztecSlotDuration !== 72) { throw new Error( diff --git a/yarn-project/end-to-end/src/e2e_static_calls.test.ts b/yarn-project/end-to-end/src/e2e_static_calls.test.ts index 9576d39f7845..1e2cc3117f1b 100644 --- a/yarn-project/end-to-end/src/e2e_static_calls.test.ts +++ b/yarn-project/end-to-end/src/e2e_static_calls.test.ts @@ -21,8 +21,8 @@ describe('e2e_static_calls', () => { accounts: [owner], } = await setup()); sender = owner; - parentContract = await StaticParentContract.deploy(wallet).send({ from: owner }); - childContract = await StaticChildContract.deploy(wallet).send({ from: owner }); + ({ contract: parentContract } = await StaticParentContract.deploy(wallet).send({ from: owner })); + ({ contract: childContract } = await StaticChildContract.deploy(wallet).send({ from: owner })); // We create a note in the set, such that later reads doesn't fail due to get_notes returning 0 notes await childContract.methods.private_set_value(42n, owner, sender).send({ from: owner }); diff --git a/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts b/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts index cdee4c492b29..70c6ce8b0b8f 100644 --- a/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts +++ b/yarn-project/end-to-end/src/e2e_storage_proof/e2e_storage_proof.test.ts @@ -13,7 +13,7 @@ describe('Storage proof', () => { beforeAll(async () => { ctx = await setup(1); - contract = await StorageProofTestContract.deploy(ctx.wallet).send({ from: ctx.accounts[0] }); + ({ contract } = await StorageProofTestContract.deploy(ctx.wallet).send({ from: ctx.accounts[0] })); }); afterAll(async () => { @@ -26,7 +26,7 @@ describe('Storage proof', () => { ctx.logger.info('Sending storage proof TX...'); - const receipt = await contract.methods + const { receipt } = await contract.methods .storage_proof(ethAddress, slotKey, slotContents, root) .with({ capsules }) .send({ from: ctx.accounts[0] }); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index 76a54cd05832..c93560f43064 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -203,7 +203,7 @@ class TestVariant { ); this.contractAddresses.push(accountManager.address); const deployMethod = await accountManager.getDeployMethod(); - const txHash = await deployMethod.send({ + const { txHash } = await deployMethod.send({ from: deployAccount, skipClassPublication: true, skipInstancePublication: true, @@ -218,7 +218,9 @@ class TestVariant { for (let i = 0; i < this.txCount; i++) { const recipient = this.accounts[(i + 1) % this.txCount]; const tk = TokenContract.at(this.token.address, this.wallet); - txHashes.push(await tk.methods.transfer(recipient, 1n).send({ from: this.accounts[i], wait: NO_WAIT })); + txHashes.push( + (await tk.methods.transfer(recipient, 1n).send({ from: this.accounts[i], wait: NO_WAIT })).txHash, + ); } return txHashes; } else if (this.txComplexity == TxComplexity.PublicTransfer) { @@ -229,7 +231,7 @@ class TestVariant { const recipient = this.accounts[(i + 1) % this.txCount]; const tk = TokenContract.at(this.token.address, this.wallet); txHashes.push( - await tk.methods.transfer_in_public(sender, recipient, 1n, 0).send({ from: sender, wait: NO_WAIT }), + (await tk.methods.transfer_in_public(sender, recipient, 1n, 0).send({ from: sender, wait: NO_WAIT })).txHash, ); } return txHashes; @@ -247,7 +249,7 @@ class TestVariant { ]); this.seed += 100n; - txHashes.push(await batch.send({ from: this.accounts[0], wait: NO_WAIT })); + txHashes.push((await batch.send({ from: this.accounts[0], wait: NO_WAIT })).txHash); } return txHashes; } else { @@ -340,10 +342,16 @@ describe('e2e_synching', () => { variant.setWallet(wallet); // Deploy a token, such that we could use it - const token = await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + const { contract: token } = await TokenContract.deploy( + wallet, + defaultAccountAddress, + 'TestToken', + 'TST', + 18n, + ).send({ from: defaultAccountAddress, }); - const spam = await SpamContract.deploy(wallet).send({ from: defaultAccountAddress }); + const { contract: spam } = await SpamContract.deploy(wallet).send({ from: defaultAccountAddress }); variant.setToken(token); variant.setSpam(spam); @@ -542,15 +550,21 @@ describe('e2e_synching', () => { const defaultAccountAddress = (await variant.deployAccounts(opts.initialFundedAccounts!.slice(0, 1)))[0]; contracts.push( - await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ - from: defaultAccountAddress, - }), + ( + await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + from: defaultAccountAddress, + }) + ).contract, + ); + contracts.push( + (await SchnorrHardcodedAccountContract.deploy(wallet).send({ from: defaultAccountAddress })).contract, ); - contracts.push(await SchnorrHardcodedAccountContract.deploy(wallet).send({ from: defaultAccountAddress })); contracts.push( - await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ - from: defaultAccountAddress, - }), + ( + await TokenContract.deploy(wallet, defaultAccountAddress, 'TestToken', 'TST', 18n).send({ + from: defaultAccountAddress, + }) + ).contract, ); await watcher.stop(); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts index 1c9b670771ca..b9760a983627 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/burn.test.ts @@ -27,7 +27,7 @@ describe('e2e_token_contract burn', () => { describe('public', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_public(adminAddress, amount, 0).send({ from: adminAddress }); @@ -36,7 +36,7 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -61,7 +61,9 @@ describe('e2e_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -70,7 +72,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); const authwitNonce = 1; @@ -82,7 +86,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -91,7 +97,9 @@ describe('e2e_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -109,7 +117,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -132,7 +142,9 @@ describe('e2e_token_contract burn', () => { describe('private', () => { it('burn less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.burn_private(adminAddress, amount, 0).send({ from: adminAddress }); @@ -140,7 +152,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -160,7 +174,9 @@ describe('e2e_token_contract burn', () => { describe('failure cases', () => { it('burn more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -169,7 +185,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -180,7 +198,9 @@ describe('e2e_token_contract burn', () => { }); it('burn more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -195,7 +215,9 @@ describe('e2e_token_contract burn', () => { }); it('burn on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -214,7 +236,9 @@ describe('e2e_token_contract burn', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index a986ab6799ba..90bc22162730 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -23,7 +23,9 @@ describe('e2e_token_contract private transfer recursion', () => { // itself to consume them all (since it retrieves 2 notes on the first pass and 8 in each subsequent pass). const totalNotes = 16; const totalBalance = await mintNotes(wallet, adminAddress, adminAddress, asset, Array(totalNotes).fill(10n)); - const txReceipt = await asset.methods.transfer(account1Address, totalBalance).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods + .transfer(account1Address, totalBalance) + .send({ from: adminAddress }); const txEffects = await node.getTxEffect(txReceipt.txHash); // We should have nullified all notes, plus an extra nullifier for the transaction and one for the event commitment. @@ -59,7 +61,7 @@ describe('e2e_token_contract private transfer recursion', () => { const totalBalance = await mintNotes(wallet, adminAddress, adminAddress, asset, noteAmounts); const toSend = totalBalance - expectedChange; - const txReceipt = await asset.methods.transfer(account1Address, toSend).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods.transfer(account1Address, toSend).send({ from: adminAddress }); const txEffects = await node.getTxEffect(txReceipt.txHash); // We should have nullified all notes, plus an extra nullifier for the transaction and one for the event commitment. @@ -93,7 +95,9 @@ describe('e2e_token_contract private transfer recursion', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts index a564a5610fed..ef69b7556739 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts @@ -22,24 +22,30 @@ describe('e2e_token_contract reading constants', () => { }); it('check name private', async () => { - const name = readFieldCompressedString(await t.asset.methods.private_get_name().simulate({ from: t.adminAddress })); + const name = readFieldCompressedString( + (await t.asset.methods.private_get_name().simulate({ from: t.adminAddress })).result, + ); expect(name).toBe(TOKEN_NAME); }); it('check name public', async () => { - const name = readFieldCompressedString(await t.asset.methods.public_get_name().simulate({ from: t.adminAddress })); + const name = readFieldCompressedString( + (await t.asset.methods.public_get_name().simulate({ from: t.adminAddress })).result, + ); expect(name).toBe(TOKEN_NAME); }); it('check symbol private', async () => { const sym = readFieldCompressedString( - await t.asset.methods.private_get_symbol().simulate({ from: t.adminAddress }), + (await t.asset.methods.private_get_symbol().simulate({ from: t.adminAddress })).result, ); expect(sym).toBe(TOKEN_SYMBOL); }); it('check symbol public', async () => { - const sym = readFieldCompressedString(await t.asset.methods.public_get_symbol().simulate({ from: t.adminAddress })); + const sym = readFieldCompressedString( + (await t.asset.methods.public_get_symbol().simulate({ from: t.adminAddress })).result, + ); expect(sym).toBe(TOKEN_SYMBOL); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index ebfde8461643..f6462de40520 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -81,24 +81,28 @@ export class TokenContractTest { await publicDeployAccounts(this.wallet, [this.adminAddress, this.account1Address]); this.logger.verbose(`Deploying TokenContract...`); - this.asset = await TokenContract.deploy( + ({ contract: this.asset } = await TokenContract.deploy( this.wallet, this.adminAddress, TokenContractTest.TOKEN_NAME, TokenContractTest.TOKEN_SYMBOL, TokenContractTest.TOKEN_DECIMALS, - ).send({ from: this.adminAddress }); + ).send({ from: this.adminAddress })); this.logger.verbose(`Token deployed to ${this.asset.address}`); this.logger.verbose(`Deploying bad account...`); - this.badAccount = await InvalidAccountContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.badAccount } = await InvalidAccountContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.badAccount.address}.`); // Deploy a proxy contract for "on behalf of other" tests. The note owner must be the tx sender // (so their notes are in scope), but msg_sender in the target must differ from the note owner // to trigger authwit validation. The proxy forwards calls so that msg_sender != tx sender. this.logger.verbose(`Deploying generic proxy...`); - this.authwitProxy = await GenericProxyContract.deploy(this.wallet).send({ from: this.adminAddress }); + ({ contract: this.authwitProxy } = await GenericProxyContract.deploy(this.wallet).send({ + from: this.adminAddress, + })); this.logger.verbose(`Deployed to ${this.authwitProxy.address}.`); this.tokenSim = new TokenSimulator(this.asset, this.wallet, this.adminAddress, this.logger, [ @@ -140,7 +144,9 @@ export class TokenContractTest { await asset.methods.mint_to_public(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPublic(adminAddress, amount); - const publicBalance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: publicBalance } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(adminAddress)); @@ -148,11 +154,13 @@ export class TokenContractTest { await mintTokensToPrivate(asset, adminAddress, adminAddress, amount); tokenSim.mintPrivate(adminAddress, amount); - const privateBalance = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: privateBalance } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(adminAddress)); - const totalSupply = await asset.methods.total_supply().simulate({ from: adminAddress }); + const { result: totalSupply } = await asset.methods.total_supply().simulate({ from: adminAddress }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts index d801532f8478..564707a74c77 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer.test.ts @@ -24,11 +24,11 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); - const txReceipt = await asset.methods.transfer(account1Address, amount).send({ from: adminAddress }); + const { receipt: txReceipt } = await asset.methods.transfer(account1Address, amount).send({ from: adminAddress }); tokenSim.transferPrivate(adminAddress, account1Address, amount); const events = await wallet.getPrivateEvents(TokenContract.events.Transfer, { @@ -53,7 +53,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer less than balance to non-deployed account', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); @@ -68,7 +68,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer to self', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer(adminAddress, amount).send({ from: adminAddress }); @@ -77,7 +77,9 @@ describe('e2e_token_contract transfer private', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 + 1n; expect(amount).toBeGreaterThan(0n); await expect(asset.methods.transfer(account1Address, amount).simulate({ from: adminAddress })).rejects.toThrow( diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts index 6177d2845832..3ea047560895 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts @@ -25,7 +25,7 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -45,7 +45,9 @@ describe('e2e_token_contract transfer private', () => { describe('failure cases', () => { it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 - 1n; expect(amount).toBeGreaterThan(0n); await expect( @@ -61,8 +63,12 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_private(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -88,7 +94,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other without approval', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -107,7 +115,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -129,7 +139,9 @@ describe('e2e_token_contract transfer private', () => { }); it('transfer on behalf of other, cancelled authwit', async () => { - const balance0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balance0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts index 689a99d61bc9..3bb951b28192 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts @@ -43,7 +43,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer less than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_in_public(adminAddress, account1Address, amount, 0).send({ from: adminAddress }); @@ -52,7 +52,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer to self', async () => { - const balance = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance / 2n; expect(amount).toBeGreaterThan(0n); await asset.methods.transfer_in_public(adminAddress, adminAddress, amount, 0).send({ from: adminAddress }); @@ -61,7 +61,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -90,7 +90,7 @@ describe('e2e_token_contract transfer public', () => { describe('failure cases', () => { it('transfer more than balance', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = 0; await expect( @@ -101,7 +101,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of self with non-zero nonce', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 - 1n; const authwitNonce = 1; await expect( @@ -114,7 +114,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other without "approval"', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); await expect( @@ -125,8 +125,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer more than balance on behalf of other', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 1n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -152,8 +154,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -178,8 +182,10 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, wrong designated caller', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); - const balance1 = await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance1 } = await asset.methods + .balance_of_public(account1Address) + .simulate({ from: account1Address }); const amount = balance0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -203,7 +209,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, cancelled authwit', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); @@ -232,7 +238,7 @@ describe('e2e_token_contract transfer public', () => { }); it('transfer on behalf of other, cancelled authwit, flow 2', async () => { - const balance0 = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balance0 } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balance0 / 2n; expect(amount).toBeGreaterThan(0n); const authwitNonce = Fr.random(); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts index 051ba9370fce..2688fea57172 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_private.test.ts @@ -22,7 +22,7 @@ describe('e2e_token_contract transfer_to_private', () => { }); it('to self', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); @@ -34,7 +34,7 @@ describe('e2e_token_contract transfer_to_private', () => { }); it('to someone else', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); const amount = balancePub / 2n; expect(amount).toBeGreaterThan(0n); @@ -47,7 +47,9 @@ describe('e2e_token_contract transfer_to_private', () => { describe('failure cases', () => { it('to self (more than balance)', async () => { - const balancePub = await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress }); + const { result: balancePub } = await asset.methods + .balance_of_public(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePub + 1n; expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts index 5394130c2429..1ff6f31ef53b 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_to_public.test.ts @@ -26,7 +26,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of self', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv / 2n; expect(amount).toBeGreaterThan(0n); @@ -36,7 +38,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 / 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -56,7 +60,9 @@ describe('e2e_token_contract transfer_to_public', () => { describe('failure cases', () => { it('on behalf of self (more than balance)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -66,7 +72,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of self (invalid authwit nonce)', async () => { - const balancePriv = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv + 1n; expect(amount).toBeGreaterThan(0n); @@ -78,7 +86,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other (more than balance)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); @@ -93,7 +103,9 @@ describe('e2e_token_contract transfer_to_public', () => { }); it('on behalf of other (invalid designated caller)', async () => { - const balancePriv0 = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: balancePriv0 } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); const amount = balancePriv0 + 2n; const authwitNonce = Fr.random(); expect(amount).toBeGreaterThan(0n); diff --git a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts index 6dad123c594a..59f38bfeedea 100644 --- a/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/fixtures/e2e_prover_test.ts @@ -106,7 +106,9 @@ export class FullProverTest { await publicDeployAccounts(this.wallet, this.accounts.slice(0, 2)); this.logger.info('Applying base setup: deploying token contract'); - const { contract: asset, instance } = await TokenContract.deploy( + const { + receipt: { contract: asset, instance }, + } = await TokenContract.deploy( this.wallet, this.accounts[0], FullProverTest.TOKEN_NAME, @@ -121,7 +123,7 @@ export class FullProverTest { this.tokenSim = new TokenSimulator(this.fakeProofsAsset, this.wallet, this.accounts[0], this.logger, this.accounts); - expect(await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).toBe( + expect((await this.fakeProofsAsset.methods.get_admin().simulate({ from: this.accounts[0] })).result).toBe( this.accounts[0].toBigInt(), ); } @@ -310,16 +312,20 @@ export class FullProverTest { } = this; tokenSim.mintPublic(address, publicAmount); - const publicBalance = await fakeProofsAsset.methods.balance_of_public(address).simulate({ from: address }); + const { result: publicBalance } = await fakeProofsAsset.methods + .balance_of_public(address) + .simulate({ from: address }); this.logger.verbose(`Public balance of wallet 0: ${publicBalance}`); expect(publicBalance).toEqual(this.tokenSim.balanceOfPublic(address)); tokenSim.mintPrivate(address, publicAmount); - const privateBalance = await fakeProofsAsset.methods.balance_of_private(address).simulate({ from: address }); + const { result: privateBalance } = await fakeProofsAsset.methods + .balance_of_private(address) + .simulate({ from: address }); this.logger.verbose(`Private balance of wallet 0: ${privateBalance}`); expect(privateBalance).toEqual(tokenSim.balanceOfPrivate(address)); - const totalSupply = await fakeProofsAsset.methods.total_supply().simulate({ from: address }); + const { result: totalSupply } = await fakeProofsAsset.methods.total_supply().simulate({ from: address }); this.logger.verbose(`Total supply: ${totalSupply}`); expect(totalSupply).toEqual(tokenSim.totalSupply); } diff --git a/yarn-project/end-to-end/src/fixtures/setup.ts b/yarn-project/end-to-end/src/fixtures/setup.ts index 517da8f7498e..acb06a638f73 100644 --- a/yarn-project/end-to-end/src/fixtures/setup.ts +++ b/yarn-project/end-to-end/src/fixtures/setup.ts @@ -753,7 +753,9 @@ export function getBalancesFn( ): (...addresses: (AztecAddress | { address: AztecAddress })[]) => Promise { const balances = async (...addressLikes: (AztecAddress | { address: AztecAddress })[]) => { const addresses = addressLikes.map(addressLike => ('address' in addressLike ? addressLike.address : addressLike)); - const b = await Promise.all(addresses.map(address => method(address).simulate({ from: address }))); + const b = await Promise.all( + addresses.map(async address => (await method(address).simulate({ from: address })).result), + ); const debugString = `${symbol} balances: ${addresses.map((address, i) => `${address}: ${b[i]}`).join(', ')}`; logger.verbose(debugString); return b; @@ -871,7 +873,7 @@ export async function publicDeployAccounts( const batch = new BatchCall(wallet, calls); - const txReceipt = await batch.send({ from: accountsToDeploy[0] }); + const { receipt: txReceipt } = await batch.send({ from: accountsToDeploy[0] }); if (waitUntilProven) { if (!node) { throw new Error('Need to provide an AztecNode to wait for proven.'); diff --git a/yarn-project/end-to-end/src/fixtures/token_utils.ts b/yarn-project/end-to-end/src/fixtures/token_utils.ts index aabcf5d6a215..a0d67993b7eb 100644 --- a/yarn-project/end-to-end/src/fixtures/token_utils.ts +++ b/yarn-project/end-to-end/src/fixtures/token_utils.ts @@ -6,7 +6,9 @@ import { TokenContract } from '@aztec/noir-contracts.js/Token'; export async function deployToken(wallet: Wallet, admin: AztecAddress, initialAdminBalance: bigint, logger: Logger) { logger.info(`Deploying Token contract...`); - const { contract, instance } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({ + const { + receipt: { contract, instance }, + } = await TokenContract.deploy(wallet, admin, 'TokenName', 'TokenSymbol', 18).send({ from: admin, wait: { returnReceipt: true }, }); @@ -39,7 +41,7 @@ export async function expectTokenBalance( ) { // Then check the balance const contractWithWallet = TokenContract.at(token.address, wallet); - const balance = await contractWithWallet.methods.balance_of_private(owner).simulate({ from: owner }); + const { result: balance } = await contractWithWallet.methods.balance_of_private(owner).simulate({ from: owner }); logger.info(`Account ${owner} balance: ${balance}`); expect(balance).toBe(expectedBalance); } diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 9eaf2473b961..b9f1893bf500 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -73,7 +73,7 @@ describe('guides/writing_an_account_contract', () => { const address = account.address; logger.info(`Deployed account contract at ${address}`); - const token = await TokenContract.deploy(wallet, fundedAccount, 'TokenName', 'TokenSymbol', 18).send({ + const { contract: token } = await TokenContract.deploy(wallet, fundedAccount, 'TokenName', 'TokenSymbol', 18).send({ from: fundedAccount, }); logger.info(`Deployed token contract at ${token.address}`); @@ -81,7 +81,7 @@ describe('guides/writing_an_account_contract', () => { const mintAmount = 50n; await token.methods.mint_to_private(address, mintAmount).send({ from: fundedAccount }); - const balance = await token.methods.balance_of_private(address).simulate({ from: address }); + const { result: balance } = await token.methods.balance_of_private(address).simulate({ from: address }); logger.info(`Balance of wallet is now ${balance}`); expect(balance).toEqual(50n); diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index be4564b2a67d..c1b2b39780eb 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -73,22 +73,26 @@ export async function deployAndInitializeTokenAndBridgeContracts( }); // deploy l2 token - const token = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ from: owner }); + const { contract: token } = await TokenContract.deploy(wallet, owner, 'TokenName', 'TokenSymbol', 18).send({ + from: owner, + }); // deploy l2 token bridge and attach to the portal - const bridge = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({ from: owner }); + const { contract: bridge } = await TokenBridgeContract.deploy(wallet, token.address, tokenPortalAddress).send({ + from: owner, + }); - if ((await token.methods.get_admin().simulate({ from: owner })) !== owner.toBigInt()) { + if ((await token.methods.get_admin().simulate({ from: owner })).result !== owner.toBigInt()) { throw new Error(`Token admin is not ${owner}`); } - if (!(await bridge.methods.get_config().simulate({ from: owner })).token.equals(token.address)) { + if (!(await bridge.methods.get_config().simulate({ from: owner })).result.token.equals(token.address)) { throw new Error(`Bridge token is not ${token.address}`); } // make the bridge a minter on the token: await token.methods.set_minter(bridge.address, true).send({ from: owner }); - if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })) === 1n) { + if ((await token.methods.is_minter(bridge.address).simulate({ from: owner })).result === 1n) { throw new Error(`Bridge is not a minter`); } @@ -269,7 +273,7 @@ export class CrossChainTestHarness { authwitNonce: Fr = Fr.ZERO, authWitness: AuthWitness, ): Promise { - const withdrawReceipt = await this.l2Bridge.methods + const { receipt: withdrawReceipt } = await this.l2Bridge.methods .exit_to_l1_private(this.l2Token.address, this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ authWitnesses: [authWitness], from: this.ownerAddress }); @@ -277,7 +281,7 @@ export class CrossChainTestHarness { } async withdrawPublicFromAztecToL1(withdrawAmount: bigint, authwitNonce: Fr = Fr.ZERO): Promise { - const withdrawReceipt = await this.l2Bridge.methods + const { receipt: withdrawReceipt } = await this.l2Bridge.methods .exit_to_l1_public(this.ethAccount, withdrawAmount, EthAddress.ZERO, authwitNonce) .send({ from: this.ownerAddress }); @@ -285,7 +289,7 @@ export class CrossChainTestHarness { } async getL2PrivateBalanceOf(owner: AztecAddress) { - return await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner }); + return (await this.l2Token.methods.balance_of_private(owner).simulate({ from: owner })).result; } async expectPrivateBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { @@ -295,7 +299,7 @@ export class CrossChainTestHarness { } async getL2PublicBalanceOf(owner: AztecAddress) { - return await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress }); + return (await this.l2Token.methods.balance_of_public(owner).simulate({ from: this.ownerAddress })).result; } async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { diff --git a/yarn-project/end-to-end/src/shared/submit-transactions.ts b/yarn-project/end-to-end/src/shared/submit-transactions.ts index c77fb97e8799..b8b6c5c1a11e 100644 --- a/yarn-project/end-to-end/src/shared/submit-transactions.ts +++ b/yarn-project/end-to-end/src/shared/submit-transactions.ts @@ -19,10 +19,7 @@ export const submitTxsTo = async ( times(numTxs, async () => { const accountManager = await wallet.createSchnorrAccount(Fr.random(), Fr.random(), GrumpkinScalar.random()); const deployMethod = await accountManager.getDeployMethod(); - const txHash = await deployMethod.send({ - from: submitter, - wait: NO_WAIT, - }); + const { txHash } = await deployMethod.send({ from: submitter, wait: NO_WAIT }); logger.info(`Tx sent with hash ${txHash}`); const receipt: TxReceipt = await wallet.getTxReceipt(txHash); diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 7f25e1a2b4eb..6795cc1bf180 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -130,7 +130,9 @@ export const uniswapL1L2TestSuite = ( client: l1Client, }); // deploy l2 uniswap contract and attach to portal - uniswapL2Contract = await UniswapContract.deploy(wallet, uniswapPortalAddress).send({ from: ownerAddress }); + ({ contract: uniswapL2Contract } = await UniswapContract.deploy(wallet, uniswapPortalAddress).send({ + from: ownerAddress, + })); const registryAddress = (await aztecNode.getNodeInfo()).l1ContractAddresses.registryAddress; @@ -195,7 +197,7 @@ export const uniswapL1L2TestSuite = ( logger.info('Withdrawing weth to L1 and sending message to swap to dai'); const [secretForDepositingSwappedDai, secretHashForDepositingSwappedDai] = await generateClaimSecret(); - const l2UniswapInteractionReceipt = await uniswapL2Contract.methods + const { receipt: l2UniswapInteractionReceipt } = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, wethCrossChainHarness.l2Bridge.address, @@ -787,7 +789,7 @@ export const uniswapL1L2TestSuite = ( logger.info('Withdrawing weth to L1 and sending message to swap to dai'); const [, secretHashForDepositingSwappedDai] = await generateClaimSecret(); - const withdrawReceipt = await uniswapL2Contract.methods + const { receipt: withdrawReceipt } = await uniswapL2Contract.methods .swap_private( wethCrossChainHarness.l2Token.address, wethCrossChainHarness.l2Bridge.address, @@ -915,7 +917,7 @@ export const uniswapL1L2TestSuite = ( // Call swap_public on L2 const secretHashForDepositingSwappedDai = Fr.random(); - const withdrawReceipt = await uniswapL2Contract.methods + const { receipt: withdrawReceipt } = await uniswapL2Contract.methods .swap_public( ownerAddress, wethCrossChainHarness.l2Bridge.address, diff --git a/yarn-project/end-to-end/src/simulators/lending_simulator.ts b/yarn-project/end-to-end/src/simulators/lending_simulator.ts index fe10d7ed2276..404bb3d5ad8d 100644 --- a/yarn-project/end-to-end/src/simulators/lending_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/lending_simulator.ts @@ -186,14 +186,16 @@ export class LendingSimulator { expect(this.borrowed).toEqual(this.stableCoin.totalSupply - this.mintedOutside); - const asset = await this.lendingContract.methods.get_asset(0).simulate({ from: this.account.address }); + const { result: asset } = await this.lendingContract.methods.get_asset(0).simulate({ from: this.account.address }); const interestAccumulator = asset['interest_accumulator']; expect(interestAccumulator).toEqual(this.accumulator); expect(asset['last_updated_ts']).toEqual(BigInt(this.time)); for (const key of [this.account.address, AztecAddress.fromField(await this.account.key())]) { - const privatePos = await this.lendingContract.methods.get_position(key).simulate({ from: this.account.address }); + const { result: privatePos } = await this.lendingContract.methods + .get_position(key) + .simulate({ from: this.account.address }); expect(new Fr(privatePos['collateral'])).toEqual(this.collateral[key.toString()] ?? Fr.ZERO); expect(new Fr(privatePos['static_debt'])).toEqual(this.staticDebt[key.toString()] ?? Fr.ZERO); expect(privatePos['debt']).toEqual( diff --git a/yarn-project/end-to-end/src/spartan/1tps.test.ts b/yarn-project/end-to-end/src/spartan/1tps.test.ts index b771d4621308..6d939deb5886 100644 --- a/yarn-project/end-to-end/src/spartan/1tps.test.ts +++ b/yarn-project/end-to-end/src/spartan/1tps.test.ts @@ -63,7 +63,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); @@ -74,16 +75,20 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); const defaultAccountAddress = testAccounts.accounts[0]; @@ -128,7 +133,7 @@ describe('token transfer test', () => { }), ); - const recipientBalance = await testAccounts.tokenContract.methods + const { result: recipientBalance } = await testAccounts.tokenContract.methods .balance_of_public(recipient) .simulate({ from: testAccounts.tokenAdminAddress }); logger.info(`recipientBalance: ${recipientBalance}`); diff --git a/yarn-project/end-to-end/src/spartan/4epochs.test.ts b/yarn-project/end-to-end/src/spartan/4epochs.test.ts index b2dcdd2ca987..a0d42328fb7a 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -68,7 +68,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); logger.info(`Token name verified: ${name}`); @@ -85,18 +86,22 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } logger.info('Minted tokens'); expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); // For each round, make both private and public transfers @@ -132,16 +137,20 @@ describe('token transfer test', () => { for (const acc of testAccounts.accounts) { expect(MINT_AMOUNT - ROUNDS * transferAmount).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(ROUNDS * transferAmount * BigInt(testAccounts.accounts.length)).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); }); }); diff --git a/yarn-project/end-to-end/src/spartan/n_tps.test.ts b/yarn-project/end-to-end/src/spartan/n_tps.test.ts index d7a774b5fc3b..f2f452f20074 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps.test.ts @@ -306,10 +306,10 @@ describe('sustained N TPS test', () => { logger.info('Deploying benchmark contract...'); const sponsor = new SponsoredFeePaymentMethod(await getSponsoredFPCAddress()); - benchmarkContract = await BenchmarkingContract.deploy(localTestAccounts[0].wallet).send({ + ({ contract: benchmarkContract } = await BenchmarkingContract.deploy(localTestAccounts[0].wallet).send({ from: localTestAccounts[0].recipientAddress, fee: { paymentMethod: sponsor }, - }); + })); logger.info('Benchmark contract deployed', { address: benchmarkContract.address.toString() }); logger.info(`Test setup complete`); diff --git a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts index 5c1750c41b24..e5db55329823 100644 --- a/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts +++ b/yarn-project/end-to-end/src/spartan/n_tps_prove.test.ts @@ -304,10 +304,10 @@ describe(`prove ${TARGET_TPS}TPS test`, () => { } logger.info('Deploying benchmark contract...'); - benchmarkContract = await BenchmarkingContract.deploy(wallets[0]).send({ + ({ contract: benchmarkContract } = await BenchmarkingContract.deploy(wallets[0]).send({ from: accountAddresses[0], fee: { paymentMethod: sponsor }, - }); + })); logger.info('Test setup complete'); }); diff --git a/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts b/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts index 3c740fdba873..435341a8b4af 100644 --- a/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts +++ b/yarn-project/end-to-end/src/spartan/reqresp_effectiveness.test.ts @@ -71,7 +71,8 @@ describe('reqresp effectiveness under tx drop', () => { testAccounts = await deploySponsoredTestAccountsWithTokens(wallet, aztecNode, MINT_AMOUNT, logger); recipient = testAccounts.recipientAddress; const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index 8009d474e601..e5deb3f6044d 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -310,13 +310,9 @@ async function deployTokenAndMint( logger: Logger, ) { logger.verbose(`Deploying TokenContract...`); - const { contract: tokenContract } = await TokenContract.deploy( - wallet, - admin, - TOKEN_NAME, - TOKEN_SYMBOL, - TOKEN_DECIMALS, - ).send({ + const { + receipt: { contract: tokenContract }, + } = await TokenContract.deploy(wallet, admin, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS).send({ from: admin, fee: { paymentMethod, diff --git a/yarn-project/end-to-end/src/spartan/transfer.test.ts b/yarn-project/end-to-end/src/spartan/transfer.test.ts index b3fda8fb1186..7db4b2c0e2aa 100644 --- a/yarn-project/end-to-end/src/spartan/transfer.test.ts +++ b/yarn-project/end-to-end/src/spartan/transfer.test.ts @@ -52,7 +52,8 @@ describe('token transfer test', () => { it('can get info', async () => { const name = readFieldCompressedString( - await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress }), + (await testAccounts.tokenContract.methods.private_get_name().simulate({ from: testAccounts.tokenAdminAddress })) + .result, ); expect(name).toBe(testAccounts.tokenName); }); @@ -63,16 +64,20 @@ describe('token transfer test', () => { for (const a of testAccounts.accounts) { expect(MINT_AMOUNT).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(a) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(a) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); } expect(0n).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); // For each round, make both private and public transfers @@ -94,14 +99,16 @@ describe('token transfer test', () => { for (const a of testAccounts.accounts) { expect(MINT_AMOUNT - ROUNDS * transferAmount).toBe( - await testAccounts.tokenContract.methods.balance_of_public(a).simulate({ from: a }), + (await testAccounts.tokenContract.methods.balance_of_public(a).simulate({ from: a })).result, ); } expect(ROUNDS * transferAmount * BigInt(testAccounts.accounts.length)).toBe( - await testAccounts.tokenContract.methods - .balance_of_public(recipient) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(recipient) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ); }); }); diff --git a/yarn-project/stdlib/src/tx/simulated_tx.ts b/yarn-project/stdlib/src/tx/simulated_tx.ts index 3ad4e7b73c4e..5fb1ef4b8bd4 100644 --- a/yarn-project/stdlib/src/tx/simulated_tx.ts +++ b/yarn-project/stdlib/src/tx/simulated_tx.ts @@ -12,9 +12,11 @@ import { Gas } from '../gas/gas.js'; import type { GasUsed } from '../gas/gas_used.js'; import { PrivateKernelTailCircuitPublicInputs } from '../kernel/private_kernel_tail_circuit_public_inputs.js'; import { ChonkProof } from '../proofs/chonk_proof.js'; +import type { OffchainEffect } from './offchain_effect.js'; import { PrivateCallExecutionResult, PrivateExecutionResult, + collectOffchainEffects, collectSortedContractClassLogs, } from './private_execution_result.js'; import { type SimulationStats, SimulationStatsSchema } from './profiling.js'; @@ -84,6 +86,11 @@ export class TxSimulationResult { public stats?: SimulationStats, ) {} + /** Returns offchain effects collected from private execution. */ + get offchainEffects(): OffchainEffect[] { + return collectOffchainEffects(this.privateExecutionResult); + } + get gasUsed(): GasUsed { return ( this.publicOutput?.gasUsed ?? { @@ -106,7 +113,7 @@ export class TxSimulationResult { .transform(TxSimulationResult.from); } - static from(fields: Omit, 'gasUsed'>) { + static from(fields: Omit, 'gasUsed' | 'offchainEffects'>) { return new TxSimulationResult( fields.privateExecutionResult, fields.publicInputs, diff --git a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts index f45118ed2773..8551e012e7e7 100644 --- a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts +++ b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts @@ -383,6 +383,7 @@ export abstract class BaseWallet implements Wallet { const feeOptions = await this.completeFeeOptions(opts.from, executionPayload.feePayer, opts.fee?.gasSettings); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(executionPayload, opts.from, feeOptions); const provenTx = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes)); + const offchainEffects = provenTx.getOffchainEffects(); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { @@ -396,7 +397,7 @@ export abstract class BaseWallet implements Wallet { // If wait is NO_WAIT, return txHash immediately if (opts.wait === NO_WAIT) { - return txHash as SendReturn; + return { txHash, offchainEffects } as SendReturn; } // Otherwise, wait for the full receipt (default behavior on wait: undefined) @@ -408,7 +409,7 @@ export abstract class BaseWallet implements Wallet { await displayDebugLogs(receipt.debugLogs, this.getContractName.bind(this)); } - return receipt as SendReturn; + return { receipt, offchainEffects } as SendReturn; } /** From 06e28d05afc20cc8df3c891463491117f5e33cc0 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 10:23:16 +0000 Subject: [PATCH 053/119] simplify SimulationReturn --- .../contract/contract_function_interaction.ts | 10 +--- .../aztec.js/src/contract/deploy_method.ts | 2 +- .../src/contract/interaction_options.ts | 46 +++++-------------- yarn-project/aztec.js/src/utils/authwit.ts | 10 +--- .../cli-wallet/src/cmds/create_account.ts | 5 +- yarn-project/cli-wallet/src/cmds/deploy.ts | 5 +- .../cli-wallet/src/cmds/deploy_account.ts | 5 +- yarn-project/cli-wallet/src/cmds/send.ts | 5 +- .../src/e2e_kernelless_simulation.test.ts | 42 ++++++++++------- 9 files changed, 56 insertions(+), 74 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 909d599891e3..743c9b99566b 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -18,8 +18,6 @@ import { type ProfileInteractionOptions, type RequestInteractionOptions, type SimulateInteractionOptions, - type SimulateWithGasEstimationOptions, - type SimulateWithMetadataOptions, type SimulationReturn, toProfileOptions, toSimulateOptions, @@ -99,15 +97,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * function or a rich object containing extra metadata, such as estimated gas costs (if requested via options), * execution statistics and emitted offchain effects */ - public async simulate(options: SimulateWithMetadataOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate(options: SimulateWithGasEstimationOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc - public async simulate(options?: SimulateInteractionOptions): Promise>; - // eslint-disable-next-line jsdoc/require-jsdoc public async simulate( options: SimulateInteractionOptions = {} as SimulateInteractionOptions, - ): Promise> { + ): Promise { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const call = await this.getFunctionCall(); diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index dfb55d006989..bb2d01ee28a0 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -414,7 +414,7 @@ export class DeployMethod extends * @returns A simulation result object containing metadata of the execution, including gas * estimations (if requested via options), execution statistics and emitted offchain effects */ - public async simulate(options: SimulateDeployOptions): Promise> { + public async simulate(options: SimulateDeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); const simulatedTx = await this.wallet.simulateTx( executionPayload, diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index c8c98436626c..a9b69f1deccc 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -121,21 +121,6 @@ export type SimulateInteractionOptions = Omit & { includeMetadata?: boolean; }; -/** Simulation options that request full metadata (stats, gas estimation) in the result. */ -export type SimulateWithMetadataOptions = SimulateInteractionOptions & { - /** Request metadata in the simulation result. */ - includeMetadata: true; -}; - -/** Simulation options that request gas estimation in the result. */ -export type SimulateWithGasEstimationOptions = SimulateInteractionOptions & { - /** Fee options with gas estimation enabled. */ - fee: { - /** Request gas estimation in the simulation result. */ - estimateGas: true; - }; -}; - /** * Represents the options for profiling an interaction. */ @@ -147,27 +132,20 @@ export type ProfileInteractionOptions = SimulateInteractionOptions & { }; /** - * Represents the result type of a simulation. + * Represents the result of a simulation. * Always includes the return value and offchain effects. - * When `includeMetadata` is set to true, also includes stats and gas estimation. + * When `includeMetadata` or `fee.estimateGas` is set, also includes stats and gas estimation. */ -export type SimulationReturn = T extends true - ? { - /** Additional stats about the simulation */ - stats: SimulationStats; - /** Offchain effects generated during the simulation */ - offchainEffects: OffchainEffect[]; - /** Return value of the function */ - result: any; - /** Gas estimation results */ - estimatedGas: Pick; - } - : { - /** Return value of the function */ - result: any; - /** Offchain effects generated during the simulation */ - offchainEffects: OffchainEffect[]; - }; +export type SimulationReturn = { + /** Return value of the function */ + result: any; + /** Offchain effects generated during the simulation */ + offchainEffects: OffchainEffect[]; + /** Additional stats about the simulation. Present when `includeMetadata` is set. */ + stats?: SimulationStats; + /** Gas estimation results. Present when `includeMetadata` or `fee.estimateGas` is set. */ + estimatedGas?: Pick; +}; /** Result of sendTx when not waiting for mining. */ export type TxSendResultImmediate = { diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index c1ff57e9f454..acec99c5dd29 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -15,7 +15,6 @@ import type { SendInteractionOptionsWithoutWait, SendReturn, SimulateInteractionOptions, - SimulateWithMetadataOptions, SimulationReturn, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -265,16 +264,9 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing additional configuration for the transaction. * @returns The result of the transaction as returned by the contract function. */ - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - public override simulate(options: Omit): Promise>; - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - // eslint-disable-next-line jsdoc/require-jsdoc - public override simulate(options?: Omit): Promise>; - // @ts-expect-error Override narrows parameter type by omitting 'from' (auto-injected) - // eslint-disable-next-line jsdoc/require-jsdoc public override simulate( options: Omit = {} as Omit, - ): Promise> { + ): Promise { return super.simulate({ ...options, from: this.from } as SimulateInteractionOptions); } diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index 1279cf8b0f54..2e317c6a7799 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -83,10 +83,13 @@ export async function createAccount( }; const deployMethod = await account.getDeployMethod(); - const { estimatedGas, stats } = await deployMethod.simulate({ + const sim = await deployMethod.simulate({ ...deployAccountOpts, fee: { ...deployAccountOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/deploy.ts b/yarn-project/cli-wallet/src/cmds/deploy.ts index d4ddc6a3c968..edc7e5db29a9 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy.ts @@ -71,10 +71,13 @@ export async function deploy( skipInstancePublication, }; - const { estimatedGas, stats } = await deploy.simulate({ + const sim = await deploy.simulate({ ...deployOpts, fee: { ...deployOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/deploy_account.ts b/yarn-project/cli-wallet/src/cmds/deploy_account.ts index 7c3c6e020023..4667fa6f12c6 100644 --- a/yarn-project/cli-wallet/src/cmds/deploy_account.ts +++ b/yarn-project/cli-wallet/src/cmds/deploy_account.ts @@ -63,10 +63,13 @@ export async function deployAccount( }; const deployMethod = await account.getDeployMethod(); - const { estimatedGas, stats } = await deployMethod.simulate({ + const sim = await deployMethod.simulate({ ...deployAccountOpts, fee: { ...deployAccountOpts.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { if (json) { diff --git a/yarn-project/cli-wallet/src/cmds/send.ts b/yarn-project/cli-wallet/src/cmds/send.ts index 30129ef78e0d..4cc3c69b4505 100644 --- a/yarn-project/cli-wallet/src/cmds/send.ts +++ b/yarn-project/cli-wallet/src/cmds/send.ts @@ -37,10 +37,13 @@ export async function send( authWitnesses, }; - const { estimatedGas, stats } = await call.simulate({ + const sim = await call.simulate({ ...sendOptions, fee: { ...sendOptions.fee, estimateGas: true }, }); + // estimateGas: true guarantees these fields are present + const estimatedGas = sim.estimatedGas!; + const stats = sim.stats!; if (feeOpts.estimateOnly) { return; diff --git a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts index d0a9cedf6b12..4cdd919dacad 100644 --- a/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts +++ b/yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts @@ -237,10 +237,12 @@ describe('Kernelless simulation', () => { ); wallet.enableSimulatedSimulations(); - const { estimatedGas: swapKernellessGas } = await swapExactTokensInteraction.simulate({ - from: swapperAddress, - includeMetadata: true, - }); + const swapKernellessGas = ( + await swapExactTokensInteraction.simulate({ + from: swapperAddress, + includeMetadata: true, + }) + ).estimatedGas!; const swapAuthwit = await wallet.createAuthWit(swapperAddress, { caller: amm.address, @@ -248,11 +250,13 @@ describe('Kernelless simulation', () => { }); wallet.disableSimulatedSimulations(); - const { estimatedGas: swapWithKernelsGas } = await swapExactTokensInteraction.simulate({ - from: swapperAddress, - includeMetadata: true, - authWitnesses: [swapAuthwit], - }); + const swapWithKernelsGas = ( + await swapExactTokensInteraction.simulate({ + from: swapperAddress, + includeMetadata: true, + authWitnesses: [swapAuthwit], + }) + ).estimatedGas!; logger.info(`Kernelless gas: L2=${swapKernellessGas.gasLimits.l2Gas} DA=${swapKernellessGas.gasLimits.daGas}`); logger.info( @@ -285,16 +289,20 @@ describe('Kernelless simulation', () => { ); wallet.enableSimulatedSimulations(); - const { estimatedGas: kernellessGas } = await interaction.simulate({ - from: adminAddress, - includeMetadata: true, - }); + const kernellessGas = ( + await interaction.simulate({ + from: adminAddress, + includeMetadata: true, + }) + ).estimatedGas!; wallet.disableSimulatedSimulations(); - const { estimatedGas: withKernelsGas } = await interaction.simulate({ - from: adminAddress, - includeMetadata: true, - }); + const withKernelsGas = ( + await interaction.simulate({ + from: adminAddress, + includeMetadata: true, + }) + ).estimatedGas!; logger.info(`Kernelless gas: L2=${kernellessGas.gasLimits.l2Gas} DA=${kernellessGas.gasLimits.daGas}`); logger.info(`With kernels gas: L2=${withKernelsGas.gasLimits.l2Gas} DA=${withKernelsGas.gasLimits.daGas}`); From 67809c9f726f676f0f045febbc627cf5df5f97fa Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 10:37:36 +0000 Subject: [PATCH 054/119] add migration notes --- .../docs/resources/migration_notes.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 63c71c0e7bbd..c98075bfc595 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,76 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects + +All SDK interaction methods now return structured objects that include `offchainEffects` alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. + +**`simulate()` — always returns `{ result, offchainEffects }` object:** + +```diff +- const value = await contract.methods.foo(args).simulate({ from: sender }); ++ const { result: value } = await contract.methods.foo(args).simulate({ from: sender }); +``` + +When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` are also available as optional fields on the same object: + +```diff +- const { stats, estimatedGas } = await contract.methods.foo(args).simulate({ ++ const sim = await contract.methods.foo(args).simulate({ + from: sender, + includeMetadata: true, + }); ++ const stats = sim.stats!; ++ const estimatedGas = sim.estimatedGas!; +``` + +`SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields. + +**`send()` — returns `{ receipt, offchainEffects }` object:** + +```diff +- const receipt = await contract.methods.foo(args).send({ from: sender }); ++ const { receipt } = await contract.methods.foo(args).send({ from: sender }); +``` + +When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `TxHash`: + +```diff +- const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ++ const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); +``` + +**Deploy — returns `{ contract, offchainEffects }` object:** + +```diff +- const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); ++ const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); +``` + +**Custom wallet implementations — `sendTx()` must return objects:** + +If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): + +```diff + async sendTx(executionPayload, opts) { + const provenTx = await this.pxe.proveTx(...); ++ const offchainEffects = provenTx.getOffchainEffects(); + const tx = await provenTx.toTx(); + const txHash = tx.getTxHash(); + await this.aztecNode.sendTx(tx); + + if (opts.wait === NO_WAIT) { +- return txHash; ++ return { txHash, offchainEffects }; + } + const receipt = await waitForTx(this.aztecNode, txHash, opts.wait); +- return receipt; ++ return { receipt, offchainEffects }; + } +``` + +**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. + ### [Aztec.js] Removed `SingleKeyAccountContract` The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAccountContract` have been removed. This contract was insecure: it used `ivpk_m` (incoming viewing public key) as its Schnorr signing key, meaning anyone who received a user's viewing key could sign transactions on their behalf. From 71e3d6acffd45adc1f825c7014b84d3c31f492a1 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:23:09 +0000 Subject: [PATCH 055/119] add offchainEffects usage after send() e2e --- .../docs/resources/migration_notes.md | 4 +++- .../aztec.js/src/contract/deploy_method.ts | 8 +++---- .../src/e2e_fees/gas_estimation.test.ts | 9 +++++--- .../src/e2e_offchain_effect.test.ts | 22 +++++++++++++++++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index c98075bfc595..7cf68e2b630f 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -48,13 +48,15 @@ When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `T + const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ``` -**Deploy — returns `{ contract, offchainEffects }` object:** +**Deploy — returns `{ contract, receipt, offchainEffects }` object:** ```diff - const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); + const { contract: myContract } = await MyContract.deploy(wallet, ...args).send({ from: sender }); ``` +The deploy receipt is also available via `receipt` if needed (e.g. for `receipt.txHash` or `receipt.transactionFee`). + **Custom wallet implementations — `sendTx()` must return objects:** If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index bb2d01ee28a0..57c77078fc23 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -148,6 +148,8 @@ export type DeployReturn; /** Offchain effects generated during proving. */ offchainEffects: OffchainEffect[]; }; @@ -350,15 +352,11 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class public override send(options: DeployOptionsWithoutWait): Promise>; - - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, ): Promise>; - // @ts-expect-error DeployMethod intentionally narrows the return type from the base class // eslint-disable-next-line jsdoc/require-jsdoc public override async send(options: DeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); @@ -385,7 +383,7 @@ export class DeployMethod extends return { receipt: { ...receipt, contract, instance }, offchainEffects }; } - return { contract, offchainEffects }; + return { contract, receipt, offchainEffects }; } /** diff --git a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts index 47fd12c85569..b456cd167784 100644 --- a/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -104,10 +104,11 @@ describe('e2e_fees gas_estimation', () => { }); it('estimates gas with Fee Juice payment method', async () => { - const { estimatedGas } = await makeTransferRequest().simulate({ + const sim = await makeTransferRequest().simulate({ from: aliceAddress, fee: { gasSettings, estimateGas: true, estimatedGasPadding: 0 }, }); + const estimatedGas = sim.estimatedGas!; logGasEstimate(estimatedGas); const sequencer = t.context.sequencer!.getSequencer(); @@ -147,10 +148,11 @@ describe('e2e_fees gas_estimation', () => { ); const paymentMethod = new PublicFeePaymentMethod(bananaFPC.address, aliceAddress, wallet, gasSettingsForEstimation); - const { estimatedGas } = await makeTransferRequest().simulate({ + const sim2 = await makeTransferRequest().simulate({ from: aliceAddress, fee: { paymentMethod, estimatedGasPadding: 0, estimateGas: true }, }); + const estimatedGas = sim2.estimatedGas!; logGasEstimate(estimatedGas); const [withEstimate, withoutEstimate] = await sendTransfers(estimatedGas, paymentMethod); @@ -188,7 +190,7 @@ describe('e2e_fees gas_estimation', () => { }; }; - const { estimatedGas } = await deployMethod().simulate({ + const sim3 = await deployMethod().simulate({ from: aliceAddress, skipClassPublication: true, fee: { @@ -196,6 +198,7 @@ describe('e2e_fees gas_estimation', () => { estimatedGasPadding: 0, }, }); + const estimatedGas = sim3.estimatedGas!; logGasEstimate(estimatedGas); const [{ receipt: withEstimate }, { receipt: withoutEstimate }] = (await Promise.all([ diff --git a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts index c045303bed85..9968050c18b7 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_effect.test.ts @@ -39,6 +39,28 @@ describe('e2e_offchain_effect', () => { afterAll(() => teardown()); + it('should return offchain effects from send()', async () => { + const effects = Array(2) + .fill(null) + .map(() => ({ + data: [Fr.random(), Fr.random(), Fr.random(), Fr.random(), Fr.random()], + // eslint-disable-next-line camelcase + next_contract: contract1.address, + })); + + const { receipt, offchainEffects } = await contract1.methods + .emit_offchain_effects(effects) + .send({ from: defaultAccountAddress }); + + expect(receipt.hasExecutionSucceeded()).toBe(true); + // Effects are popped from the end of the BoundedVec, so they come out reversed + expect(offchainEffects).toHaveLength(2); + expect(offchainEffects[0].contractAddress).toEqual(contract1.address); + expect(offchainEffects[0].data).toEqual(effects[1].data); + expect(offchainEffects[1].contractAddress).toEqual(contract1.address); + expect(offchainEffects[1].data).toEqual(effects[0].data); + }); + it('should emit offchain effects', async () => { const effects = Array(3) .fill(null) From 9e486155bec132fcf5f6a7e55c5253d67b64959b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:43:21 +0000 Subject: [PATCH 056/119] fix playground --- .../src/components/contract/components/FunctionCard.tsx | 2 +- playground/src/hooks/useTransaction.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/playground/src/components/contract/components/FunctionCard.tsx b/playground/src/components/contract/components/FunctionCard.tsx index 6f4c9e5415a2..21f3b2696b2e 100644 --- a/playground/src/components/contract/components/FunctionCard.tsx +++ b/playground/src/components/contract/components/FunctionCard.tsx @@ -90,7 +90,7 @@ export function FunctionCard({ fn, contract, contractArtifact, onSendTxRequested let result; try { const call = contract.methods[fnName](...parameters); - result = await call.simulate({ from, skipFeeEnforcement: true }); + ({ result } = await call.simulate({ from, skipFeeEnforcement: true })); const stringResult = JSON.stringify(result, (key, value) => { if (typeof value === 'bigint') { return value.toString(); diff --git a/playground/src/hooks/useTransaction.tsx b/playground/src/hooks/useTransaction.tsx index 202c6017481f..9f1e6832e6c2 100644 --- a/playground/src/hooks/useTransaction.tsx +++ b/playground/src/hooks/useTransaction.tsx @@ -47,10 +47,10 @@ export function useTransaction() { if (interaction instanceof DeployMethod) { const { from, fee, ...deployOpts } = opts as DeployOptions; - txHash = await interaction.send({ from, fee, ...deployOpts, wait: NO_WAIT }); + ({ txHash } = await interaction.send({ from, fee, ...deployOpts, wait: NO_WAIT })); } else { const { from, fee, authWitnesses, capsules } = opts as SendInteractionOptions; - txHash = await interaction.send({ from, fee, authWitnesses, capsules, wait: NO_WAIT }); + ({ txHash } = await interaction.send({ from, fee, authWitnesses, capsules, wait: NO_WAIT })); } setCurrentTx({ From 28a983806fd94fb83af71400ad462167dac55d04 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 11:55:50 +0000 Subject: [PATCH 057/119] fix boxes --- boxes/boxes/react/src/hooks/useContract.tsx | 4 ++-- boxes/boxes/react/src/hooks/useNumber.tsx | 4 ++-- boxes/boxes/vanilla/app/main.ts | 2 +- boxes/boxes/vite/src/hooks/useContract.tsx | 4 ++-- boxes/boxes/vite/src/hooks/useNumber.tsx | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boxes/boxes/react/src/hooks/useContract.tsx b/boxes/boxes/react/src/hooks/useContract.tsx index 5b3c7aca373f..ff180dfa5b59 100644 --- a/boxes/boxes/react/src/hooks/useContract.tsx +++ b/boxes/boxes/react/src/hooks/useContract.tsx @@ -24,10 +24,10 @@ export function useContract() { contractAddressSalt: salt, }); - const contract = await toast.promise(deploymentPromise, { + const { contract } = await toast.promise(deploymentPromise, { pending: 'Deploying contract...', success: { - render: ({ data }) => `Address: ${data.address}`, + render: ({ data }) => `Address: ${data.contract.address}`, }, error: 'Error deploying contract', }); diff --git a/boxes/boxes/react/src/hooks/useNumber.tsx b/boxes/boxes/react/src/hooks/useNumber.tsx index 8809b6555b42..7435cf5ebfa7 100644 --- a/boxes/boxes/react/src/hooks/useNumber.tsx +++ b/boxes/boxes/react/src/hooks/useNumber.tsx @@ -11,10 +11,10 @@ export function useNumber({ contract }: { contract: Contract }) { setWait(true); const defaultAccountAddress = deployerEnv.getDefaultAccountAddress(); - const viewTxReceipt = await contract!.methods + const { result } = await contract!.methods .getNumber(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - toast(`Number is: ${viewTxReceipt.value}`); + toast(`Number is: ${result}`); setWait(false); }; diff --git a/boxes/boxes/vanilla/app/main.ts b/boxes/boxes/vanilla/app/main.ts index fa55a4cae208..ddce11af435c 100644 --- a/boxes/boxes/vanilla/app/main.ts +++ b/boxes/boxes/vanilla/app/main.ts @@ -200,7 +200,7 @@ async function updateVoteTally(wallet: Wallet, from: AztecAddress) { const batchResult = await new BatchCall(wallet, payloads).simulate({ from }); - batchResult.forEach((value, i) => { + batchResult.forEach(({ result: value }, i) => { results[i + 1] = value; }); diff --git a/boxes/boxes/vite/src/hooks/useContract.tsx b/boxes/boxes/vite/src/hooks/useContract.tsx index 5b3c7aca373f..ff180dfa5b59 100644 --- a/boxes/boxes/vite/src/hooks/useContract.tsx +++ b/boxes/boxes/vite/src/hooks/useContract.tsx @@ -24,10 +24,10 @@ export function useContract() { contractAddressSalt: salt, }); - const contract = await toast.promise(deploymentPromise, { + const { contract } = await toast.promise(deploymentPromise, { pending: 'Deploying contract...', success: { - render: ({ data }) => `Address: ${data.address}`, + render: ({ data }) => `Address: ${data.contract.address}`, }, error: 'Error deploying contract', }); diff --git a/boxes/boxes/vite/src/hooks/useNumber.tsx b/boxes/boxes/vite/src/hooks/useNumber.tsx index 8809b6555b42..7435cf5ebfa7 100644 --- a/boxes/boxes/vite/src/hooks/useNumber.tsx +++ b/boxes/boxes/vite/src/hooks/useNumber.tsx @@ -11,10 +11,10 @@ export function useNumber({ contract }: { contract: Contract }) { setWait(true); const defaultAccountAddress = deployerEnv.getDefaultAccountAddress(); - const viewTxReceipt = await contract!.methods + const { result } = await contract!.methods .getNumber(defaultAccountAddress) .simulate({ from: defaultAccountAddress }); - toast(`Number is: ${viewTxReceipt.value}`); + toast(`Number is: ${result}`); setWait(false); }; From bb3fc260bf5d09206db17ac76b10ebb6f678f7b9 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 12:34:23 +0000 Subject: [PATCH 058/119] fix aztec.js for boxes --- .../aztec.js/src/contract/base_contract_interaction.ts | 6 ++++-- yarn-project/aztec.js/src/contract/deploy_method.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index bfa319bc77df..261d7f43e55d 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -1,6 +1,6 @@ import { createLogger } from '@aztec/foundation/log'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; -import type { Capsule, ExecutionPayload, TxReceipt } from '@aztec/stdlib/tx'; +import type { Capsule, ExecutionPayload, OffchainEffect, TxReceipt } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { @@ -42,7 +42,9 @@ export abstract class BaseContractInteraction { * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } - public send(options: SendInteractionOptionsWithoutWait): Promise>; + public send( + options: SendInteractionOptionsWithoutWait, + ): Promise<{ receipt: TReturn; offchainEffects: OffchainEffect[] }>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 57c77078fc23..113ac5b574b7 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -352,7 +352,9 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send(options: DeployOptionsWithoutWait): Promise>; + public override send( + options: DeployOptionsWithoutWait, + ): Promise<{ contract: TContract; receipt: DeployTxReceipt; offchainEffects: OffchainEffect[] }>; // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, From e08d59d3f342346c7b19a7b0a9047d5f6ae2a19d Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 12:55:49 +0000 Subject: [PATCH 059/119] sort out types --- yarn-project/aztec.js/src/api/contract.ts | 3 ++ .../src/contract/base_contract_interaction.ts | 7 ++-- .../aztec.js/src/contract/deploy_method.ts | 32 +++++++++---------- .../src/contract/interaction_options.ts | 11 ++----- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/yarn-project/aztec.js/src/api/contract.ts b/yarn-project/aztec.js/src/api/contract.ts index d3e9a692c67f..1dc15515abc8 100644 --- a/yarn-project/aztec.js/src/api/contract.ts +++ b/yarn-project/aztec.js/src/api/contract.ts @@ -57,6 +57,8 @@ export { type GasSettingsOption, type SendReturn, type SimulationReturn, + type TxSendResultImmediate, + type TxSendResultMined, toProfileOptions, toSendOptions, toSimulateOptions, @@ -67,6 +69,7 @@ export { ContractBase, type ContractMethod, type ContractStorageLayout } from '. export { BatchCall } from '../contract/batch_call.js'; export { type DeployOptions, + type DeployResultMined, type DeployReturn, type DeployTxReceipt, type DeployWaitOptions, diff --git a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts index 261d7f43e55d..12983df3a0ea 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -1,6 +1,6 @@ import { createLogger } from '@aztec/foundation/log'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; -import type { Capsule, ExecutionPayload, OffchainEffect, TxReceipt } from '@aztec/stdlib/tx'; +import type { Capsule, ExecutionPayload, TxReceipt } from '@aztec/stdlib/tx'; import type { Wallet } from '../wallet/wallet.js'; import { @@ -9,6 +9,7 @@ import { type SendInteractionOptions, type SendInteractionOptionsWithoutWait, type SendReturn, + type TxSendResultMined, toSendOptions, } from './interaction_options.js'; @@ -42,9 +43,7 @@ export abstract class BaseContractInteraction { * @returns TReturn (if wait is undefined/WaitOpts) or TxHash (if wait is NO_WAIT) */ // Overload for when wait is not specified at all - returns { receipt: TReturn, offchainEffects } - public send( - options: SendInteractionOptionsWithoutWait, - ): Promise<{ receipt: TReturn; offchainEffects: OffchainEffect[] }>; + public send(options: SendInteractionOptionsWithoutWait): Promise>; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public send( diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 113ac5b574b7..aa5175bd3ab6 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -28,6 +28,7 @@ import { type SimulationInteractionFeeOptions, type SimulationReturn, type TxSendResultImmediate, + type TxSendResultMined, toProfileOptions, toSendOptions, toSimulateOptions, @@ -136,23 +137,22 @@ type WaitWithReturnReceipt = { * - If wait has returnReceipt: true, returns DeployTxReceipt after waiting. * - Otherwise (undefined or DeployWaitOptions without returnReceipt), returns TContract after waiting. */ +/** Result of deploying a contract when waiting for mining (default case). */ +export type DeployResultMined = { + /** The deployed contract instance. */ + contract: TContract; + /** The deploy transaction receipt. */ + receipt: DeployTxReceipt; + /** Offchain effects generated during proving. */ + offchainEffects: OffchainEffect[]; +}; + +/** Conditional return type for deploy based on wait options. */ export type DeployReturn = W extends NoWait ? TxSendResultImmediate : W extends WaitWithReturnReceipt - ? { - /** The deploy transaction receipt. */ - receipt: DeployTxReceipt; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - } - : { - /** The deployed contract instance. */ - contract: TContract; - /** The deploy transaction receipt. */ - receipt: DeployTxReceipt; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - }; + ? TxSendResultMined> + : DeployResultMined; /** * Contract interaction for deployment. @@ -352,9 +352,7 @@ export class DeployMethod extends * @returns TxHash (if wait is NO_WAIT), TContract (if wait is undefined or doesn't have returnReceipt), or DeployTxReceipt (if wait.returnReceipt is true) */ // Overload for when wait is not specified at all - returns the contract - public override send( - options: DeployOptionsWithoutWait, - ): Promise<{ contract: TContract; receipt: DeployTxReceipt; offchainEffects: OffchainEffect[] }>; + public override send(options: DeployOptionsWithoutWait): Promise>; // eslint-disable-next-line jsdoc/require-jsdoc public override send( options: DeployOptions, diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index a9b69f1deccc..7864f19b2bcc 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -156,9 +156,9 @@ export type TxSendResultImmediate = { }; /** Result of sendTx when waiting for mining. */ -export type TxSendResultMined = { +export type TxSendResultMined = { /** The transaction receipt. */ - receipt: TxReceipt; + receipt: TReturn; /** Offchain effects generated during proving. */ offchainEffects: OffchainEffect[]; }; @@ -170,12 +170,7 @@ export type TxSendResultMined = { */ export type SendReturn = T extends NoWait ? TxSendResultImmediate - : { - /** The transaction receipt. */ - receipt: TReturn; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; - }; + : TxSendResultMined; /** * Transforms and cleans up the higher level SendInteractionOptions defined by the interaction into From c742fd84fc11cda864f03b6603c716a87b055cfe Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 13:11:10 +0000 Subject: [PATCH 060/119] fix authwit types --- yarn-project/aztec.js/src/utils/authwit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index acec99c5dd29..b9b889907ba5 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -16,6 +16,7 @@ import type { SendReturn, SimulateInteractionOptions, SimulationReturn, + TxSendResultMined, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -288,8 +289,7 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac * @param options - An optional object containing 'fee' options information * @returns A TxReceipt (if wait is true/undefined) or TxHash (if wait is false) */ - // Overload for when wait is not specified at all - public override send(options?: Omit): Promise>; + public override send(options?: Omit): Promise; // Generic overload for explicit wait values // eslint-disable-next-line jsdoc/require-jsdoc public override send( From 6fc1f3a2c860a9472f44c99cb8491145c05b158b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 13:50:07 +0000 Subject: [PATCH 061/119] fix e2e --- .../end-to-end/src/e2e_avm_simulator.test.ts | 22 +++++++++------- .../access_control.test.ts | 8 +++--- .../src/e2e_contract_updates.test.ts | 14 +++++----- .../contract_class_registration.test.ts | 20 ++++++++++---- .../e2e_deploy_contract/deploy_method.test.ts | 26 ++++++++++++------- .../private_initialization.test.ts | 14 +++++----- .../src/e2e_private_voting_contract.test.ts | 2 +- .../end-to-end/src/e2e_pruned_blocks.test.ts | 6 +++-- .../e2e_token_contract/access_control.test.ts | 8 +++--- .../src/e2e_token_contract/minting.test.ts | 12 ++++++--- .../e2e_token_contract/token_contract_test.ts | 2 +- .../transfer_in_private.test.ts | 12 ++++++--- .../transfer_in_public.test.ts | 24 ++++++++++------- 13 files changed, 104 insertions(+), 66 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts index 1cf55068255e..b40af1162298 100644 --- a/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts +++ b/yarn-project/end-to-end/src/e2e_avm_simulator.test.ts @@ -119,16 +119,18 @@ describe('e2e_avm_simulator', () => { describe('Storage', () => { it('Modifies storage (Field)', async () => { await avmContract.methods.set_storage_single(20n).send({ from: defaultAccountAddress }); - expect(await avmContract.methods.read_storage_single().simulate({ from: defaultAccountAddress })).toEqual(20n); + expect( + (await avmContract.methods.read_storage_single().simulate({ from: defaultAccountAddress })).result, + ).toEqual(20n); }); it('Modifies storage (Map)', async () => { const address = AztecAddress.fromBigInt(9090n); await avmContract.methods.set_storage_map(address, 100).send({ from: defaultAccountAddress }); await avmContract.methods.add_storage_map(address, 100).send({ from: defaultAccountAddress }); - expect(await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).toEqual( - 200n, - ); + expect( + (await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).result, + ).toEqual(200n); }); it('Preserves storage across enqueued public calls', async () => { @@ -139,9 +141,9 @@ describe('e2e_avm_simulator', () => { avmContract.methods.add_storage_map(address, 100), ]).send({ from: defaultAccountAddress }); // On a separate tx, we check the result. - expect(await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).toEqual( - 200n, - ); + expect( + (await avmContract.methods.read_storage_map(address).simulate({ from: defaultAccountAddress })).result, + ).toEqual(200n); }); }); @@ -261,9 +263,9 @@ describe('e2e_avm_simulator', () => { describe('Storage', () => { it('Read immutable (initialized) storage (Field)', async () => { - expect(await avmContract.methods.read_storage_immutable().simulate({ from: defaultAccountAddress })).toEqual( - 42n, - ); + expect( + (await avmContract.methods.read_storage_immutable().simulate({ from: defaultAccountAddress })).result, + ).toEqual(42n); }); }); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts index 97e8bbb19c23..76f015066d44 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract/access_control.test.ts @@ -23,7 +23,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.adminAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.adminAddress).simulate({ from: t.adminAddress })).result).toEqual( adminMinterRole.toNoirStruct(), ); }); @@ -34,7 +34,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).result).toEqual( adminRole.toNoirStruct(), ); }); @@ -45,7 +45,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.otherAddress).simulate({ from: t.adminAddress })).result).toEqual( noRole.toNoirStruct(), ); }); @@ -58,7 +58,7 @@ describe('e2e_blacklist_token_contract access control', () => { await t.crossTimestampOfChange(); - expect(await t.asset.methods.get_roles(t.blacklistedAddress).simulate({ from: t.adminAddress })).toEqual( + expect((await t.asset.methods.get_roles(t.blacklistedAddress).simulate({ from: t.adminAddress })).result).toEqual( blacklistRole.toNoirStruct(), ); }); diff --git a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts index 34af7e7d44da..e34cedae2e97 100644 --- a/yarn-project/end-to-end/src/e2e_contract_updates.test.ts +++ b/yarn-project/end-to-end/src/e2e_contract_updates.test.ts @@ -128,9 +128,10 @@ describe('e2e_contract_updates', () => { it('should update the contract', async () => { expect( - await contract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + (await contract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress })) + .result, ).toEqual(INITIAL_UPDATABLE_CONTRACT_VALUE); - expect(await contract.methods.get_public_value().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_public_value().simulate({ from: defaultAccountAddress })).result).toEqual( INITIAL_UPDATABLE_CONTRACT_VALUE, ); await contract.methods.update_to(updatedContractClassId).send({ from: defaultAccountAddress }); @@ -143,18 +144,19 @@ describe('e2e_contract_updates', () => { await updatedContract.methods.set_private_value().send({ from: defaultAccountAddress }); // Read state that was changed by the previous tx expect( - await updatedContract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + (await updatedContract.methods.get_private_value(defaultAccountAddress).simulate({ from: defaultAccountAddress })) + .result, ).toEqual(UPDATED_CONTRACT_PUBLIC_VALUE); // Call a public method with a new implementation await updatedContract.methods.set_public_value().send({ from: defaultAccountAddress }); - expect(await updatedContract.methods.get_public_value().simulate({ from: defaultAccountAddress })).toEqual( + expect((await updatedContract.methods.get_public_value().simulate({ from: defaultAccountAddress })).result).toEqual( UPDATED_CONTRACT_PUBLIC_VALUE, ); }); it('should change the update delay and then update the contract', async () => { - expect(await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).result).toEqual( BigInt(DEFAULT_TEST_UPDATE_DELAY), ); @@ -163,7 +165,7 @@ describe('e2e_contract_updates', () => { .set_update_delay(BigInt(DEFAULT_TEST_UPDATE_DELAY) + 1n) .send({ from: defaultAccountAddress }); - expect(await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).toEqual( + expect((await contract.methods.get_update_delay().simulate({ from: defaultAccountAddress })).result).toEqual( BigInt(DEFAULT_TEST_UPDATE_DELAY) + 1n, ); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts index d027cd6c4b39..37da34dd7f3c 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/contract_class_registration.test.ts @@ -149,7 +149,9 @@ describe('e2e_deploy_contract contract class registration', () => { it('calls a public function with no init check on the deployed instance', async () => { const whom = await AztecAddress.random(); await contract.methods.increment_public_value_no_init_check(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); @@ -161,7 +163,9 @@ describe('e2e_deploy_contract contract class registration', () => { expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); // Meanwhile we check we didn't increment the value - expect(await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).toEqual(0n); + expect( + (await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).result, + ).toEqual(0n); }); it('refuses to initialize the instance with wrong args via a private function', async () => { @@ -174,7 +178,9 @@ describe('e2e_deploy_contract contract class registration', () => { await contract.methods.constructor(...initArgs).send({ from: defaultAccountAddress }); const whom = await AztecAddress.random(); await contract.methods.increment_public_value(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); @@ -199,14 +205,18 @@ describe('e2e_deploy_contract contract class registration', () => { .public_constructor(whom, 43) .send({ from: defaultAccountAddress, wait: { dontThrowOnRevert: true } }); expect(receipt.executionResult).toEqual(TxExecutionResult.APP_LOGIC_REVERTED); - expect(await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).toEqual(0n); + expect( + (await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress })).result, + ).toEqual(0n); }); it('initializes the contract and calls a public function', async () => { await contract.methods.public_constructor(...initArgs).send({ from: defaultAccountAddress }); const whom = await AztecAddress.random(); await contract.methods.increment_public_value(whom, 10).send({ from: defaultAccountAddress }); - const stored = await contract.methods.get_public_value(whom).simulate({ from: defaultAccountAddress }); + const { result: stored } = await contract.methods + .get_public_value(whom) + .simulate({ from: defaultAccountAddress }); expect(stored).toEqual(10n); }); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts index 9ce2b7366aea..ed7cfba703fb 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/deploy_method.test.ts @@ -42,10 +42,12 @@ describe('e2e_deploy_contract deploy method', () => { // docs:start:deploy_basic const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send({ from: defaultAccountAddress }); // docs:end:deploy_basic - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(42n); logger.debug(`Calling public method on stateful test contract at ${contract.address.toString()}`); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(84n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 84n, + ); // docs:start:verify_deployment const metadata = await wallet.getContractMetadata(contract.address); const classMetadata = await wallet.getContractClassMetadata(metadata.instance!.currentContractClassId); @@ -60,9 +62,11 @@ describe('e2e_deploy_contract deploy method', () => { const opts = { universalDeploy: true, from: defaultAccountAddress }; const { contract } = await StatefulTestContract.deploy(wallet, owner, 42).send(opts); // docs:end:deploy_universal - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(42n); await contract.methods.increment_public_value(owner, 84).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(84n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 84n, + ); }); it('publicly deploys and calls a public function from the constructor', async () => { @@ -72,7 +76,7 @@ describe('e2e_deploy_contract deploy method', () => { from: defaultAccountAddress, }); // docs:end:deploy_token - expect(await token.methods.is_minter(owner).simulate({ from: defaultAccountAddress })).toEqual(true); + expect((await token.methods.is_minter(owner).simulate({ from: defaultAccountAddress })).result).toEqual(true); }); it('publicly deploys and initializes via a public function', async () => { @@ -87,10 +91,12 @@ describe('e2e_deploy_contract deploy method', () => { from: defaultAccountAddress, }); // docs:end:deploy_with_opts - expect(await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).toEqual(42n); + expect((await contract.methods.get_public_value(owner).simulate({ from: defaultAccountAddress })).result).toEqual( + 42n, + ); logger.debug(`Calling a private function to ensure the contract was properly initialized`); await contract.methods.create_note(owner, 30).send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).toEqual(30n); + expect((await contract.methods.summed_values(owner).simulate({ from: defaultAccountAddress })).result).toEqual(30n); }); it('deploys a contract with a default initializer not named constructor', async () => { @@ -99,9 +105,9 @@ describe('e2e_deploy_contract deploy method', () => { const { contract } = await CounterContract.deploy(wallet, 10, defaultAccountAddress).send(opts); logger.debug(`Calling a function to ensure the contract was properly initialized`); await contract.methods.increment_twice(defaultAccountAddress).send({ from: defaultAccountAddress }); - expect(await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).toEqual( - 12n, - ); + expect( + (await contract.methods.get_counter(defaultAccountAddress).simulate({ from: defaultAccountAddress })).result, + ).toEqual(12n); }); it('publicly deploys a contract with no constructor', async () => { diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts index 4a40eef5e2ec..a6814602e130 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract/private_initialization.test.ts @@ -45,11 +45,11 @@ describe('e2e_deploy_contract private initialization', () => { const contract = await t.registerContract(wallet, NoConstructorContract); await expect( contract.methods.is_private_mutable_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), - ).resolves.toEqual(false); + ).resolves.toEqual(expect.objectContaining({ result: false })); await contract.methods.initialize_private_mutable(42).send({ from: defaultAccountAddress }); await expect( contract.methods.is_private_mutable_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), - ).resolves.toEqual(true); + ).resolves.toEqual(expect.objectContaining({ result: true })); }); // Tests privately initializing an undeployed contract. Also requires pxe registration in advance. @@ -60,10 +60,10 @@ describe('e2e_deploy_contract private initialization', () => { logger.info(`Calling the constructor for ${contract.address}`); await contract.methods.constructor(...initArgs).send({ from: defaultAccountAddress }); logger.info(`Checking if the constructor was run for ${contract.address}`); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(42n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(42n); logger.info(`Calling a private function that requires initialization on ${contract.address}`); await contract.methods.create_note(owner, 10).send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); // Tests privately initializing multiple undeployed contracts on the same tx through an account contract. @@ -75,8 +75,8 @@ describe('e2e_deploy_contract private initialization', () => { ); const calls = contracts.map((c, i) => c.methods.constructor(...initArgs[i])); await new BatchCall(wallet, calls).send({ from: defaultAccountAddress }); - expect(await contracts[0].methods.summed_values(owner).simulate({ from: owner })).toEqual(42n); - expect(await contracts[1].methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contracts[0].methods.summed_values(owner).simulate({ from: owner })).result).toEqual(42n); + expect((await contracts[1].methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); it('initializes and calls a private function in a single tx', async () => { @@ -89,7 +89,7 @@ describe('e2e_deploy_contract private initialization', () => { ]); logger.info(`Executing constructor and private function in batch at ${contract.address}`); await batch.send({ from: defaultAccountAddress }); - expect(await contract.methods.summed_values(owner).simulate({ from: owner })).toEqual(52n); + expect((await contract.methods.summed_values(owner).simulate({ from: owner })).result).toEqual(52n); }); it('refuses to initialize a contract twice', async () => { diff --git a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts index 6c94f188985a..6a60739c2d36 100644 --- a/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_private_voting_contract.test.ts @@ -40,7 +40,7 @@ describe('e2e_voting_contract', () => { await votingContract.methods.start_vote(electionId).send({ from: owner }); await votingContract.methods.cast_vote(electionId, candidate).send({ from: owner }); - expect(await votingContract.methods.get_tally(electionId, candidate).simulate({ from: owner })).toBe(1n); + expect((await votingContract.methods.get_tally(electionId, candidate).simulate({ from: owner })).result).toBe(1n); // We try voting again, but our TX is dropped due to trying to emit duplicate nullifiers // first confirm that it fails simulation diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 2c9b8142105c..02db37673052 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -124,7 +124,9 @@ describe('e2e_pruned_blocks', () => { await token.methods.transfer(recipient, MINT_AMOUNT).send({ from: sender }); - expect(await token.methods.balance_of_private(recipient).simulate({ from: recipient })).toEqual(MINT_AMOUNT); - expect(await token.methods.balance_of_private(sender).simulate({ from: sender })).toEqual(0n); + expect((await token.methods.balance_of_private(recipient).simulate({ from: recipient })).result).toEqual( + MINT_AMOUNT, + ); + expect((await token.methods.balance_of_private(sender).simulate({ from: sender })).result).toEqual(0n); }); }); diff --git a/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts index cab2d394475f..50ac4a7f36be 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/access_control.test.ts @@ -18,17 +18,19 @@ describe('e2e_token_contract access control', () => { it('Set admin', async () => { await t.asset.methods.set_admin(t.account1Address).send({ from: t.adminAddress }); - expect(await t.asset.methods.get_admin().simulate({ from: t.adminAddress })).toBe(t.account1Address.toBigInt()); + expect((await t.asset.methods.get_admin().simulate({ from: t.adminAddress })).result).toBe( + t.account1Address.toBigInt(), + ); }); it('Add minter as admin', async () => { await t.asset.methods.set_minter(t.account1Address, true).send({ from: t.account1Address }); - expect(await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).toBe(true); + expect((await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).result).toBe(true); }); it('Revoke minter as admin', async () => { await t.asset.methods.set_minter(t.account1Address, false).send({ from: t.account1Address }); - expect(await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).toBe(false); + expect((await t.asset.methods.is_minter(t.account1Address).simulate({ from: t.adminAddress })).result).toBe(false); }); describe('failure cases', () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts index bc6a1251a833..e7479801a3a6 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/minting.test.ts @@ -25,10 +25,12 @@ describe('e2e_token_contract minting', () => { await asset.methods.mint_to_public(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPublic(adminAddress, amount); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual( + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( tokenSim.balanceOfPublic(adminAddress), ); - expect(await asset.methods.total_supply().simulate({ from: adminAddress })).toEqual(tokenSim.totalSupply); + expect((await asset.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + tokenSim.totalSupply, + ); }); describe('failure cases', () => { @@ -61,10 +63,12 @@ describe('e2e_token_contract minting', () => { await asset.methods.mint_to_private(adminAddress, amount).send({ from: adminAddress }); tokenSim.mintPrivate(adminAddress, amount); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual( + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( tokenSim.balanceOfPrivate(adminAddress), ); - expect(await asset.methods.total_supply().simulate({ from: adminAddress })).toEqual(tokenSim.totalSupply); + expect((await asset.methods.total_supply().simulate({ from: adminAddress })).result).toEqual( + tokenSim.totalSupply, + ); }); describe('failure cases', () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts index f6462de40520..bbc7f024fd19 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/token_contract_test.ts @@ -110,7 +110,7 @@ export class TokenContractTest { this.account1Address, ]); - expect(await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).toBe( + expect((await this.asset.methods.get_admin().simulate({ from: this.adminAddress })).result).toBe( this.adminAddress.toBigInt(), ); } diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts index 3ea047560895..385c912cec06 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_private.test.ts @@ -80,10 +80,12 @@ describe('e2e_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow('Assertion failed: Balance too low'); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_private(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it.skip('transfer into account to overflow', () => { @@ -135,7 +137,9 @@ describe('e2e_token_contract transfer private', () => { await expect( simulateThroughAuthwitProxy(t.authwitProxy, action, { from: adminAddress, authWitnesses: [witness] }), ).rejects.toThrow(`Unknown auth witness for message hash ${expectedMessageHash.toString()}`); - expect(await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); + expect((await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, + ); }); it('transfer on behalf of other, cancelled authwit', async () => { diff --git a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts index 3bb951b28192..f50c89d80b5a 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/transfer_in_public.test.ts @@ -147,10 +147,12 @@ describe('e2e_token_contract transfer public', () => { U128_UNDERFLOW_ERROR, ); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { @@ -175,10 +177,12 @@ describe('e2e_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: account1Address })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, wrong designated caller', async () => { @@ -202,10 +206,12 @@ describe('e2e_token_contract transfer public', () => { // Perform the transfer await expect(action.simulate({ from: account1Address })).rejects.toThrow(/unauthorized/); - expect(await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).toEqual(balance0); - expect(await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).toEqual( - balance1, + expect((await asset.methods.balance_of_public(adminAddress).simulate({ from: adminAddress })).result).toEqual( + balance0, ); + expect( + (await asset.methods.balance_of_public(account1Address).simulate({ from: account1Address })).result, + ).toEqual(balance1); }); it('transfer on behalf of other, cancelled authwit', async () => { From 2cf5d3ad10730938becf6afeeee1ee7c954d1854 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:10:57 +0000 Subject: [PATCH 062/119] fix more e2e --- yarn-project/end-to-end/src/simulators/token_simulator.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/simulators/token_simulator.ts b/yarn-project/end-to-end/src/simulators/token_simulator.ts index f29f6403a4d2..a2065beb4426 100644 --- a/yarn-project/end-to-end/src/simulators/token_simulator.ts +++ b/yarn-project/end-to-end/src/simulators/token_simulator.ts @@ -109,7 +109,9 @@ export class TokenSimulator { await Promise.all( chunk(calls, 5).map(batch => new BatchCall(this.defaultWallet, batch).simulate({ from: this.defaultAddress })), ) - ).flat(); + ) + .flat() + .map(r => r.result); expect(results[0]).toEqual(this.totalSupply); // Check that all our balances match @@ -123,7 +125,9 @@ export class TokenSimulator { const wallet = this.lookupProvider.get(address.toString()); const asset = wallet ? this.token.withWallet(wallet) : this.token; - const actualPrivateBalance = await asset.methods.balance_of_private(address).simulate({ from: address }); + const { result: actualPrivateBalance } = await asset.methods + .balance_of_private(address) + .simulate({ from: address }); expect(actualPrivateBalance).toEqual(this.balanceOfPrivate(address)); } } From 1c42899c817905009dde6102663d2ce901b994e0 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:37:21 +0000 Subject: [PATCH 063/119] fix e2e tests --- .../src/e2e_token_contract/reading_constants.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts index ef69b7556739..185ac4231e8e 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/reading_constants.test.ts @@ -50,12 +50,12 @@ describe('e2e_token_contract reading constants', () => { }); it('check decimals private', async () => { - const dec = await t.asset.methods.private_get_decimals().simulate({ from: t.adminAddress }); + const { result: dec } = await t.asset.methods.private_get_decimals().simulate({ from: t.adminAddress }); expect(dec).toBe(TOKEN_DECIMALS); }); it('check decimals public', async () => { - const dec = await t.asset.methods.public_get_decimals().simulate({ from: t.adminAddress }); + const { result: dec } = await t.asset.methods.public_get_decimals().simulate({ from: t.adminAddress }); expect(dec).toBe(TOKEN_DECIMALS); }); }); From 7aadeebd6a03c46099341d9f14ded8fdffc71ddd Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 17 Feb 2026 14:53:22 +0000 Subject: [PATCH 064/119] update docs examples --- docs/examples/ts/aztecjs_advanced/index.ts | 28 +++++++++---------- docs/examples/ts/aztecjs_authwit/index.ts | 2 +- docs/examples/ts/aztecjs_connection/index.ts | 6 ++-- .../ts/aztecjs_getting_started/index.ts | 14 +++++----- docs/examples/ts/aztecjs_testing/index.ts | 12 ++++---- docs/examples/ts/bob_token_contract/index.ts | 14 ++++++---- .../ts/recursive_verification/index.ts | 10 +++---- docs/examples/ts/token_bridge/index.ts | 12 ++++---- 8 files changed, 51 insertions(+), 47 deletions(-) diff --git a/docs/examples/ts/aztecjs_advanced/index.ts b/docs/examples/ts/aztecjs_advanced/index.ts index 85f3a2aef2ca..bd138a88ff79 100644 --- a/docs/examples/ts/aztecjs_advanced/index.ts +++ b/docs/examples/ts/aztecjs_advanced/index.ts @@ -30,7 +30,7 @@ const [aliceAddress, bobAddress] = await Promise.all( // docs:start:deploy_basic_local // wallet and aliceAddress are from the connection guide // Deploy with constructor arguments -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", @@ -49,7 +49,7 @@ await wallet.registerContract(sponsoredFPCInstance, SponsoredFPCContract.artifac const sponsoredPaymentMethod = new SponsoredFeePaymentMethod(sponsoredFPCInstance.address); // wallet is from the connection guide; sponsoredPaymentMethod is from the fees guide -const sponsoredContract = await TokenContract.deploy( +const { contract: sponsoredContract } = await TokenContract.deploy( wallet, aliceAddress, "SponsoredToken", @@ -62,7 +62,7 @@ const sponsoredContract = await TokenContract.deploy( // wallet and aliceAddress are from the connection guide const customSalt = Fr.random(); -const saltedContract = await TokenContract.deploy( +const { contract: saltedContract } = await TokenContract.deploy( wallet, aliceAddress, "SaltedToken", @@ -95,7 +95,7 @@ console.log(`Contract will deploy at: ${predictedAddress}`); // token is from the deployment step above; aliceAddress is from the connection guide try { // Try calling a view function - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); console.log("Contract is callable, balance:", balance); @@ -128,7 +128,7 @@ await token.methods // docs:start:no_wait_deploy // Use NO_WAIT to get the transaction hash immediately and track deployment -const txHash = await TokenContract.deploy( +const { txHash } = await TokenContract.deploy( wallet, aliceAddress, "AnotherToken", @@ -148,7 +148,7 @@ console.log(`Deployed in block ${receipt.blockNumber}`); // docs:start:no_wait_transaction // Use NO_WAIT for regular transactions too -const transferTxHash = await token.methods +const { txHash: transferTxHash } = await token.methods .transfer(bobAddress, 100n) .send({ from: aliceAddress, wait: NO_WAIT }); @@ -166,7 +166,7 @@ const batch = new BatchCall(wallet, [ token.methods.transfer(bobAddress, 200n), ]); -const batchReceipt = await batch.send({ from: aliceAddress }); +const { receipt: batchReceipt } = await batch.send({ from: aliceAddress }); console.log(`Batch executed in block ${batchReceipt.blockNumber}`); // docs:end:batch_call @@ -193,7 +193,7 @@ console.log( // docs:start:query_tx_status // Query transaction status after sending without waiting -const statusTxHash = await token.methods +const { txHash: statusTxHash } = await token.methods .transfer(bobAddress, 10n) .send({ from: aliceAddress, wait: NO_WAIT }); @@ -207,7 +207,7 @@ console.log(`Transaction fee: ${txReceipt.transactionFee}`); // docs:start:deploy_with_dependencies // Deploy contracts with dependencies - deploy sequentially and pass addresses -const baseToken = await TokenContract.deploy( +const { contract: baseToken } = await TokenContract.deploy( wallet, aliceAddress, "BaseToken", @@ -216,7 +216,7 @@ const baseToken = await TokenContract.deploy( ).send({ from: aliceAddress }); // A second contract could reference the first (example pattern) -const derivedToken = await TokenContract.deploy( +const { contract: derivedToken } = await TokenContract.deploy( wallet, baseToken.address, // Use first contract's address as admin "DerivedToken", @@ -233,13 +233,13 @@ console.log(`Derived token at: ${derivedToken.address.toString()}`); const contracts = await Promise.all([ TokenContract.deploy(wallet, aliceAddress, "Token1", "T1", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), TokenContract.deploy(wallet, aliceAddress, "Token2", "T2", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), TokenContract.deploy(wallet, aliceAddress, "Token3", "T3", 18).send({ from: aliceAddress, - }), + }).then(({ contract }) => contract), ]); console.log(`Contract 1 at: ${contracts[0].address}`); @@ -249,7 +249,7 @@ console.log(`Contract 3 at: ${contracts[2].address}`); // docs:start:skip_initialization // Deploy without running the constructor using skipInitialization -const delayedToken = await TokenContract.deploy( +const { contract: delayedToken } = await TokenContract.deploy( wallet, aliceAddress, "DelayedToken", diff --git a/docs/examples/ts/aztecjs_authwit/index.ts b/docs/examples/ts/aztecjs_authwit/index.ts index da0f03a80be0..04833cd90951 100644 --- a/docs/examples/ts/aztecjs_authwit/index.ts +++ b/docs/examples/ts/aztecjs_authwit/index.ts @@ -23,7 +23,7 @@ const [aliceAddress, bobAddress] = await Promise.all( }), ); -const tokenContract = await TokenContract.deploy( +const { contract: tokenContract } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", diff --git a/docs/examples/ts/aztecjs_connection/index.ts b/docs/examples/ts/aztecjs_connection/index.ts index 4267a6597b9b..5b4074c9b5ec 100644 --- a/docs/examples/ts/aztecjs_connection/index.ts +++ b/docs/examples/ts/aztecjs_connection/index.ts @@ -91,7 +91,7 @@ console.log("Account deployed:", metadata.isContractInitialized); // docs:start:deploy_contract import { TokenContract } from "@aztec/noir-contracts.js/Token"; -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, aliceAddress, "TestToken", @@ -103,7 +103,7 @@ console.log(`Token deployed at: ${token.address.toString()}`); // docs:end:deploy_contract // docs:start:send_transaction -const receipt = await token.methods +const { receipt } = await token.methods .mint_to_public(aliceAddress, 1000n) .send({ from: aliceAddress }); @@ -112,7 +112,7 @@ console.log(`Transaction fee: ${receipt.transactionFee}`); // docs:end:send_transaction // docs:start:simulate_function -const balance = await token.methods +const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); diff --git a/docs/examples/ts/aztecjs_getting_started/index.ts b/docs/examples/ts/aztecjs_getting_started/index.ts index 9b3e7ec27ff5..de5a4ac22eae 100644 --- a/docs/examples/ts/aztecjs_getting_started/index.ts +++ b/docs/examples/ts/aztecjs_getting_started/index.ts @@ -13,7 +13,7 @@ await wallet.createSchnorrAccount(bob.secret, bob.salt); // docs:start:deploy import { TokenContract } from "@aztec/noir-contracts.js/Token"; -const token = await TokenContract.deploy( +const { contract: token } = await TokenContract.deploy( wallet, alice.address, "TokenName", @@ -29,11 +29,11 @@ await token.methods // docs:end:mint // docs:start:check_balances -let aliceBalance = await token.methods +let { result: aliceBalance } = await token.methods .balance_of_private(alice.address) .simulate({ from: alice.address }); console.log(`Alice's balance: ${aliceBalance}`); -let bobBalance = await token.methods +let { result: bobBalance } = await token.methods .balance_of_private(bob.address) .simulate({ from: bob.address }); console.log(`Bob's balance: ${bobBalance}`); @@ -41,9 +41,9 @@ console.log(`Bob's balance: ${bobBalance}`); // docs:start:transfer await token.methods.transfer(bob.address, 10).send({ from: alice.address }); -bobBalance = await token.methods +({ result: bobBalance } = await token.methods .balance_of_private(bob.address) - .simulate({ from: bob.address }); + .simulate({ from: bob.address })); console.log(`Bob's balance: ${bobBalance}`); // docs:end:transfer @@ -55,8 +55,8 @@ await token.methods.set_minter(bob.address, true).send({ from: alice.address }); await token.methods .mint_to_private(bob.address, 100) .send({ from: bob.address }); -bobBalance = await token.methods +({ result: bobBalance } = await token.methods .balance_of_private(bob.address) - .simulate({ from: bob.address }); + .simulate({ from: bob.address })); console.log(`Bob's balance: ${bobBalance}`); // docs:end:bob_mints diff --git a/docs/examples/ts/aztecjs_testing/index.ts b/docs/examples/ts/aztecjs_testing/index.ts index 68af11270b77..0c50ac8e5a33 100644 --- a/docs/examples/ts/aztecjs_testing/index.ts +++ b/docs/examples/ts/aztecjs_testing/index.ts @@ -26,7 +26,7 @@ async function setup() { }), ); - token = await TokenContract.deploy( + ({ contract: token } = await TokenContract.deploy( wallet, aliceAddress, "Test", @@ -34,7 +34,7 @@ async function setup() { 18, ).send({ from: aliceAddress, - }); + })); } // Test: mints tokens to an account @@ -43,7 +43,7 @@ async function testMintTokens() { .mint_to_public(aliceAddress, 1000n) .send({ from: aliceAddress }); - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); @@ -63,10 +63,10 @@ async function testTransferTokens() { // Transfer to bob using the simple transfer method await token.methods.transfer(bobAddress, 100n).send({ from: aliceAddress }); - const aliceBalance = await token.methods + const { result: aliceBalance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); - const bobBalance = await token.methods + const { result: bobBalance } = await token.methods .balance_of_public(bobAddress) .simulate({ from: bobAddress }); @@ -77,7 +77,7 @@ async function testTransferTokens() { // Test: reverts when transferring more than balance async function testRevertOnOverTransfer() { - const balance = await token.methods + const { result: balance } = await token.methods .balance_of_public(aliceAddress) .simulate({ from: aliceAddress }); diff --git a/docs/examples/ts/bob_token_contract/index.ts b/docs/examples/ts/bob_token_contract/index.ts index ec71b47da703..9b154e1279a3 100644 --- a/docs/examples/ts/bob_token_contract/index.ts +++ b/docs/examples/ts/bob_token_contract/index.ts @@ -15,16 +15,20 @@ async function getBalances( await Promise.all([ contract.methods .public_balance_of(aliceAddress) - .simulate({ from: aliceAddress }), + .simulate({ from: aliceAddress }) + .then(({ result }) => result), contract.methods .private_balance_of(aliceAddress) - .simulate({ from: aliceAddress }), + .simulate({ from: aliceAddress }) + .then(({ result }) => result), contract.methods .public_balance_of(bobAddress) - .simulate({ from: bobAddress }), + .simulate({ from: bobAddress }) + .then(({ result }) => result), contract.methods .private_balance_of(bobAddress) - .simulate({ from: bobAddress }), + .simulate({ from: bobAddress }) + .then(({ result }) => result), ]).then( ([ alicePublicBalance, @@ -69,7 +73,7 @@ async function main() { const aliceAddress = aliceAccountManager.address; const bobClinicAddress = bobClinicAccountManager.address; - const bobToken = await BobTokenContract.deploy(wallet).send({ + const { contract: bobToken } = await BobTokenContract.deploy(wallet).send({ from: giggleAddress, }); diff --git a/docs/examples/ts/recursive_verification/index.ts b/docs/examples/ts/recursive_verification/index.ts index 37597276d764..4ded9c585249 100644 --- a/docs/examples/ts/recursive_verification/index.ts +++ b/docs/examples/ts/recursive_verification/index.ts @@ -65,7 +65,7 @@ async function main() { // Step 2: Deploy ValueNotEqual contract // Constructor args: initial counter (10), owner, VK hash - const valueNotEqual = await ValueNotEqualContract.deploy( + const { contract: valueNotEqual } = await ValueNotEqualContract.deploy( wallet, 10, // Initial counter value accounts[0].item, // Owner address @@ -84,9 +84,9 @@ async function main() { // Step 3: Read initial counter value // simulate() executes without submitting a transaction - let counterValue = await valueNotEqual.methods + let counterValue = (await valueNotEqual.methods .get_counter(accounts[0].item) - .simulate({ from: accounts[0].item }); + .simulate({ from: accounts[0].item })).result; console.log(`Counter value: ${counterValue}`); // Should be 10 // Step 4: Call increment() with proof data @@ -107,9 +107,9 @@ async function main() { await interaction.send(opts); // Step 6: Read updated counter - counterValue = await valueNotEqual.methods + counterValue = (await valueNotEqual.methods .get_counter(accounts[0].item) - .simulate({ from: accounts[0].item }); + .simulate({ from: accounts[0].item })).result; console.log(`Counter value: ${counterValue}`); // Should be 11 assert(counterValue === 11n, "Counter should be 11 after verification"); diff --git a/docs/examples/ts/token_bridge/index.ts b/docs/examples/ts/token_bridge/index.ts index 74ad78653034..81c410bae820 100644 --- a/docs/examples/ts/token_bridge/index.ts +++ b/docs/examples/ts/token_bridge/index.ts @@ -69,11 +69,11 @@ console.log(`NFTPortal: ${portalAddress}\n`); // docs:start:deploy_l2_contracts console.log("Deploying L2 contracts...\n"); -const l2Nft = await NFTPunkContract.deploy(aztecWallet, account.address).send({ +const { contract: l2Nft } = await NFTPunkContract.deploy(aztecWallet, account.address).send({ from: account.address, }); -const l2Bridge = await NFTBridgeContract.deploy( +const { contract: l2Bridge } = await NFTBridgeContract.deploy( aztecWallet, l2Nft.address, ).send({ from: account.address }); @@ -222,7 +222,7 @@ await mine2Blocks(aztecWallet, account.address); // Check notes before claiming (should be 0) console.log("Checking notes before claim..."); -const notesBefore = await l2Nft.methods +const { result: notesBefore } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesBefore}`); @@ -235,7 +235,7 @@ console.log("NFT claimed on L2\n"); // Check notes after claiming (should be 1) console.log("Checking notes after claim..."); -const notesAfterClaim = await l2Nft.methods +const { result: notesAfterClaim } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesAfterClaim}\n`); @@ -249,7 +249,7 @@ await mine2Blocks(aztecWallet, account.address); const recipientEthAddress = EthAddress.fromString(ownerEthAddress); -const exitReceipt = await l2Bridge.methods +const { receipt: exitReceipt } = await l2Bridge.methods .exit(new Fr(Number(tokenId)), recipientEthAddress) .send({ from: account.address }); @@ -257,7 +257,7 @@ console.log(`Exit message sent (block: ${exitReceipt.blockNumber})\n`); // Check notes after burning (should be 0 again) console.log("Checking notes after burn..."); -const notesAfterBurn = await l2Nft.methods +const { result: notesAfterBurn } = await l2Nft.methods .notes_of(account.address) .simulate({ from: account.address }); console.log(` Notes count: ${notesAfterBurn}\n`); From 9d9dd3e24f5a545f40e6fc4932b230db3cec42db Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 09:04:33 +0000 Subject: [PATCH 065/119] fix more e2e tests --- .../src/e2e_crowdfunding_and_claim.test.ts | 8 +- .../end-to-end/src/e2e_partial_notes.test.ts | 3 +- .../src/e2e_scope_isolation.test.ts | 12 +- .../end-to-end/src/e2e_state_vars.test.ts | 136 ++++++++++++------ .../private_transfer_recursion.test.ts | 4 +- .../src/shared/gas_portal_test_harness.ts | 2 +- .../end-to-end/src/spartan/reorg.test.ts | 16 ++- 7 files changed, 118 insertions(+), 63 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index ced063f43993..1b99f2b3657e 100644 --- a/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts +++ b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts @@ -150,10 +150,12 @@ describe('e2e_crowdfunding_and_claim', () => { } // Since the RWT is minted 1:1 with the DNT, the balance of the reward token should be equal to the donation amount - const balanceRWT = await rewardToken.methods.balance_of_public(donor1Address).simulate({ from: operatorAddress }); + const { result: balanceRWT } = await rewardToken.methods + .balance_of_public(donor1Address) + .simulate({ from: operatorAddress }); expect(balanceRWT).toEqual(donationAmount); - const balanceDNTBeforeWithdrawal = await donationToken.methods + const { result: balanceDNTBeforeWithdrawal } = await donationToken.methods .balance_of_private(operatorAddress) .simulate({ from: operatorAddress }); expect(balanceDNTBeforeWithdrawal).toEqual(0n); @@ -164,7 +166,7 @@ describe('e2e_crowdfunding_and_claim', () => { // Withdraw nullifies the contract's own token notes, which requires its nullifier key. .send({ from: operatorAddress, additionalScopes: [crowdfundingContract.address] }); - const balanceDNTAfterWithdrawal = await donationToken.methods + const { result: balanceDNTAfterWithdrawal } = await donationToken.methods .balance_of_private(operatorAddress) .simulate({ from: operatorAddress }); diff --git a/yarn-project/end-to-end/src/e2e_partial_notes.test.ts b/yarn-project/end-to-end/src/e2e_partial_notes.test.ts index 104d7ef3b215..8742a20129a0 100644 --- a/yarn-project/end-to-end/src/e2e_partial_notes.test.ts +++ b/yarn-project/end-to-end/src/e2e_partial_notes.test.ts @@ -43,7 +43,8 @@ describe('partial notes', () => { it('mint to private', async () => { await mintTokensToPrivate(token0, adminAddress, liquidityProviderAddress, INITIAL_TOKEN_BALANCE); expect( - await token0.methods.balance_of_private(liquidityProviderAddress).simulate({ from: liquidityProviderAddress }), + (await token0.methods.balance_of_private(liquidityProviderAddress).simulate({ from: liquidityProviderAddress })) + .result, ).toEqual(INITIAL_TOKEN_BALANCE); }); }); diff --git a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts index b30a5cefdfe8..5c8e4e2fdfc2 100644 --- a/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts +++ b/yarn-project/end-to-end/src/e2e_scope_isolation.test.ts @@ -32,7 +32,7 @@ describe('e2e scope isolation', () => { describe('external private', () => { it('owner can read own notes', async () => { - const value = await contract.methods.read_note(alice).simulate({ from: alice }); + const { result: value } = await contract.methods.read_note(alice).simulate({ from: alice }); expect(value).toEqual(ALICE_NOTE_VALUE); }); @@ -47,8 +47,8 @@ describe('e2e scope isolation', () => { }); it('each account can access their isolated state on a shared wallet', async () => { - const aliceValue = await contract.methods.read_note(alice).simulate({ from: alice }); - const bobValue = await contract.methods.read_note(bob).simulate({ from: bob }); + const { result: aliceValue } = await contract.methods.read_note(alice).simulate({ from: alice }); + const { result: bobValue } = await contract.methods.read_note(bob).simulate({ from: bob }); expect(aliceValue).toEqual(ALICE_NOTE_VALUE); expect(bobValue).toEqual(BOB_NOTE_VALUE); @@ -57,7 +57,7 @@ describe('e2e scope isolation', () => { describe('external utility', () => { it('owner can read own notes', async () => { - const value = await contract.methods.read_note_utility(alice).simulate({ from: alice }); + const { result: value } = await contract.methods.read_note_utility(alice).simulate({ from: alice }); expect(value).toEqual(ALICE_NOTE_VALUE); }); @@ -74,8 +74,8 @@ describe('e2e scope isolation', () => { }); it('each account can access their isolated state on a shared wallet', async () => { - const aliceValue = await contract.methods.read_note_utility(alice).simulate({ from: alice }); - const bobValue = await contract.methods.read_note_utility(bob).simulate({ from: bob }); + const { result: aliceValue } = await contract.methods.read_note_utility(alice).simulate({ from: alice }); + const { result: bobValue } = await contract.methods.read_note_utility(bob).simulate({ from: bob }); expect(aliceValue).toEqual(ALICE_NOTE_VALUE); expect(bobValue).toEqual(BOB_NOTE_VALUE); diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 22c8a8b89713..6d2f58a7f37d 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -62,11 +62,13 @@ describe('e2e_state_vars', () => { // 2. A constrained private function that calls another private function that reads. // The indirect, adds 1 to the point to ensure that we are returning the correct value. - const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_private(), - contract.methods.get_public_immutable_constrained_private_indirect(), - contract.methods.get_public_immutable(), - ]).simulate({ from: defaultAccountAddress }); + const [a, b, c] = ( + await new BatchCall(wallet, [ + contract.methods.get_public_immutable_constrained_private(), + contract.methods.get_public_immutable_constrained_private_indirect(), + contract.methods.get_public_immutable(), + ]).simulate({ from: defaultAccountAddress }) + ).map((r: any) => r.result); expect(a).toEqual(c); expect(b).toEqual({ account: c.account, value: c.value + 1n }); @@ -79,11 +81,13 @@ describe('e2e_state_vars', () => { // 2. A constrained public function that calls another public function that reads. // The indirect, adds 1 to the point to ensure that we are returning the correct value. - const [a, b, c] = await new BatchCall(wallet, [ - contract.methods.get_public_immutable_constrained_public(), - contract.methods.get_public_immutable_constrained_public_indirect(), - contract.methods.get_public_immutable(), - ]).simulate({ from: defaultAccountAddress }); + const [a, b, c] = ( + await new BatchCall(wallet, [ + contract.methods.get_public_immutable_constrained_public(), + contract.methods.get_public_immutable_constrained_public_indirect(), + contract.methods.get_public_immutable(), + ]).simulate({ from: defaultAccountAddress }) + ).map((r: any) => r.result); expect(a).toEqual(c); expect(b).toEqual({ account: c.account, value: c.value + 1n }); @@ -95,10 +99,10 @@ describe('e2e_state_vars', () => { // Reads the value using a utility function checking the return values with: // 1. A constrained public function that reads 5 times directly (going beyond the previous 4 Field return value) - const a = await contract.methods + const { result: a } = await contract.methods .get_public_immutable_constrained_public_multiple() .simulate({ from: defaultAccountAddress }); - const c = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); + const { result: c } = await contract.methods.get_public_immutable().simulate({ from: defaultAccountAddress }); expect(a).toEqual([c, c, c, c, c]); }); @@ -115,9 +119,11 @@ describe('e2e_state_vars', () => { describe('PrivateMutable', () => { it('fail to read uninitialized PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); await expect( contract.methods.get_private_mutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }), @@ -126,9 +132,11 @@ describe('e2e_state_vars', () => { it('initialize PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); // Send the transaction and wait for it to be mined (wait function throws if the tx is not mined) const { receipt: txReceipt } = await contract.methods @@ -140,33 +148,41 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the initializer expect(txEffects?.data.nullifiers.length).toEqual(2); expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('fail to reinitialize', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); await expect( contract.methods.initialize_private(RANDOMNESS, VALUE).send({ from: defaultAccountAddress }), ).rejects.toThrow(); expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('read initialized PrivateMutable', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: { value }, @@ -176,9 +192,11 @@ describe('e2e_state_vars', () => { it('replace with same value', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) @@ -202,9 +220,11 @@ describe('e2e_state_vars', () => { it('replace PrivateMutable with other values', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { receipt: txReceipt } = await contract.methods .update_private_mutable(RANDOMNESS + 2n, VALUE + 1n) @@ -224,9 +244,11 @@ describe('e2e_state_vars', () => { it('replace PrivateMutable dependent on prior value', async () => { expect( - await contract.methods - .is_private_mutable_initialized(defaultAccountAddress) - .simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_private_mutable_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: noteBefore } = await contract.methods .get_private_mutable(defaultAccountAddress) @@ -251,7 +273,11 @@ describe('e2e_state_vars', () => { describe('PrivateImmutable', () => { it('fail to read uninitialized PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); await expect( contract.methods.view_private_immutable(defaultAccountAddress).simulate({ from: defaultAccountAddress }), @@ -260,7 +286,11 @@ describe('e2e_state_vars', () => { it('initialize PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(false); const { receipt: txReceipt } = await contract.methods .initialize_private_immutable(RANDOMNESS, VALUE) @@ -272,25 +302,41 @@ describe('e2e_state_vars', () => { // 1 for the tx, another for the initializer expect(txEffects?.data.nullifiers.length).toEqual(2); expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('fail to reinitialize', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); await expect( contract.methods.initialize_private_immutable(RANDOMNESS, VALUE).send({ from: defaultAccountAddress }), ).rejects.toThrow(); expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); }); it('read initialized PrivateImmutable', async () => { expect( - await contract.methods.is_priv_imm_initialized(defaultAccountAddress).simulate({ from: defaultAccountAddress }), + ( + await contract.methods + .is_priv_imm_initialized(defaultAccountAddress) + .simulate({ from: defaultAccountAddress }) + ).result, ).toEqual(true); const { result: { value }, diff --git a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts index 90bc22162730..ffa5688df3b5 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract/private_transfer_recursion.test.ts @@ -69,7 +69,9 @@ describe('e2e_token_contract private transfer recursion', () => { // We should have created two new notes, one for the recipient and one for the sender (with the change) expect(txEffects!.data.noteHashes.length).toBe(2); - const senderBalance = await asset.methods.balance_of_private(adminAddress).simulate({ from: adminAddress }); + const { result: senderBalance } = await asset.methods + .balance_of_private(adminAddress) + .simulate({ from: adminAddress }); expect(senderBalance).toEqual(expectedChange); const events = await wallet.getPrivateEvents(TokenContract.events.Transfer, { diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 4062e7def159..2e8426fa1351 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -131,7 +131,7 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness { } async getL2PublicBalanceOf(owner: AztecAddress) { - return await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner }); + return (await this.feeJuice.methods.balance_of_public(owner).simulate({ from: owner })).result; } async expectPublicBalanceOnL2(owner: AztecAddress, expectedBalance: bigint) { diff --git a/yarn-project/end-to-end/src/spartan/reorg.test.ts b/yarn-project/end-to-end/src/spartan/reorg.test.ts index 946f4f4a3b1a..e01d4ee06f20 100644 --- a/yarn-project/end-to-end/src/spartan/reorg.test.ts +++ b/yarn-project/end-to-end/src/spartan/reorg.test.ts @@ -32,16 +32,20 @@ const debugLogger = createLogger('e2e:spartan-test:reorg'); async function checkBalances(testAccounts: TestAccounts, mintAmount: bigint, totalAmountTransferred: bigint) { for (const acc of testAccounts.accounts) { expect( - await testAccounts.tokenContract.methods - .balance_of_public(acc) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(acc) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ).toBe(mintAmount - totalAmountTransferred); } expect( - await testAccounts.tokenContract.methods - .balance_of_public(testAccounts.recipientAddress) - .simulate({ from: testAccounts.tokenAdminAddress }), + ( + await testAccounts.tokenContract.methods + .balance_of_public(testAccounts.recipientAddress) + .simulate({ from: testAccounts.tokenAdminAddress }) + ).result, ).toBe(totalAmountTransferred * BigInt(testAccounts.accounts.length)); } From 64321b25c88ba9447efcdd5182f56d7537f49056 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 10:14:58 +0000 Subject: [PATCH 066/119] merge --- boxes/boxes/vanilla/scripts/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boxes/boxes/vanilla/scripts/deploy.ts b/boxes/boxes/vanilla/scripts/deploy.ts index b02edaa7e6b1..e0e0f971a739 100644 --- a/boxes/boxes/vanilla/scripts/deploy.ts +++ b/boxes/boxes/vanilla/scripts/deploy.ts @@ -71,7 +71,7 @@ async function deployContract(wallet: Wallet, deployer: AztecAddress) { const sponsoredPFCContract = await getSponsoredPFCContract(); - const contract = await PrivateVotingContract.deploy(wallet, deployer).send({ + const { contract } = await PrivateVotingContract.deploy(wallet, deployer).send({ from: deployer, contractAddressSalt: salt, fee: { From 70754f0b40ebe458722cf4503481dacc312a1c84 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 10:51:25 +0000 Subject: [PATCH 067/119] another e2e fix --- boxes/boxes/vanilla/app/embedded-wallet.ts | 2 +- .../aztec.js/src/contract/batch_call.test.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boxes/boxes/vanilla/app/embedded-wallet.ts b/boxes/boxes/vanilla/app/embedded-wallet.ts index 04951e383ee9..bbf32271f6b5 100644 --- a/boxes/boxes/vanilla/app/embedded-wallet.ts +++ b/boxes/boxes/vanilla/app/embedded-wallet.ts @@ -162,7 +162,7 @@ export class EmbeddedWallet extends EmbeddedWalletBase { wait: { timeout: 120 }, }; - const receipt = await deployMethod.send(deployOpts); + const { receipt } = await deployMethod.send(deployOpts); logger.info('Account deployed', receipt); diff --git a/yarn-project/aztec.js/src/contract/batch_call.test.ts b/yarn-project/aztec.js/src/contract/batch_call.test.ts index 976884e68745..740105f9c9fd 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.test.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.test.ts @@ -166,13 +166,13 @@ describe('BatchCall', () => { expect(results).toHaveLength(4); // First utility - decoded from Fr[] to bigint (single field returns the value directly, not as array) - expect(results[0]).toEqual(utilityResult1.result[0].toBigInt()); + expect(results[0].result).toEqual(utilityResult1.result[0].toBigInt()); // Results[1] will be the decoded private values (decoded from privateReturnValues) - expect(results[1]).toEqual(privateReturnValues.map(v => v.toBigInt())); // Private call (decoded) + expect(results[1].result).toEqual(privateReturnValues.map(v => v.toBigInt())); // Private call (decoded) // Second utility - decoded from Fr[] to bigint - expect(results[2]).toEqual(utilityResult2.result[0].toBigInt()); + expect(results[2].result).toEqual(utilityResult2.result[0].toBigInt()); // Results[3] will be the decoded public value (single value is returned directly, not as array) - expect(results[3]).toEqual(publicReturnValues[0].toBigInt()); // Public call (decoded) + expect(results[3].result).toEqual(publicReturnValues[0].toBigInt()); // Public call (decoded) }); it('should handle only utility calls without calling simulateTx', async () => { @@ -215,8 +215,8 @@ describe('BatchCall', () => { // Verify results - decoded from Fr[] to bigint expect(results).toHaveLength(2); - expect(results[0]).toEqual(utilityResult1.result[0].toBigInt()); - expect(results[1]).toEqual(utilityResult2.result[0].toBigInt()); + expect(results[0].result).toEqual(utilityResult1.result[0].toBigInt()); + expect(results[1].result).toEqual(utilityResult2.result[0].toBigInt()); }); it('should handle only private/public calls using wallet.batch with simulateTx', async () => { @@ -259,8 +259,8 @@ describe('BatchCall', () => { // Verify results (decoded) expect(results).toHaveLength(2); - expect(results[0]).toEqual(privateReturnValues[0].toBigInt()); // Single value returned directly - expect(results[1]).toEqual(publicReturnValues[0].toBigInt()); // Single value returned directly + expect(results[0].result).toEqual(privateReturnValues[0].toBigInt()); // Single value returned directly + expect(results[1].result).toEqual(publicReturnValues[0].toBigInt()); // Single value returned directly }); it('should handle empty batch', async () => { From cc8685e1f390b6a71830c1986cf5f4de0e486e0e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 18 Feb 2026 11:48:49 +0000 Subject: [PATCH 068/119] fix e2es after rebase --- .../end-to-end/src/e2e_sequencer/reload_keystore.test.ts | 4 ++-- yarn-project/end-to-end/src/spartan/setup_test_wallets.ts | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts index 09deed9db69d..667728c4056b 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/reload_keystore.test.ts @@ -130,7 +130,7 @@ describe('e2e_reload_keystore', () => { // Send a tx and verify the block uses the initial coinbase const deployer = new ContractDeployer(artifact, wallet); - const sentTx1 = await deployer.deploy(ownerAddress, ownerAddress, 1).send({ + const { txHash: sentTx1 } = await deployer.deploy(ownerAddress, ownerAddress, 1).send({ from: ownerAddress, contractAddressSalt: new Fr(1), skipClassPublication: true, @@ -197,7 +197,7 @@ describe('e2e_reload_keystore', () => { // Whichever validator is the proposer, its coinbase must be from the reloaded keystore. const allNewCoinbasesLower = newCoinbases.map(c => c.toString().toLowerCase()); - const sentTx2 = await deployer.deploy(ownerAddress, ownerAddress, 2).send({ + const { txHash: sentTx2 } = await deployer.deploy(ownerAddress, ownerAddress, 2).send({ from: ownerAddress, contractAddressSalt: new Fr(2), skipClassPublication: true, diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index e5deb3f6044d..bb4d7328d90f 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -138,11 +138,8 @@ async function deployAccountWithDiagnostics( const deployMethod = await account.getDeployMethod(); let txHash; try { - txHash = await deployMethod.send({ - from: AztecAddress.ZERO, - fee: { paymentMethod }, - wait: NO_WAIT, - }); + const deployResult = await deployMethod.send({ from: AztecAddress.ZERO, fee: { paymentMethod }, wait: NO_WAIT }); + txHash = deployResult.txHash; await waitForTx(aztecNode, txHash, { timeout: 2400 }); logger.info(`${accountLabel} deployed at ${account.address}`); } catch (error) { From 1e855a5c425e17127781643fbbee19dfae4e8d69 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 17:11:23 +0000 Subject: [PATCH 069/119] bring migration notes up to date --- docs/docs-developers/docs/resources/migration_notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 7cf68e2b630f..62f03e582f01 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -81,6 +81,7 @@ If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` **Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. +<<<<<<< HEAD ### [Aztec.js] Removed `SingleKeyAccountContract` The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAccountContract` have been removed. This contract was insecure: it used `ivpk_m` (incoming viewing public key) as its Schnorr signing key, meaning anyone who received a user's viewing key could sign transactions on their behalf. @@ -96,6 +97,8 @@ The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAcco **Impact**: If you were using `@aztec/accounts/single_key`, switch to `@aztec/accounts/schnorr` which uses separate keys for encryption and authentication. +======= +>>>>>>> 37bca03c41 (bring migration notes up to date) ### `aztec new` and `aztec init` now create a 2-crate workspace `aztec new` and `aztec init` now create a workspace with two crates instead of a single contract crate: From 59f75e48483c9b865ea50987dd7264912b9f2ea6 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 18:09:43 +0000 Subject: [PATCH 070/119] fix lint issue --- yarn-project/aztec.js/src/contract/deploy_method.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index aa5175bd3ab6..ed0de5ba6143 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -9,7 +9,13 @@ import { getContractInstanceFromInstantiationParams, } from '@aztec/stdlib/contract'; import type { PublicKeys } from '@aztec/stdlib/keys'; -import { type Capsule, HashedValues, type OffchainEffect, type TxProfileResult, type TxReceipt } from '@aztec/stdlib/tx'; +import { + type Capsule, + HashedValues, + type OffchainEffect, + type TxProfileResult, + type TxReceipt, +} from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import { publishContractClass } from '../deployment/publish_class.js'; From 3711ec83fb8e9e5c63b26c886ccee39a1c91a50b Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 24 Feb 2026 18:30:03 +0000 Subject: [PATCH 071/119] fix new e2es --- yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts | 6 +++--- yarn-project/end-to-end/src/e2e_custom_message.test.ts | 6 +++--- yarn-project/end-to-end/src/e2e_pxe.test.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts index 4256ab0ca551..2bc0799dacde 100644 --- a/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts +++ b/yarn-project/end-to-end/src/composed/ha/e2e_ha_full.test.ts @@ -604,12 +604,12 @@ describe('HA Full Setup', () => { skipInstancePublication: true, wait: { returnReceipt: true }, }); - expect(receipt.blockNumber).toBeDefined(); - const [block] = await aztecNode.getCheckpointedBlocks(receipt.blockNumber!, 1); + expect(receipt.receipt.blockNumber).toBeDefined(); + const [block] = await aztecNode.getCheckpointedBlocks(receipt.receipt.blockNumber!, 1); const [cp] = await aztecNode.getCheckpoints(block!.checkpointNumber, 1); const att = cp.attestations.filter(a => !a.signature.isEmpty()); expect(att.length).toBeGreaterThanOrEqual(quorum); - logger.info(`Phase 2: block ${receipt.blockNumber}, ${att.length} attestations (quorum ${quorum})`); + logger.info(`Phase 2: block ${receipt.receipt.blockNumber}, ${att.length} attestations (quorum ${quorum})`); } finally { // Restore each node's saved initial keystore so subsequent tests see original state for (let i = 0; i < NODE_COUNT; i++) { diff --git a/yarn-project/end-to-end/src/e2e_custom_message.test.ts b/yarn-project/end-to-end/src/e2e_custom_message.test.ts index ce29ec91dae1..0ff387019d2e 100644 --- a/yarn-project/end-to-end/src/e2e_custom_message.test.ts +++ b/yarn-project/end-to-end/src/e2e_custom_message.test.ts @@ -26,7 +26,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { accounts: [account], } = await setup(1)); await ensureAccountContractsPublished(wallet, [account]); - contract = await CustomMessageContract.deploy(wallet).send({ from: account }); + ({ contract } = await CustomMessageContract.deploy(wallet).send({ from: account })); }); afterAll(() => teardown()); @@ -34,7 +34,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { it('reassembles a multi-log event from multiple private logs', async () => { const values = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; - const tx = await contract.methods + const { receipt: tx } = await contract.methods .emit_multi_log_event(values[0], values[1], values[2], values[3], account) .send({ from: account }); @@ -56,7 +56,7 @@ describe('CustomMessage - Multi-Log Pattern', () => { const valuesA = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; const valuesB = [Fr.random(), Fr.random(), Fr.random(), Fr.random()]; - const tx = await new BatchCall(wallet, [ + const { receipt: tx } = await new BatchCall(wallet, [ contract.methods.emit_multi_log_event(valuesA[0], valuesA[1], valuesA[2], valuesA[3], account), contract.methods.emit_multi_log_event(valuesB[0], valuesB[1], valuesB[2], valuesB[3], account), ]).send({ from: account }); diff --git a/yarn-project/end-to-end/src/e2e_pxe.test.ts b/yarn-project/end-to-end/src/e2e_pxe.test.ts index 5f86fc373e11..efeb60c27cbe 100644 --- a/yarn-project/end-to-end/src/e2e_pxe.test.ts +++ b/yarn-project/end-to-end/src/e2e_pxe.test.ts @@ -21,7 +21,7 @@ describe('e2e_pxe', () => { wallet, accounts: [defaultAccountAddress], } = await setup()); - contract = await TestContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract } = await TestContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); From bb6d848dd78628a73ced6a450fc20925c40b4cef Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 11:07:26 +0000 Subject: [PATCH 072/119] separate offchainMessages from offchainEffects --- yarn-project/aztec.js/src/api/contract.ts | 4 + .../aztec.js/src/contract/batch_call.ts | 3 +- .../aztec.js/src/contract/contract.test.ts | 2 +- .../contract/contract_function_interaction.ts | 12 +-- .../aztec.js/src/contract/deploy_method.ts | 22 ++--- .../src/contract/interaction_options.test.ts | 81 +++++++++++++++++++ .../src/contract/interaction_options.ts | 75 ++++++++++++++--- .../wallet-sdk/src/base-wallet/base_wallet.ts | 13 ++- 8 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 yarn-project/aztec.js/src/contract/interaction_options.test.ts diff --git a/yarn-project/aztec.js/src/api/contract.ts b/yarn-project/aztec.js/src/api/contract.ts index 1dc15515abc8..d07b28e4378c 100644 --- a/yarn-project/aztec.js/src/api/contract.ts +++ b/yarn-project/aztec.js/src/api/contract.ts @@ -48,6 +48,8 @@ export { ContractFunctionInteraction } from '../contract/contract_function_inter export { NO_WAIT, type NoWait, + type OffchainMessage, + type OffchainOutput, type RequestInteractionOptions, type SendInteractionOptions, type ProfileInteractionOptions, @@ -59,6 +61,8 @@ export { type SimulationReturn, type TxSendResultImmediate, type TxSendResultMined, + emptyOffchainOutput, + extractOffchainOutput, toProfileOptions, toSendOptions, toSimulateOptions, diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index 1ec44f3f3d25..c1d5d183880c 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -6,6 +6,7 @@ import { BaseContractInteraction } from './base_contract_interaction.js'; import { type RequestInteractionOptions, type SimulateInteractionOptions, + extractOffchainOutput, toSimulateOptions, } from './interaction_options.js'; @@ -132,7 +133,7 @@ export class BatchCall extends BaseContractInteraction { results[callIndex] = { result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], - offchainEffects: simulatedTx.offchainEffects, + ...extractOffchainOutput(simulatedTx.offchainEffects), }; }); } diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 7f815be7c056..36519b918f7c 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -136,7 +136,7 @@ describe('Contract Class', () => { wallet.simulateTx.mockResolvedValue(mockTxSimulationResult); account.createTxExecutionRequest.mockResolvedValue(mockTxRequest); wallet.registerContract.mockResolvedValue(contractInstance); - wallet.sendTx.mockResolvedValue({ receipt: mockTxReceipt, offchainEffects: [] }); + wallet.sendTx.mockResolvedValue({ receipt: mockTxReceipt, offchainEffects: [], offchainMessages: [] }); wallet.executeUtility.mockResolvedValue(mockUtilityResultValue); }); diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 743c9b99566b..ad2a8449024c 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -19,6 +19,8 @@ import { type RequestInteractionOptions, type SimulateInteractionOptions, type SimulationReturn, + emptyOffchainOutput, + extractOffchainOutput, toProfileOptions, toSimulateOptions, } from './interaction_options.js'; @@ -114,11 +116,11 @@ export class ContractFunctionInteraction extends BaseContractInteraction { if (options.includeMetadata) { return { stats: utilityResult.stats, - offchainEffects: [], + ...emptyOffchainOutput(), result: returnValue, }; } - return { result: returnValue, offchainEffects: [] }; + return { result: returnValue, ...emptyOffchainOutput() }; } const executionPayload = await this.request(options); @@ -140,7 +142,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } const returnValue = rawReturnValues ? decodeFromAbi(this.functionDao.returnTypes, rawReturnValues) : []; - const offchainEffects = simulatedTx.offchainEffects; + const offchainOutput = extractOffchainOutput(simulatedTx.offchainEffects); if (options.includeMetadata || options.fee?.estimateGas) { const { gasLimits, teardownGasLimits } = getGasLimits(simulatedTx, options.fee?.estimatedGasPadding); @@ -149,12 +151,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction { ); return { stats: simulatedTx.stats, - offchainEffects, + ...offchainOutput, result: returnValue, estimatedGas: { gasLimits, teardownGasLimits }, }; } - return { result: returnValue, offchainEffects }; + return { result: returnValue, ...offchainOutput }; } /** diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index ed0de5ba6143..92ef38c1e0ef 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -9,13 +9,7 @@ import { getContractInstanceFromInstantiationParams, } from '@aztec/stdlib/contract'; import type { PublicKeys } from '@aztec/stdlib/keys'; -import { - type Capsule, - HashedValues, - type OffchainEffect, - type TxProfileResult, - type TxReceipt, -} from '@aztec/stdlib/tx'; +import { type Capsule, HashedValues, type TxProfileResult, type TxReceipt } from '@aztec/stdlib/tx'; import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx'; import { publishContractClass } from '../deployment/publish_class.js'; @@ -28,6 +22,7 @@ import { getGasLimits } from './get_gas_limits.js'; import { NO_WAIT, type NoWait, + type OffchainOutput, type ProfileInteractionOptions, type RequestInteractionOptions, type SendInteractionOptionsWithoutWait, @@ -35,6 +30,7 @@ import { type SimulationReturn, type TxSendResultImmediate, type TxSendResultMined, + extractOffchainOutput, toProfileOptions, toSendOptions, toSimulateOptions, @@ -149,9 +145,7 @@ export type DeployResultMined = { contract: TContract; /** The deploy transaction receipt. */ receipt: DeployTxReceipt; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; -}; +} & OffchainOutput; /** Conditional return type for deploy based on wait options. */ export type DeployReturn = W extends NoWait @@ -374,7 +368,7 @@ export class DeployMethod extends return result; } - const { receipt, offchainEffects } = await this.wallet.sendTx( + const { receipt, ...offchainOutput } = await this.wallet.sendTx( executionPayload, sendOptions as SendOptions, ); @@ -386,10 +380,10 @@ export class DeployMethod extends // Return full receipt if requested, otherwise just the contract if (options.wait && typeof options.wait === 'object' && options.wait.returnReceipt) { - return { receipt: { ...receipt, contract, instance }, offchainEffects }; + return { receipt: { ...receipt, contract, instance }, ...offchainOutput }; } - return { contract, receipt, offchainEffects }; + return { contract, receipt, ...offchainOutput }; } /** @@ -431,7 +425,7 @@ export class DeployMethod extends ); return { stats: simulatedTx.stats!, - offchainEffects: simulatedTx.offchainEffects, + ...extractOffchainOutput(simulatedTx.offchainEffects), result: undefined, estimatedGas: { gasLimits, teardownGasLimits }, }; diff --git a/yarn-project/aztec.js/src/contract/interaction_options.test.ts b/yarn-project/aztec.js/src/contract/interaction_options.test.ts new file mode 100644 index 000000000000..7818299a3854 --- /dev/null +++ b/yarn-project/aztec.js/src/contract/interaction_options.test.ts @@ -0,0 +1,81 @@ +import { Fr } from '@aztec/foundation/curves/bn254'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { OFFCHAIN_MESSAGE_IDENTIFIER, type OffchainEffect } from '@aztec/stdlib/tx'; + +import { extractOffchainOutput } from './interaction_options.js'; + +describe('extractOffchainOutput', () => { + const makeEffect = (data: Fr[], contractAddress?: AztecAddress): OffchainEffect => ({ + data, + contractAddress: contractAddress ?? AztecAddress.fromField(Fr.random()), + }); + + const makeMessageEffect = async (recipient?: AztecAddress, payload?: Fr[], contractAddress?: AztecAddress) => + makeEffect( + [ + OFFCHAIN_MESSAGE_IDENTIFIER, + (recipient ?? (await AztecAddress.random())).toField(), + ...(payload ?? [Fr.random()]), + ], + contractAddress, + ); + + it('returns empty output for empty input', () => { + const result = extractOffchainOutput([]); + expect(result.offchainEffects).toEqual([]); + expect(result.offchainMessages).toEqual([]); + }); + + it('keeps non-message effects as-is', () => { + const effects = [makeEffect([Fr.random(), Fr.random()]), makeEffect([Fr.random()])]; + const result = extractOffchainOutput(effects); + expect(result.offchainEffects).toEqual(effects); + expect(result.offchainMessages).toEqual([]); + }); + + it('extracts a message effect into offchainMessages', async () => { + const recipient = await AztecAddress.random(); + const payload = [Fr.random(), Fr.random(), Fr.random()]; + const contractAddress = await AztecAddress.random(); + const effect = await makeMessageEffect(recipient, payload, contractAddress); + + const result = extractOffchainOutput([effect]); + + expect(result.offchainEffects).toEqual([]); + expect(result.offchainMessages).toHaveLength(1); + expect(result.offchainMessages[0]).toEqual({ + recipient, + payload, + contractAddress, + }); + }); + + it('splits a mixed array of effects and messages', async () => { + const plainEffect1 = makeEffect([Fr.random()]); + const plainEffect2 = makeEffect([Fr.random(), Fr.random()]); + const messageEffect = await makeMessageEffect(); + + const result = extractOffchainOutput([plainEffect1, messageEffect, plainEffect2]); + + expect(result.offchainEffects).toEqual([plainEffect1, plainEffect2]); + expect(result.offchainMessages).toHaveLength(1); + }); + + it('handles multiple message effects', async () => { + const msg1 = await makeMessageEffect(); + const msg2 = await makeMessageEffect(); + + const result = extractOffchainOutput([msg1, msg2]); + + expect(result.offchainEffects).toEqual([]); + expect(result.offchainMessages).toHaveLength(2); + }); + + it('does not treat an effect as a message if data has only the identifier (no recipient)', () => { + const effect = makeEffect([OFFCHAIN_MESSAGE_IDENTIFIER]); + const result = extractOffchainOutput([effect]); + + expect(result.offchainEffects).toEqual([effect]); + expect(result.offchainMessages).toEqual([]); + }); +}); diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 7864f19b2bcc..be2db7f6eb22 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -1,8 +1,16 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; import type { FieldsOf } from '@aztec/foundation/types'; import type { AuthWitness } from '@aztec/stdlib/auth-witness'; -import type { AztecAddress } from '@aztec/stdlib/aztec-address'; +import { AztecAddress } from '@aztec/stdlib/aztec-address'; import type { GasSettings } from '@aztec/stdlib/gas'; -import type { Capsule, OffchainEffect, SimulationStats, TxHash, TxReceipt } from '@aztec/stdlib/tx'; +import { + type Capsule, + OFFCHAIN_MESSAGE_IDENTIFIER, + type OffchainEffect, + type SimulationStats, + type TxHash, + type TxReceipt, +} from '@aztec/stdlib/tx'; import type { FeePaymentMethod } from '../fee/fee_payment_method.js'; import type { ProfileOptions, SendOptions, SimulateOptions } from '../wallet/index.js'; @@ -131,37 +139,80 @@ export type ProfileInteractionOptions = SimulateInteractionOptions & { skipProofGeneration?: boolean; }; +/** A message emitted during execution or proving, to be delivered offchain. */ +export type OffchainMessage = { + /** The intended recipient of the message. */ + recipient: AztecAddress; + /** The message payload (typically encrypted). */ + payload: Fr[]; + /** The contract that emitted the message. */ + contractAddress: AztecAddress; +}; + +/** Groups all unproven outputs from private execution that are returned to the client. */ +export type OffchainOutput = { + /** Raw offchain effects emitted during execution. */ + offchainEffects: OffchainEffect[]; + /** Messages emitted during execution, to be delivered offchain. */ + offchainMessages: OffchainMessage[]; +}; + +/** + * Splits an array of offchain effects into decoded offchain messages and remaining effects. + * Effects whose data starts with `OFFCHAIN_MESSAGE_IDENTIFIER` are parsed as messages and removed + * from the effects array. + */ +export function extractOffchainOutput(effects: OffchainEffect[]): OffchainOutput { + const offchainEffects: OffchainEffect[] = []; + const offchainMessages: OffchainMessage[] = []; + + for (const effect of effects) { + if (effect.data.length >= 2 && effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER)) { + offchainMessages.push({ + recipient: AztecAddress.fromField(effect.data[1]), + payload: effect.data.slice(2), + contractAddress: effect.contractAddress, + }); + } else { + offchainEffects.push(effect); + } + } + + return { offchainEffects, offchainMessages }; +} + +/** + * Returns an empty `OffchainOutput` (no effects, no messages). + */ +export function emptyOffchainOutput(): OffchainOutput { + return { offchainEffects: [], offchainMessages: [] }; +} + /** * Represents the result of a simulation. - * Always includes the return value and offchain effects. + * Always includes the return value and offchain output. * When `includeMetadata` or `fee.estimateGas` is set, also includes stats and gas estimation. */ export type SimulationReturn = { /** Return value of the function */ result: any; - /** Offchain effects generated during the simulation */ - offchainEffects: OffchainEffect[]; /** Additional stats about the simulation. Present when `includeMetadata` is set. */ stats?: SimulationStats; /** Gas estimation results. Present when `includeMetadata` or `fee.estimateGas` is set. */ estimatedGas?: Pick; -}; +} & OffchainOutput; /** Result of sendTx when not waiting for mining. */ export type TxSendResultImmediate = { /** The hash of the sent transaction. */ txHash: TxHash; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; -}; +} & OffchainOutput; /** Result of sendTx when waiting for mining. */ export type TxSendResultMined = { /** The transaction receipt. */ receipt: TReturn; - /** Offchain effects generated during proving. */ - offchainEffects: OffchainEffect[]; -}; +} & OffchainOutput; /** * Represents the result type of sending a transaction. diff --git a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts index 8551e012e7e7..484ed4b37edb 100644 --- a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts +++ b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts @@ -1,6 +1,11 @@ import type { Account } from '@aztec/aztec.js/account'; import type { CallIntent, IntentInnerHash } from '@aztec/aztec.js/authorization'; -import { type InteractionWaitOptions, NO_WAIT, type SendReturn } from '@aztec/aztec.js/contracts'; +import { + type InteractionWaitOptions, + NO_WAIT, + type SendReturn, + extractOffchainOutput, +} from '@aztec/aztec.js/contracts'; import type { FeePaymentMethod } from '@aztec/aztec.js/fee'; import { waitForTx } from '@aztec/aztec.js/node'; import type { @@ -383,7 +388,7 @@ export abstract class BaseWallet implements Wallet { const feeOptions = await this.completeFeeOptions(opts.from, executionPayload.feePayer, opts.fee?.gasSettings); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(executionPayload, opts.from, feeOptions); const provenTx = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes)); - const offchainEffects = provenTx.getOffchainEffects(); + const offchainOutput = extractOffchainOutput(provenTx.getOffchainEffects()); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { @@ -397,7 +402,7 @@ export abstract class BaseWallet implements Wallet { // If wait is NO_WAIT, return txHash immediately if (opts.wait === NO_WAIT) { - return { txHash, offchainEffects } as SendReturn; + return { txHash, ...offchainOutput } as SendReturn; } // Otherwise, wait for the full receipt (default behavior on wait: undefined) @@ -409,7 +414,7 @@ export abstract class BaseWallet implements Wallet { await displayDebugLogs(receipt.debugLogs, this.getContractName.bind(this)); } - return { receipt, offchainEffects } as SendReturn; + return { receipt, ...offchainOutput } as SendReturn; } /** From e24c4db604a0da1f16100b2bb1f63db95973154c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 12:45:56 +0000 Subject: [PATCH 073/119] migration_notes --- docs/docs-developers/docs/resources/migration_notes.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 62f03e582f01..7cf68e2b630f 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -81,7 +81,6 @@ If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` **Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. -<<<<<<< HEAD ### [Aztec.js] Removed `SingleKeyAccountContract` The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAccountContract` have been removed. This contract was insecure: it used `ivpk_m` (incoming viewing public key) as its Schnorr signing key, meaning anyone who received a user's viewing key could sign transactions on their behalf. @@ -97,8 +96,6 @@ The `SchnorrSingleKeyAccount` contract and its TypeScript wrapper `SingleKeyAcco **Impact**: If you were using `@aztec/accounts/single_key`, switch to `@aztec/accounts/schnorr` which uses separate keys for encryption and authentication. -======= ->>>>>>> 37bca03c41 (bring migration notes up to date) ### `aztec new` and `aztec init` now create a 2-crate workspace `aztec new` and `aztec init` now create a workspace with two crates instead of a single contract crate: From 71f0bac4fca36bf6d363e6a8dac0e9a810dcf0aa Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 12:59:33 +0000 Subject: [PATCH 074/119] fix bootstrap --- yarn-project/bot/src/factory.ts | 6 +++++- yarn-project/end-to-end/src/e2e_abi_types.test.ts | 2 +- yarn-project/end-to-end/src/e2e_escrow_contract.test.ts | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 0d7aa408e476..e22663cebe43 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -497,7 +497,11 @@ export class BotFactory { // PrivateToken's mint accesses contract-level private storage vars (admin, total_supply). const additionalScopes = isStandardToken ? undefined : [token.address]; await this.withNoMinTxsPerBlock(async () => { - const { txHash } = await new BatchCall(token.wallet, calls).send({ from: minter, additionalScopes, wait: NO_WAIT }); + const { txHash } = await new BatchCall(token.wallet, calls).send({ + from: minter, + additionalScopes, + wait: NO_WAIT, + }); this.log.info(`Sent token mint tx with hash ${txHash.toString()}`); return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds }); }); diff --git a/yarn-project/end-to-end/src/e2e_abi_types.test.ts b/yarn-project/end-to-end/src/e2e_abi_types.test.ts index d970d9c105b2..d2d3f40d7dd1 100644 --- a/yarn-project/end-to-end/src/e2e_abi_types.test.ts +++ b/yarn-project/end-to-end/src/e2e_abi_types.test.ts @@ -29,7 +29,7 @@ describe('AbiTypes', () => { wallet, accounts: [defaultAccountAddress], } = await setup(1)); - abiTypesContract = await AbiTypesContract.deploy(wallet).send({ from: defaultAccountAddress }); + ({ contract: abiTypesContract } = await AbiTypesContract.deploy(wallet).send({ from: defaultAccountAddress })); }); afterAll(() => teardown()); diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index 7da5c6cd9faa..949e5eb6e15c 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -42,7 +42,10 @@ describe('e2e_escrow_contract', () => { const escrowInstance = await escrowDeployment.getInstance(); await wallet.registerContract(escrowInstance, EscrowContract.artifact, escrowSecretKey); // The contract constructor initializes private storage vars that need the contract's own nullifier key. - ({ contract: escrowContract } = await escrowDeployment.send({ from: owner, additionalScopes: [escrowInstance.address] })); + ({ contract: escrowContract } = await escrowDeployment.send({ + from: owner, + additionalScopes: [escrowInstance.address], + })); logger.info(`Escrow contract deployed at ${escrowContract.address}`); // Deploy Token contract and mint funds for the escrow contract From 2aa9f7b515ec0114345007dd4ac875e8ecf09552 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 13:09:23 +0000 Subject: [PATCH 075/119] update migration notes --- .../docs/resources/migration_notes.md | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 7cf68e2b630f..697d986c657a 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -11,9 +11,14 @@ Aztec is in active development. Each version may introduce breaking changes that ### [Aztec.js] `simulate()`, `send()`, and deploy return types changed to always return objects -All SDK interaction methods now return structured objects that include `offchainEffects` alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. +All SDK interaction methods now return structured objects that include offchain output alongside the primary result. This affects `.simulate()`, `.send()`, deploy `.send()`, and `Wallet.sendTx()`. -**`simulate()` — always returns `{ result, offchainEffects }` object:** +The offchain output includes two fields: + +- `offchainEffects` — raw offchain effects emitted during execution, other than `offchainMessages` +- `offchainMessages` — decoded messages intended for specific recipients + +**`simulate()` — always returns `{ result, offchainEffects, offchainMessages }` object:** ```diff - const value = await contract.methods.foo(args).simulate({ from: sender }); @@ -34,21 +39,30 @@ When using `includeMetadata` or `fee.estimateGas`, `stats` and `estimatedGas` ar `SimulationReturn` is no longer a generic conditional type — it's a single flat type with optional `stats` and `estimatedGas` fields. -**`send()` — returns `{ receipt, offchainEffects }` object:** +**`send()` — returns `{ receipt, offchainEffects, offchainMessages }` object:** ```diff - const receipt = await contract.methods.foo(args).send({ from: sender }); + const { receipt } = await contract.methods.foo(args).send({ from: sender }); ``` -When using `NO_WAIT`, returns `{ txHash, offchainEffects }` instead of a bare `TxHash`: +When using `NO_WAIT`, returns `{ txHash, offchainEffects, offchainMessages }` instead of a bare `TxHash`: ```diff - const txHash = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); + const { txHash } = await contract.methods.foo(args).send({ from: sender, wait: NO_WAIT }); ``` -**Deploy — returns `{ contract, receipt, offchainEffects }` object:** +Offchain messages emitted by the transaction are available on the result: + +```typescript +const { receipt, offchainMessages } = await contract.methods.foo(args).send({ from: sender }); +for (const msg of offchainMessages) { + console.log(`Message for ${msg.recipient} from contract ${msg.contractAddress}:`, msg.payload); +} +``` + +**Deploy — returns `{ contract, receipt, offchainEffects, offchainMessages }` object:** ```diff - const myContract = await MyContract.deploy(wallet, ...args).send({ from: sender }); @@ -59,27 +73,29 @@ The deploy receipt is also available via `receipt` if needed (e.g. for `receipt. **Custom wallet implementations — `sendTx()` must return objects:** -If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return `{ txHash, offchainEffects }` (for `NO_WAIT`) or `{ receipt, offchainEffects }` (when waiting): +If you implement the `Wallet` interface (or extend `BaseWallet`), the `sendTx()` method must now return objects that include offchain output. Use `extractOffchainOutput` to split raw effects into decoded messages and remaining effects: ```diff ++ import { extractOffchainOutput } from '@aztec/aztec.js/contracts'; + async sendTx(executionPayload, opts) { const provenTx = await this.pxe.proveTx(...); -+ const offchainEffects = provenTx.getOffchainEffects(); ++ const offchainOutput = extractOffchainOutput(provenTx.getOffchainEffects()); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); await this.aztecNode.sendTx(tx); if (opts.wait === NO_WAIT) { - return txHash; -+ return { txHash, offchainEffects }; ++ return { txHash, ...offchainOutput }; } const receipt = await waitForTx(this.aztecNode, txHash, opts.wait); - return receipt; -+ return { receipt, offchainEffects }; ++ return { receipt, ...offchainOutput }; } ``` -**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes. +**Impact**: Every call site that uses `.simulate()`, `.send()`, or deploy must destructure the result. This is a mechanical transformation. Custom wallet implementations must update `sendTx()` to return the new object shapes, using `extractOffchainOutput` to decode offchain messages from raw effects. ### [Aztec.js] Removed `SingleKeyAccountContract` From 961492034c292940db283439ae774ed2206aa1fe Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 13:20:24 +0000 Subject: [PATCH 076/119] fix e2e --- yarn-project/end-to-end/src/e2e_abi_types.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_abi_types.test.ts b/yarn-project/end-to-end/src/e2e_abi_types.test.ts index d2d3f40d7dd1..a7702f78c720 100644 --- a/yarn-project/end-to-end/src/e2e_abi_types.test.ts +++ b/yarn-project/end-to-end/src/e2e_abi_types.test.ts @@ -35,13 +35,13 @@ describe('AbiTypes', () => { afterAll(() => teardown()); it('passes public parameters', async () => { - const minResult = await abiTypesContract.methods + const { result: minResult } = await abiTypesContract.methods .return_public_parameters(false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }) .simulate({ from: defaultAccountAddress }); expect(minResult).toEqual([false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }]); - const maxResult = await abiTypesContract.methods + const { result: maxResult } = await abiTypesContract.methods .return_public_parameters(true, MAX_FIELD_VALUE, U64_MAX, I64_MAX, { w: MAX_FIELD_VALUE, x: true, @@ -60,13 +60,13 @@ describe('AbiTypes', () => { }); it('passes private parameters', async () => { - const minResult = await abiTypesContract.methods + const { result: minResult } = await abiTypesContract.methods .return_private_parameters(false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }) .simulate({ from: defaultAccountAddress }); expect(minResult).toEqual([false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }]); - const maxResult = await abiTypesContract.methods + const { result: maxResult } = await abiTypesContract.methods .return_private_parameters(true, MAX_FIELD_VALUE, U64_MAX, I64_MAX, { w: MAX_FIELD_VALUE, x: true, @@ -85,13 +85,13 @@ describe('AbiTypes', () => { }); it('passes utility parameters', async () => { - const minResult = await abiTypesContract.methods + const { result: minResult } = await abiTypesContract.methods .return_utility_parameters(false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }) .simulate({ from: defaultAccountAddress }); expect(minResult).toEqual([false, 0n, 0n, I64_MIN, { w: 0n, x: false, y: 0n, z: I64_MIN }]); - const maxResult = await abiTypesContract.methods + const { result: maxResult } = await abiTypesContract.methods .return_utility_parameters(true, MAX_FIELD_VALUE, U64_MAX, I64_MAX, { w: MAX_FIELD_VALUE, x: true, From 2532396974c385435a8bd700e5ceb7ea813a2a15 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 13:26:04 +0000 Subject: [PATCH 077/119] SimulationReturn -> SimulationResult --- yarn-project/aztec.js/src/api/contract.ts | 2 +- .../aztec.js/src/contract/contract_function_interaction.ts | 4 ++-- yarn-project/aztec.js/src/contract/deploy_method.ts | 4 ++-- yarn-project/aztec.js/src/contract/interaction_options.ts | 2 +- yarn-project/aztec.js/src/utils/authwit.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn-project/aztec.js/src/api/contract.ts b/yarn-project/aztec.js/src/api/contract.ts index d07b28e4378c..37481a99f94f 100644 --- a/yarn-project/aztec.js/src/api/contract.ts +++ b/yarn-project/aztec.js/src/api/contract.ts @@ -58,7 +58,7 @@ export { type InteractionWaitOptions, type GasSettingsOption, type SendReturn, - type SimulationReturn, + type SimulationResult, type TxSendResultImmediate, type TxSendResultMined, emptyOffchainOutput, diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index ad2a8449024c..30d553d55151 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -18,7 +18,7 @@ import { type ProfileInteractionOptions, type RequestInteractionOptions, type SimulateInteractionOptions, - type SimulationReturn, + type SimulationResult, emptyOffchainOutput, extractOffchainOutput, toProfileOptions, @@ -101,7 +101,7 @@ export class ContractFunctionInteraction extends BaseContractInteraction { */ public async simulate( options: SimulateInteractionOptions = {} as SimulateInteractionOptions, - ): Promise { + ): Promise { // docs:end:simulate if (this.functionDao.functionType == FunctionType.UTILITY) { const call = await this.getFunctionCall(); diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 92ef38c1e0ef..98249acefed4 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -27,7 +27,7 @@ import { type RequestInteractionOptions, type SendInteractionOptionsWithoutWait, type SimulationInteractionFeeOptions, - type SimulationReturn, + type SimulationResult, type TxSendResultImmediate, type TxSendResultMined, extractOffchainOutput, @@ -412,7 +412,7 @@ export class DeployMethod extends * @returns A simulation result object containing metadata of the execution, including gas * estimations (if requested via options), execution statistics and emitted offchain effects */ - public async simulate(options: SimulateDeployOptions): Promise { + public async simulate(options: SimulateDeployOptions): Promise { const executionPayload = await this.request(this.convertDeployOptionsToRequestOptions(options)); const simulatedTx = await this.wallet.simulateTx( executionPayload, diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index be2db7f6eb22..2554f944d9a0 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -193,7 +193,7 @@ export function emptyOffchainOutput(): OffchainOutput { * Always includes the return value and offchain output. * When `includeMetadata` or `fee.estimateGas` is set, also includes stats and gas estimation. */ -export type SimulationReturn = { +export type SimulationResult = { /** Return value of the function */ result: any; /** Additional stats about the simulation. Present when `includeMetadata` is set. */ diff --git a/yarn-project/aztec.js/src/utils/authwit.ts b/yarn-project/aztec.js/src/utils/authwit.ts index b9b889907ba5..67ac5edf627c 100644 --- a/yarn-project/aztec.js/src/utils/authwit.ts +++ b/yarn-project/aztec.js/src/utils/authwit.ts @@ -15,7 +15,7 @@ import type { SendInteractionOptionsWithoutWait, SendReturn, SimulateInteractionOptions, - SimulationReturn, + SimulationResult, TxSendResultMined, } from '../contract/interaction_options.js'; import type { Wallet } from '../wallet/index.js'; @@ -267,7 +267,7 @@ export class SetPublicAuthwitContractInteraction extends ContractFunctionInterac */ public override simulate( options: Omit = {} as Omit, - ): Promise { + ): Promise { return super.simulate({ ...options, from: this.from } as SimulateInteractionOptions); } From 548851c0ddb04144f7271f260372bc7da1d17130 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 13:39:29 +0000 Subject: [PATCH 078/119] update wallet schema --- yarn-project/aztec.js/src/wallet/wallet.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/yarn-project/aztec.js/src/wallet/wallet.ts b/yarn-project/aztec.js/src/wallet/wallet.ts index a08ca74ec867..0e48f2f42726 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.ts @@ -497,6 +497,17 @@ const OffchainEffectSchema = z.object({ contractAddress: schemas.AztecAddress, }); +const OffchainMessageSchema = z.object({ + recipient: schemas.AztecAddress, + payload: z.array(schemas.Fr), + contractAddress: schemas.AztecAddress, +}); + +const OffchainOutputSchema = z.object({ + offchainEffects: z.array(OffchainEffectSchema), + offchainMessages: z.array(OffchainMessageSchema), +}); + /** * Record of all wallet method schemas (excluding batch). * This is the single source of truth for method schemas - batch schemas are derived from this. @@ -542,8 +553,8 @@ const WalletMethodSchemas = { .args(ExecutionPayloadSchema, SendOptionsSchema) .returns( z.union([ - z.object({ txHash: TxHash.schema, offchainEffects: z.array(OffchainEffectSchema) }), - z.object({ receipt: TxReceipt.schema, offchainEffects: z.array(OffchainEffectSchema) }), + z.object({ txHash: TxHash.schema }).merge(OffchainOutputSchema), + z.object({ receipt: TxReceipt.schema }).merge(OffchainOutputSchema), ]), ), createAuthWit: z.function().args(schemas.AztecAddress, MessageHashOrIntentSchema).returns(AuthWitness.schema), From 63bea950204bb4a80dd142d9d89b482c230b5436 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 13:43:32 +0000 Subject: [PATCH 079/119] fix batch_call test --- yarn-project/aztec.js/src/contract/interaction_options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 2554f944d9a0..03efc5aa0b76 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -162,7 +162,7 @@ export type OffchainOutput = { * Effects whose data starts with `OFFCHAIN_MESSAGE_IDENTIFIER` are parsed as messages and removed * from the effects array. */ -export function extractOffchainOutput(effects: OffchainEffect[]): OffchainOutput { +export function extractOffchainOutput(effects: OffchainEffect[] = []): OffchainOutput { const offchainEffects: OffchainEffect[] = []; const offchainMessages: OffchainMessage[] = []; From 566b6953f96c1a005ffa46484c07992450501d1e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 14:03:57 +0000 Subject: [PATCH 080/119] fix units --- .../aztec.js/src/contract/batch_call.test.ts | 2 ++ .../src/contract/interaction_options.ts | 2 +- .../aztec.js/src/wallet/wallet.test.ts | 28 +++++++++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/batch_call.test.ts b/yarn-project/aztec.js/src/contract/batch_call.test.ts index 740105f9c9fd..bcb976b1729c 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.test.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.test.ts @@ -119,6 +119,7 @@ describe('BatchCall', () => { nested: [{ values: privateReturnValues }], } as any); txSimResult.getPublicReturnValues.mockReturnValue([{ values: publicReturnValues }] as any); + Object.defineProperty(txSimResult, 'offchainEffects', { value: [] }); // Mock wallet.batch to return both utility results and simulateTx result wallet.batch.mockResolvedValue([ @@ -236,6 +237,7 @@ describe('BatchCall', () => { nested: [{ values: privateReturnValues }], } as any); txSimResult.getPublicReturnValues.mockReturnValue([{ values: publicReturnValues }] as any); + Object.defineProperty(txSimResult, 'offchainEffects', { value: [] }); wallet.batch.mockResolvedValue([{ name: 'simulateTx', result: txSimResult }] as any); diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 03efc5aa0b76..2554f944d9a0 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -162,7 +162,7 @@ export type OffchainOutput = { * Effects whose data starts with `OFFCHAIN_MESSAGE_IDENTIFIER` are parsed as messages and removed * from the effects array. */ -export function extractOffchainOutput(effects: OffchainEffect[] = []): OffchainOutput { +export function extractOffchainOutput(effects: OffchainEffect[]): OffchainOutput { const offchainEffects: OffchainEffect[] = []; const offchainMessages: OffchainMessage[] = []; diff --git a/yarn-project/aztec.js/src/wallet/wallet.test.ts b/yarn-project/aztec.js/src/wallet/wallet.test.ts index 1149e418a795..44e11dc8f9a9 100644 --- a/yarn-project/aztec.js/src/wallet/wallet.test.ts +++ b/yarn-project/aztec.js/src/wallet/wallet.test.ts @@ -19,7 +19,12 @@ import { UtilityExecutionResult, } from '@aztec/stdlib/tx'; -import { type InteractionWaitOptions, NO_WAIT, type SendReturn } from '../contract/interaction_options.js'; +import { + type InteractionWaitOptions, + NO_WAIT, + type OffchainMessage, + type SendReturn, +} from '../contract/interaction_options.js'; import type { AppCapabilities, WalletCapabilities } from './capabilities.js'; import type { Aliased, @@ -366,7 +371,10 @@ describe('WalletSchema', () => { expect(results[8]).toEqual({ name: 'simulateTx', result: expect.any(TxSimulationResult) }); expect(results[9]).toEqual({ name: 'executeUtility', result: expect.any(UtilityExecutionResult) }); expect(results[10]).toEqual({ name: 'profileTx', result: expect.any(TxProfileResult) }); - expect(results[11]).toEqual({ name: 'sendTx', result: { receipt: expect.any(TxReceipt), offchainEffects: [] } }); + expect(results[11]).toEqual({ + name: 'sendTx', + result: { receipt: expect.any(TxReceipt), offchainEffects: [], offchainMessages: [] }, + }); expect(results[12]).toEqual({ name: 'createAuthWit', result: expect.any(AuthWitness) }); }); }); @@ -459,13 +467,17 @@ class MockWallet implements Wallet { opts: SendOptions, ): Promise> { if (opts.wait === NO_WAIT) { - return Promise.resolve({ txHash: TxHash.random(), offchainEffects: [] as OffchainEffect[] }) as Promise< - SendReturn - >; + return Promise.resolve({ + txHash: TxHash.random(), + offchainEffects: [] as OffchainEffect[], + offchainMessages: [] as OffchainMessage[], + }) as Promise>; } - return Promise.resolve({ receipt: TxReceipt.empty(), offchainEffects: [] as OffchainEffect[] }) as Promise< - SendReturn - >; + return Promise.resolve({ + receipt: TxReceipt.empty(), + offchainEffects: [] as OffchainEffect[], + offchainMessages: [] as OffchainMessage[], + }) as Promise>; } createAuthWit(_from: AztecAddress, _messageHashOrIntent: any): Promise { From d3bdfdec4dce8045b95c05aae6b38ed2533e74f1 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 14:05:17 +0000 Subject: [PATCH 081/119] add todo --- noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 6f1875df17cc..3240f1c3a891 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -68,6 +68,8 @@ pub unconstrained fn receive( CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT).push( PendingOffchainMsg { ciphertext, recipient, tx_hash, received_at: UtilityContext::new().timestamp() }, ); + + // Invoke an oracle to invalidate contract sync state cache } /// Returns offchain-delivered messages to process during sync. From b3d02ccb7910c5de875407d237d1c2066be493f2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 15:01:53 +0000 Subject: [PATCH 082/119] fix imports --- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 1 + 1 file changed, 1 insertion(+) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index e847cb131530..16d758859264 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -4,6 +4,7 @@ pub mod nonce_discovery; pub mod offchain; pub mod partial_notes; pub mod private_events; +pub mod private_notes; pub mod process_message; use crate::{ From efe87d01a139bb97e51e1ab9708c6a9eead9b7b3 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 15:34:21 +0000 Subject: [PATCH 083/119] use new apis in e2e --- .../src/e2e_offchain_payment.test.ts | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 1650d0169f8e..4de01ea96e5c 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -1,13 +1,12 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; -import { NO_WAIT } from '@aztec/aztec.js/contracts'; +import { NO_WAIT, extractOffchainOutput } from '@aztec/aztec.js/contracts'; import type { AztecNode } from '@aztec/aztec.js/node'; import { waitForTx } from '@aztec/aztec.js/node'; import type { CheatCodes } from '@aztec/aztec/testing'; -import { PRIVATE_LOG_CIPHERTEXT_LEN } from '@aztec/constants'; import { retryUntil } from '@aztec/foundation/retry'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; -import { OFFCHAIN_MESSAGE_IDENTIFIER, TxStatus } from '@aztec/stdlib/tx'; +import { TxStatus } from '@aztec/stdlib/tx'; import { jest } from '@jest/globals'; @@ -65,24 +64,17 @@ describe('e2e_offchain_payment', () => { // Mint to Alice using onchain delivery so she can spend the note. await contract.methods.mint(mintAmount, alice).send({ from: alice }); - // Alice prepares the private transfer which emits offchain effects. - const provenTx = await proveInteraction(wallet, contract.methods.transfer_offchain(paymentAmount, bob), { - from: alice, - }); - const { txHash } = await provenTx.send(); + // Alice sends the private transfer which emits offchain messages. + const { receipt, offchainMessages } = await contract.methods + .transfer_offchain(paymentAmount, bob) + .send({ from: alice }); + expect(offchainMessages.length).toBeGreaterThan(0); - const offchainEffects = provenTx.offchainEffects; - expect(offchainEffects.length).toBeGreaterThan(0); + // QR payload is the offchain message for Bob. + const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob)); + expect(messageForBob).toBeTruthy(); - // QR payload is the offchain effect for Bob. - const effectForBob = offchainEffects.find( - effect => effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER) && effect.data[1].equals(bob.toField()), - ); - expect(effectForBob).toBeTruthy(); - - const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); - - await contract.methods.offchain_receive(ciphertext, bob, txHash.hash).simulate({ from: bob }); + await contract.methods.offchain_receive(messageForBob!.payload, bob, receipt.txHash.hash).simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. await forceEmptyBlock(); @@ -107,14 +99,12 @@ describe('e2e_offchain_payment', () => { const txBlockNumber = receipt.blockNumber!; const txHash = provenTx.getTxHash(); - const effectForBob = provenTx.offchainEffects.find( - effect => effect.data[0].equals(OFFCHAIN_MESSAGE_IDENTIFIER) && effect.data[1].equals(bob.toField()), - ); - expect(effectForBob).toBeTruthy(); - const ciphertext = effectForBob!.data.slice(2, 2 + PRIVATE_LOG_CIPHERTEXT_LEN); + const { offchainMessages } = extractOffchainOutput(provenTx.offchainEffects); + const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob)); + expect(messageForBob).toBeTruthy(); // Deliver the offchain message for eventual processing - await contract.methods.offchain_receive(ciphertext, bob, txHash.hash).simulate({ from: bob }); + await contract.methods.offchain_receive(messageForBob!.payload, bob, txHash.hash).simulate({ from: bob }); // TODO: revisit this. The call to offchain_receive is a utility and as such it causes the contract to sync, which, // in combination with our caching policies, means subsequent utility calls won't trigger a re-sync. From 2fbd2dc76bfbf0b23821567a5f4b8a52f53303ed Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 16:44:27 +0000 Subject: [PATCH 084/119] fix reorg test --- .../src/e2e_offchain_payment.test.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 4de01ea96e5c..dd3bbe981cf3 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -1,12 +1,10 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; import { NO_WAIT, extractOffchainOutput } from '@aztec/aztec.js/contracts'; import type { AztecNode } from '@aztec/aztec.js/node'; -import { waitForTx } from '@aztec/aztec.js/node'; import type { CheatCodes } from '@aztec/aztec/testing'; import { retryUntil } from '@aztec/foundation/retry'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; -import { TxStatus } from '@aztec/stdlib/tx'; import { jest } from '@jest/globals'; @@ -119,21 +117,22 @@ describe('e2e_offchain_payment', () => { expect(bobBalance).toBe(paymentAmount); // Force a re-org - const checkpointed = await retryUntil( + await retryUntil( async () => { - const blocks = await aztecNode.getCheckpointedBlocks(txBlockNumber, 1); - return blocks[0]; + const tips = await aztecNode.getL2Tips(); + return tips.checkpointed.block.number >= txBlockNumber; }, 'checkpointed block', 30, 1, ); - const l1BlockNumber = Number(checkpointed.l1.blockNumber - 1n); await aztecNodeAdmin.pauseSync(); - await cheatCodes.eth.reorgTo(l1BlockNumber); + + await cheatCodes.eth.reorg(1); await aztecNodeAdmin.rollbackTo(Number(txBlockNumber) - 1); expect(await aztecNode.getBlockNumber()).toBe(Number(txBlockNumber) - 1); + await aztecNodeAdmin.resumeSync(); // Verify that the payment TX is no longer present after the reorg @@ -144,11 +143,9 @@ describe('e2e_offchain_payment', () => { const { result: bobAfterRollback } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobAfterRollback).toBe(0n); - // Resend the tx after the reorg + // Resend the tx after the reorg and force block production so the sequencer picks it up. await provenTx.send({ wait: NO_WAIT }); - - // Wait for the tx to be available again - await waitForTx(aztecNode, txHash, { waitForStatus: TxStatus.PROPOSED }); + await forceEmptyBlock(); // Check that the message was reprocessed and Bob has his payment again. // Notice what we want to test here is that the offchain effects don't need to be re-enqueued From 3d806c0d9b5f58e39c02fce34d41884b57ecfb77 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 3 Mar 2026 17:33:12 +0000 Subject: [PATCH 085/119] more explanatary comments on inbox loop --- .../aztec/src/messages/discovery/offchain.nr | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 3240f1c3a891..9a4dc0d0b61c 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -69,7 +69,7 @@ pub unconstrained fn receive( PendingOffchainMsg { ciphertext, recipient, tx_hash, received_at: UtilityContext::new().timestamp() }, ); - // Invoke an oracle to invalidate contract sync state cache + // TODO: Invoke an oracle to invalidate contract sync state cache } /// Returns offchain-delivered messages to process during sync. @@ -116,18 +116,46 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray msg.received_at + OFFCHAIN_MESSAGE_TTL_SECONDS { inbox.remove(j); continue; } - // Tx not yet available; keep the message in the inbox. + // Scenarios 2, 3: emitting TX not yet available. We keep the message in the inbox. As it might become + // processable as new blocks get mined. + // Scenario 4: no TX associated to message, means with this algorithm we will never clear it from the inbox. + // TODO: decide on a better strategy for scenario 4. if maybe_ctx.is_none() { continue; } - // Message is ready to process add to result + // Scenario 5: Message is ready to process, add ready_to_process let ctx = maybe_ctx.unwrap(); let message_context = MessageContext { tx_hash: ctx.tx_hash, From f98a26aed927fde9c5e978321abc7dae505fcaeb Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 10:23:17 +0000 Subject: [PATCH 086/119] wip: receive many msgs at once --- .../aztec-nr/aztec/src/macros/aztec.nr | 23 ++++----- .../aztec/src/messages/discovery/mod.nr | 6 +-- .../aztec/src/messages/discovery/offchain.nr | 49 ++++++++++++++----- .../aztec/src/messages/processing/mod.nr | 4 +- .../src/e2e_offchain_payment.test.ts | 14 +++++- 5 files changed, 63 insertions(+), 33 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 902dc9d24711..ff0c9e67ae69 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -433,9 +433,10 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q comptime fn generate_offchain_receive() -> Quoted { quote { pub struct offchain_receive_parameters { - pub ciphertext: BoundedVec, - pub recipient: aztec::protocol::address::AztecAddress, - pub tx_hash: Field, + pub messages: BoundedVec< + aztec::messages::discovery::offchain::OffchainMessage, + aztec::messages::discovery::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, + >, } #[abi(functions)] @@ -443,24 +444,20 @@ comptime fn generate_offchain_receive() -> Quoted { parameters: offchain_receive_parameters, } - /// Delivers an offchain message to this contract's offchain inbox for subsequent processing. + /// Delivers offchain messages to this contract's offchain inbox for subsequent processing. /// /// For more details, see [`aztec::messages::discovery::offchain::receive`]. /// /// This function is automatically injected by the `#[aztec]` macro. #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] unconstrained fn offchain_receive( - ciphertext: BoundedVec, - recipient: aztec::protocol::address::AztecAddress, - tx_hash: Field, + messages: BoundedVec< + aztec::messages::discovery::offchain::OffchainMessage, + aztec::messages::discovery::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, + >, ) { let address = aztec::context::UtilityContext::new().this_address(); - aztec::messages::discovery::offchain::receive( - address, - ciphertext, - recipient, - tx_hash, - ); + aztec::messages::discovery::offchain::receive(address, messages); } } } diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 16d758859264..2e2714f0a501 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -14,7 +14,7 @@ use crate::{ encoding::MAX_MESSAGE_CONTENT_LEN, logs::note::MAX_NOTE_PACKED_LEN, processing::{ - get_private_logs, MessageContext, OffchainMessage, pending_tagged_log::PendingTaggedLog, + get_private_logs, MessageContext, OffchainMessageWithContext, pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events, }, }, @@ -107,7 +107,7 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /// expiry and automatic context resolution. It can be overridden via /// [`crate::macros::AztecConfig::offchain_message_handler`]. pub type OffchainMessageHandler = unconstrained fn[Env]( -/* contract_address */AztecAddress) -> CapsuleArray; +/* contract_address */AztecAddress) -> CapsuleArray; /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private /// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. @@ -157,7 +157,7 @@ pub unconstrained fn do_sync_state = fetch(contract_address); + let msgs: CapsuleArray = fetch(contract_address); msgs.for_each(|i, msg| { process_message_ciphertext( contract_address, diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 9a4dc0d0b61c..5b916e01b8f1 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -3,7 +3,7 @@ use crate::{ context::UtilityContext, messages::{ encoding::MESSAGE_CIPHERTEXT_LEN, - processing::{MessageContext, MessageTxContext, OffchainMessage, resolve_message_contexts}, + processing::{MessageContext, MessageTxContext, OffchainMessageWithContext, resolve_message_contexts}, }, protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, }; @@ -22,17 +22,30 @@ global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCH /// Ephemeral capsule slot used by [`default_handler`] to collect messages ready for processing. global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); +/// Maximum number of offchain messages accepted by `offchain_receive` in a single call. +pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; + /// Minimum time that a message is kept in the inbox before eviction: 24 hours. After 24 hours, the protocol guarantees /// that the original TX that emitted the message can no longer be included in a block. This gives us a bounded time /// window within which we need to keep messages around in case we need to reprocess them after reorgs. global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 24 * 3600; +/// A message delivered via the `offchain_receive` utility function. +pub struct OffchainMessage { + /// The encrypted message payload. + pub ciphertext: BoundedVec, + /// The intended recipient of the message. + pub recipient: AztecAddress, + /// The hash of the transaction that produced this message. `Option::none` indicates a tx-less message. + pub tx_hash: Option, +} + /// An offchain message awaiting processing (or re-processing) in the inbox. /// /// Messages remain in the inbox until they expire after [`OFFCHAIN_MESSAGE_TTL_SECONDS`], even if they have already /// been processed. This is necessary to handle reorgs: a processed message may need to be re-processed if the /// transaction that provided its context is reverted. On each sync, resolved messages are promoted to -/// [`OffchainMessage`] for processing. +/// [`OffchainMessageWithContext`] for processing. #[derive(Serialize, Deserialize)] struct PendingOffchainMsg { /// The encrypted message payload. @@ -45,11 +58,11 @@ struct PendingOffchainMsg { received_at: u64, } -/// Delivers an offchain message to the given contract's offchain inbox for subsequent processing. +/// Delivers offchain messages to the given contract's offchain inbox for subsequent processing. /// /// Offchain messages are transaction effects that are not broadcasted via onchain logs. Instead, the sender shares the /// message to the recipient through an external channel (e.g. a URL accessible by the recipient). The recipient then -/// calls this function to hand the message to the contract so it can be processed through the same mechanisms as +/// calls this function to hand the messages to the contract so they can be processed through the same mechanisms as /// onchain messages. /// /// Messages are processed when their originating transaction is found onchain (providing the context needed to @@ -61,13 +74,22 @@ struct PendingOffchainMsg { /// Processing order is not guaranteed. pub unconstrained fn receive( contract_address: AztecAddress, - ciphertext: BoundedVec, - recipient: AztecAddress, - tx_hash: Field, + messages: BoundedVec, ) { - CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT).push( - PendingOffchainMsg { ciphertext, recipient, tx_hash, received_at: UtilityContext::new().timestamp() }, - ); + let inbox: CapsuleArray = CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT); + let received_at = UtilityContext::new().timestamp(); + let mut i = 0; + let messages_len = messages.len(); + while i < messages_len { + let msg = messages.get(i); + let tx_hash = if msg.tx_hash.is_some() { + msg.tx_hash.unwrap() + } else { + 0 + }; + inbox.push(PendingOffchainMsg { ciphertext: msg.ciphertext, recipient: msg.recipient, tx_hash, received_at }); + i += 1; + } // TODO: Invoke an oracle to invalidate contract sync state cache } @@ -76,12 +98,13 @@ pub unconstrained fn receive( /// /// Messages remain in the inbox and are reprocessed on each sync until their originating transaction is no longer at /// risk of being dropped by a reorg. -pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { +pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let context_resolution_requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); let resolved_contexts: CapsuleArray> = CapsuleArray::at(address, OFFCHAIN_CONTEXT_RESPONSES_SLOT); - let ready_to_process: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); + let ready_to_process: CapsuleArray = + CapsuleArray::at(address, OFFCHAIN_READY_MESSAGES_SLOT); // Clear any stale ready messages from a previous run. ready_to_process.for_each(|i, _| { ready_to_process.remove(i); }); @@ -164,7 +187,7 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray, pub message_context: MessageContext, } diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index dd3bbe981cf3..ca17f3abb2ea 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -23,6 +23,10 @@ describe('e2e_offchain_payment', () => { let accounts: AztecAddress[]; let teardown: () => Promise; const logger = getLogger(); + const someField = (value: bigint | string | number | { toField: () => any }) => ({ + _is_some: true, + _value: value, + }); jest.setTimeout(TIMEOUT); @@ -72,7 +76,11 @@ describe('e2e_offchain_payment', () => { const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob)); expect(messageForBob).toBeTruthy(); - await contract.methods.offchain_receive(messageForBob!.payload, bob, receipt.txHash.hash).simulate({ from: bob }); + await contract.methods + .offchain_receive([ + { ciphertext: messageForBob!.payload, recipient: bob, tx_hash: someField(receipt.txHash.hash) }, + ]) + .simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. await forceEmptyBlock(); @@ -102,7 +110,9 @@ describe('e2e_offchain_payment', () => { expect(messageForBob).toBeTruthy(); // Deliver the offchain message for eventual processing - await contract.methods.offchain_receive(messageForBob!.payload, bob, txHash.hash).simulate({ from: bob }); + await contract.methods + .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: someField(txHash.hash) }]) + .simulate({ from: bob }); // TODO: revisit this. The call to offchain_receive is a utility and as such it causes the contract to sync, which, // in combination with our caching policies, means subsequent utility calls won't trigger a re-sync. From c0ff5a0a67318c7a3585a193c2ae9c28277ad24a Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 11:29:40 +0000 Subject: [PATCH 087/119] receive many offchain messages on a single call --- .../aztec-nr/aztec/src/oracle/message_processing.nr | 2 +- noir-projects/aztec-nr/aztec/src/oracle/version.nr | 2 +- .../src/scripts/generate_protocol_contract_types.ts | 2 +- .../end-to-end/src/e2e_offchain_payment.test.ts | 10 ++-------- .../src/contract_function_simulator/oracle/oracle.ts | 3 ++- .../oracle/utility_execution_oracle.ts | 2 +- yarn-project/pxe/src/oracle_version.ts | 4 ++-- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr b/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr index 9231b2dac746..ec9308b4489b 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/message_processing.nr @@ -61,7 +61,7 @@ pub(crate) unconstrained fn resolve_message_contexts( ); } -#[oracle(utilityResolveMessageContexts)] +#[oracle(aztec_utl_utilityResolveMessageContexts)] unconstrained fn resolve_message_contexts_oracle( contract_address: AztecAddress, message_context_requests_array_base_slot: Field, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/version.nr b/noir-projects/aztec-nr/aztec/src/oracle/version.nr index e3040821aa8a..9c569725cc82 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/version.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/version.nr @@ -5,7 +5,7 @@ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is /// called /// and if the oracle version is incompatible an error is thrown. -pub global ORACLE_VERSION: Field = 14; +pub global ORACLE_VERSION: Field = 15; /// Asserts that the version of the oracle is compatible with the version expected by the contract. pub fn assert_compatible_oracle_version() { diff --git a/yarn-project/aztec.js/src/scripts/generate_protocol_contract_types.ts b/yarn-project/aztec.js/src/scripts/generate_protocol_contract_types.ts index 9198556de7cc..6e0f31e72f49 100644 --- a/yarn-project/aztec.js/src/scripts/generate_protocol_contract_types.ts +++ b/yarn-project/aztec.js/src/scripts/generate_protocol_contract_types.ts @@ -99,7 +99,7 @@ import { FunctionType } from '@aztec/stdlib/abi'; import type { ContractArtifact } from '../../api/abi.js'; import { PublicKeys } from '../../api/keys.js'; -import type { AztecAddressLike, EthAddressLike, FieldLike, FunctionSelectorLike, WrappedFieldLike } from '../../utils/abi_types.js'; +import type { AztecAddressLike, EthAddressLike, FieldLike, FunctionSelectorLike, OptionLike, WrappedFieldLike } from '../../utils/abi_types.js'; import { ContractBase, type ContractMethod } from '../contract_base.js'; import { ContractFunctionInteraction } from '../contract_function_interaction.js'; import type { Wallet } from '../../wallet/wallet.js'; diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index ca17f3abb2ea..b787fd3be586 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -23,10 +23,6 @@ describe('e2e_offchain_payment', () => { let accounts: AztecAddress[]; let teardown: () => Promise; const logger = getLogger(); - const someField = (value: bigint | string | number | { toField: () => any }) => ({ - _is_some: true, - _value: value, - }); jest.setTimeout(TIMEOUT); @@ -77,9 +73,7 @@ describe('e2e_offchain_payment', () => { expect(messageForBob).toBeTruthy(); await contract.methods - .offchain_receive([ - { ciphertext: messageForBob!.payload, recipient: bob, tx_hash: someField(receipt.txHash.hash) }, - ]) + .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: receipt.txHash.hash }]) .simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. @@ -111,7 +105,7 @@ describe('e2e_offchain_payment', () => { // Deliver the offchain message for eventual processing await contract.methods - .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: someField(txHash.hash) }]) + .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: txHash.hash }]) .simulate({ from: bob }); // TODO: revisit this. The call to offchain_receive is a utility and as such it causes the contract to sync, which, diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts index 5be070be0dc5..63f7f00ef14c 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/oracle.ts @@ -554,7 +554,8 @@ export class Oracle { return []; } - async utilityResolveMessageContexts( + // eslint-disable-next-line camelcase + async aztec_utl_utilityResolveMessageContexts( [contractAddress]: ACVMField[], [messageContextRequestsArrayBaseSlot]: ACVMField[], [messageContextResponsesArrayBaseSlot]: ACVMField[], diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index 49aeade588d5..fe722587aa2c 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -586,7 +586,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra ); } - public utilityStoreCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { + public storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { if (!contractAddress.equals(this.contractAddress)) { // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`); diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index e282867c911f..38319d9b4313 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -4,9 +4,9 @@ /// /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -export const ORACLE_VERSION = 14; +export const ORACLE_VERSION = 15; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`. -export const ORACLE_INTERFACE_HASH = '9fb918682455c164ce8dd3acb71c751e2b9b2fc48913604069c9ea885fa378ca'; +export const ORACLE_INTERFACE_HASH = 'bb3b4547ee403b1a1a1401219f02b27424a9370fc1a52566d03c84471f307b74'; From 85491aebc20dd55dd6b57adaad6e594929f3094c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 11:51:25 +0000 Subject: [PATCH 088/119] add message ttl data --- yarn-project/aztec.js/src/contract/batch_call.ts | 2 +- .../src/contract/contract_function_interaction.ts | 5 ++++- .../aztec.js/src/contract/deploy_method.ts | 2 +- .../src/contract/interaction_options.test.ts | 15 +++++++++------ .../aztec.js/src/contract/interaction_options.ts | 5 ++++- .../end-to-end/src/e2e_offchain_payment.test.ts | 2 +- .../wallet-sdk/src/base-wallet/base_wallet.ts | 5 ++++- 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index 66c668340687..62a2a27ee59f 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -134,7 +134,7 @@ export class BatchCall extends BaseContractInteraction { results[callIndex] = { result: rawReturnValues ? decodeFromAbi(call.returnTypes, rawReturnValues) : [], - ...extractOffchainOutput(simulatedTx.offchainEffects), + ...extractOffchainOutput(simulatedTx.offchainEffects, simulatedTx.publicInputs.expirationTimestamp), }; }); } diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index e230aa1cddf8..9b95675d3c6b 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -167,7 +167,10 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } const returnValue = rawReturnValues ? decodeFromAbi(this.functionDao.returnTypes, rawReturnValues) : []; - const offchainOutput = extractOffchainOutput(simulatedTx.offchainEffects); + const offchainOutput = extractOffchainOutput( + simulatedTx.offchainEffects, + simulatedTx.publicInputs.expirationTimestamp, + ); if (options.includeMetadata || options.fee?.estimateGas) { const { gasLimits, teardownGasLimits } = getGasLimits(simulatedTx, options.fee?.estimatedGasPadding); diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 98249acefed4..b0bfced88225 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -425,7 +425,7 @@ export class DeployMethod extends ); return { stats: simulatedTx.stats!, - ...extractOffchainOutput(simulatedTx.offchainEffects), + ...extractOffchainOutput(simulatedTx.offchainEffects, simulatedTx.publicInputs.expirationTimestamp), result: undefined, estimatedGas: { gasLimits, teardownGasLimits }, }; diff --git a/yarn-project/aztec.js/src/contract/interaction_options.test.ts b/yarn-project/aztec.js/src/contract/interaction_options.test.ts index 7818299a3854..05b7edfdf9ff 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.test.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.test.ts @@ -5,6 +5,8 @@ import { OFFCHAIN_MESSAGE_IDENTIFIER, type OffchainEffect } from '@aztec/stdlib/ import { extractOffchainOutput } from './interaction_options.js'; describe('extractOffchainOutput', () => { + const expirationTimestamp = 1234567890n; + const makeEffect = (data: Fr[], contractAddress?: AztecAddress): OffchainEffect => ({ data, contractAddress: contractAddress ?? AztecAddress.fromField(Fr.random()), @@ -21,14 +23,14 @@ describe('extractOffchainOutput', () => { ); it('returns empty output for empty input', () => { - const result = extractOffchainOutput([]); + const result = extractOffchainOutput([], expirationTimestamp); expect(result.offchainEffects).toEqual([]); expect(result.offchainMessages).toEqual([]); }); it('keeps non-message effects as-is', () => { const effects = [makeEffect([Fr.random(), Fr.random()]), makeEffect([Fr.random()])]; - const result = extractOffchainOutput(effects); + const result = extractOffchainOutput(effects, expirationTimestamp); expect(result.offchainEffects).toEqual(effects); expect(result.offchainMessages).toEqual([]); }); @@ -39,7 +41,7 @@ describe('extractOffchainOutput', () => { const contractAddress = await AztecAddress.random(); const effect = await makeMessageEffect(recipient, payload, contractAddress); - const result = extractOffchainOutput([effect]); + const result = extractOffchainOutput([effect], expirationTimestamp); expect(result.offchainEffects).toEqual([]); expect(result.offchainMessages).toHaveLength(1); @@ -47,6 +49,7 @@ describe('extractOffchainOutput', () => { recipient, payload, contractAddress, + expirationTimestamp, }); }); @@ -55,7 +58,7 @@ describe('extractOffchainOutput', () => { const plainEffect2 = makeEffect([Fr.random(), Fr.random()]); const messageEffect = await makeMessageEffect(); - const result = extractOffchainOutput([plainEffect1, messageEffect, plainEffect2]); + const result = extractOffchainOutput([plainEffect1, messageEffect, plainEffect2], expirationTimestamp); expect(result.offchainEffects).toEqual([plainEffect1, plainEffect2]); expect(result.offchainMessages).toHaveLength(1); @@ -65,7 +68,7 @@ describe('extractOffchainOutput', () => { const msg1 = await makeMessageEffect(); const msg2 = await makeMessageEffect(); - const result = extractOffchainOutput([msg1, msg2]); + const result = extractOffchainOutput([msg1, msg2], expirationTimestamp); expect(result.offchainEffects).toEqual([]); expect(result.offchainMessages).toHaveLength(2); @@ -73,7 +76,7 @@ describe('extractOffchainOutput', () => { it('does not treat an effect as a message if data has only the identifier (no recipient)', () => { const effect = makeEffect([OFFCHAIN_MESSAGE_IDENTIFIER]); - const result = extractOffchainOutput([effect]); + const result = extractOffchainOutput([effect], expirationTimestamp); expect(result.offchainEffects).toEqual([effect]); expect(result.offchainMessages).toEqual([]); diff --git a/yarn-project/aztec.js/src/contract/interaction_options.ts b/yarn-project/aztec.js/src/contract/interaction_options.ts index 2554f944d9a0..e81af07bd37e 100644 --- a/yarn-project/aztec.js/src/contract/interaction_options.ts +++ b/yarn-project/aztec.js/src/contract/interaction_options.ts @@ -147,6 +147,8 @@ export type OffchainMessage = { payload: Fr[]; /** The contract that emitted the message. */ contractAddress: AztecAddress; + /** The timestamp by which this message expires and can be evicted from the inbox. */ + expirationTimestamp: bigint; }; /** Groups all unproven outputs from private execution that are returned to the client. */ @@ -162,7 +164,7 @@ export type OffchainOutput = { * Effects whose data starts with `OFFCHAIN_MESSAGE_IDENTIFIER` are parsed as messages and removed * from the effects array. */ -export function extractOffchainOutput(effects: OffchainEffect[]): OffchainOutput { +export function extractOffchainOutput(effects: OffchainEffect[], txExpirationTimestamp: bigint): OffchainOutput { const offchainEffects: OffchainEffect[] = []; const offchainMessages: OffchainMessage[] = []; @@ -172,6 +174,7 @@ export function extractOffchainOutput(effects: OffchainEffect[]): OffchainOutput recipient: AztecAddress.fromField(effect.data[1]), payload: effect.data.slice(2), contractAddress: effect.contractAddress, + expirationTimestamp: txExpirationTimestamp, }); } else { offchainEffects.push(effect); diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index b787fd3be586..741a1494d012 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -99,7 +99,7 @@ describe('e2e_offchain_payment', () => { const txBlockNumber = receipt.blockNumber!; const txHash = provenTx.getTxHash(); - const { offchainMessages } = extractOffchainOutput(provenTx.offchainEffects); + const { offchainMessages } = extractOffchainOutput(provenTx.offchainEffects, provenTx.data.expirationTimestamp); const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob)); expect(messageForBob).toBeTruthy(); diff --git a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts index 484ed4b37edb..d25ae8ebcc5e 100644 --- a/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts +++ b/yarn-project/wallet-sdk/src/base-wallet/base_wallet.ts @@ -388,7 +388,10 @@ export abstract class BaseWallet implements Wallet { const feeOptions = await this.completeFeeOptions(opts.from, executionPayload.feePayer, opts.fee?.gasSettings); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee(executionPayload, opts.from, feeOptions); const provenTx = await this.pxe.proveTx(txRequest, this.scopesFrom(opts.from, opts.additionalScopes)); - const offchainOutput = extractOffchainOutput(provenTx.getOffchainEffects()); + const offchainOutput = extractOffchainOutput( + provenTx.getOffchainEffects(), + provenTx.publicInputs.expirationTimestamp, + ); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { From 6c22f228f5869a1ebb9021dec39dd50271153bdc Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 16:32:18 +0000 Subject: [PATCH 089/119] accept expiration --- .../aztec/src/messages/discovery/offchain.nr | 49 +++++++++---------- .../aztec-nr/aztec/src/oracle/version.nr | 2 +- .../src/e2e_offchain_payment.test.ts | 18 ++++++- yarn-project/pxe/src/oracle_version.ts | 4 +- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr index 5b916e01b8f1..ca15cd3aa7c6 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr @@ -25,11 +25,6 @@ global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN /// Maximum number of offchain messages accepted by `offchain_receive` in a single call. pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; -/// Minimum time that a message is kept in the inbox before eviction: 24 hours. After 24 hours, the protocol guarantees -/// that the original TX that emitted the message can no longer be included in a block. This gives us a bounded time -/// window within which we need to keep messages around in case we need to reprocess them after reorgs. -global OFFCHAIN_MESSAGE_TTL_SECONDS: u64 = 24 * 3600; - /// A message delivered via the `offchain_receive` utility function. pub struct OffchainMessage { /// The encrypted message payload. @@ -38,14 +33,15 @@ pub struct OffchainMessage { pub recipient: AztecAddress, /// The hash of the transaction that produced this message. `Option::none` indicates a tx-less message. pub tx_hash: Option, + /// The timestamp after which this message can be evicted from the inbox. + pub expiration_timestamp: u64, } /// An offchain message awaiting processing (or re-processing) in the inbox. /// -/// Messages remain in the inbox until they expire after [`OFFCHAIN_MESSAGE_TTL_SECONDS`], even if they have already -/// been processed. This is necessary to handle reorgs: a processed message may need to be re-processed if the -/// transaction that provided its context is reverted. On each sync, resolved messages are promoted to -/// [`OffchainMessageWithContext`] for processing. +/// Messages remain in the inbox until they expire, even if they have already been processed. This is necessary to +/// handle reorgs: a processed message may need to be re-processed if the transaction that provided its context is +/// reverted. On each sync, resolved messages are promoted to [`OffchainMessageWithContext`] for processing. #[derive(Serialize, Deserialize)] struct PendingOffchainMsg { /// The encrypted message payload. @@ -54,8 +50,8 @@ struct PendingOffchainMsg { recipient: AztecAddress, /// The hash of the transaction that produced this message. A value of 0 indicates a tx-less message. tx_hash: Field, - /// Timestamp at which the message was received, used for TTL-based eviction. - received_at: u64, + /// The timestamp after which this message can be evicted from the inbox. + expiration_timestamp: u64, } /// Delivers offchain messages to the given contract's offchain inbox for subsequent processing. @@ -68,8 +64,8 @@ struct PendingOffchainMsg { /// Messages are processed when their originating transaction is found onchain (providing the context needed to /// validate resulting notes and events). /// -/// Messages are kept in the inbox until the originating transaction can be assumed to have expired. This is necessary -/// to make the mechanism resilient to reorgs. +/// Messages are kept in the inbox until their expiration timestamp is reached. This is necessary to make the +/// mechanism resilient to reorgs. /// /// Processing order is not guaranteed. pub unconstrained fn receive( @@ -77,7 +73,6 @@ pub unconstrained fn receive( messages: BoundedVec, ) { let inbox: CapsuleArray = CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT); - let received_at = UtilityContext::new().timestamp(); let mut i = 0; let messages_len = messages.len(); while i < messages_len { @@ -87,7 +82,14 @@ pub unconstrained fn receive( } else { 0 }; - inbox.push(PendingOffchainMsg { ciphertext: msg.ciphertext, recipient: msg.recipient, tx_hash, received_at }); + inbox.push( + PendingOffchainMsg { + ciphertext: msg.ciphertext, + recipient: msg.recipient, + tx_hash, + expiration_timestamp: msg.expiration_timestamp, + }, + ); i += 1; } @@ -142,9 +144,7 @@ pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray CapsuleArray msg.received_at + OFFCHAIN_MESSAGE_TTL_SECONDS { + // Scenario 1: message expired. We remove it from the inbox. + if now > msg.expiration_timestamp { inbox.remove(j); continue; } - // Scenarios 2, 3: emitting TX not yet available. We keep the message in the inbox. As it might become + // Scenarios 2, 3: emitting TX not yet available. We keep the message in the inbox, as it might become // processable as new blocks get mined. - // Scenario 4: no TX associated to message, means with this algorithm we will never clear it from the inbox. - // TODO: decide on a better strategy for scenario 4. + // Scenario 4: no TX associated to message. The message will sit in the inbox until it expires. if maybe_ctx.is_none() { continue; } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/version.nr b/noir-projects/aztec-nr/aztec/src/oracle/version.nr index 9c569725cc82..72947ef20ee3 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/version.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/version.nr @@ -5,7 +5,7 @@ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is /// called /// and if the oracle version is incompatible an error is thrown. -pub global ORACLE_VERSION: Field = 15; +pub global ORACLE_VERSION: Field = 16; /// Asserts that the version of the oracle is compatible with the version expected by the contract. pub fn assert_compatible_oracle_version() { diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 741a1494d012..a44acadb681a 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -73,7 +73,14 @@ describe('e2e_offchain_payment', () => { expect(messageForBob).toBeTruthy(); await contract.methods - .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: receipt.txHash.hash }]) + .offchain_receive([ + { + ciphertext: messageForBob!.payload, + recipient: bob, + tx_hash: receipt.txHash.hash, + expiration_timestamp: messageForBob!.expirationTimestamp, + }, + ]) .simulate({ from: bob }); // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. @@ -105,7 +112,14 @@ describe('e2e_offchain_payment', () => { // Deliver the offchain message for eventual processing await contract.methods - .offchain_receive([{ ciphertext: messageForBob!.payload, recipient: bob, tx_hash: txHash.hash }]) + .offchain_receive([ + { + ciphertext: messageForBob!.payload, + recipient: bob, + tx_hash: txHash.hash, + expiration_timestamp: messageForBob!.expirationTimestamp, + }, + ]) .simulate({ from: bob }); // TODO: revisit this. The call to offchain_receive is a utility and as such it causes the contract to sync, which, diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index 38319d9b4313..a46244816e7a 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -4,9 +4,9 @@ /// /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -export const ORACLE_VERSION = 15; +export const ORACLE_VERSION = 16; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`. -export const ORACLE_INTERFACE_HASH = 'bb3b4547ee403b1a1a1401219f02b27424a9370fc1a52566d03c84471f307b74'; +export const ORACLE_INTERFACE_HASH = '09435cb322e757e165c5bbb74acb95dbbab4a60498073b32224ce9bdc1584d09'; From 84e3f6b3fdc136544cae7b4d29552bd69f3c537e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 16:44:28 +0000 Subject: [PATCH 090/119] remove extension point --- .../aztec-nr/aztec/src/macros/aztec.nr | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index ff0c9e67ae69..a1fc3def09e3 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -13,7 +13,7 @@ use crate::{ module_has_storage, }, }, - messages::discovery::{CustomMessageHandler, OffchainMessageHandler}, + messages::discovery::CustomMessageHandler, }; /// Configuration for the [`aztec`] macro. @@ -30,7 +30,6 @@ use crate::{ /// ``` pub struct AztecConfig { custom_message_handler: Option>, - offchain_message_handler: Option>, } impl AztecConfig { @@ -39,7 +38,7 @@ impl AztecConfig { /// Calling `new` is equivalent to invoking the [`aztec`] macro with no parameters. The different methods /// (e.g. [`AztecConfig::custom_message_handler`]) can then be used to change the default behavior. pub comptime fn new() -> Self { - Self { custom_message_handler: Option::none(), offchain_message_handler: Option::none() } + Self { custom_message_handler: Option::none() } } /// Sets a handler for custom messages. @@ -49,22 +48,8 @@ impl AztecConfig { /// /// `handler` must be a function that conforms to the /// [`crate::messages::discovery::CustomMessageHandler`] type signature. - pub comptime fn custom_message_handler(self: Self, handler: CustomMessageHandler<()>) -> Self { - Self { custom_message_handler: Option::some(handler), offchain_message_handler: self.offchain_message_handler } - } - - /// Sets a handler to customize how offchain-delivered messages are discovered and managed by the contract. - /// - /// This is an advanced feature that requires knowledge about how offchain messages are stored, how to ensure - /// reprocessing under reorgs, etc. If you are unsure about how you could use this, it probably means you - /// should not. - /// - /// By default, the lifecycle of offchain messages is handled by - /// [`crate::messages::discovery::offchain::default_handler`]. - /// - /// This config allows for the default behavior to be overridden. - pub comptime fn offchain_message_handler(self: Self, handler: OffchainMessageHandler<()>) -> Self { - Self { custom_message_handler: self.custom_message_handler, offchain_message_handler: Option::some(handler) } + pub comptime fn custom_message_handler(_self: Self, handler: CustomMessageHandler<()>) -> Self { + Self { custom_message_handler: Option::some(handler) } } } @@ -130,11 +115,8 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote { Option::>::none() } }; - let offchain_message_handler_option = if config.offchain_message_handler.is_some() { - let handler = config.offchain_message_handler.unwrap(); - quote { Option::some($handler) } - } else { - quote { Option::some(aztec::messages::discovery::offchain::default_handler) } + let offchain_message_handler_option = quote { + Option::some(aztec::messages::discovery::offchain::default_handler) }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { From accae7543c00245064846fb266bae8b8d124e7bc Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 17:06:42 +0000 Subject: [PATCH 091/119] reject contracts with an offchain_receive fn --- docs/netlify.toml | 5 +++++ noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/netlify.toml b/docs/netlify.toml index 7b4b052257b3..16fb6a0ba6ff 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -791,3 +791,8 @@ from = "/errors/5" to = "/aztec-nr-api/nightly/noir_aztec/macros/events/fn.event.html" +[[redirects]] + # Aztec-nr: user-defined 'offchain_receive' is not allowed + from = "/errors/6" + to = "/aztec-nr-api/nightly/noir_aztec/messages/discovery/offchain/fn.receive.html" + diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index a1fc3def09e3..8e0906e344b3 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -128,11 +128,12 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote {} }; - let offchain_receive_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { offchain_receive }) { - generate_offchain_receive() - } else { - quote {} - }; + if m.functions().any(|f| f.name() == quote { offchain_receive }) { + panic( + "User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/6", + ); + } + let offchain_receive_fn_and_abi_export = generate_offchain_receive(); let process_message_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { process_message }) { generate_process_message(process_custom_message_option) From 3cb806f4833f6b869142f9e97216f067e1d9734f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 17:37:22 +0000 Subject: [PATCH 092/119] move offchain stuff from delivery to processing --- docs/netlify.toml | 2 +- .../aztec-nr/aztec/src/macros/aztec.nr | 18 ++++++++++-------- .../aztec/src/messages/discovery/mod.nr | 7 +++---- .../aztec/src/messages/processing/mod.nr | 1 + .../{discovery => processing}/offchain.nr | 0 5 files changed, 15 insertions(+), 13 deletions(-) rename noir-projects/aztec-nr/aztec/src/messages/{discovery => processing}/offchain.nr (100%) diff --git a/docs/netlify.toml b/docs/netlify.toml index 16fb6a0ba6ff..151fb7190c24 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -794,5 +794,5 @@ [[redirects]] # Aztec-nr: user-defined 'offchain_receive' is not allowed from = "/errors/6" - to = "/aztec-nr-api/nightly/noir_aztec/messages/discovery/offchain/fn.receive.html" + to = "/aztec-nr-api/nightly/noir_aztec/messages/processing/offchain/fn.receive.html" diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 8e0906e344b3..1652dfc97ba6 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -116,7 +116,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { }; let offchain_message_handler_option = quote { - Option::some(aztec::messages::discovery::offchain::default_handler) + Option::some(aztec::messages::processing::offchain::default_handler) }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { @@ -412,13 +412,15 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q } } -/// Generates the `offchain_receive` utility function that adds a message to the offchain message inbox. +/// Generates an `offchain_receive` utility function that lets callers add messages to the offchain message inbox. +/// +/// For more details, see [`aztec::messages::processing::offchain::receive`]. comptime fn generate_offchain_receive() -> Quoted { quote { pub struct offchain_receive_parameters { pub messages: BoundedVec< - aztec::messages::discovery::offchain::OffchainMessage, - aztec::messages::discovery::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, + aztec::messages::processing::offchain::OffchainMessage, + aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, >, } @@ -429,18 +431,18 @@ comptime fn generate_offchain_receive() -> Quoted { /// Delivers offchain messages to this contract's offchain inbox for subsequent processing. /// - /// For more details, see [`aztec::messages::discovery::offchain::receive`]. + /// For more details, see [`aztec::messages::processing::offchain::receive`]. /// /// This function is automatically injected by the `#[aztec]` macro. #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] unconstrained fn offchain_receive( messages: BoundedVec< - aztec::messages::discovery::offchain::OffchainMessage, - aztec::messages::discovery::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, + aztec::messages::processing::offchain::OffchainMessage, + aztec::messages::processing::offchain::MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL, >, ) { let address = aztec::context::UtilityContext::new().this_address(); - aztec::messages::discovery::offchain::receive(address, messages); + aztec::messages::processing::offchain::receive(address, messages); } } } diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 77013038718b..0211d426a943 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -2,7 +2,6 @@ use crate::logging::{aztecnr_debug_log, aztecnr_debug_log_format}; use crate::protocol::address::AztecAddress; pub mod nonce_discovery; -pub mod offchain; pub mod partial_notes; pub mod private_events; pub mod private_notes; @@ -104,9 +103,9 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /// utility function (injected by [`crate::macros::aztec`]). This handler is responsible for deciding when each message /// is ready for processing, when it can be safely disposed of, etc. /// -/// The default implementation is [`offchain::default_handler`], which manages an inbox with max TX TTL-based -/// expiry and automatic context resolution. It can be overridden via -/// [`crate::macros::AztecConfig::offchain_message_handler`]. +/// The default implementation is +/// [`crate::messages::processing::offchain::default_handler`], which manages an inbox with +/// expiration-based eviction and automatic context resolution. pub type OffchainMessageHandler = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr index d9bc9af505f4..37e8914e6be4 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/mod.nr @@ -1,4 +1,5 @@ pub(crate) mod event_validation_request; +pub mod offchain; mod message_context; pub use message_context::MessageContext; diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr similarity index 100% rename from noir-projects/aztec-nr/aztec/src/messages/discovery/offchain.nr rename to noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr From 62de4b3865412e4cdb0541879628e3c368f5a84f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 17:50:51 +0000 Subject: [PATCH 093/119] reduce visibility of mods --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 4 ++-- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 1652dfc97ba6..acfa8469f632 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -414,7 +414,7 @@ comptime fn generate_process_message(process_custom_message_option: Quoted) -> Q /// Generates an `offchain_receive` utility function that lets callers add messages to the offchain message inbox. /// -/// For more details, see [`aztec::messages::processing::offchain::receive`]. +/// For more details, see `aztec::messages::processing::offchain::receive`. comptime fn generate_offchain_receive() -> Quoted { quote { pub struct offchain_receive_parameters { @@ -431,7 +431,7 @@ comptime fn generate_offchain_receive() -> Quoted { /// Delivers offchain messages to this contract's offchain inbox for subsequent processing. /// - /// For more details, see [`aztec::messages::processing::offchain::receive`]. + /// For more details, see `aztec::messages::processing::offchain::receive`. /// /// This function is automatically injected by the `#[aztec]` macro. #[aztec::macros::internals_functions_generation::abi_attributes::abi_utility] diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 0211d426a943..a9e267e5cc0d 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -1,9 +1,9 @@ use crate::logging::{aztecnr_debug_log, aztecnr_debug_log_format}; use crate::protocol::address::AztecAddress; -pub mod nonce_discovery; -pub mod partial_notes; -pub mod private_events; +pub(crate) mod nonce_discovery; +pub(crate) mod partial_notes; +pub(crate) mod private_events; pub mod private_notes; pub mod process_message; From 3458acffe83dd822217083ae05de4c3f23b954ca Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:00:19 +0000 Subject: [PATCH 094/119] move more stuff to offchain mod --- .../aztec/src/messages/discovery/mod.nr | 17 ++--------------- .../aztec/src/messages/processing/offchain.nr | 12 ++++++++++++ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index a9e267e5cc0d..4987b63d1168 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -14,8 +14,8 @@ use crate::{ encoding::MAX_MESSAGE_CONTENT_LEN, logs::note::MAX_NOTE_PACKED_LEN, processing::{ - get_private_logs, MessageContext, OffchainMessageWithContext, pending_tagged_log::PendingTaggedLog, - validate_and_store_enqueued_notes_and_events, + get_private_logs, MessageContext, offchain::OffchainMessageHandler, OffchainMessageWithContext, + pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events, }, }, utils::array, @@ -96,19 +96,6 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); -/// A function that manages offchain-delivered messages for processing during sync. -/// -/// Offchain messages are transaction effects that are not broadcasted via onchain logs. They are instead delivered to -/// the recipient through an external channel (e.g. a URL) and stored locally via the auto-generated `offchain_receive` -/// utility function (injected by [`crate::macros::aztec`]). This handler is responsible for deciding when each message -/// is ready for processing, when it can be safely disposed of, etc. -/// -/// The default implementation is -/// [`crate::messages::processing::offchain::default_handler`], which manages an inbox with -/// expiration-based eviction and automatic context resolution. -pub type OffchainMessageHandler = unconstrained fn[Env]( -/* contract_address */AztecAddress) -> CapsuleArray; - /// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private /// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. /// This is the mechanism via which a contract updates its knowledge of its private state. diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index ca15cd3aa7c6..229e09137029 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -25,6 +25,18 @@ global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN /// Maximum number of offchain messages accepted by `offchain_receive` in a single call. pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; +/// A function that manages offchain-delivered messages for processing during sync. +/// +/// Offchain messages are transaction effects that are not broadcasted via onchain logs. They are instead delivered to +/// the recipient through an external channel (e.g. a URL) and stored locally via the auto-generated +/// `offchain_receive` utility function (injected by the `#[aztec]` macro). This handler is responsible for deciding +/// when each message is ready for processing, when it can be safely disposed of, etc. +/// +/// The default implementation is [`default_handler`], which manages an inbox with expiration-based eviction and +/// automatic context resolution. +pub type OffchainMessageHandler = unconstrained fn[Env]( +/* contract_address */AztecAddress) -> CapsuleArray; + /// A message delivered via the `offchain_receive` utility function. pub struct OffchainMessage { /// The encrypted message payload. From 84d5b66fafdc859bb037898c6a401d680c046a1c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:04:37 +0000 Subject: [PATCH 095/119] remove accidental comment --- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 3 --- 1 file changed, 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 4987b63d1168..52f92aa4ec51 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -96,9 +96,6 @@ pub type CustomMessageHandler = unconstrained fn[Env]( /* msg_content */ BoundedVec, /* message_context */ MessageContext); -/// Performs the state synchronization process, in which private logs are downloaded and inspected to find new private -/// notes, partial notes and events, etc., and pending partial notes are processed to search for their completion logs. -/// This is the mechanism via which a contract updates its knowledge of its private state. /// Synchronizes the contract's private state with the network. /// /// As blocks are mined, it is possible for a contract's private state to change (e.g. with new notes being created), From e639cb9efc795ed2ca77d52d9572636dfb42f911 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:24:58 +0000 Subject: [PATCH 096/119] OffchainMessageHandler -> OffchainInboxSync --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 14 ++++---------- .../aztec-nr/aztec/src/messages/discovery/mod.nr | 8 ++++---- .../aztec/src/messages/processing/offchain.nr | 2 +- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index acfa8469f632..314983d60f73 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -115,15 +115,12 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { quote { Option::>::none() } }; - let offchain_message_handler_option = quote { + let offchain_inbox_sync_option = quote { Option::some(aztec::messages::processing::offchain::default_handler) }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { - generate_sync_state( - process_custom_message_option, - offchain_message_handler_option, - ) + generate_sync_state(process_custom_message_option, offchain_inbox_sync_option) } else { quote {} }; @@ -352,10 +349,7 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - } /// Generates the `sync_state` utility function that performs message discovery. -comptime fn generate_sync_state( - process_custom_message_option: Quoted, - offchain_message_handler_option: Quoted, -) -> Quoted { +comptime fn generate_sync_state(process_custom_message_option: Quoted, offchain_inbox_sync_option: Quoted) -> Quoted { quote { pub struct sync_state_parameters {} @@ -371,7 +365,7 @@ comptime fn generate_sync_state( address, _compute_note_hash_and_nullifier, $process_custom_message_option, - $offchain_message_handler_option, + $offchain_inbox_sync_option, ); } } diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 52f92aa4ec51..32d066f25329 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -14,7 +14,7 @@ use crate::{ encoding::MAX_MESSAGE_CONTENT_LEN, logs::note::MAX_NOTE_PACKED_LEN, processing::{ - get_private_logs, MessageContext, offchain::OffchainMessageHandler, OffchainMessageWithContext, + get_private_logs, MessageContext, offchain::OffchainInboxSync, OffchainMessageWithContext, pending_tagged_log::PendingTaggedLog, validate_and_store_enqueued_notes_and_events, }, }, @@ -108,7 +108,7 @@ pub unconstrained fn do_sync_state, process_custom_message: Option>, - offchain_message_handler: Option>, + offchain_inbox_sync: Option>, ) { aztecnr_debug_log!("Performing state synchronization"); @@ -136,8 +136,8 @@ pub unconstrained fn do_sync_state = fetch(contract_address); msgs.for_each(|i, msg| { process_message_ciphertext( diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 229e09137029..77b8f8a946bc 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -34,7 +34,7 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// /// The default implementation is [`default_handler`], which manages an inbox with expiration-based eviction and /// automatic context resolution. -pub type OffchainMessageHandler = unconstrained fn[Env]( +pub type OffchainInboxSync = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; /// A message delivered via the `offchain_receive` utility function. From 93f1dda6fa5f90b211c7d58f41fe28994075f1ef Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:33:52 +0000 Subject: [PATCH 097/119] offchain::default_handler -> offchain::sync_inbox --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 2 +- .../aztec-nr/aztec/src/messages/processing/offchain.nr | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index 314983d60f73..aa508445337c 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -116,7 +116,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { }; let offchain_inbox_sync_option = quote { - Option::some(aztec::messages::processing::offchain::default_handler) + Option::some(aztec::messages::processing::offchain::sync_inbox) }; let sync_state_fn_and_abi_export = if !m.functions().any(|f| f.name() == quote { sync_state }) { diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 77b8f8a946bc..98471c95da45 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -13,13 +13,13 @@ use crate::{ /// This is the slot where we accumulate messages received through [`receive`]. global OFFCHAIN_INBOX_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_INBOX_SLOT".as_bytes()); -/// Ephemeral capsule slot used by [`default_handler`] to pass tx hash resolution requests to PXE. +/// Ephemeral capsule slot used by [`sync_inbox`] to pass tx hash resolution requests to PXE. global OFFCHAIN_CONTEXT_REQUESTS_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_REQUESTS_SLOT".as_bytes()); -/// Ephemeral capsule slot used by [`default_handler`] to read tx context responses from PXE. +/// Ephemeral capsule slot used by [`sync_inbox`] to read tx context responses from PXE. global OFFCHAIN_CONTEXT_RESPONSES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_CONTEXT_RESPONSES_SLOT".as_bytes()); -/// Ephemeral capsule slot used by [`default_handler`] to collect messages ready for processing. +/// Ephemeral capsule slot used by [`sync_inbox`] to collect messages ready for processing. global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN_READY_MESSAGES_SLOT".as_bytes()); /// Maximum number of offchain messages accepted by `offchain_receive` in a single call. @@ -32,7 +32,7 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// `offchain_receive` utility function (injected by the `#[aztec]` macro). This handler is responsible for deciding /// when each message is ready for processing, when it can be safely disposed of, etc. /// -/// The default implementation is [`default_handler`], which manages an inbox with expiration-based eviction and +/// The default implementation is [`sync_inbox`], which manages an inbox with expiration-based eviction and /// automatic context resolution. pub type OffchainInboxSync = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; @@ -112,7 +112,7 @@ pub unconstrained fn receive( /// /// Messages remain in the inbox and are reprocessed on each sync until their originating transaction is no longer at /// risk of being dropped by a reorg. -pub unconstrained fn default_handler(address: AztecAddress) -> CapsuleArray { +pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray { let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); let context_resolution_requests: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_CONTEXT_REQUESTS_SLOT); let resolved_contexts: CapsuleArray> = From 2bca4e12de202b87f21ab7f27badfa48ae53adc2 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:44:27 +0000 Subject: [PATCH 098/119] reduce visibility of OffchainInboxSync --- .../aztec-nr/aztec/src/messages/processing/offchain.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 98471c95da45..d9db4d06b5a7 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -34,7 +34,7 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// /// The default implementation is [`sync_inbox`], which manages an inbox with expiration-based eviction and /// automatic context resolution. -pub type OffchainInboxSync = unconstrained fn[Env]( +pub(crate) type OffchainInboxSync = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; /// A message delivered via the `offchain_receive` utility function. From 6e401de95f5d0ff5baa59d60c5cef0b17fb5e264 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 18:58:12 +0000 Subject: [PATCH 099/119] better comments --- .../aztec/src/messages/processing/offchain.nr | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index d9db4d06b5a7..21c9c905e7e7 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -27,13 +27,19 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// A function that manages offchain-delivered messages for processing during sync. /// -/// Offchain messages are transaction effects that are not broadcasted via onchain logs. They are instead delivered to -/// the recipient through an external channel (e.g. a URL) and stored locally via the auto-generated -/// `offchain_receive` utility function (injected by the `#[aztec]` macro). This handler is responsible for deciding -/// when each message is ready for processing, when it can be safely disposed of, etc. +/// Offchain messages are messages that are not broadcasted via onchain logs. They are instead delivered to the +/// recipient by calling the `offchain_receive` utility function (injected by the `#[aztec]` macro). Message transport +/// is the app's responsibility. Typical examples of transport methods are: messaging apps, email, QR codes, etc. /// -/// The default implementation is [`sync_inbox`], which manages an inbox with expiration-based eviction and -/// automatic context resolution. +/// Once offchain messages are delivered to the recipient's private environment via `offchain_receive`, messages are +/// locally stored in a persistent inbox. +/// +/// This function determines when each message in said inbox is ready for processing, when it can be safely disposed +/// of, +/// etc. +/// +/// The only current implementation of an `OffchainInboxSync` is [`sync_inbox`], which manages an inbox with expiration +/// based eviction and automatic transaction context resolution. pub(crate) type OffchainInboxSync = unconstrained fn[Env]( /* contract_address */AztecAddress) -> CapsuleArray; From 0844a69cf888eff5e9ece4a35371d35dd104e7c5 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 19:02:59 +0000 Subject: [PATCH 100/119] wat auto-formatter --- .../aztec-nr/aztec/src/messages/processing/offchain.nr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 21c9c905e7e7..3d161ea18b50 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -35,8 +35,7 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// locally stored in a persistent inbox. /// /// This function determines when each message in said inbox is ready for processing, when it can be safely disposed -/// of, -/// etc. +/// of, etc. /// /// The only current implementation of an `OffchainInboxSync` is [`sync_inbox`], which manages an inbox with expiration /// based eviction and automatic transaction context resolution. From ea21ed1fe7b5ad17ab396ad50d64131827765053 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 19:05:59 +0000 Subject: [PATCH 101/119] nit --- noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 32d066f25329..185de9620a09 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -137,8 +137,7 @@ pub unconstrained fn do_sync_state = fetch(contract_address); + let msgs: CapsuleArray = offchain_inbox_sync.unwrap()(contract_address); msgs.for_each(|i, msg| { process_message_ciphertext( contract_address, From ac69d747e615969cc70178a4ac1456330bcebb9e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 19:54:44 +0000 Subject: [PATCH 102/119] bound by tx ttl --- .../aztec/src/messages/processing/offchain.nr | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 3d161ea18b50..6b75c4a0b4d0 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -5,7 +5,12 @@ use crate::{ encoding::MESSAGE_CIPHERTEXT_LEN, processing::{MessageContext, MessageTxContext, OffchainMessageWithContext, resolve_message_contexts}, }, - protocol::{address::AztecAddress, hash::sha256_to_field, traits::{Deserialize, Serialize}}, + protocol::{ + address::AztecAddress, + constants::MAX_TX_LIFETIME, + hash::sha256_to_field, + traits::{Deserialize, Serialize}, + }, }; /// Base capsule slot for the persistent inbox of [`PendingOffchainMsg`] entries. @@ -67,8 +72,14 @@ struct PendingOffchainMsg { recipient: AztecAddress, /// The hash of the transaction that produced this message. A value of 0 indicates a tx-less message. tx_hash: Field, - /// The timestamp after which this message can be evicted from the inbox. + /// The timestamp after which this message can be evicted from the inbox (as provided by the sender). expiration_timestamp: u64, + /// The timestamp at which this message was received (i.e. added to the inbox via `receive`). + /// + /// Used to cap the effective expiration of messages with an associated transaction: since a tx can only live for + /// `MAX_TX_LIFETIME`, we don't need to keep the message around longer than `received_at + MAX_TX_LIFETIME`, + /// regardless of the sender-provided `expiration_timestamp`. + received_at: u64, } /// Delivers offchain messages to the given contract's offchain inbox for subsequent processing. @@ -81,8 +92,9 @@ struct PendingOffchainMsg { /// Messages are processed when their originating transaction is found onchain (providing the context needed to /// validate resulting notes and events). /// -/// Messages are kept in the inbox until their expiration timestamp is reached. This is necessary to make the -/// mechanism resilient to reorgs. +/// Messages are kept in the inbox until their expiration timestamp is reached. For messages with an associated +/// transaction, the effective expiration is capped to `received_at + MAX_TX_LIFETIME`, since there's no point in +/// keeping a message around longer than its originating transaction could possibly live. /// /// Processing order is not guaranteed. pub unconstrained fn receive( @@ -90,6 +102,7 @@ pub unconstrained fn receive( messages: BoundedVec, ) { let inbox: CapsuleArray = CapsuleArray::at(contract_address, OFFCHAIN_INBOX_SLOT); + let now = UtilityContext::new().timestamp(); let mut i = 0; let messages_len = messages.len(); while i < messages_len { @@ -105,6 +118,7 @@ pub unconstrained fn receive( recipient: msg.recipient, tx_hash, expiration_timestamp: msg.expiration_timestamp, + received_at: now, }, ); i += 1; @@ -179,8 +193,22 @@ pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray msg.expiration_timestamp { + if now > effective_expiration { inbox.remove(j); continue; } From 0685eb3698be3af7c39c38fd70dbdeb13c08665c Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 20:37:36 +0000 Subject: [PATCH 103/119] fix bug in loop and better comments --- .../aztec/src/messages/processing/offchain.nr | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 6b75c4a0b4d0..ada0f87adf3b 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -30,6 +30,9 @@ global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN /// Maximum number of offchain messages accepted by `offchain_receive` in a single call. pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; +/// Tolerance added to the `MAX_TX_LIFETIME` cap for message expiration. +global TX_EXPIRATION_CAP_TOLERANCE: u64 = 3600; // 1 hour + /// A function that manages offchain-delivered messages for processing during sync. /// /// Offchain messages are messages that are not broadcasted via onchain logs. They are instead delivered to the @@ -168,59 +171,63 @@ pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray 0 { - j -= 1; - let maybe_ctx = resolved_contexts.get(j); - let msg = inbox.get(j); - - // This loop decides what to do with each message in the offchain message inbox. We need to handle 5 + // This loop decides what to do with each message in the offchain message inbox. We need to handle 3 // different scenarios for each message. // - // 1. The message has expired. We remove it from the inbox. - // - // 2. The TX that emitted this message is still not known to PXE: in this case we can't yet process this + // 1. The TX that emitted this message is still not known to PXE: in this case we can't yet process this // message, as any notes or events discovered will fail to be validated. So we leave the message in the inbox, // awaiting for future syncs to detect that the TX became available. // - // 3. The TX that emitted this message had been included in a block known to PXE, but as result of a reorg, - // we no longer see it. From the point of view of this handler we're essentially in the same scenario: we need - // to keep the message in the inbox awaiting for the TX to be available to PXE (or eventually expire). - // - // 4. The message is not associated to a TX to begin with. The current version of offchain message processing + // 2. The message is not associated to a TX to begin with. The current version of offchain message processing // does not support this case, but in the future it will. Right now, a message without an associated TX will // sit in the inbox until it expires. // - // 5. The TX that emitted this message has been found by PXE. We have a proper MessageContext, and the message - // is now processable. We add the message to the `ready_to_process` CapsuleArray and the sync_state loop will - // process it. + // 3. The TX that emitted this message has been found by PXE. That gives us all the information needed to + // process the message. We add the message to the `ready_to_process` CapsuleArray so that the `sync_state` loop + // processes it. + // + // In all cases, if the message has expired, or the associated TX is older than than `MAX_TX_LIFETIME` + // (24 hours), we remove it from the inbox. + // + // Note: the loop runs backwards because it might call `inbox.remove(j)` to purge expired messages and we also + // need to align it with `resolved_contexts.get(j)`. Going from last to first simplifies the algorithm as + // not yet visited element indexes remain stable. + j -= 1; + let maybe_ctx = resolved_contexts.get(j); + let msg = inbox.get(j); - // Compute the effective expiration. For messages with an associated transaction, we cap the - // sender-provided expiration to `received_at + MAX_TX_LIFETIME`: there's no point in keeping a message - // around longer than the tx could possibly live. + // Compute the message's effective expiration timestamp to determine if we can purge it from the inbox. let effective_expiration = if msg.tx_hash != 0 { - let tx_lifetime_cap = msg.received_at + MAX_TX_LIFETIME; + // For messages with an associated transaction, we cap the sender-provided expiration timestamp to + // `received_at + MAX_TX_LIFETIME + 1 hour tolerance`: there's no point in keeping a message once the TX + // can no longer possibly be included in a block. + let tx_lifetime_cap = msg.received_at + MAX_TX_LIFETIME + TX_EXPIRATION_CAP_TOLERANCE; if msg.expiration_timestamp < tx_lifetime_cap { msg.expiration_timestamp } else { tx_lifetime_cap } } else { + // Messages with no associated TX expire on their `expiration_timestamp` msg.expiration_timestamp }; - // Scenario 1: message expired. We remove it from the inbox. + // Message expired. We remove it from the inbox. if now > effective_expiration { inbox.remove(j); - continue; } - // Scenarios 2, 3: emitting TX not yet available. We keep the message in the inbox, as it might become + // Scenario 1: associated TX not yet available. We keep the message in the inbox, as it might become // processable as new blocks get mined. - // Scenario 4: no TX associated to message. The message will sit in the inbox until it expires. + // Scenario 2: no TX associated to message. The message will sit in the inbox until it expires. if maybe_ctx.is_none() { continue; } - // Scenario 5: Message is ready to process, add ready_to_process + // Scenario 3: Message is ready to process, add to result array. Note we still keep it in the inbox unless we + // consider it has expired: this is because we need to account for reorgs. If reorg occurs after we processed + // a message, the effects of processing the message get rewind. However, the associated TX can be included in + // a subsequent block. Should that happen, the message must be re-processed to ensure consistency. let ctx = maybe_ctx.unwrap(); let message_context = MessageContext { tx_hash: ctx.tx_hash, @@ -228,7 +235,6 @@ pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray Date: Mon, 9 Mar 2026 20:39:12 +0000 Subject: [PATCH 104/119] bump tolerance to 2 hours --- .../aztec-nr/aztec/src/messages/processing/offchain.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index ada0f87adf3b..2b0428d50075 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -31,7 +31,7 @@ global OFFCHAIN_READY_MESSAGES_SLOT: Field = sha256_to_field("AZTEC_NR::OFFCHAIN pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// Tolerance added to the `MAX_TX_LIFETIME` cap for message expiration. -global TX_EXPIRATION_CAP_TOLERANCE: u64 = 3600; // 1 hour +global TX_EXPIRATION_CAP_TOLERANCE: u64 = 7200; // 2 hours /// A function that manages offchain-delivered messages for processing during sync. /// @@ -199,7 +199,7 @@ pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray Date: Mon, 9 Mar 2026 20:40:51 +0000 Subject: [PATCH 105/119] mark MessageTxContext as Serialize --- .../aztec-nr/aztec/src/messages/processing/message_context.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr index 1e6eb9b59dd1..bef4f1ed1e65 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr @@ -18,7 +18,7 @@ pub struct MessageContext { /// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. This type is used when processing /// offchain messages. MessageTxContext's are resolved via a PXE oracle call, and then it's the contract who pairs /// that data up with a recipient to build a `MessageContext`. -#[derive(Deserialize, Eq)] +#[derive(Serialize, Deserialize, Eq)] pub(crate) struct MessageTxContext { pub tx_hash: Field, pub unique_note_hashes_in_tx: BoundedVec, From 04120d7c5269a2cc873f60b6a2fc991afb8e4a72 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 20:53:47 +0000 Subject: [PATCH 106/119] better comments for MessageTxContext --- .../aztec/src/messages/processing/message_context.nr | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr index bef4f1ed1e65..b98b4bdeff20 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/message_context.nr @@ -15,9 +15,11 @@ pub struct MessageContext { /// Transaction context needed to process a message. /// -/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. This type is used when processing -/// offchain messages. MessageTxContext's are resolved via a PXE oracle call, and then it's the contract who pairs -/// that data up with a recipient to build a `MessageContext`. +/// Like [`MessageContext`], but `MessageTxContext` does not include the recipient. MessageTxContext's are kind of +/// adhoc: they are just the minimal data structure that the contract needs to get from a PXE oracle to prepare +/// offchain messages to be processed. We reify it with a type just because it crosses Noir<->TS boundaries. +/// The contract knows how to pair the context data with a recipient: then it is able to build a `MessageContext` for +/// subsequent processing. #[derive(Serialize, Deserialize, Eq)] pub(crate) struct MessageTxContext { pub tx_hash: Field, From 2ab7299b0a8e7409a6c36fd7473790a410a86f4e Mon Sep 17 00:00:00 2001 From: mverzilli Date: Mon, 9 Mar 2026 20:57:25 +0000 Subject: [PATCH 107/119] nit --- yarn-project/end-to-end/src/e2e_offchain_payment.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index a44acadb681a..534484a10597 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -68,7 +68,6 @@ describe('e2e_offchain_payment', () => { .send({ from: alice }); expect(offchainMessages.length).toBeGreaterThan(0); - // QR payload is the offchain message for Bob. const messageForBob = offchainMessages.find(msg => msg.recipient.equals(bob)); expect(messageForBob).toBeTruthy(); From 2ba8b7e30155137b8022e4518b182af4d8e6ddb7 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 08:24:14 +0000 Subject: [PATCH 108/119] extract test aux for reorg --- .../src/e2e_offchain_payment.test.ts | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 534484a10597..1f4108580ad6 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -36,7 +36,7 @@ describe('e2e_offchain_payment', () => { ({ contract } = await OffchainPaymentContract.deploy(wallet).send({ from: accounts[0] })); }); - const forceEmptyBlock = async () => { + async function forceEmptyBlock() { const blockBefore = await aztecNode.getBlockNumber(); logger.info(`Forcing empty block. Current L2 block: ${blockBefore}`); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 }); @@ -51,7 +51,27 @@ describe('e2e_offchain_payment', () => { 1, ); await aztecNodeAdmin.setConfig({ minTxsPerBlock: 1 }); - }; + } + + async function forceReorg(block: BlockNumber) { + await retryUntil( + async () => { + const tips = await aztecNode.getL2Tips(); + return tips.checkpointed.block.number >= block; + }, + 'checkpointed block', + 30, + 1, + ); + + await aztecNodeAdmin.pauseSync(); + + await cheatCodes.eth.reorg(1); + await aztecNodeAdmin.rollbackTo(Number(block) - 1); + expect(await aztecNode.getBlockNumber()).toBe(Number(block) - 1); + + await aztecNodeAdmin.resumeSync(); + } it('processes an offchain-delivered private payment via QR-style handoff', async () => { const [alice, bob] = accounts; @@ -82,7 +102,7 @@ describe('e2e_offchain_payment', () => { ]) .simulate({ from: bob }); - // Force an empty block so the PXE re-syncs and discovers the offchain-delivered note. + // Force an empty block so the PXE re-syncs and discovers the offchain-delivered notes. await forceEmptyBlock(); const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); @@ -91,9 +111,10 @@ describe('e2e_offchain_payment', () => { it('reprocesses an offchain-delivered payment after an L1 reorg', async () => { const [alice, bob] = accounts; + const mintAmount = 100n; const paymentAmount = 40n; - await contract.methods.mint(100n, alice).send({ from: alice }); + await contract.methods.mint(mintAmount, alice).send({ from: alice }); const provenTx = await proveInteraction(wallet, contract.methods.transfer_offchain(paymentAmount, bob), { from: alice, @@ -133,24 +154,7 @@ describe('e2e_offchain_payment', () => { const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobBalance).toBe(paymentAmount); - // Force a re-org - await retryUntil( - async () => { - const tips = await aztecNode.getL2Tips(); - return tips.checkpointed.block.number >= txBlockNumber; - }, - 'checkpointed block', - 30, - 1, - ); - - await aztecNodeAdmin.pauseSync(); - - await cheatCodes.eth.reorg(1); - await aztecNodeAdmin.rollbackTo(Number(txBlockNumber) - 1); - expect(await aztecNode.getBlockNumber()).toBe(Number(txBlockNumber) - 1); - - await aztecNodeAdmin.resumeSync(); + await forceReorg(txBlockNumber); // Verify that the payment TX is no longer present after the reorg const txEffectAfterRollback = await aztecNode.getTxEffect(txHash); From 046bbfeb8268359efa8269fe384664440df50b7f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 08:56:23 +0000 Subject: [PATCH 109/119] make it a bit more robust --- .../oracle/utility_execution.test.ts | 26 ++++++++- .../oracle/utility_execution_oracle.ts | 54 +++++++++++-------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts index 5c32d45f5ecd..7f95c59cd9a9 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts @@ -249,7 +249,7 @@ describe('Utility Execution test suite', () => { ).rejects.toThrow(`Got a message context request from ${wrongAddress}, expected ${contractAddress}`); }); - it('returns null for zero tx hashes', async () => { + it('sets null in response capsule for zero tx hashes', async () => { capsuleStore.readCapsuleArray.mockResolvedValueOnce([[Fr.ZERO]]); await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); @@ -287,7 +287,7 @@ describe('Utility Execution test suite', () => { expect(responseFields).toEqual(expected); }); - it('returns null for tx effects beyond anchor block', async () => { + it('sets null in response capsule for tx effects beyond anchor block', async () => { const txHash = TxHash.random(); capsuleStore.readCapsuleArray.mockResolvedValueOnce([[txHash.hash]]); @@ -308,11 +308,33 @@ describe('Utility Execution test suite', () => { expect(responseFields).toEqual(MessageTxContext.toSerializedOption(null)); }); + it('throws on empty capsule entry', async () => { + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[]]); + await expect( + utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot), + ).rejects.toThrow('Malformed message context request at index 0: expected 1 field (tx hash), got 0'); + }); + + it('throws on capsule entry with extra fields', async () => { + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[Fr.random(), Fr.random()]]); + await expect( + utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot), + ).rejects.toThrow('Malformed message context request at index 0: expected 1 field (tx hash), got 2'); + }); + it('clears the request capsule after processing', async () => { capsuleStore.readCapsuleArray.mockResolvedValueOnce([]); await utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot); expect(capsuleStore.setCapsuleArray).toHaveBeenCalledWith(contractAddress, requestSlot, [], 'test-job-id'); }); + + it('clears the request capsule even on error', async () => { + capsuleStore.readCapsuleArray.mockResolvedValueOnce([[]]); + await expect( + utilityExecutionOracle.utilityResolveMessageContexts(contractAddress, requestSlot, responseSlot), + ).rejects.toThrow(); + expect(capsuleStore.setCapsuleArray).toHaveBeenCalledWith(contractAddress, requestSlot, [], 'test-job-id'); + }); }); }); }); diff --git a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts index fe722587aa2c..a55c9c9a06ea 100644 --- a/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts +++ b/yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts @@ -558,32 +558,40 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra messageContextRequestsArrayBaseSlot: Fr, messageContextResponsesArrayBaseSlot: Fr, ) { - if (!this.contractAddress.equals(contractAddress)) { - throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`); - } - - const requestCapsules = await this.capsuleStore.readCapsuleArray( - contractAddress, - messageContextRequestsArrayBaseSlot, - this.jobId, - ); + try { + if (!this.contractAddress.equals(contractAddress)) { + throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`); + } + const requestCapsules = await this.capsuleStore.readCapsuleArray( + contractAddress, + messageContextRequestsArrayBaseSlot, + this.jobId, + ); - const txHashes = requestCapsules.map(fields => fields[0] ?? Fr.ZERO); - const maybeMessageContexts = await this.messageContextService.resolveMessageContexts( - txHashes, - this.anchorBlockHeader.getBlockNumber(), - ); + const txHashes = requestCapsules.map((fields, i) => { + if (fields.length !== 1) { + throw new Error( + `Malformed message context request at index ${i}: expected 1 field (tx hash), got ${fields.length}`, + ); + } + return fields[0]; + }); - // Clear request capsule - await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); + const maybeMessageContexts = await this.messageContextService.resolveMessageContexts( + txHashes, + this.anchorBlockHeader.getBlockNumber(), + ); - // Leave response in response capsule array. - await this.capsuleStore.setCapsuleArray( - contractAddress, - messageContextResponsesArrayBaseSlot, - maybeMessageContexts.map(MessageTxContext.toSerializedOption), - this.jobId, - ); + // Leave response in response capsule array. + await this.capsuleStore.setCapsuleArray( + contractAddress, + messageContextResponsesArrayBaseSlot, + maybeMessageContexts.map(MessageTxContext.toSerializedOption), + this.jobId, + ); + } finally { + await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId); + } } public storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise { From e4336c5972715856e5e5a06e67db739626b6f529 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 09:02:59 +0000 Subject: [PATCH 110/119] throw on malformed tx effects --- yarn-project/pxe/src/messages/message_context_service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn-project/pxe/src/messages/message_context_service.ts b/yarn-project/pxe/src/messages/message_context_service.ts index 3ee5bcbdaeea..ae39812a7c3b 100644 --- a/yarn-project/pxe/src/messages/message_context_service.ts +++ b/yarn-project/pxe/src/messages/message_context_service.ts @@ -19,6 +19,8 @@ export class MessageContextService { // TODO: optimize, we might be hitting the node to get the same txHash repeatedly return Promise.all( txHashes.map(async txHashField => { + // A zero tx hash indicates a tx-less offchain message (e.g. one not tied to any onchain transaction). + // These messages don't have a transaction context to resolve, so we return null. if (txHashField.isZero()) { return null; } @@ -29,9 +31,11 @@ export class MessageContextService { return null; } + // Every tx has at least one nullifier (the first nullifier derived from the tx hash). Hitting this condition + // would mean a buggy node, but since we need to access data.nullifiers[0], the defensive check does no harm. const data = txEffect.data; if (data.nullifiers.length === 0) { - return null; + throw new Error(`Tx effect for ${txHash} has no nullifiers`); } return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]); From ac0a5c42c19a80dc3a23d944eb4226f89ba67432 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 09:25:35 +0000 Subject: [PATCH 111/119] remove unnecessary oracle version bump --- noir-projects/aztec-nr/aztec/src/oracle/version.nr | 2 +- yarn-project/end-to-end/src/e2e_offchain_payment.test.ts | 1 + yarn-project/pxe/src/oracle_version.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/version.nr b/noir-projects/aztec-nr/aztec/src/oracle/version.nr index 72947ef20ee3..9c569725cc82 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/version.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/version.nr @@ -5,7 +5,7 @@ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is /// called /// and if the oracle version is incompatible an error is thrown. -pub global ORACLE_VERSION: Field = 16; +pub global ORACLE_VERSION: Field = 15; /// Asserts that the version of the oracle is compatible with the version expected by the contract. pub fn assert_compatible_oracle_version() { diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 1f4108580ad6..38a2e2b3136e 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -2,6 +2,7 @@ import { AztecAddress } from '@aztec/aztec.js/addresses'; import { NO_WAIT, extractOffchainOutput } from '@aztec/aztec.js/contracts'; import type { AztecNode } from '@aztec/aztec.js/node'; import type { CheatCodes } from '@aztec/aztec/testing'; +import type { BlockNumber } from '@aztec/foundation/branded-types'; import { retryUntil } from '@aztec/foundation/retry'; import { OffchainPaymentContract } from '@aztec/noir-test-contracts.js/OffchainPayment'; import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client'; diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index a46244816e7a..b83b9d833a64 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -4,7 +4,7 @@ /// /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -export const ORACLE_VERSION = 16; +export const ORACLE_VERSION = 15; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in From 11303bd849660fde37f1c09e5d300502d7cb2bfd Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 11:00:00 +0000 Subject: [PATCH 112/119] lint disable camel case warnings --- yarn-project/end-to-end/src/e2e_offchain_payment.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index 38a2e2b3136e..bb1fe30cfdce 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ import { AztecAddress } from '@aztec/aztec.js/addresses'; import { NO_WAIT, extractOffchainOutput } from '@aztec/aztec.js/contracts'; import type { AztecNode } from '@aztec/aztec.js/node'; From 15fe10207b1199e408b3044595be4a50ff272c0d Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 12:44:27 +0000 Subject: [PATCH 113/119] add tests for offchain sync --- .../aztec/src/messages/processing/offchain.nr | 313 +++++++++++++++++- yarn-project/txe/src/rpc_translator.ts | 3 +- 2 files changed, 311 insertions(+), 5 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 2b0428d50075..ae7e4be23482 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -33,6 +33,11 @@ pub global MAX_OFFCHAIN_MESSAGES_PER_RECEIVE_CALL: u32 = 16; /// Tolerance added to the `MAX_TX_LIFETIME` cap for message expiration. global TX_EXPIRATION_CAP_TOLERANCE: u64 = 7200; // 2 hours +/// Maximum time-to-live for a tx-bound offchain message: `MAX_TX_LIFETIME + TX_EXPIRATION_CAP_TOLERANCE`. +/// +/// After `received_at + MAX_MSG_TTL`, the message is evicted regardless of the sender-provided expiration. +global MAX_MSG_TTL: u64 = MAX_TX_LIFETIME + TX_EXPIRATION_CAP_TOLERANCE; + /// A function that manages offchain-delivered messages for processing during sync. /// /// Offchain messages are messages that are not broadcasted via onchain logs. They are instead delivered to the @@ -80,7 +85,7 @@ struct PendingOffchainMsg { /// The timestamp at which this message was received (i.e. added to the inbox via `receive`). /// /// Used to cap the effective expiration of messages with an associated transaction: since a tx can only live for - /// `MAX_TX_LIFETIME`, we don't need to keep the message around longer than `received_at + MAX_TX_LIFETIME`, + /// `MAX_TX_LIFETIME`, we don't need to keep the message around longer than `received_at + MAX_MSG_TTL`, /// regardless of the sender-provided `expiration_timestamp`. received_at: u64, } @@ -96,7 +101,7 @@ struct PendingOffchainMsg { /// validate resulting notes and events). /// /// Messages are kept in the inbox until their expiration timestamp is reached. For messages with an associated -/// transaction, the effective expiration is capped to `received_at + MAX_TX_LIFETIME`, since there's no point in +/// transaction, the effective expiration is capped to `received_at + MAX_MSG_TTL`, since there's no point in /// keeping a message around longer than its originating transaction could possibly live. /// /// Processing order is not guaranteed. @@ -199,9 +204,9 @@ pub unconstrained fn sync_inbox(address: AztecAddress) -> CapsuleArray CapsuleArray, expiration_timestamp: u64) -> OffchainMessage { + OffchainMessage { + ciphertext: BoundedVec::new(), + recipient: AztecAddress::from_field(42), + tx_hash, + expiration_timestamp, + } + } + + /// Calls `receive` with a single message. + unconstrained fn receive_one(address: AztecAddress, msg: OffchainMessage) { + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(msg); + receive(address, msgs); + } + + /// Advances the TXE block timestamp by `offset` seconds and returns the resulting timestamp. + unconstrained fn advance_by(env: TestEnvironment, offset: u64) -> u64 { + env.advance_next_block_timestamp_by(offset); + env.mine_block(); + env.last_block_timestamp() + } + + #[test] + unconstrained fn empty_inbox_returns_empty_result() { + let env = TestEnvironment::new(); + let _now = advance_by(env, 1); + env.utility_context(|context| { + let result = sync_inbox(context.this_address()); + assert_eq(result.len(), 0); + }); + } + + // -- Tx-bound message expiration -------------------------------------- + + #[test] + unconstrained fn tx_bound_msg_expires_by_sender_timestamp() { + // Sender's expiration is well before the lifetime cap, so it is the effective expiration. + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + let sender_expiration_offset = 100; + + // Receive message with expiration at receive_time + offset. + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg( + Option::some(random()), + receive_time + sender_expiration_offset, + ), + ); + }); + + // Advance past sender expiration but before the lifetime cap. + let _now = advance_by(env, sender_expiration_offset + 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // context is None, not ready + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 0); // expired, removed + }); + } + + #[test] + unconstrained fn tx_bound_msg_expires_by_lifetime_cap() { + // Sender's expiration is far in the future, so the lifetime cap kicks in. + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), + ); + }); + + // Advance past the lifetime cap. + let _now = advance_by(env, MAX_MSG_TTL + 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 0); // capped expiration exceeded, removed + }); + } + + #[test] + unconstrained fn tx_bound_msg_not_expired_sender_timestamp_binding() { + // Sender expiration is in the future and below the cap, so it is the binding expiration. + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + let sender_expiration_offset = 100; + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg( + Option::some(random()), + receive_time + sender_expiration_offset, + ), + ); + }); + + // Advance, but not past sender expiration. + let _now = advance_by(env, sender_expiration_offset - 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // context is None, not ready + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 1); // not expired, stays + }); + } + + #[test] + unconstrained fn tx_bound_msg_not_expired_lifetime_cap_binding() { + // Sender expiration is way beyond the cap, but `now` is still before the cap. + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), + ); + }); + + // Advance, but not past the lifetime cap. + let _now = advance_by(env, 100); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // context is None, not ready + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 1); // not expired, stays + }); + } + + // -- Tx-less message expiration --------------------------------------- + + #[test] + unconstrained fn tx_less_msg_expires_by_sender_timestamp() { + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + let sender_expiration_offset = 100; + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg(Option::none(), receive_time + sender_expiration_offset), + ); + }); + + let _now = advance_by(env, sender_expiration_offset + 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 0); // expired, removed + }); + } + + #[test] + unconstrained fn tx_less_msg_with_large_expiration_is_not_capped() { + // The message is tx-less, so the lifetime cap does not apply and the sender's large + // expiration is trusted. Even though `now` is past what the cap would be for a tx-bound + // message, the message stays. + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg(Option::none(), receive_time + 10 * MAX_MSG_TTL), + ); + }); + + // Advance past what the lifetime cap would be for a tx-bound message. + let _now = advance_by(env, MAX_MSG_TTL + 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // context is None, not ready + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 1); // tx-less, not capped, stays + }); + } + + // -- Context resolution scenarios ------------------------------------- + + #[test] + unconstrained fn unresolved_tx_stays_in_inbox() { + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + receive_one( + context.this_address(), + make_msg(Option::some(random()), receive_time + MAX_MSG_TTL), + ); + }); + + let _now = advance_by(env, 100); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // not resolved, not ready + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 1); // not expired, stays + }); + } + + // -- Mixed messages --------------------------------------------------- + + #[test] + unconstrained fn multiple_messages_mixed_expiration() { + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + let address = context.this_address(); + let mut msgs: BoundedVec = BoundedVec::new(); + // Message 0: tx-bound, will expire after 50s. + msgs.push(make_msg(Option::some(random()), receive_time + 50)); + // Message 1: tx-bound, will expire after 50_000s. + msgs.push(make_msg(Option::some(random()), receive_time + 50_000)); + // Message 2: tx-less, will expire after 50s. + msgs.push(make_msg(Option::none(), receive_time + 50)); + receive(address, msgs); + }); + + // Advance past 50s but before 50_000s. + let _now = advance_by(env, 51); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); // all contexts are None + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + // Only message 1 should remain: messages 0 and 2 expired. + assert_eq(inbox.len(), 1); + }); + } + + #[test] + unconstrained fn backward_iteration_removes_all_expired() { + let env = TestEnvironment::new(); + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + let address = context.this_address(); + // Receive 5 tx-bound messages with expiration way beyond the cap. + let mut i: u32 = 0; + while i < 5 { + receive_one( + address, + make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), + ); + i += 1; + } + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 5); + }); + + // Advance past the lifetime cap so all 5 are expired. + let _now = advance_by(env, MAX_MSG_TTL + 1); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + assert_eq(result.len(), 0); + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + assert_eq(inbox.len(), 0); // all 5 removed without index corruption + }); + } +} diff --git a/yarn-project/txe/src/rpc_translator.ts b/yarn-project/txe/src/rpc_translator.ts index 60c462201056..2466bee7037f 100644 --- a/yarn-project/txe/src/rpc_translator.ts +++ b/yarn-project/txe/src/rpc_translator.ts @@ -799,7 +799,8 @@ export class RPCTranslator { return toForeignCallResult([]); } - public async utilityResolveMessageContexts( + // eslint-disable-next-line camelcase + public async aztec_utl_utilityResolveMessageContexts( foreignContractAddress: ForeignCallSingle, foreignMessageContextRequestsArrayBaseSlot: ForeignCallSingle, foreignMessageContextResponsesArrayBaseSlot: ForeignCallSingle, From 3c609114509e1e5361462f34f84d582d6a7f40d6 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 13:03:31 +0000 Subject: [PATCH 114/119] bump oracle version --- noir-projects/aztec-nr/aztec/src/oracle/version.nr | 2 +- yarn-project/pxe/src/oracle_version.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/oracle/version.nr b/noir-projects/aztec-nr/aztec/src/oracle/version.nr index 9c569725cc82..72947ef20ee3 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/version.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/version.nr @@ -5,7 +5,7 @@ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is /// called /// and if the oracle version is incompatible an error is thrown. -pub global ORACLE_VERSION: Field = 15; +pub global ORACLE_VERSION: Field = 16; /// Asserts that the version of the oracle is compatible with the version expected by the contract. pub fn assert_compatible_oracle_version() { diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index 105b8947478b..fb6411081591 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -4,9 +4,9 @@ /// /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called /// and if the oracle version is incompatible an error is thrown. -export const ORACLE_VERSION = 15; +export const ORACLE_VERSION = 16; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`. -export const ORACLE_INTERFACE_HASH = '34238ab576c377b4ffc1ddbf557560f07a66baf879920191897bc784fa5a97ee'; +export const ORACLE_INTERFACE_HASH = '74f288878a8010aa4b99ee42bb7c3456446e942ba77f74484084e886c35a1545'; From 54225cb96963b22543c380de50ae3ceb7d02051f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 13:21:07 +0000 Subject: [PATCH 115/119] cleanup test cases --- .../aztec/src/messages/processing/offchain.nr | 164 +++++++----------- 1 file changed, 66 insertions(+), 98 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index ae7e4be23482..43763abb3f55 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -268,13 +268,6 @@ mod test { } } - /// Calls `receive` with a single message. - unconstrained fn receive_one(address: AztecAddress, msg: OffchainMessage) { - let mut msgs: BoundedVec = BoundedVec::new(); - msgs.push(msg); - receive(address, msgs); - } - /// Advances the TXE block timestamp by `offset` seconds and returns the resulting timestamp. unconstrained fn advance_by(env: TestEnvironment, offset: u64) -> u64 { env.advance_next_block_timestamp_by(offset); @@ -288,28 +281,32 @@ mod test { let _now = advance_by(env, 1); env.utility_context(|context| { let result = sync_inbox(context.this_address()); + let inbox: CapsuleArray = + CapsuleArray::at(context.this_address(), OFFCHAIN_INBOX_SLOT); + assert_eq(result.len(), 0); + assert_eq(inbox.len(), 0); }); } - // -- Tx-bound message expiration -------------------------------------- - #[test] unconstrained fn tx_bound_msg_expires_by_sender_timestamp() { - // Sender's expiration is well before the lifetime cap, so it is the effective expiration. let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); + // Msg expiration attribute is well before the lifetime cap, so it is the effective expiration. let sender_expiration_offset = 100; // Receive message with expiration at receive_time + offset. env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg( - Option::some(random()), - receive_time + sender_expiration_offset, - ), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs + .push( + make_msg( + Option::some(random()), + receive_time + sender_expiration_offset, + ), + ); + receive(context.this_address(), msgs); }); // Advance past sender expiration but before the lifetime cap. @@ -318,23 +315,23 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // context is None, not ready let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // context is None, not ready assert_eq(inbox.len(), 0); // expired, removed }); } #[test] unconstrained fn tx_bound_msg_expires_by_lifetime_cap() { - // Sender's expiration is far in the future, so the lifetime cap kicks in. let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + // Sender's expiration is far in the future, so the standard TX lifetime cap kicks in. + msgs.push(make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL)); + receive(context.this_address(), msgs); }); // Advance past the lifetime cap. @@ -343,27 +340,30 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); assert_eq(inbox.len(), 0); // capped expiration exceeded, removed }); } #[test] unconstrained fn tx_bound_msg_not_expired_sender_timestamp_binding() { - // Sender expiration is in the future and below the cap, so it is the binding expiration. let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); + // Sender expiration is in the future and below the cap, so it is the binding expiration. let sender_expiration_offset = 100; env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg( - Option::some(random()), - receive_time + sender_expiration_offset, - ), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs + .push( + make_msg( + Option::some(random()), + receive_time + sender_expiration_offset, + ), + ); + receive(context.this_address(), msgs); }); // Advance, but not past sender expiration. @@ -372,23 +372,24 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // context is None, not ready let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // context is None, not ready assert_eq(inbox.len(), 1); // not expired, stays }); } #[test] unconstrained fn tx_bound_msg_not_expired_lifetime_cap_binding() { - // Sender expiration is way beyond the cap, but `now` is still before the cap. let env = TestEnvironment::new(); + + // Sender expiration is way beyond the cap, but `now` is still before the cap. let receive_time = advance_by(env, 10); env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL)); + receive(context.this_address(), msgs); }); // Advance, but not past the lifetime cap. @@ -397,14 +398,13 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // context is None, not ready let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // context is None, not ready assert_eq(inbox.len(), 1); // not expired, stays }); } - // -- Tx-less message expiration --------------------------------------- - #[test] unconstrained fn tx_less_msg_expires_by_sender_timestamp() { let env = TestEnvironment::new(); @@ -412,10 +412,9 @@ mod test { let sender_expiration_offset = 100; env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg(Option::none(), receive_time + sender_expiration_offset), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(make_msg(Option::none(), receive_time + sender_expiration_offset)); + receive(context.this_address(), msgs); }); let _now = advance_by(env, sender_expiration_offset + 1); @@ -423,51 +422,49 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); assert_eq(inbox.len(), 0); // expired, removed }); } #[test] unconstrained fn tx_less_msg_with_large_expiration_is_not_capped() { - // The message is tx-less, so the lifetime cap does not apply and the sender's large - // expiration is trusted. Even though `now` is past what the cap would be for a tx-bound - // message, the message stays. let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg(Option::none(), receive_time + 10 * MAX_MSG_TTL), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(make_msg(Option::none(), receive_time + 10 * MAX_MSG_TTL)); + receive(context.this_address(), msgs); }); // Advance past what the lifetime cap would be for a tx-bound message. let _now = advance_by(env, MAX_MSG_TTL + 1); + // The message is tx-less, so the lifetime cap does not apply and the sender's large + // expiration is trusted. Even though `now` is past what the cap would be for a tx-bound + // message, the message stays. env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // context is None, not ready let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // context is None, not ready assert_eq(inbox.len(), 1); // tx-less, not capped, stays }); } - // -- Context resolution scenarios ------------------------------------- - #[test] unconstrained fn unresolved_tx_stays_in_inbox() { let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); env.utility_context(|context| { - receive_one( - context.this_address(), - make_msg(Option::some(random()), receive_time + MAX_MSG_TTL), - ); + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(make_msg(Option::some(random()), receive_time + MAX_MSG_TTL)); + receive(context.this_address(), msgs); }); let _now = advance_by(env, 100); @@ -475,26 +472,27 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // not resolved, not ready let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // not resolved, not ready assert_eq(inbox.len(), 1); // not expired, stays }); } - // -- Mixed messages --------------------------------------------------- - #[test] unconstrained fn multiple_messages_mixed_expiration() { let env = TestEnvironment::new(); let receive_time = advance_by(env, 10); + let survivor_tx_hash = random(); + env.utility_context(|context| { let address = context.this_address(); let mut msgs: BoundedVec = BoundedVec::new(); // Message 0: tx-bound, will expire after 50s. msgs.push(make_msg(Option::some(random()), receive_time + 50)); - // Message 1: tx-bound, will expire after 50_000s. - msgs.push(make_msg(Option::some(random()), receive_time + 50_000)); + // Message 1: tx-bound, will expire after 50_000s (the survivor). + msgs.push(make_msg(Option::some(survivor_tx_hash), receive_time + 50_000)); // Message 2: tx-less, will expire after 50s. msgs.push(make_msg(Option::none(), receive_time + 50)); receive(address, msgs); @@ -506,42 +504,12 @@ mod test { env.utility_context(|context| { let address = context.this_address(); let result = sync_inbox(address); - assert_eq(result.len(), 0); // all contexts are None let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + assert_eq(result.len(), 0); // all contexts are None // Only message 1 should remain: messages 0 and 2 expired. assert_eq(inbox.len(), 1); - }); - } - - #[test] - unconstrained fn backward_iteration_removes_all_expired() { - let env = TestEnvironment::new(); - let receive_time = advance_by(env, 10); - - env.utility_context(|context| { - let address = context.this_address(); - // Receive 5 tx-bound messages with expiration way beyond the cap. - let mut i: u32 = 0; - while i < 5 { - receive_one( - address, - make_msg(Option::some(random()), receive_time + 10 * MAX_MSG_TTL), - ); - i += 1; - } - let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); - assert_eq(inbox.len(), 5); - }); - - // Advance past the lifetime cap so all 5 are expired. - let _now = advance_by(env, MAX_MSG_TTL + 1); - - env.utility_context(|context| { - let address = context.this_address(); - let result = sync_inbox(address); - assert_eq(result.len(), 0); - let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); - assert_eq(inbox.len(), 0); // all 5 removed without index corruption + assert_eq(inbox.get(0).tx_hash, survivor_tx_hash); }); } } From 81de2dbb9017ce025b3945d1be702016e489983f Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 13:33:21 +0000 Subject: [PATCH 116/119] fix message context tests --- .../messages/message_context_service.test.ts | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/yarn-project/pxe/src/messages/message_context_service.test.ts b/yarn-project/pxe/src/messages/message_context_service.test.ts index 7815ebbd21e7..6449f1e1b6cf 100644 --- a/yarn-project/pxe/src/messages/message_context_service.test.ts +++ b/yarn-project/pxe/src/messages/message_context_service.test.ts @@ -49,7 +49,7 @@ describe('MessageContextService', () => { expect(results).toEqual([null]); }); - it('returns null when tx effect has no nullifiers', async () => { + it('throws when tx effect has no nullifiers', async () => { const txHash = TxHash.random(); aztecNode.getTxEffect.mockResolvedValueOnce({ l2BlockNumber: BlockNumber(anchorBlockNumber - 1), @@ -58,9 +58,9 @@ describe('MessageContextService', () => { data: { txHash, noteHashes: [Fr.random()], nullifiers: [] }, } as any); - const results = await service.resolveMessageContexts([txHash.hash], anchorBlockNumber); - - expect(results).toEqual([null]); + await expect(service.resolveMessageContexts([txHash.hash], anchorBlockNumber)).rejects.toThrow( + `Tx effect for ${txHash} has no nullifiers`, + ); }); it('resolves a valid tx hash into a MessageTxContext', async () => { @@ -87,7 +87,6 @@ describe('MessageContextService', () => { const notFoundTxHash = TxHash.random(); const futureTxHash = TxHash.random(); - const noNullifiersTxHash = TxHash.random(); aztecNode.getTxEffect.mockImplementation((hash: TxHash) => { if (hash.equals(validTxHash)) { @@ -106,14 +105,6 @@ describe('MessageContextService', () => { data: { txHash: futureTxHash, noteHashes: [], nullifiers: [Fr.random()] }, } as any; } - if (hash.equals(noNullifiersTxHash)) { - return { - l2BlockNumber: BlockNumber(anchorBlockNumber - 1), - l2BlockHash: BlockHash.random(), - txIndexInBlock: 0, - data: { txHash: noNullifiersTxHash, noteHashes: [], nullifiers: [] }, - } as any; - } return undefined; // notFoundTxHash }); @@ -123,20 +114,13 @@ describe('MessageContextService', () => { validTxHash.hash, // valid → MessageTxContext notFoundTxHash.hash, // not found → null futureTxHash.hash, // beyond anchor → null - noNullifiersTxHash.hash, // no nullifiers → null ], anchorBlockNumber, ); - expect(results).toEqual([ - null, - new MessageTxContext(validTxHash, validNoteHashes, validNullifier), - null, - null, - null, - ]); + expect(results).toEqual([null, new MessageTxContext(validTxHash, validNoteHashes, validNullifier), null, null]); // Zero hash should not trigger getTxEffect - expect(aztecNode.getTxEffect).toHaveBeenCalledTimes(4); + expect(aztecNode.getTxEffect).toHaveBeenCalledTimes(3); }); }); From e51d3609c1bb3da7ba6a5c5882b4f4fe2be4b264 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Tue, 10 Mar 2026 13:56:35 +0000 Subject: [PATCH 117/119] add test case for resolved message --- .../aztec/src/messages/processing/offchain.nr | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr index 43763abb3f55..0f169272d6dc 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/processing/offchain.nr @@ -512,4 +512,41 @@ mod test { assert_eq(inbox.get(0).tx_hash, survivor_tx_hash); }); } + + // -- Resolved context (ready to process) ------------------------------ + + #[test] + unconstrained fn resolved_msg_is_ready_to_process() { + let env = TestEnvironment::new(); + // TestEnvironment::new() deploys protocol contracts, creating blocks with tx effects. + // In TXE, tx hashes equal Fr(blockNumber), so Fr(1) is the tx effect from block 1. + // We use this as a "known resolvable" tx hash. + let known_tx_hash: Field = 1; + let receive_time = advance_by(env, 10); + + env.utility_context(|context| { + let mut msgs: BoundedVec = BoundedVec::new(); + msgs.push(make_msg(Option::some(known_tx_hash), receive_time + MAX_MSG_TTL)); + receive(context.this_address(), msgs); + }); + + let _now = advance_by(env, 100); + + env.utility_context(|context| { + let address = context.this_address(); + let result = sync_inbox(address); + let inbox: CapsuleArray = CapsuleArray::at(address, OFFCHAIN_INBOX_SLOT); + + // The message should be ready to process since its tx context was resolved. + assert_eq(result.len(), 1); + + let ctx = result.get(0).message_context; + assert_eq(ctx.tx_hash, known_tx_hash); + assert_eq(ctx.recipient, AztecAddress::from_field(42)); + assert(ctx.first_nullifier_in_tx != 0, "resolved context must have a first nullifier"); + + // Message stays in inbox (not expired) for potential reorg reprocessing. + assert_eq(inbox.len(), 1); + }); + } } From f368122ffcd0b8016a42af925a7daa281471d4f8 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 11 Mar 2026 11:42:36 +0000 Subject: [PATCH 118/119] code review comments --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 2 +- .../aztec-nr/aztec/src/messages/discovery/mod.nr | 2 ++ .../aztec/src/messages/processing/offchain.nr | 11 +++++------ .../end-to-end/src/e2e_offchain_payment.test.ts | 3 +++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index aa508445337c..c4c49d00228e 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -423,7 +423,7 @@ comptime fn generate_offchain_receive() -> Quoted { parameters: offchain_receive_parameters, } - /// Delivers offchain messages to this contract's offchain inbox for subsequent processing. + /// Receives offchain messages into this contract's offchain inbox for subsequent processing. /// /// For more details, see `aztec::messages::processing::offchain::receive`. /// diff --git a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr index 185de9620a09..fac8b8734ffb 100644 --- a/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/messages/discovery/mod.nr @@ -146,6 +146,8 @@ pub unconstrained fn do_sync_state = diff --git a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts index bb1fe30cfdce..ffe2fd7ce271 100644 --- a/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts +++ b/yarn-project/end-to-end/src/e2e_offchain_payment.test.ts @@ -107,6 +107,9 @@ describe('e2e_offchain_payment', () => { // Force an empty block so the PXE re-syncs and discovers the offchain-delivered notes. await forceEmptyBlock(); + // TODO(F-413): we need to implement scopes on capsules so we can check Alice's balance too here. This is not + // possible right now because the offchain inbox is shared for all accounts using this contract in the same PXE, + // which is bad. const { result: bobBalance } = await contract.methods.get_balance(bob).simulate({ from: bob }); expect(bobBalance).toBe(paymentAmount); }); From 52de33fb9f054c4634b7f0367063e09d63fbf284 Mon Sep 17 00:00:00 2001 From: mverzilli Date: Wed, 11 Mar 2026 11:57:00 +0000 Subject: [PATCH 119/119] update oracle version hash --- noir-projects/aztec-nr/aztec/src/macros/aztec.nr | 2 +- yarn-project/pxe/src/oracle_version.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr index c4c49d00228e..161fa46ea8b3 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/aztec.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/aztec.nr @@ -127,7 +127,7 @@ pub comptime fn aztec(m: Module, args: [AztecConfig]) -> Quoted { if m.functions().any(|f| f.name() == quote { offchain_receive }) { panic( - "User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/6", + "User-defined 'offchain_receive' is not allowed. The function is auto-injected by the #[aztec] macro. See https://docs.aztec.network/errors/7", ); } let offchain_receive_fn_and_abi_export = generate_offchain_receive(); diff --git a/yarn-project/pxe/src/oracle_version.ts b/yarn-project/pxe/src/oracle_version.ts index fb6411081591..f3069e1bf869 100644 --- a/yarn-project/pxe/src/oracle_version.ts +++ b/yarn-project/pxe/src/oracle_version.ts @@ -9,4 +9,4 @@ export const ORACLE_VERSION = 16; /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes, /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`. -export const ORACLE_INTERFACE_HASH = '74f288878a8010aa4b99ee42bb7c3456446e942ba77f74484084e886c35a1545'; +export const ORACLE_INTERFACE_HASH = '73ccb2a24bc9fe7514108be9ff98d7ca8734bc316fb7c1ec4329d1d32f412a55';