Skip to content

Commit 15f769d

Browse files
authored
test(e2e_fees): bridge fee juice from a dedicated L1 account (#24054)
## What `e2e_fees/account_init` → *"account pays its own fee › pays natively in the Fee Juice by bridging funds themselves"* flakes while preparing L1 fee juice (before the account deploy), failing on the bridge write: ``` ContractFunctionExecutionError: ... contract function "depositToAztecPublic" from: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Details: replacement transaction underpriced ``` CI: http://ci.aztec-labs.com/60b00f39a7a0e3cc ## Why The fee-juice bridge test harness does direct L1 writes (`mint` → `approve` → `depositToAztecPublic`) through a plain viem client with **no nonce manager**, so each write picks its nonce via `getTransactionCount('pending')`. That client uses Anvil account `0xf39f…92266` (mnemonic index 0) — which is **also the sequencer publisher account**. The publisher tracks its own nonce in `l1-tx-utils`, independently of the test client. When a test write and a publisher checkpoint tx land on the same nonce concurrently, the higher-priced publisher tx wins and the test's lower-priced tx is rejected. In the failed run the publisher was sending on account #0 at ~95–120 gwei: ``` node:l1-tx-utils Sent L1 transaction 0x9652… {nonce: 67, account: 0xf39f…92266, maxFeePerGas: "95.396…"} ``` while the test's `depositToAztecPublic` went out on the same account at nonce **60** / ~26 gwei → `replacement transaction underpriced`. The shared-account race is pre-existing but rarely hit; #24037 (changing publisher send timing) makes the overlap more likely, which surfaced it. ## Fix Send the harness's direct L1 writes from a dedicated mnemonic account (`L1_DIRECT_WRITE_ACCOUNT_INDEX = 1`) instead of the deployer/publisher account #0. Index 1 is unused by any L1-tx sender in these setups (publisher = 0, prover = 2, validators = 3+), so the test's writes no longer share a nonce stream with the publisher. Affects `FeesTest` and `ClientFlowsBenchmark`.
1 parent 49c510a commit 15f769d

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

yarn-project/end-to-end/src/bench/client_flows/client_flows_benchmark.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
2828
import { Gas, GasSettings } from '@aztec/stdlib/gas';
2929
import { deriveSigningKey } from '@aztec/stdlib/keys';
3030

31-
import { AUTOMINE_E2E_OPTS, MNEMONIC, getPaddedMaxFeesPerGas } from '../../fixtures/fixtures.js';
31+
import {
32+
AUTOMINE_E2E_OPTS,
33+
L1_DIRECT_WRITE_ACCOUNT_INDEX,
34+
MNEMONIC,
35+
getPaddedMaxFeesPerGas,
36+
} from '../../fixtures/fixtures.js';
3237
import { type EndToEndContext, type SetupOptions, deployAccounts, setup, teardown } from '../../fixtures/setup.js';
3338
import { mintTokensToPrivate } from '../../fixtures/token_utils.js';
3439
import { setupSponsoredFPC } from '../../fixtures/utils.js';
@@ -244,7 +249,15 @@ export class ClientFlowsBenchmark {
244249
this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({
245250
aztecNode: this.context.aztecNodeService,
246251
aztecNodeAdmin: this.context.aztecNodeService,
247-
l1Client: this.context.deployL1ContractsValues.l1Client,
252+
// Bridge from a dedicated L1 account so its direct writes don't race the sequencer publisher's
253+
// txs on the deployer account (see L1_DIRECT_WRITE_ACCOUNT_INDEX).
254+
l1Client: createExtendedL1Client(
255+
this.context.config.l1RpcUrls,
256+
MNEMONIC,
257+
undefined,
258+
undefined,
259+
L1_DIRECT_WRITE_ACCOUNT_INDEX,
260+
),
248261
wallet: this.adminWallet,
249262
logger: this.logger,
250263
});

yarn-project/end-to-end/src/e2e_fees/fees_test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
2323

2424
import { getContract } from 'viem';
2525

26-
import { MNEMONIC, getPaddedMaxFeesPerGas } from '../fixtures/fixtures.js';
26+
import { L1_DIRECT_WRITE_ACCOUNT_INDEX, MNEMONIC, getPaddedMaxFeesPerGas } from '../fixtures/fixtures.js';
2727
import {
2828
type EndToEndContext,
2929
type SetupOptions,
@@ -230,7 +230,15 @@ export class FeesTest {
230230
this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({
231231
aztecNode: this.context.aztecNodeService,
232232
aztecNodeAdmin: this.context.aztecNodeService,
233-
l1Client: this.context.deployL1ContractsValues.l1Client,
233+
// Bridge from a dedicated L1 account so its direct writes don't race the sequencer publisher's
234+
// txs on the deployer account (see L1_DIRECT_WRITE_ACCOUNT_INDEX).
235+
l1Client: createExtendedL1Client(
236+
this.context.config.l1RpcUrls,
237+
MNEMONIC,
238+
undefined,
239+
undefined,
240+
L1_DIRECT_WRITE_ACCOUNT_INDEX,
241+
),
234242
wallet: this.wallet,
235243
logger: this.logger,
236244
});

yarn-project/end-to-end/src/fixtures/fixtures.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ export const TEST_PEER_CHECK_INTERVAL_MS = 1000;
109109
export const TEST_MAX_PENDING_TX_POOL_COUNT = 10_000; // Number of max pending TXs ~ 1.56GB
110110

111111
export const MNEMONIC = 'test test test test test test test test test test test junk';
112+
113+
// Mnemonic account index for tests that issue direct L1 writes (e.g. bridging fee juice) while a
114+
// sequencer is running. The deployer/sequencer publisher uses index 0, the prover index 2, and
115+
// validators index 3+. Test-side viem writes and the publisher's l1-tx-utils track nonces
116+
// independently, so sharing an account causes "replacement transaction underpriced" races; index 1
117+
// is otherwise unused, so issuing those writes from it keeps them off the publisher's nonce stream.
118+
export const L1_DIRECT_WRITE_ACCOUNT_INDEX = 1;
119+
112120
export const privateKey = Buffer.from('ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', 'hex');
113121
export const privateKey2 = Buffer.from('59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', 'hex');
114122

0 commit comments

Comments
 (0)