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 1125a528ec2..be48d9bf477 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,9 @@ -import { type PXE, type Tx, type TxExecutionRequest } from '@aztec/circuit-types'; +import { type Tx, type TxExecutionRequest } from '@aztec/circuit-types'; +import { GasSettings } from '@aztec/circuits.js'; -import { type FeeOptions } from '../entrypoint/entrypoint.js'; +import { type Wallet } from '../account/wallet.js'; +import { type ExecutionRequestInit, type FeeOptions } from '../entrypoint/entrypoint.js'; +import { getGasLimits } from './get_gas_limits.js'; import { SentTx } from './sent_tx.js'; /** @@ -8,15 +11,12 @@ import { SentTx } from './sent_tx.js'; * Allows the user to specify the sender address and nonce for a transaction. */ export type SendMethodOptions = { - /** - * Wether to skip the simulation of the public part of the transaction. - */ + /** Wether to skip the simulation of the public part of the transaction. */ skipPublicSimulation?: boolean; - - /** - * The fee options for the transaction. - */ + /** The fee options for the transaction. */ fee?: FeeOptions; + /** Whether to run an initial simulation of the tx with high gas limit to figure out actual gas settings (will default to true later down the road). */ + estimateGas?: boolean; }; /** @@ -27,7 +27,7 @@ export abstract class BaseContractInteraction { protected tx?: Tx; protected txRequest?: TxExecutionRequest; - constructor(protected pxe: PXE) {} + constructor(protected wallet: Wallet) {} /** * Create a transaction execution request ready to be simulated. @@ -43,7 +43,7 @@ export abstract class BaseContractInteraction { */ public async prove(options: SendMethodOptions = {}): Promise { const txRequest = this.txRequest ?? (await this.create(options)); - this.tx = await this.pxe.proveTx(txRequest, !options.skipPublicSimulation); + this.tx = await this.wallet.proveTx(txRequest, !options.skipPublicSimulation); return this.tx; } @@ -59,9 +59,36 @@ export abstract class BaseContractInteraction { public send(options: SendMethodOptions = {}) { const promise = (async () => { const tx = this.tx ?? (await this.prove(options)); - return this.pxe.sendTx(tx); + return this.wallet.sendTx(tx); })(); - return new SentTx(this.pxe, promise); + return new SentTx(this.wallet, promise); + } + + /** + * Estimates gas for a given tx request and returns defaults gas settings for it. + * @param txRequest - Transaction execution request to process. + * @returns Gas settings. + */ + protected async estimateGas(txRequest: TxExecutionRequest) { + const simulationResult = await this.wallet.simulateTx(txRequest, true); + const { totalGas: gasLimits, teardownGas: teardownGasLimits } = getGasLimits(simulationResult); + return GasSettings.default({ gasLimits, teardownGasLimits }); + } + + /** + * Helper method to return fee options based on the user opts, estimating tx gas if needed. + * @param request - Request to execute for this interaction. + * @returns Fee options for the actual transaction. + */ + protected async getFeeOptions(request: ExecutionRequestInit) { + const fee = request.fee; + if (fee) { + const txRequest = await this.wallet.createTxExecutionRequest(request); + const { gasLimits, teardownGasLimits } = await this.estimateGas(txRequest); + const gasSettings = GasSettings.default({ ...fee.gasSettings, gasLimits, teardownGasLimits }); + return { ...fee, gasSettings }; + } + return fee; } } diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index 79cead71f4d..ac7a65f40a1 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -5,7 +5,7 @@ import { BaseContractInteraction, type SendMethodOptions } from './base_contract /** A batch of function calls to be sent as a single transaction through a wallet. */ export class BatchCall extends BaseContractInteraction { - constructor(protected wallet: Wallet, protected calls: FunctionCall[]) { + constructor(wallet: Wallet, protected calls: FunctionCall[]) { super(wallet); } @@ -17,10 +17,9 @@ export class BatchCall extends BaseContractInteraction { */ public async create(opts?: SendMethodOptions): Promise { if (!this.txRequest) { - this.txRequest = await this.wallet.createTxExecutionRequest({ - calls: this.calls, - fee: opts?.fee, - }); + const calls = this.calls; + const fee = opts?.estimateGas ? await this.getFeeOptions({ calls, fee: opts?.fee }) : opts?.fee; + this.txRequest = await this.wallet.createTxExecutionRequest({ calls, fee }); } return this.txRequest; } 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 6d194bfa9f1..39344586891 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -25,7 +25,7 @@ export type SimulateMethodOptions = { */ export class ContractFunctionInteraction extends BaseContractInteraction { constructor( - protected wallet: Wallet, + wallet: Wallet, protected contractAddress: AztecAddress, protected functionDao: FunctionAbi, protected args: any[], @@ -47,10 +47,9 @@ export class ContractFunctionInteraction extends BaseContractInteraction { throw new Error("Can't call `create` on an unconstrained function."); } if (!this.txRequest) { - this.txRequest = await this.wallet.createTxExecutionRequest({ - calls: [this.request()], - fee: opts?.fee, - }); + const calls = [this.request()]; + const fee = opts?.estimateGas ? await this.getFeeOptions({ calls, fee: opts?.fee }) : opts?.fee; + this.txRequest = await this.wallet.createTxExecutionRequest({ calls, fee }); } return this.txRequest; } @@ -98,12 +97,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction { argsOfCalls: [packedArgs], authWitnesses: [], }); - const simulatedTx = await this.pxe.simulateTx(txRequest, true, options.from ?? this.wallet.getAddress()); + const simulatedTx = await this.wallet.simulateTx(txRequest, true, options.from ?? this.wallet.getAddress()); const flattened = simulatedTx.privateReturnValues; return flattened ? decodeReturnValues(this.functionDao, flattened) : []; } else { const txRequest = await this.create(); - const simulatedTx = await this.pxe.simulateTx(txRequest, true); + const simulatedTx = await this.wallet.simulateTx(txRequest, true); this.txRequest = undefined; const flattened = simulatedTx.publicOutput?.publicReturnValues; return flattened ? decodeReturnValues(this.functionDao, flattened) : []; diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 40fd67bbcf4..5521901f943 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -57,7 +57,7 @@ export class DeployMethod extends Bas constructor( private publicKeysHash: Fr, - protected wallet: Wallet, + wallet: Wallet, private artifact: ContractArtifact, private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise, private args: any[] = [], @@ -80,11 +80,14 @@ export class DeployMethod extends Bas if (!this.txRequest) { this.txRequest = await this.wallet.createTxExecutionRequest(await this.request(options)); // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? - await this.pxe.registerContract({ artifact: this.artifact, instance: this.instance! }); + await this.wallet.registerContract({ artifact: this.artifact, instance: this.instance! }); } return this.txRequest; } + // REFACTOR: Having a `request` method with different semantics than the ones in the other + // derived ContractInteractions is confusing. We should unify the flow of all ContractInteractions. + /** * Returns an array of function calls that represent this operation. Useful as a building * block for constructing batch requests. @@ -102,13 +105,20 @@ export class DeployMethod extends Bas throw new Error(`No function calls needed to deploy contract ${this.artifact.name}`); } - this.functionCalls = { + const request = { calls: [...deployment.calls, ...bootstrap.calls], authWitnesses: [...(deployment.authWitnesses ?? []), ...(bootstrap.authWitnesses ?? [])], packedArguments: [...(deployment.packedArguments ?? []), ...(bootstrap.packedArguments ?? [])], fee: options.fee, }; + + if (options.estimateGas) { + request.fee = await this.getFeeOptions(request); + } + + this.functionCalls = request; } + return this.functionCalls; } @@ -134,7 +144,7 @@ export class DeployMethod extends Bas // Register the contract class if it hasn't been published already. if (!options.skipClassRegistration) { - if (await this.pxe.isContractClassPubliclyRegistered(contractClass.id)) { + if (await this.wallet.isContractClassPubliclyRegistered(contractClass.id)) { this.log.debug( `Skipping registration of already registered contract class ${contractClass.id.toString()} for ${instance.address.toString()}`, ); @@ -192,7 +202,7 @@ export class DeployMethod extends Bas this.log.debug( `Sent deployment tx of ${this.artifact.name} contract with deployment address ${instance.address.toString()}`, ); - return new DeploySentTx(this.pxe, txHashPromise, this.postDeployCtor, instance); + return new DeploySentTx(this.wallet, txHashPromise, this.postDeployCtor, instance); } /** diff --git a/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts b/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts new file mode 100644 index 00000000000..b10d8aca63b --- /dev/null +++ b/yarn-project/aztec.js/src/contract/get_gas_limits.test.ts @@ -0,0 +1,41 @@ +import { PublicKernelType, type SimulatedTx, mockSimulatedTx } from '@aztec/circuit-types'; +import { Gas } from '@aztec/circuits.js'; + +import { getGasLimits } from './get_gas_limits.js'; + +describe('getGasLimits', () => { + let simulatedTx: SimulatedTx; + + beforeEach(() => { + simulatedTx = mockSimulatedTx(); + simulatedTx.tx.data.publicInputs.end.gasUsed = Gas.from({ daGas: 100, l2Gas: 200 }); + simulatedTx.publicOutput!.gasUsed = { + [PublicKernelType.SETUP]: Gas.from({ daGas: 10, l2Gas: 20 }), + [PublicKernelType.APP_LOGIC]: Gas.from({ daGas: 20, l2Gas: 40 }), + [PublicKernelType.TEARDOWN]: Gas.from({ daGas: 10, l2Gas: 20 }), + }; + }); + + it('returns gas limits from private gas usage only', () => { + simulatedTx.publicOutput = undefined; + // Should be 110 and 220 but oh floating point + expect(getGasLimits(simulatedTx)).toEqual({ + totalGas: Gas.from({ daGas: 111, l2Gas: 221 }), + teardownGas: Gas.empty(), + }); + }); + + it('returns gas limits for private and public', () => { + expect(getGasLimits(simulatedTx)).toEqual({ + totalGas: Gas.from({ daGas: 154, l2Gas: 308 }), + teardownGas: Gas.from({ daGas: 11, l2Gas: 22 }), + }); + }); + + it('pads gas limits', () => { + expect(getGasLimits(simulatedTx, 1)).toEqual({ + totalGas: Gas.from({ daGas: 280, l2Gas: 560 }), + teardownGas: Gas.from({ daGas: 20, l2Gas: 40 }), + }); + }); +}); diff --git a/yarn-project/aztec.js/src/contract/get_gas_limits.ts b/yarn-project/aztec.js/src/contract/get_gas_limits.ts new file mode 100644 index 00000000000..d2fc65af17e --- /dev/null +++ b/yarn-project/aztec.js/src/contract/get_gas_limits.ts @@ -0,0 +1,24 @@ +import { PublicKernelType, type SimulatedTx } from '@aztec/circuit-types'; +import { Gas } from '@aztec/circuits.js'; + +/** + * Returns suggested total and teardown gas limits for a simulated tx. + * Note that public gas usage is only accounted for if the publicOutput is present. + * @param pad - Percentage to pad the suggested gas limits by, defaults to 10%. + */ +export function getGasLimits(simulatedTx: SimulatedTx, pad = 0.1) { + const privateGasUsed = simulatedTx.tx.data.publicInputs.end.gasUsed; + if (simulatedTx.publicOutput) { + const publicGasUsed = Object.values(simulatedTx.publicOutput.gasUsed) + .filter(Boolean) + .reduce((total, current) => total.add(current), Gas.empty()); + const teardownGas = simulatedTx.publicOutput.gasUsed[PublicKernelType.TEARDOWN] ?? Gas.empty(); + + return { + totalGas: privateGasUsed.add(publicGasUsed).mul(1 + pad), + teardownGas: teardownGas.mul(1 + pad), + }; + } + + return { totalGas: privateGasUsed.mul(1 + pad), teardownGas: Gas.empty() }; +} diff --git a/yarn-project/circuit-types/src/tx/simulated_tx.test.ts b/yarn-project/circuit-types/src/tx/simulated_tx.test.ts index be7f94291a3..d41ab66a881 100644 --- a/yarn-project/circuit-types/src/tx/simulated_tx.test.ts +++ b/yarn-project/circuit-types/src/tx/simulated_tx.test.ts @@ -1,7 +1,4 @@ -import { Gas } from '@aztec/circuits.js'; - import { mockSimulatedTx } from '../mocks.js'; -import { PublicKernelType } from './processed_tx.js'; import { SimulatedTx } from './simulated_tx.js'; describe('simulated_tx', () => { @@ -22,38 +19,4 @@ describe('simulated_tx', () => { expect(SimulatedTx.fromJSON(simulatedTx.toJSON())).toEqual(simulatedTx); }); }); - - describe('getGasLimits', () => { - beforeEach(() => { - simulatedTx.tx.data.publicInputs.end.gasUsed = Gas.from({ daGas: 100, l2Gas: 200 }); - simulatedTx.publicOutput!.gasUsed = { - [PublicKernelType.SETUP]: Gas.from({ daGas: 10, l2Gas: 20 }), - [PublicKernelType.APP_LOGIC]: Gas.from({ daGas: 20, l2Gas: 40 }), - [PublicKernelType.TEARDOWN]: Gas.from({ daGas: 10, l2Gas: 20 }), - }; - }); - - it('returns gas limits from private gas usage only', () => { - simulatedTx.publicOutput = undefined; - // Should be 110 and 220 but oh floating point - expect(simulatedTx.getGasLimits()).toEqual({ - totalGas: Gas.from({ daGas: 111, l2Gas: 221 }), - teardownGas: Gas.empty(), - }); - }); - - it('returns gas limits for private and public', () => { - expect(simulatedTx.getGasLimits()).toEqual({ - totalGas: Gas.from({ daGas: 154, l2Gas: 308 }), - teardownGas: Gas.from({ daGas: 11, l2Gas: 22 }), - }); - }); - - it('pads gas limits', () => { - expect(simulatedTx.getGasLimits(1)).toEqual({ - totalGas: Gas.from({ daGas: 280, l2Gas: 560 }), - teardownGas: Gas.from({ daGas: 20, l2Gas: 40 }), - }); - }); - }); }); diff --git a/yarn-project/circuit-types/src/tx/simulated_tx.ts b/yarn-project/circuit-types/src/tx/simulated_tx.ts index 45387f1664a..e3808a1e463 100644 --- a/yarn-project/circuit-types/src/tx/simulated_tx.ts +++ b/yarn-project/circuit-types/src/tx/simulated_tx.ts @@ -1,6 +1,5 @@ -import { Fr, Gas } from '@aztec/circuits.js'; +import { Fr } from '@aztec/circuits.js'; -import { PublicKernelType } from './processed_tx.js'; import { type ProcessReturnValues, PublicSimulationOutput } from './public_simulation_output.js'; import { Tx } from './tx.js'; @@ -17,29 +16,6 @@ export class SimulatedTx { public publicOutput?: PublicSimulationOutput, ) {} - /** - * Returns suggested total and teardown gas limits for the simulated tx. - * Note that public gas usage is only accounted for if the publicOutput is present. - * @param pad - Percentage to pad the suggested gas limits by, defaults to 10%. - */ - public getGasLimits(pad = 0.1) { - const privateGasUsed = this.tx.data.publicInputs.end.gasUsed; - if (this.publicOutput) { - const publicGasUsed = Object.values(this.publicOutput.gasUsed).reduce( - (total, current) => total.add(current), - Gas.empty(), - ); - const teardownGas = this.publicOutput.gasUsed[PublicKernelType.TEARDOWN] ?? Gas.empty(); - - return { - totalGas: privateGasUsed.add(publicGasUsed).mul(1 + pad), - teardownGas: teardownGas.mul(1 + pad), - }; - } - - return { totalGas: privateGasUsed.mul(1 + pad), teardownGas: Gas.empty() }; - } - /** * Convert a SimulatedTx class object to a plain JSON object. * @returns A plain object with SimulatedTx properties. diff --git a/yarn-project/circuits.js/src/structs/gas_settings.ts b/yarn-project/circuits.js/src/structs/gas_settings.ts index 3a0878bff5d..e777c9467d3 100644 --- a/yarn-project/circuits.js/src/structs/gas_settings.ts +++ b/yarn-project/circuits.js/src/structs/gas_settings.ts @@ -62,13 +62,14 @@ export class GasSettings { } /** Default gas settings to use when user has not provided them. */ - static default() { - return new GasSettings( - new Gas(DEFAULT_GAS_LIMIT, DEFAULT_GAS_LIMIT), - new Gas(DEFAULT_TEARDOWN_GAS_LIMIT, DEFAULT_TEARDOWN_GAS_LIMIT), - new GasFees(new Fr(DEFAULT_MAX_FEE_PER_GAS), new Fr(DEFAULT_MAX_FEE_PER_GAS)), - new Fr(DEFAULT_INCLUSION_FEE), - ); + static default(overrides?: Partial>) { + return GasSettings.from({ + gasLimits: { l2Gas: DEFAULT_GAS_LIMIT, daGas: DEFAULT_GAS_LIMIT }, + teardownGasLimits: { l2Gas: DEFAULT_TEARDOWN_GAS_LIMIT, daGas: DEFAULT_TEARDOWN_GAS_LIMIT }, + maxFeesPerGas: { feePerL2Gas: new Fr(DEFAULT_MAX_FEE_PER_GAS), feePerDaGas: new Fr(DEFAULT_MAX_FEE_PER_GAS) }, + inclusionFee: new Fr(DEFAULT_INCLUSION_FEE), + ...overrides, + }); } /** Gas settings to use for simulating a contract call. */ diff --git a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts index 5d7f61b36c5..f28616697e7 100644 --- a/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/dapp_subscription.test.ts @@ -44,7 +44,7 @@ describe('e2e_fees dapp_subscription', () => { beforeAll(async () => { await t.applyBaseSnapshots(); - await t.applyFundAlice(); + await t.applyFundAliceWithBananas(); await t.applySetupSubscription(); ({ 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 39cd2308db8..3f115a52c46 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 @@ -203,7 +203,7 @@ export class FeesTest { ); } - public async applyFundAlice() { + public async applyFundAliceWithBananas() { await this.snapshotManager.snapshot( 'fund_alice', async () => { @@ -214,6 +214,16 @@ export class FeesTest { ); } + public async applyFundAliceWithGasToken() { + await this.snapshotManager.snapshot( + 'fund_alice_with_gas_token', + async () => { + await this.gasTokenContract.methods.mint_public(this.aliceAddress, this.INITIAL_GAS_BALANCE).send().wait(); + }, + () => Promise.resolve(), + ); + } + public async applySetupSubscription() { await this.snapshotManager.snapshot( 'setup_subscription', 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 new file mode 100644 index 00000000000..6b90cee1fba --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts @@ -0,0 +1,66 @@ +import { + type AccountWallet, + type AztecAddress, + type FeePaymentMethod, + NativeFeePaymentMethod, + PublicFeePaymentMethod, +} from '@aztec/aztec.js'; +import { GasFees, type GasSettings } from '@aztec/circuits.js'; +import { type TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js'; + +import { FeesTest } from './fees_test.js'; + +describe('e2e_fees gas_estimation', () => { + let aliceWallet: AccountWallet; + let aliceAddress: AztecAddress; + let bobAddress: AztecAddress; + let bananaCoin: BananaCoin; + let bananaFPC: FPCContract; + let gasSettings: GasSettings; + let teardownFixedFee: bigint; + + const t = new FeesTest('gas_estimation'); + + beforeAll(async () => { + await t.applyBaseSnapshots(); + await t.applyFundAliceWithBananas(); + await t.applyFundAliceWithGasToken(); + ({ aliceWallet, aliceAddress, bobAddress, bananaCoin, bananaFPC, gasSettings } = await t.setup()); + + teardownFixedFee = gasSettings.teardownGasLimits.computeFee(GasFees.default()).toBigInt(); + }); + + afterAll(async () => { + await t.teardown(); + }); + + // Sends two tx with transfers of public tokens: one with estimateGas on, one with estimateGas off + const sendTransfers = (paymentMethod: FeePaymentMethod) => + Promise.all( + [true, false].map(estimateGas => + bananaCoin.methods + .transfer_public(aliceAddress, bobAddress, 1n, 0n) + .send({ estimateGas, fee: { gasSettings, paymentMethod } }) + .wait(), + ), + ); + + it('estimates gas with native fee payment method', async () => { + const paymentMethod = await NativeFeePaymentMethod.create(aliceWallet); + const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod); + + // Estimation should yield that teardown has no cost, so should send the tx with zero for teardown + expect(withEstimate.transactionFee! + teardownFixedFee).toEqual(withoutEstimate.transactionFee!); + }); + + it('estimates gas with public payment method', async () => { + const paymentMethod = new PublicFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet); + const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod); + + // Estimation should yield that teardown has reduced cost, but is not zero + // TODO(palla/gas): We set toBeGreaterThanOrEqual because gas in public functions is zero for now (we only meter on AVM). + // We should be able to change this to a strict equality once we meter gas in public functions or we replace brillig with the AVM. + expect(withEstimate.transactionFee!).toBeLessThan(withoutEstimate.transactionFee!); + expect(withEstimate.transactionFee! + teardownFixedFee).toBeGreaterThanOrEqual(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 572b4947ee7..e85adf4e678 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 @@ -27,7 +27,7 @@ describe('e2e_fees private_payment', () => { beforeAll(async () => { await t.applyBaseSnapshots(); - await t.applyFundAlice(); + await t.applyFundAliceWithBananas(); ({ aliceWallet, aliceAddress, bobAddress, sequencerAddress, gasTokenContract, bananaCoin, bananaFPC, gasSettings } = await t.setup()); }); diff --git a/yarn-project/p2p/package.json b/yarn-project/p2p/package.json index fd4ef211f05..f7ad5cd7120 100644 --- a/yarn-project/p2p/package.json +++ b/yarn-project/p2p/package.json @@ -44,7 +44,8 @@ "summaryThreshold": 9999 } ] - ] + ], + "testTimeout": 15000 }, "dependencies": { "@aztec/circuit-types": "workspace:^", @@ -96,4 +97,4 @@ "engines": { "node": ">=18" } -} +} \ No newline at end of file