diff --git a/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts b/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts index eac072ed08e1..045ea90480de 100644 --- a/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts +++ b/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts @@ -1,5 +1,7 @@ -import { type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js'; +import { type ContractArtifact, type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js'; import EcdsaKAccountContractJson from '../../../artifacts/EcdsaKAccount.json' assert { type: 'json' }; -export const EcdsaKAccountContractArtifact = loadContractArtifact(EcdsaKAccountContractJson as NoirCompiledContract); +export const EcdsaKAccountContractArtifact: ContractArtifact = loadContractArtifact( + EcdsaKAccountContractJson as NoirCompiledContract, +); diff --git a/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts b/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts index d0e9b281e0e7..57d10fa67a43 100644 --- a/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts +++ b/yarn-project/aztec.js/src/account_manager/deploy_account_method.ts @@ -46,13 +46,16 @@ export class DeployAccountMethod extends DeployMethod { : feePaymentNameOrArtifact; } - protected override async getInitializeFunctionCalls(options: DeployOptions): Promise { + protected override async getInitializeFunctionCalls( + options: DeployOptions, + ): Promise> { const exec = await super.getInitializeFunctionCalls(options); if (options.fee && this.#feePaymentArtifact) { const { address } = this.getInstance(); const emptyAppPayload = EntrypointPayload.fromAppExecution([]); - const feePayload = await EntrypointPayload.fromFeeOptions(address, options?.fee); + const fee = await this.getDefaultFeeOptions(options.fee); + const feePayload = await EntrypointPayload.fromFeeOptions(address, fee); exec.calls.push({ name: this.#feePaymentArtifact.name, diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index e4e3316a6dba..a9f5e4cc3280 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -17,7 +17,7 @@ import { DeployAccountSentTx } from './deploy_account_sent_tx.js'; */ export type DeployAccountOptions = Pick< DeployOptions, - 'fee' | 'skipClassRegistration' | 'skipPublicDeployment' | 'estimateGas' | 'skipInitialization' + 'fee' | 'skipClassRegistration' | 'skipPublicDeployment' | 'skipInitialization' >; /** @@ -166,7 +166,6 @@ export class AccountManager { skipInitialization: opts?.skipInitialization ?? false, universalDeploy: true, fee: opts?.fee, - estimateGas: opts?.estimateGas, }), ) .then(tx => tx.getTxHash()); 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 b62583c13caa..6269d5cca1a5 100644 --- a/yarn-project/aztec.js/src/contract/base_contract_interaction.ts +++ b/yarn-project/aztec.js/src/contract/base_contract_interaction.ts @@ -3,7 +3,9 @@ import { type Fr, GasSettings } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { type Wallet } from '../account/wallet.js'; -import { type ExecutionRequestInit, type FeeOptions } from '../entrypoint/entrypoint.js'; +import { type ExecutionRequestInit } from '../entrypoint/entrypoint.js'; +import { type FeeOptions, type UserFeeOptions } from '../entrypoint/payload.js'; +import { NoFeePaymentMethod } from '../fee/no_fee_payment_method.js'; import { getGasLimits } from './get_gas_limits.js'; import { ProvenTx } from './proven_tx.js'; import { SentTx } from './sent_tx.js'; @@ -16,9 +18,7 @@ export type SendMethodOptions = { /** Wether to skip the simulation of the public part of the transaction. */ skipPublicSimulation?: boolean; /** 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; + fee?: UserFeeOptions; /** Custom nonce to inject into the app payload of the transaction. Useful when trying to cancel an ongoing transaction by creating a new one with a higher fee */ nonce?: Fr; /** Whether the transaction can be cancelled. If true, an extra nullifier will be emitted: H(nonce, GENERATOR_INDEX__TX_NULLIFIER) */ @@ -89,29 +89,47 @@ export abstract class BaseContractInteraction { public async estimateGas( opts?: Omit, ): Promise> { - const txRequest = await this.create({ ...opts, estimateGas: false }); + const txRequest = await this.create({ ...opts, fee: { ...opts?.fee, estimateGas: false } }); const simulationResult = await this.wallet.simulateTx(txRequest, true); const { totalGas: gasLimits, teardownGas: teardownGasLimits } = getGasLimits(simulationResult); return { gasLimits, teardownGasLimits }; } /** - * Helper method to return fee options based on the user opts, estimating tx gas if needed. + * Returns default fee options based on the user opts without running a simulation for gas estimation. + * @param fee - User-provided fee options. + */ + protected async getDefaultFeeOptions(fee: UserFeeOptions | undefined): Promise { + const maxFeesPerGas = fee?.gasSettings?.maxFeesPerGas ?? (await this.wallet.getCurrentBaseFees()); + const paymentMethod = fee?.paymentMethod ?? new NoFeePaymentMethod(); + const gasSettings: GasSettings = GasSettings.default({ ...fee?.gasSettings, maxFeesPerGas }); + return { gasSettings, paymentMethod }; + } + + /** + * 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 getFeeOptionsFromEstimatedGas(request: ExecutionRequestInit) { - const fee = request.fee; - if (fee) { - const txRequest = await this.wallet.createTxExecutionRequest(request); + protected async getFeeOptions( + request: Omit & { /** User-provided fee options */ fee?: UserFeeOptions }, + ): Promise { + const defaultFeeOptions = await this.getDefaultFeeOptions(request.fee); + const paymentMethod = defaultFeeOptions.paymentMethod; + const maxFeesPerGas = defaultFeeOptions.gasSettings.maxFeesPerGas; + + let gasSettings = defaultFeeOptions.gasSettings; + if (request.fee?.estimateGas) { + const feeForEstimation: FeeOptions = { paymentMethod, gasSettings }; + const txRequest = await this.wallet.createTxExecutionRequest({ ...request, fee: feeForEstimation }); const simulationResult = await this.wallet.simulateTx(txRequest, true); const { totalGas: gasLimits, teardownGas: teardownGasLimits } = getGasLimits(simulationResult); - this.log.debug( + gasSettings = GasSettings.from({ maxFeesPerGas, gasLimits, teardownGasLimits }); + this.log.verbose( `Estimated gas limits for tx: DA=${gasLimits.daGas} L2=${gasLimits.l2Gas} teardownDA=${teardownGasLimits.daGas} teardownL2=${teardownGasLimits.l2Gas}`, ); - const gasSettings = GasSettings.default({ ...fee.gasSettings, gasLimits, teardownGasLimits }); - return { ...fee, gasSettings }; } - return fee; + + return { gasSettings, paymentMethod }; } } diff --git a/yarn-project/aztec.js/src/contract/batch_call.ts b/yarn-project/aztec.js/src/contract/batch_call.ts index 31f6ce37df15..1a19a393d906 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -19,8 +19,8 @@ export class BatchCall extends BaseContractInteraction { */ public async create(opts?: SendMethodOptions): Promise { const calls = this.calls; - const fee = opts?.estimateGas ? await this.getFeeOptionsFromEstimatedGas({ calls, fee: opts?.fee }) : opts?.fee; - return await this.wallet.createTxExecutionRequest({ calls, fee }); + const fee = await this.getFeeOptions({ calls, ...opts }); + return await this.wallet.createTxExecutionRequest({ calls, ...opts, fee }); } /** @@ -33,29 +33,21 @@ export class BatchCall extends BaseContractInteraction { * @returns The result of the transaction as returned by the contract function. */ public async simulate(options: SimulateMethodOptions = {}): Promise { - const { calls, unconstrained } = this.calls.reduce<{ - /** - * Keep track of the number of private calls to retrieve the return values - */ + const { indexedCalls, unconstrained } = this.calls.reduce<{ + /** Keep track of the number of private calls to retrieve the return values */ privateIndex: 0; - /** - * Keep track of the number of private calls to retrieve the return values - */ + /** Keep track of the number of public calls to retrieve the return values */ publicIndex: 0; - /** - * The public and private function calls in the batch - */ - calls: [FunctionCall, number, number][]; - /** - * The unconstrained function calls in the batch. - */ + /** The public and private function calls in the batch */ + indexedCalls: [FunctionCall, number, number][]; + /** The unconstrained function calls in the batch. */ unconstrained: [FunctionCall, number][]; }>( (acc, current, index) => { if (current.type === FunctionType.UNCONSTRAINED) { acc.unconstrained.push([current, index]); } else { - acc.calls.push([ + acc.indexedCalls.push([ current, index, current.type === FunctionType.PRIVATE ? acc.privateIndex++ : acc.publicIndex++, @@ -63,18 +55,17 @@ export class BatchCall extends BaseContractInteraction { } return acc; }, - { calls: [], unconstrained: [], publicIndex: 0, privateIndex: 0 }, + { indexedCalls: [], unconstrained: [], publicIndex: 0, privateIndex: 0 }, ); - const txRequest = await this.wallet.createTxExecutionRequest({ calls: calls.map(indexedCall => indexedCall[0]) }); + const calls = indexedCalls.map(([call]) => call); + const fee = await this.getFeeOptions({ calls, ...options }); + const txRequest = await this.wallet.createTxExecutionRequest({ calls, ...options, fee }); - const unconstrainedCalls = unconstrained.map(async indexedCall => { - const call = indexedCall[0]; - return [ - await this.wallet.simulateUnconstrained(call.name, call.args, call.to, options?.from), - indexedCall[1], - ] as const; - }); + const unconstrainedCalls = unconstrained.map( + async ([call, index]) => + [await this.wallet.simulateUnconstrained(call.name, call.args, call.to, options?.from), index] as const, + ); const [unconstrainedResults, simulatedTx] = await Promise.all([ Promise.all(unconstrainedCalls), @@ -86,7 +77,7 @@ export class BatchCall extends BaseContractInteraction { unconstrainedResults.forEach(([result, index]) => { results[index] = result; }); - calls.forEach(([call, callIndex, resultIndex]) => { + indexedCalls.forEach(([call, callIndex, resultIndex]) => { // As account entrypoints are private, for private functions we retrieve the return values from the first nested call // since we're interested in the first set of values AFTER the account entrypoint // For public functions we retrieve the first values directly from the public output. 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 d9bf7ff2192c..71117bdf280f 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -58,19 +58,14 @@ export class ContractFunctionInteraction extends BaseContractInteraction { * @param opts - An optional object containing additional configuration for the transaction. * @returns A Promise that resolves to a transaction instance. */ - public async create(opts?: SendMethodOptions): Promise { + public async create(opts: SendMethodOptions = {}): Promise { if (this.functionDao.functionType === FunctionType.UNCONSTRAINED) { throw new Error("Can't call `create` on an unconstrained function."); } const calls = [this.request()]; - const fee = opts?.estimateGas ? await this.getFeeOptionsFromEstimatedGas({ calls, fee: opts?.fee }) : opts?.fee; - const txRequest = await this.wallet.createTxExecutionRequest({ - calls, - fee, - nonce: opts?.nonce, - cancellable: opts?.cancellable, - }); - return txRequest; + const fee = await this.getFeeOptions({ calls, ...opts }); + const { nonce, cancellable } = opts; + return await this.wallet.createTxExecutionRequest({ calls, fee, nonce, cancellable }); } /** diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 96651e470e48..9b45db9b49cb 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -104,18 +104,15 @@ export class DeployMethod extends Bas throw new Error(`No function calls needed to deploy contract ${this.artifact.name}`); } - const request = { - calls: [...deployment.calls, ...bootstrap.calls], - authWitnesses: [...(deployment.authWitnesses ?? []), ...(bootstrap.authWitnesses ?? [])], - packedArguments: [...(deployment.packedArguments ?? []), ...(bootstrap.packedArguments ?? [])], - fee: options.fee, - }; + const calls = [...deployment.calls, ...bootstrap.calls]; + const authWitnesses = [...(deployment.authWitnesses ?? []), ...(bootstrap.authWitnesses ?? [])]; + const packedArguments = [...(deployment.packedArguments ?? []), ...(bootstrap.packedArguments ?? [])]; + const { cancellable, nonce, fee: userFee } = options; - if (options.estimateGas) { - request.fee = await this.getFeeOptionsFromEstimatedGas(request); - } + const request = { calls, authWitnesses, packedArguments, cancellable, fee: userFee, nonce }; - return request; + const fee = await this.getFeeOptions(request); + return { ...request, fee }; } /** @@ -133,7 +130,9 @@ export class DeployMethod extends Bas * @param options - Deployment options. * @returns A function call array with potentially requests to the class registerer and instance deployer. */ - protected async getDeploymentFunctionCalls(options: DeployOptions = {}): Promise { + protected async getDeploymentFunctionCalls( + options: DeployOptions = {}, + ): Promise> { const calls: FunctionCall[] = []; // Set contract instance object so it's available for populating the DeploySendTx object @@ -167,9 +166,7 @@ export class DeployMethod extends Bas calls.push(deployInstance(this.wallet, instance).request()); } - return { - calls, - }; + return { calls }; } /** @@ -177,7 +174,9 @@ export class DeployMethod extends Bas * @param options - Deployment options. * @returns - An array of function calls. */ - protected getInitializeFunctionCalls(options: DeployOptions): Promise { + protected getInitializeFunctionCalls( + options: DeployOptions, + ): Promise> { const { address } = this.getInstance(options); const calls: FunctionCall[] = []; if (this.constructorArtifact && !options.skipInitialization) { @@ -189,9 +188,7 @@ export class DeployMethod extends Bas ); calls.push(constructorCall.request()); } - return Promise.resolve({ - calls, - }); + return Promise.resolve({ calls }); } /** diff --git a/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts b/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts index d387f4cddb90..6b221c302d18 100644 --- a/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts +++ b/yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts @@ -1,5 +1,5 @@ import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; -import { GasSettings, TxContext } from '@aztec/circuits.js'; +import { TxContext } from '@aztec/circuits.js'; import { FunctionType } from '@aztec/foundation/abi'; import { type EntrypointInterface, type ExecutionRequestInit } from './entrypoint.js'; @@ -11,7 +11,7 @@ export class DefaultEntrypoint implements EntrypointInterface { constructor(private chainId: number, private protocolVersion: number) {} createTxExecutionRequest(exec: ExecutionRequestInit): Promise { - const { calls, authWitnesses = [], packedArguments = [] } = exec; + const { fee, calls, authWitnesses = [], packedArguments = [] } = exec; if (calls.length > 1) { throw new Error(`Expected a single call, got ${calls.length}`); @@ -24,8 +24,7 @@ export class DefaultEntrypoint implements EntrypointInterface { } const entrypointPackedValues = PackedValues.fromValues(call.args); - const gasSettings = exec.fee?.gasSettings ?? GasSettings.default(); - const txContext = new TxContext(this.chainId, this.protocolVersion, gasSettings); + const txContext = new TxContext(this.chainId, this.protocolVersion, fee.gasSettings); return Promise.resolve( new TxExecutionRequest( call.to, diff --git a/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts b/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts index c4c3a653b733..de68c922b805 100644 --- a/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts +++ b/yarn-project/aztec.js/src/entrypoint/default_multi_call_entrypoint.ts @@ -1,6 +1,6 @@ import { type EntrypointInterface, EntrypointPayload, type ExecutionRequestInit } from '@aztec/aztec.js/entrypoint'; import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; -import { type AztecAddress, GasSettings, TxContext } from '@aztec/circuits.js'; +import { type AztecAddress, TxContext } from '@aztec/circuits.js'; import { type FunctionAbi, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; @@ -15,17 +15,16 @@ export class DefaultMultiCallEntrypoint implements EntrypointInterface { ) {} createTxExecutionRequest(executions: ExecutionRequestInit): Promise { - const { calls, authWitnesses = [], packedArguments = [] } = executions; + const { fee, calls, authWitnesses = [], packedArguments = [] } = executions; const payload = EntrypointPayload.fromAppExecution(calls); const abi = this.getEntrypointAbi(); const entrypointPackedArgs = PackedValues.fromValues(encodeArguments(abi, [payload])); - const gasSettings = executions.fee?.gasSettings ?? GasSettings.default(); const txRequest = TxExecutionRequest.from({ firstCallArgsHash: entrypointPackedArgs.hash, origin: this.address, functionSelector: FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), - txContext: new TxContext(this.chainId, this.version, gasSettings), + txContext: new TxContext(this.chainId, this.version, fee.gasSettings), argsOfCalls: [...payload.packedArguments, ...packedArguments, entrypointPackedArgs], authWitnesses, }); diff --git a/yarn-project/aztec.js/src/entrypoint/entrypoint.ts b/yarn-project/aztec.js/src/entrypoint/entrypoint.ts index 779cb18b6376..49b7fafa7c1f 100644 --- a/yarn-project/aztec.js/src/entrypoint/entrypoint.ts +++ b/yarn-project/aztec.js/src/entrypoint/entrypoint.ts @@ -17,7 +17,7 @@ export type ExecutionRequestInit = { /** Any transient packed arguments for this execution */ packedArguments?: PackedValues[]; /** How the fee is going to be payed */ - fee?: FeeOptions; + fee: FeeOptions; /** An optional nonce. Used to repeat a previous tx with a higher fee so that the first one is cancelled */ nonce?: Fr; /** Whether the transaction can be cancelled. If true, an extra nullifier will be emitted: H(nonce, GENERATOR_INDEX__TX_NULLIFIER) */ diff --git a/yarn-project/aztec.js/src/entrypoint/payload.ts b/yarn-project/aztec.js/src/entrypoint/payload.ts index 0f609fb32357..334f7cc4c7dd 100644 --- a/yarn-project/aztec.js/src/entrypoint/payload.ts +++ b/yarn-project/aztec.js/src/entrypoint/payload.ts @@ -4,6 +4,7 @@ import { FunctionType } from '@aztec/foundation/abi'; import { padArrayEnd } from '@aztec/foundation/collection'; import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto'; import { type Tuple } from '@aztec/foundation/serialize'; +import { type FieldsOf } from '@aztec/foundation/types'; import { type FeePaymentMethod } from '../fee/fee_payment_method.js'; @@ -17,6 +18,16 @@ export type FeeOptions = { gasSettings: GasSettings; }; +/** Fee options as set by a user. */ +export type UserFeeOptions = { + /** The fee payment method to use */ + paymentMethod?: FeePaymentMethod; + /** The gas settings */ + gasSettings?: Partial>; + /** Whether to run an initial simulation of the tx with high gas limit to figure out actual gas settings. */ + estimateGas?: boolean; +}; + // These must match the values defined in: // - noir-projects/aztec-nr/aztec/src/entrypoint/app.nr const APP_MAX_CALLS = 4; diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index 8f3f2d659422..45d6142bb4a2 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -8,7 +8,7 @@ import { createDebugLogger, } from '@aztec/aztec.js'; import { type AztecNode, type FunctionCall, type PXE } from '@aztec/circuit-types'; -import { Gas, GasSettings } from '@aztec/circuits.js'; +import { Gas } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { type EasyPrivateTokenContract, type TokenContract } from '@aztec/noir-contracts.js'; @@ -133,15 +133,14 @@ export class Bot { let gasSettings, estimateGas; if (l2GasLimit !== undefined && l2GasLimit > 0 && daGasLimit !== undefined && daGasLimit > 0) { - gasSettings = GasSettings.default({ gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) }); + gasSettings = { gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) }; estimateGas = false; this.log.verbose(`Using gas limits ${l2GasLimit} L2 gas ${daGasLimit} DA gas`); } else { - gasSettings = GasSettings.default(); estimateGas = true; this.log.verbose(`Estimating gas for transaction`); } this.log.verbose(skipPublicSimulation ? `Skipping public simulation` : `Simulating public transfers`); - return { estimateGas, fee: { paymentMethod, gasSettings }, skipPublicSimulation }; + return { fee: { estimateGas, paymentMethod, gasSettings }, skipPublicSimulation }; } } diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 24cc07d0c3a3..2c03510d2eed 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -4,6 +4,7 @@ import { ClientIvcProof, type ContractInstanceWithAddress, EthAddress, + GasFees, GasSettings, LogHash, MAX_ENCRYPTED_LOGS_PER_TX, @@ -126,7 +127,7 @@ export const mockTx = ( const noteEncryptedLogs = EncryptedNoteTxL2Logs.empty(); // Mock seems to have no new notes => no note logs const encryptedLogs = hasLogs ? EncryptedTxL2Logs.random(2, 3) : EncryptedTxL2Logs.empty(); // 2 priv function invocations creating 3 encrypted logs each const contractClassLog = hasLogs ? ContractClassTxL2Logs.random(1, 1) : ContractClassTxL2Logs.empty(); - data.constants.txContext.gasSettings = GasSettings.default(); + data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }); data.feePayer = feePayer; let enqueuedPublicFunctionCalls: PublicExecutionRequest[] = []; diff --git a/yarn-project/circuit-types/src/test/factories.ts b/yarn-project/circuit-types/src/test/factories.ts index 41aeb67306ca..b4044bd4e2b2 100644 --- a/yarn-project/circuit-types/src/test/factories.ts +++ b/yarn-project/circuit-types/src/test/factories.ts @@ -6,6 +6,7 @@ import { FIXED_L2_GAS, Fr, Gas, + GasFees, GasSettings, GlobalVariables, type Header, @@ -34,7 +35,7 @@ export function makeBloatedProcessedTx({ db, chainId = Fr.ZERO, version = Fr.ZERO, - gasSettings = GasSettings.default(), + gasSettings = GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }), vkTreeRoot = Fr.ZERO, protocolContractTreeRoot = Fr.ZERO, globalVariables = GlobalVariables.empty(), diff --git a/yarn-project/circuits.js/src/structs/gas_settings.ts b/yarn-project/circuits.js/src/structs/gas_settings.ts index 03a38401ea18..c5449a7ac9d1 100644 --- a/yarn-project/circuits.js/src/structs/gas_settings.ts +++ b/yarn-project/circuits.js/src/structs/gas_settings.ts @@ -1,4 +1,3 @@ -import { compact } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; @@ -60,27 +59,24 @@ export class GasSettings { return new GasSettings(Gas.empty(), Gas.empty(), GasFees.empty()); } - /** Default gas settings to use when user has not provided them. */ - // @todo @lherskind The `MAX_FEES_PER_GAS` should be not be set as a default. - // deleting the default values, and trying to figure out the plumbing. - // Issue: #10104 - static default(overrides: Partial> = {}) { + /** Default gas settings to use when user has not provided them. Requires explicit max fees per gas. */ + static default(overrides: { gasLimits?: Gas; teardownGasLimits?: Gas; maxFeesPerGas: GasFees }) { 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(10), feePerDaGas: new Fr(10) }, - ...compact(overrides), + gasLimits: overrides.gasLimits ?? { l2Gas: DEFAULT_GAS_LIMIT, daGas: DEFAULT_GAS_LIMIT }, + teardownGasLimits: overrides.teardownGasLimits ?? { + l2Gas: DEFAULT_TEARDOWN_GAS_LIMIT, + daGas: DEFAULT_TEARDOWN_GAS_LIMIT, + }, + maxFeesPerGas: overrides.maxFeesPerGas, }); } /** Default gas settings with no teardown */ - static teardownless() { - return GasSettings.default({ teardownGasLimits: Gas.from({ l2Gas: 0, daGas: 0 }) }); - } - - /** Gas settings to use for simulating a contract call. */ - static simulation() { - return GasSettings.default(); + static teardownless(opts: { maxFeesPerGas: GasFees }) { + return GasSettings.default({ + teardownGasLimits: Gas.from({ l2Gas: 0, daGas: 0 }), + maxFeesPerGas: opts.maxFeesPerGas, + }); } isEmpty() { diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 3a2da20f3e93..662e21dc2db5 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -210,7 +210,7 @@ export function makeTxContext(seed: number = 1): TxContext { * Creates a default instance of gas settings. No seed value is used to ensure we allocate a sensible amount of gas for testing. */ export function makeGasSettings() { - return GasSettings.default(); + return GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }); } /** diff --git a/yarn-project/cli/src/cmds/misc/setup_contracts.ts b/yarn-project/cli/src/cmds/misc/setup_contracts.ts index fd352bdf333a..70ea4b79dc6c 100644 --- a/yarn-project/cli/src/cmds/misc/setup_contracts.ts +++ b/yarn-project/cli/src/cmds/misc/setup_contracts.ts @@ -1,5 +1,5 @@ import { DefaultWaitOpts, type EthAddress, NoFeePaymentMethod, type Wallet } from '@aztec/aztec.js'; -import { FEE_JUICE_INITIAL_MINT, GasSettings } from '@aztec/circuits.js'; +import { FEE_JUICE_INITIAL_MINT, Gas } from '@aztec/circuits.js'; import { type LogFn } from '@aztec/foundation/log'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; @@ -27,7 +27,7 @@ export async function setupCanonicalL2FeeJuice( log('setupCanonicalL2FeeJuice: Calling initialize on fee juice contract...'); await feeJuiceContract.methods .initialize(feeJuicePortalAddress, FEE_JUICE_INITIAL_MINT) - .send({ fee: { paymentMethod: new NoFeePaymentMethod(), gasSettings: GasSettings.teardownless() } }) + .send({ fee: { paymentMethod: new NoFeePaymentMethod(), gasSettings: { teardownGasLimits: Gas.empty() } } }) .wait(waitOpts); } else { log( diff --git a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts index 2934f662ff7e..4be9016a14fd 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts @@ -2,7 +2,7 @@ import { getSchnorrAccount, getSchnorrWallet } from '@aztec/accounts/schnorr'; import { PublicFeePaymentMethod, TxStatus, sleep } from '@aztec/aztec.js'; import { type AccountWallet } from '@aztec/aztec.js/wallet'; import { BBCircuitVerifier } from '@aztec/bb-prover'; -import { CompleteAddress, Fq, Fr, GasSettings } from '@aztec/circuits.js'; +import { CompleteAddress, Fq, Fr } from '@aztec/circuits.js'; import { FPCContract, FeeJuiceContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { type PXEService, type PXEServiceConfig, createPXEService } from '@aztec/pxe'; @@ -191,7 +191,6 @@ describe('benchmarks/proving', () => { ]; const feeFnCall0 = { - gasSettings: GasSettings.default(), paymentMethod: new PublicFeePaymentMethod( initialTokenContract.address, initialFpContract.address, diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 38ff078bee99..b0fe8b519ebc 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -13,6 +13,7 @@ import { EthAddress, GENESIS_ARCHIVE_ROOT, GasFees, + GasSettings, type Header, MAX_NULLIFIERS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, @@ -88,6 +89,8 @@ describe('L1Publisher integration', () => { // The header of the last block let prevHeader: Header; + let baseFee: GasFees; + let blockSource: MockProxy; const chainId = createEthereumChain(config.l1RpcUrl, config.l1ChainId).chainInfo.id; @@ -170,6 +173,8 @@ describe('L1Publisher integration', () => { prevHeader = fork.getInitialHeader(); + baseFee = new GasFees(0, await rollup.read.getManaBaseFee([true])); + // We jump to the next epoch such that the committee can be setup. const timeToJump = await rollup.read.EPOCH_DURATION(); await progressTimeBySlot(timeToJump); @@ -195,6 +200,7 @@ describe('L1Publisher integration', () => { chainId: fr(chainId), version: fr(config.version), vkTreeRoot: getVKTreeRoot(), + gasSettings: GasSettings.default({ maxFeesPerGas: baseFee }), protocolContractTreeRoot, seed, }); 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 cc3347f02fee..3ca5e9171685 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 @@ -15,7 +15,7 @@ import { import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; // eslint-disable-next-line no-restricted-imports import { PXESchema } from '@aztec/circuit-types'; -import { GasSettings, deriveSigningKey } from '@aztec/circuits.js'; +import { deriveSigningKey } from '@aztec/circuits.js'; import { createNamespacedSafeJsonRpcServer, startHttpRpcServer } from '@aztec/foundation/json-rpc/server'; import { type DebugLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; @@ -178,7 +178,6 @@ describe('End-to-end tests for devnet', () => { const txReceipt = await l2Account .deploy({ fee: { - gasSettings: GasSettings.default(), paymentMethod: new FeeJuicePaymentMethodWithClaim(l2Account.getAddress(), { claimAmount: Fr.fromString(claimAmount), claimSecret: Fr.fromString(claimSecret.value), 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 57089425c7cc..02e168a65365 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 @@ -54,7 +54,7 @@ describe('e2e_fees gas_estimation', () => { const sendTransfers = (paymentMethod: FeePaymentMethod) => Promise.all( [true, false].map(estimateGas => - makeTransferRequest().send({ estimateGas, fee: { gasSettings, paymentMethod } }).wait(), + makeTransferRequest().send({ fee: { estimateGas, gasSettings, paymentMethod } }).wait(), ), ); @@ -122,17 +122,16 @@ describe('e2e_fees gas_estimation', () => { it('estimates gas for public contract initialization with Fee Juice payment method', async () => { const paymentMethod = new FeeJuicePaymentMethod(aliceAddress); const deployMethod = () => BananaCoin.deploy(aliceWallet, aliceAddress, 'TKN', 'TKN', 8); - const deployOpts = { fee: { gasSettings, paymentMethod }, skipClassRegistration: true }; - const estimatedGas = await deployMethod().estimateGas(deployOpts); + const deployOpts = (estimateGas = false) => ({ + fee: { gasSettings, paymentMethod, estimateGas }, + skipClassRegistration: true, + }); + const estimatedGas = await deployMethod().estimateGas(deployOpts()); logGasEstimate(estimatedGas); const [withEstimate, withoutEstimate] = await Promise.all([ - deployMethod() - .send({ ...deployOpts, estimateGas: true }) - .wait(), - deployMethod() - .send({ ...deployOpts, estimateGas: false }) - .wait(), + deployMethod().send(deployOpts(true)).wait(), + deployMethod().send(deployOpts(false)).wait(), ]); // Estimation should yield that teardown has no cost, so should send the tx with zero for teardown diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 5381148d3c12..abbb9da5177f 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -30,13 +30,7 @@ import { import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment'; import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { type BBNativePrivateKernelProver } from '@aztec/bb-prover'; -import { - type EthAddress, - FEE_JUICE_INITIAL_MINT, - Fr, - GasSettings, - getContractClassFromArtifact, -} from '@aztec/circuits.js'; +import { type EthAddress, FEE_JUICE_INITIAL_MINT, Fr, Gas, getContractClassFromArtifact } from '@aztec/circuits.js'; import { type DeployL1ContractsArgs, NULL_KEY, @@ -664,7 +658,7 @@ export async function setupCanonicalFeeJuice(pxe: PXE) { try { await feeJuice.methods .initialize(feeJuicePortalAddress, FEE_JUICE_INITIAL_MINT) - .send({ fee: { paymentMethod: new NoFeePaymentMethod(), gasSettings: GasSettings.teardownless() } }) + .send({ fee: { paymentMethod: new NoFeePaymentMethod(), gasSettings: { teardownGasLimits: Gas.empty() } } }) .wait(); getLogger().info(`Fee Juice successfully setup. Portal address: ${feeJuicePortalAddress}`); } catch (error) { diff --git a/yarn-project/entrypoints/src/account_entrypoint.ts b/yarn-project/entrypoints/src/account_entrypoint.ts index 08a14fc07464..15002e020a26 100644 --- a/yarn-project/entrypoints/src/account_entrypoint.ts +++ b/yarn-project/entrypoints/src/account_entrypoint.ts @@ -6,7 +6,7 @@ import { computeCombinedPayloadHash, } from '@aztec/aztec.js/entrypoint'; import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; -import { type AztecAddress, GasSettings, TxContext } from '@aztec/circuits.js'; +import { type AztecAddress, TxContext } from '@aztec/circuits.js'; import { type FunctionAbi, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { DEFAULT_CHAIN_ID, DEFAULT_VERSION } from './constants.js'; @@ -30,7 +30,6 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { const abi = this.getEntrypointAbi(); const entrypointPackedArgs = PackedValues.fromValues(encodeArguments(abi, [appPayload, feePayload, !!cancellable])); - const gasSettings = exec.fee?.gasSettings ?? GasSettings.default(); const combinedPayloadAuthWitness = await this.auth.createAuthWit( computeCombinedPayloadHash(appPayload, feePayload), @@ -40,7 +39,7 @@ export class DefaultAccountEntrypoint implements EntrypointInterface { firstCallArgsHash: entrypointPackedArgs.hash, origin: this.address, functionSelector: FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), - txContext: new TxContext(this.chainId, this.version, gasSettings), + txContext: new TxContext(this.chainId, this.version, fee.gasSettings), argsOfCalls: [...appPayload.packedArguments, ...feePayload.packedArguments, entrypointPackedArgs], authWitnesses: [combinedPayloadAuthWitness], }); diff --git a/yarn-project/entrypoints/src/dapp_entrypoint.ts b/yarn-project/entrypoints/src/dapp_entrypoint.ts index 65ac61f55c27..569715d36301 100644 --- a/yarn-project/entrypoints/src/dapp_entrypoint.ts +++ b/yarn-project/entrypoints/src/dapp_entrypoint.ts @@ -2,7 +2,7 @@ import { computeAuthWitMessageHash, computeInnerAuthWitHash } from '@aztec/aztec import { type AuthWitnessProvider } from '@aztec/aztec.js/account'; import { type EntrypointInterface, EntrypointPayload, type ExecutionRequestInit } from '@aztec/aztec.js/entrypoint'; import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; -import { type AztecAddress, Fr, GasSettings, TxContext } from '@aztec/circuits.js'; +import { type AztecAddress, Fr, TxContext } from '@aztec/circuits.js'; import { type FunctionAbi, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { DEFAULT_CHAIN_ID, DEFAULT_VERSION } from './constants.js'; @@ -21,7 +21,7 @@ export class DefaultDappEntrypoint implements EntrypointInterface { ) {} async createTxExecutionRequest(exec: ExecutionRequestInit): Promise { - const { calls } = exec; + const { calls, fee } = exec; if (calls.length !== 1) { throw new Error(`Expected exactly 1 function call, got ${calls.length}`); } @@ -30,7 +30,6 @@ export class DefaultDappEntrypoint implements EntrypointInterface { const abi = this.getEntrypointAbi(); const entrypointPackedArgs = PackedValues.fromValues(encodeArguments(abi, [payload, this.userAddress])); - const gasSettings = exec.fee?.gasSettings ?? GasSettings.default(); const functionSelector = FunctionSelector.fromNameAndParameters(abi.name, abi.parameters); // Default msg_sender for entrypoints is now Fr.max_value rather than 0 addr (see #7190 & #7404) const innerHash = computeInnerAuthWitHash([ @@ -49,7 +48,7 @@ export class DefaultDappEntrypoint implements EntrypointInterface { firstCallArgsHash: entrypointPackedArgs.hash, origin: this.dappEntrypointAddress, functionSelector, - txContext: new TxContext(this.chainId, this.version, gasSettings), + txContext: new TxContext(this.chainId, this.version, fee.gasSettings), argsOfCalls: [...payload.packedArguments, entrypointPackedArgs], authWitnesses: [authWitness], }); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index 172c7ef67fdf..95210a1b69a9 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -1,5 +1,5 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; -import { AztecAddress, Fr, FunctionSelector, GasSettings, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js'; +import { AztecAddress, Fr, FunctionSelector, GasFees, GasSettings, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; import { FeeJuiceContract } from '@aztec/noir-contracts.js'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; @@ -31,7 +31,7 @@ describe('GasTxValidator', () => { beforeEach(() => { tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); tx.data.feePayer = AztecAddress.random(); - tx.data.constants.txContext.gasSettings = GasSettings.default(); + tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }); payer = tx.data.feePayer; expectedBalanceSlot = poseidon2Hash([FeeJuiceContract.storage.balances.slot, payer]); feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit().toBigInt(); diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 2d35d94c7f0e..9952ba91fb93 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -15,6 +15,7 @@ import { AppendOnlyTreeSnapshot, CallContext, CompleteAddress, + GasFees, GasSettings, GeneratorIndex, type GrumpkinScalar, @@ -110,7 +111,7 @@ describe('Private Execution test suite', () => { const txContextFields: FieldsOf = { chainId: new Fr(10), version: new Fr(20), - gasSettings: GasSettings.default(), + gasSettings: GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }), }; const runSimulator = ({