diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 67ae38a6201..0e095c7335c 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -97,8 +97,7 @@ library Constants { uint256 internal constant AZTEC_MAX_EPOCH_DURATION = 32; uint256 internal constant GENESIS_ARCHIVE_ROOT = 1002640778211850180189505934749257244705296832326768971348723156503780793518; - uint256 internal constant FEE_JUICE_INITIAL_MINT = 20000000000000000000; - uint256 internal constant FEE_FUNDING_FOR_TESTER_ACCOUNT = 100000000000000000000; + uint256 internal constant FEE_JUICE_INITIAL_MINT = 200000000000000000000; uint256 internal constant PUBLIC_DISPATCH_SELECTOR = 3578010381; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000; uint256 internal constant MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 130633113da..ed9b45dd0a3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -138,8 +138,7 @@ pub global GENESIS_ARCHIVE_ROOT: Field = 0x0237797d6a2c04d20d4fa06b74482bd970ccd51a43d9b05b57e9b91fa1ae1cae; // The following and the value in `deploy_l1_contracts` must match. We should not have the code both places, but // we are running into circular dependency issues. #3342 -global FEE_JUICE_INITIAL_MINT: Field = 20000000000000000000; -global FEE_FUNDING_FOR_TESTER_ACCOUNT: Field = 100000000000000000000; // 100e18 +global FEE_JUICE_INITIAL_MINT: Field = 200000000000000000000; // Last 4 bytes of the Poseidon2 hash of 'public_dispatch(Field)'. pub global PUBLIC_DISPATCH_SELECTOR: Field = 0xd5441b0d; diff --git a/yarn-project/accounts/package.json b/yarn-project/accounts/package.json index 0817971899b..d73f4c9ebf3 100644 --- a/yarn-project/accounts/package.json +++ b/yarn-project/accounts/package.json @@ -5,6 +5,7 @@ "version": "0.1.0", "type": "module", "exports": { + "./dapp": "./dest/dapp/index.js", "./defaults": "./dest/defaults/index.js", "./ecdsa": "./dest/ecdsa/index.js", "./schnorr": "./dest/schnorr/index.js", @@ -101,4 +102,4 @@ "engines": { "node": ">=18" } -} +} \ No newline at end of file diff --git a/yarn-project/accounts/src/dapp/dapp_interface.ts b/yarn-project/accounts/src/dapp/dapp_interface.ts new file mode 100644 index 00000000000..3752492d26c --- /dev/null +++ b/yarn-project/accounts/src/dapp/dapp_interface.ts @@ -0,0 +1,33 @@ +import { type AccountWallet, type AuthWitnessProvider } from '@aztec/aztec.js'; +import { type AztecAddress, type CompleteAddress, type NodeInfo } from '@aztec/circuits.js'; +import { DefaultDappEntrypoint } from '@aztec/entrypoints/dapp'; + +import { DefaultAccountInterface } from '../defaults/account_interface.js'; + +/** + * Default implementation for an account interface that uses a dapp entrypoint. + */ +export class DefaultDappInterface extends DefaultAccountInterface { + constructor( + authWitnessProvider: AuthWitnessProvider, + userAddress: CompleteAddress, + dappAddress: AztecAddress, + nodeInfo: Pick, + ) { + super(authWitnessProvider, userAddress, nodeInfo); + this.entrypoint = new DefaultDappEntrypoint( + userAddress.address, + authWitnessProvider, + dappAddress, + nodeInfo.l1ChainId, + nodeInfo.protocolVersion, + ); + } + + static createFromUserWallet(wallet: AccountWallet, dappAddress: AztecAddress): DefaultDappInterface { + return new DefaultDappInterface(wallet, wallet.getCompleteAddress(), dappAddress, { + l1ChainId: wallet.getChainId().toNumber(), + protocolVersion: wallet.getVersion().toNumber(), + }); + } +} diff --git a/yarn-project/accounts/src/dapp/index.ts b/yarn-project/accounts/src/dapp/index.ts new file mode 100644 index 00000000000..9a52950dbeb --- /dev/null +++ b/yarn-project/accounts/src/dapp/index.ts @@ -0,0 +1 @@ +export * from './dapp_interface.js'; diff --git a/yarn-project/accounts/src/defaults/account_interface.ts b/yarn-project/accounts/src/defaults/account_interface.ts index 586f790f5af..914d21d85cc 100644 --- a/yarn-project/accounts/src/defaults/account_interface.ts +++ b/yarn-project/accounts/src/defaults/account_interface.ts @@ -9,7 +9,8 @@ import { DefaultAccountEntrypoint } from '@aztec/entrypoints/account'; * entrypoint signature, which accept an AppPayload and a FeePayload as defined in noir-libs/aztec-noir/src/entrypoint module */ export class DefaultAccountInterface implements AccountInterface { - private entrypoint: EntrypointInterface; + protected entrypoint: EntrypointInterface; + private chainId: Fr; private version: Fr; diff --git a/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts b/yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts index eac072ed08e..045ea90480d 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 d0e9b281e0e..57d10fa67a4 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 e4e3316a6db..a9f5e4cc328 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 d36ea1fc2d2..7761a2da129 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,11 +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; - /** Percentage to pad the suggested gas limits by, if empty, defaults to 10%. */ - estimatedGasPad?: number; + 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) */ @@ -92,33 +90,54 @@ 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, - opts?.estimatedGasPad, + opts?.fee?.estimatedGasPadding, ); 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. * @param pad - Percentage to pad the suggested gas limits by, as decimal (e.g., 0.10 for 10%). * @returns Fee options for the actual transaction. */ - protected async getFeeOptionsFromEstimatedGas(request: ExecutionRequestInit, pad?: number) { - 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, pad); - this.log.debug( + const { totalGas: gasLimits, teardownGas: teardownGasLimits } = getGasLimits( + simulationResult, + request.fee?.estimatedGasPadding, + ); + 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 501711e4400..1a19a393d90 100644 --- a/yarn-project/aztec.js/src/contract/batch_call.ts +++ b/yarn-project/aztec.js/src/contract/batch_call.ts @@ -19,10 +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?.estimatedGasPad) - : opts?.fee; - return await this.wallet.createTxExecutionRequest({ calls, fee }); + const fee = await this.getFeeOptions({ calls, ...opts }); + return await this.wallet.createTxExecutionRequest({ calls, ...opts, fee }); } /** @@ -35,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++, @@ -65,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), @@ -88,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.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 8658743a10b..4e856779ad4 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -11,6 +11,7 @@ import { CompleteAddress, type ContractInstanceWithAddress, EthAddress, + GasFees, type NodeInfo, } from '@aztec/circuits.js'; import { type L1ContractAddresses } from '@aztec/ethereum'; @@ -153,6 +154,7 @@ describe('Contract Class', () => { wallet.getNodeInfo.mockResolvedValue(mockNodeInfo); wallet.proveTx.mockResolvedValue(mockTxProvingResult); wallet.getRegisteredAccounts.mockResolvedValue([account]); + wallet.getCurrentBaseFees.mockResolvedValue(new GasFees(100, 100)); }); it('should create and send a contract method tx', async () => { 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 ba033b21b1a..71117bdf280 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -58,21 +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?.estimatedGasPad) - : 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 caf696cb5f9..9b45db9b49c 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, options.estimatedGasPad); - } + 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 d387f4cddb9..6b221c302d1 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 c4c3a653b73..de68c922b80 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 779cb18b637..49b7fafa7c1 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 0f609fb3235..8f76aa0e06f 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,18 @@ 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; + /** Percentage to pad the estimated gas limits by, if empty, defaults to 0.1. Only relevant if estimateGas is set. */ + estimatedGasPadding?: number; +}; + // 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 8f3f2d65942..45d6142bb4a 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 24cc07d0c3a..2c03510d2ee 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 a9e4335a2e1..bd303ecd831 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/package.json b/yarn-project/circuits.js/package.json index 0c4730d4357..34f19cb3e6d 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -16,7 +16,7 @@ "./interfaces": "./dest/interfaces/index.js", "./utils": "./dest/utils/index.js", "./types": "./dest/types/index.js", - "./constants": "./dest/constants.gen.js", + "./constants": "./dest/constants.js", "./contract": "./dest/contract/index.js", "./merkle": "./dest/merkle/index.js", "./simulation": "./dest/simulator/index.js" @@ -101,4 +101,4 @@ ] ] } -} +} \ No newline at end of file diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index f015aacd3a2..183e21073d2 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -83,8 +83,7 @@ export const PRIVATE_LOG_SIZE_IN_BYTES = 576; export const BLOB_SIZE_IN_BYTES = 126976; export const AZTEC_MAX_EPOCH_DURATION = 32; export const GENESIS_ARCHIVE_ROOT = 1002640778211850180189505934749257244705296832326768971348723156503780793518n; -export const FEE_JUICE_INITIAL_MINT = 20000000000000000000n; -export const FEE_FUNDING_FOR_TESTER_ACCOUNT = 100000000000000000000n; +export const FEE_JUICE_INITIAL_MINT = 200000000000000000000n; export const PUBLIC_DISPATCH_SELECTOR = 3578010381; export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000; export const MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS = 3000; diff --git a/yarn-project/circuits.js/src/constants.ts b/yarn-project/circuits.js/src/constants.ts new file mode 100644 index 00000000000..8e2f8f06caf --- /dev/null +++ b/yarn-project/circuits.js/src/constants.ts @@ -0,0 +1,5 @@ +// Typescript-land-only constants +export const FEE_FUNDING_FOR_TESTER_ACCOUNT = BigInt(1_000e18); + +// Autogenerated constants loaded from noir-land +export * from './constants.gen.js'; diff --git a/yarn-project/circuits.js/src/index.ts b/yarn-project/circuits.js/src/index.ts index 3b7dba0dfef..9ed5971c96f 100644 --- a/yarn-project/circuits.js/src/index.ts +++ b/yarn-project/circuits.js/src/index.ts @@ -1,4 +1,4 @@ -export * from './constants.gen.js'; +export * from './constants.js'; export * from './contract/index.js'; export * from './hints/index.js'; export * from './interfaces/index.js'; diff --git a/yarn-project/circuits.js/src/structs/gas_fees.ts b/yarn-project/circuits.js/src/structs/gas_fees.ts index cd8d97a9c45..827cc41255c 100644 --- a/yarn-project/circuits.js/src/structs/gas_fees.ts +++ b/yarn-project/circuits.js/src/structs/gas_fees.ts @@ -44,6 +44,13 @@ export class GasFees { } } + mul(scalar: number | bigint) { + return new GasFees( + new Fr(this.feePerDaGas.toBigInt() * BigInt(scalar)), + new Fr(this.feePerL2Gas.toBigInt() * BigInt(scalar)), + ); + } + static from(fields: FieldsOf) { return new GasFees(fields.feePerDaGas, fields.feePerL2Gas); } diff --git a/yarn-project/circuits.js/src/structs/gas_settings.ts b/yarn-project/circuits.js/src/structs/gas_settings.ts index 03a38401ea1..c5449a7ac9d 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 1fce585ae3b..cfed4e3241f 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-wallet/src/utils/options/fees.ts b/yarn-project/cli-wallet/src/utils/options/fees.ts index 154be27e1c2..fd7f7327432 100644 --- a/yarn-project/cli-wallet/src/utils/options/fees.ts +++ b/yarn-project/cli-wallet/src/utils/options/fees.ts @@ -44,8 +44,8 @@ function formatGasEstimate(estimate: Pick, fees: GasFees) { - return GasSettings.from({ ...GasSettings.default(), ...estimate, maxFeesPerGas: fees }) +function getEstimatedCost(estimate: Pick, maxFeesPerGas: GasFees) { + return GasSettings.default({ ...estimate, maxFeesPerGas }) .getFeeLimit() .toBigInt(); } @@ -60,8 +60,8 @@ export class FeeOpts implements IFeeOpts { async toSendOpts(sender: AccountWallet): Promise { return { - estimateGas: this.estimateGas, fee: { + estimateGas: this.estimateGas, gasSettings: this.gasSettings, paymentMethod: await this.paymentMethodFactory(sender), }, @@ -90,12 +90,10 @@ export class FeeOpts implements IFeeOpts { const gasFees = args.maxFeesPerGas ? parseGasFees(args.maxFeesPerGas) : { maxFeesPerGas: await pxe.getCurrentBaseFees() }; - const input = { - ...GasSettings.default(), - ...(args.gasLimits ? parseGasLimits(args.gasLimits) : {}), + const gasSettings = GasSettings.default({ ...gasFees, - }; - const gasSettings = GasSettings.from(input); + ...(args.gasLimits ? parseGasLimits(args.gasLimits) : {}), + }); if (!args.gasLimits && !args.payment) { return new NoFeeOpts(estimateOnly, gasSettings); diff --git a/yarn-project/cli/src/cmds/misc/setup_contracts.ts b/yarn-project/cli/src/cmds/misc/setup_contracts.ts index fd352bdf333..70ea4b79dc6 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/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index bf31fcc152a..a8fd46b0938 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 @@ -12,6 +12,7 @@ import { EthAddress, GENESIS_ARCHIVE_ROOT, GasFees, + GasSettings, type Header, MAX_NULLIFIERS_PER_TX, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, @@ -85,6 +86,8 @@ describe('L1Publisher integration', () => { // The header of the last block let prevHeader: Header; + let baseFee: GasFees; + let blockSource: MockProxy; let blocks: L2Block[] = []; @@ -192,6 +195,8 @@ describe('L1Publisher integration', () => { prevHeader = fork.getInitialHeader(); await fork.close(); + 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); @@ -216,6 +221,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 cc3347f02fe..3ca5e917168 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_crowdfunding_and_claim.test.ts b/yarn-project/end-to-end/src/e2e_crowdfunding_and_claim.test.ts index 5c8d1470d4d..c05d6d0d1a7 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 @@ -337,11 +337,12 @@ describe('e2e_crowdfunding_and_claim', () => { const call = crowdfundingContract.withWallet(donorWallets[1]).methods.withdraw(donationAmount).request(); // ...using the withdraw fn as our entrypoint const entrypointPackedValues = PackedValues.fromValues(call.args); + const maxFeesPerGas = await pxe.getCurrentBaseFees(); const request = new TxExecutionRequest( call.to, call.selector, entrypointPackedValues.hash, - new TxContext(donorWallets[1].getChainId(), donorWallets[1].getVersion(), GasSettings.default()), + new TxContext(donorWallets[1].getChainId(), donorWallets[1].getVersion(), GasSettings.default({ maxFeesPerGas })), [entrypointPackedValues], [], ); 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 80116cf224e..7b2abe306b0 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 @@ -18,7 +18,7 @@ import { type CompleteAddress, FEE_FUNDING_FOR_TESTER_ACCOUNT, Fq, - GasSettings, + type GasSettings, } from '@aztec/circuits.js'; import { type TokenContract as BananaCoin, type FPCContract, SchnorrAccountContract } from '@aztec/noir-contracts.js'; @@ -82,11 +82,6 @@ describe('e2e_fees account_init', () => { bobsAddress = bobsCompleteAddress.address; bobsWallet = await bobsAccountManager.getWallet(); - gasSettings = GasSettings.from({ - ...t.gasSettings, - maxFeesPerGas: await aliceWallet.getCurrentBaseFees(), - }); - await bobsAccountManager.register(); await initBalances(); }); 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 32be6b2c5db..b9384823a14 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 @@ -1,15 +1,14 @@ +import { DefaultDappInterface } from '@aztec/accounts/dapp'; import { - type AccountWallet, + AccountWallet, type AztecAddress, type FeePaymentMethod, Fr, type PXE, PrivateFeePaymentMethod, PublicFeePaymentMethod, - SentTx, } from '@aztec/aztec.js'; -import { FEE_FUNDING_FOR_TESTER_ACCOUNT, GasSettings } from '@aztec/circuits.js'; -import { DefaultDappEntrypoint } from '@aztec/entrypoints/dapp'; +import { FEE_FUNDING_FOR_TESTER_ACCOUNT, type GasSettings } from '@aztec/circuits.js'; import { type AppSubscriptionContract, type TokenContract as BananaCoin, @@ -92,11 +91,6 @@ describe('e2e_fees dapp_subscription', () => { }); beforeEach(async () => { - gasSettings = GasSettings.from({ - ...t.gasSettings, - maxFeesPerGas: await aliceWallet.getCurrentBaseFees(), - }); - [initialSubscriptionContractGasBalance, initialSequencerGasBalance, initialFPCGasBalance] = (await t.getGasBalanceFn(subscriptionContract, sequencerAddress, bananaFPC)) as Balances; initialBananasPublicBalances = (await t.getBananaPublicBalanceFn(aliceAddress, bobAddress, bananaFPC)) as Balances; @@ -177,19 +171,14 @@ describe('e2e_fees dapp_subscription', () => { expect(await subscriptionContract.methods.is_initialized(aliceAddress).simulate()).toBe(true); - const dappPayload = new DefaultDappEntrypoint(aliceAddress, aliceWallet, subscriptionContract.address); - // Emitting the outgoing logs to Alice below - const action = counterContract.methods.increment(bobAddress, aliceAddress).request(); - const txExReq = await dappPayload.createTxExecutionRequest({ calls: [action] }); - - const txSimulationResult = await pxe.simulateTx(txExReq, true); - - const txProvingResult = await pxe.proveTx(txExReq, txSimulationResult.privateExecutionResult); - - const sentTx = new SentTx(pxe, pxe.sendTx(txProvingResult.toTx())); - - const { transactionFee } = await sentTx.wait(); + const dappInterface = DefaultDappInterface.createFromUserWallet(aliceWallet, subscriptionContract.address); + const counterContractViaDappEntrypoint = counterContract.withWallet(new AccountWallet(pxe, dappInterface)); + // Emitting the outgoing logs to Alice below + const { transactionFee } = await counterContractViaDappEntrypoint.methods + .increment(bobAddress, aliceAddress) + .send() + .wait(); expect(await counterContract.methods.get_counter(bobAddress).simulate()).toBe(1n); await expectMapping( @@ -231,17 +220,10 @@ describe('e2e_fees dapp_subscription', () => { .wait(); } - async function dappIncrement() { - const dappEntrypoint = new DefaultDappEntrypoint(aliceAddress, aliceWallet, subscriptionContract.address); - // Emitting the outgoing logs to Alice below - const action = counterContract.methods.increment(bobAddress, aliceAddress).request(); - const txExReq = await dappEntrypoint.createTxExecutionRequest({ calls: [action] }); - const txSimulationResult = await pxe.simulateTx(txExReq, true); - const txProvingResult = await pxe.proveTx(txExReq, txSimulationResult.privateExecutionResult); - const tx = txProvingResult.toTx(); - expect(tx.data.feePayer).toEqual(subscriptionContract.address); - const sentTx = new SentTx(pxe, pxe.sendTx(tx)); - return sentTx.wait(); + function dappIncrement() { + const dappInterface = DefaultDappInterface.createFromUserWallet(aliceWallet, subscriptionContract.address); + const counterContractViaDappEntrypoint = counterContract.withWallet(new AccountWallet(pxe, dappInterface)); + return counterContractViaDappEntrypoint.methods.increment(bobAddress, aliceAddress).send().wait(); } const expectBananasPrivateDelta = (aliceAmount: bigint, bobAmount: bigint, fpcAmount: bigint) => 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 664f920873d..57a7559e12f 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 @@ -35,13 +35,6 @@ describe('e2e_fees failures', () => { await t.teardown(); }); - beforeEach(async () => { - gasSettings = GasSettings.from({ - ...t.gasSettings, - maxFeesPerGas: await aliceWallet.getCurrentBaseFees(), - }); - }); - it('reverts transactions but still pays fees using PrivateFeePaymentMethod', async () => { const outrageousPublicAmountAliceDoesNotHave = t.ALICE_INITIAL_BANANAS * 5n; const privateMintedAlicePrivateBananas = t.ALICE_INITIAL_BANANAS; 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 b29ad717455..87699648c19 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 @@ -64,8 +64,7 @@ export class FeesTest { public feeRecipient!: AztecAddress; // Account that receives the fees from the fee refund flow. - public gasSettings = GasSettings.default(); - public maxFee = this.gasSettings.getFeeLimit().toBigInt(); + public gasSettings!: GasSettings; public feeJuiceContract!: FeeJuiceContract; public bananaCoin!: BananaCoin; @@ -135,6 +134,7 @@ export class FeesTest { async ({ accountKeys }, { pxe, aztecNode, aztecNodeConfig }) => { this.pxe = pxe; this.aztecNode = aztecNode; + this.gasSettings = GasSettings.default({ maxFeesPerGas: (await this.aztecNode.getCurrentBaseFees()).mul(2) }); const accountManagers = accountKeys.map(ak => getSchnorrAccount(pxe, ak[0], ak[1], 1)); await Promise.all(accountManagers.map(a => a.register())); this.wallets = await Promise.all(accountManagers.map(a => a.getWallet())); 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 cdf5b5d1f8a..f3655716c22 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 @@ -55,7 +55,9 @@ describe('e2e_fees gas_estimation', () => { const sendTransfers = (paymentMethod: FeePaymentMethod) => Promise.all( [true, false].map(estimateGas => - makeTransferRequest().send({ estimateGas, fee: { gasSettings, paymentMethod }, estimatedGasPad: 0 }).wait(), + makeTransferRequest() + .send({ fee: { estimateGas, gasSettings, paymentMethod, estimatedGasPadding: 0 } }) + .wait(), ), ); @@ -68,8 +70,7 @@ describe('e2e_fees gas_estimation', () => { it('estimates gas with Fee Juice payment method', async () => { const paymentMethod = new FeeJuicePaymentMethod(aliceAddress); const estimatedGas = await makeTransferRequest().estimateGas({ - fee: { gasSettings, paymentMethod }, - estimatedGasPad: 0, + fee: { gasSettings, paymentMethod, estimatedGasPadding: 0 }, }); logGasEstimate(estimatedGas); @@ -96,8 +97,7 @@ describe('e2e_fees gas_estimation', () => { const teardownFixedFee = gasSettings.teardownGasLimits.computeFee(gasSettings.maxFeesPerGas).toBigInt(); const paymentMethod = new PublicFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet); const estimatedGas = await makeTransferRequest().estimateGas({ - fee: { gasSettings, paymentMethod }, - estimatedGasPad: 0, + fee: { gasSettings, paymentMethod, estimatedGasPadding: 0 }, }); logGasEstimate(estimatedGas); @@ -121,17 +121,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, estimatedGasPad: 0 }); + const deployOpts = (estimateGas = false) => ({ + fee: { gasSettings, paymentMethod, estimateGas, estimatedGasPadding: 0 }, + 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 5381148d3c1..abbb9da5177 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 08a14fc0746..15002e020a2 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 65ac61f55c2..569715d3630 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/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index cb647ffda9c..e9d8522f636 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -397,7 +397,7 @@ export const deployL1Contracts = async ( // because there is circular dependency hell. This is a temporary solution. #3342 // @todo #8084 // fund the portal contract with Fee Juice - const FEE_JUICE_INITIAL_MINT = 20000000000000000000; + const FEE_JUICE_INITIAL_MINT = 200000000000000000000; const mintTxHash = await feeJuice.write.mint([feeJuicePortalAddress.toString(), FEE_JUICE_INITIAL_MINT], {} as any); // @note This is used to ensure we fully wait for the transaction when running against a real chain 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 172c7ef67fd..95210a1b69a 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/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts index e7a47a7eced..58d92c7ce1a 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts @@ -72,7 +72,11 @@ export class GasTxValidator implements TxValidator { const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[2]) : initialBalance; if (balance.lt(feeLimit)) { - this.#log.info(`Rejecting transaction due to not enough fee payer balance`, { feePayer, balance, feeLimit }); + this.#log.info(`Rejecting transaction due to not enough fee payer balance`, { + feePayer, + balance: balance.toBigInt(), + feeLimit: feeLimit.toBigInt(), + }); return false; } return true; diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 9dbbddea2ea..3b3b7a039e5 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 = ({