diff --git a/src/adapters/ethers/resources/deposits/context.ts b/src/adapters/ethers/resources/deposits/context.ts index 5135dc9..56f47d3 100644 --- a/src/adapters/ethers/resources/deposits/context.ts +++ b/src/adapters/ethers/resources/deposits/context.ts @@ -4,7 +4,7 @@ import type { EthersClient } from '../../client'; import type { Address, Hex } from '../../../../core/types/primitives'; import type { DepositParams, DepositRoute } from '../../../../core/types/flows/deposits'; import type { CommonCtx } from '../../../../core/types/flows/base'; -import type { TxOverrides } from '../../../../core/types/fees'; +import { type TxGasOverrides, toGasOverrides } from '../../../../core/types/fees'; import type { ResolvedToken, TokensResource } from '../../../../core/types/flows/token'; import type { ContractsResource } from '../contracts'; @@ -22,7 +22,7 @@ export interface BuildCtx extends CommonCtx { l1AssetRouter: Address; - gasOverrides?: TxOverrides; + gasOverrides?: TxGasOverrides; l2GasLimit?: bigint; gasPerPubdata: bigint; operatorTip: bigint; @@ -72,7 +72,7 @@ export async function commonCtx( bridgehub, chainIdL2: BigInt(chainId), sender, - gasOverrides: p.l1TxOverrides, + gasOverrides: p.l1TxOverrides ? toGasOverrides(p.l1TxOverrides) : undefined, l2GasLimit: p.l2GasLimit, gasPerPubdata, operatorTip, diff --git a/src/adapters/ethers/resources/deposits/index.ts b/src/adapters/ethers/resources/deposits/index.ts index befc32a..f88a5cb 100644 --- a/src/adapters/ethers/resources/deposits/index.ts +++ b/src/adapters/ethers/resources/deposits/index.ts @@ -182,7 +182,13 @@ export function createDepositsResource( const managed = new NonceManager(client.signer); const from = await managed.getAddress(); - let next = await client.l1.getTransactionCount(from, 'latest'); + let next: number; + if (typeof p.l1TxOverrides?.nonce === 'number') { + next = p.l1TxOverrides.nonce; + } else { + const blockTag = p.l1TxOverrides?.nonce ?? 'latest'; + next = await client.l1.getTransactionCount(from, blockTag); + } for (const step of plan.steps) { // re-check allowance diff --git a/src/adapters/ethers/resources/deposits/services/gas.ts b/src/adapters/ethers/resources/deposits/services/gas.ts index 74406a1..28ab9c6 100644 --- a/src/adapters/ethers/resources/deposits/services/gas.ts +++ b/src/adapters/ethers/resources/deposits/services/gas.ts @@ -3,7 +3,7 @@ import type { TransactionRequest } from 'ethers'; import type { BuildCtx } from '../context'; import type { DepositRoute } from '../../../../../core/types/flows/deposits'; -import type { TxOverrides } from '../../../../../core/types/fees'; +import type { TxGasOverrides } from '../../../../../core/types/fees'; import type { Address } from '../../../../../core/types/primitives'; import { quoteL1Gas as coreQuoteL1Gas, @@ -17,7 +17,7 @@ export type { GasQuote }; export type QuoteL1GasInput = { ctx: BuildCtx; tx: TransactionRequest; - overrides?: TxOverrides; + overrides?: TxGasOverrides; fallbackGasLimit?: bigint; }; diff --git a/src/adapters/ethers/resources/withdrawals/context.ts b/src/adapters/ethers/resources/withdrawals/context.ts index 5a98b18..2a3607d 100644 --- a/src/adapters/ethers/resources/withdrawals/context.ts +++ b/src/adapters/ethers/resources/withdrawals/context.ts @@ -5,7 +5,7 @@ import type { Address } from '../../../../core/types/primitives'; import { pickWithdrawRoute } from '../../../../core/resources/withdrawals/route'; import { type WithdrawParams, type WithdrawRoute } from '../../../../core/types/flows/withdrawals'; import type { CommonCtx } from '../../../../core/types/flows/base'; -import type { TxOverrides } from '../../../../core/types/fees'; +import { type TxGasOverrides, toGasOverrides } from '../../../../core/types/fees'; import type { Hex } from '../../../../core/types/primitives'; import type { ResolvedToken, TokensResource } from '../../../../core/types/flows/token'; import type { ContractsResource } from '../contracts'; @@ -31,7 +31,7 @@ export interface BuildCtx extends CommonCtx { l2BaseTokenSystem: Address; // L2 gas - gasOverrides?: TxOverrides; + gasOverrides?: TxGasOverrides; } export async function commonCtx( @@ -79,6 +79,6 @@ export async function commonCtx( l2NativeTokenVault, l2BaseTokenSystem, baseIsEth, - gasOverrides: p.l2TxOverrides, + gasOverrides: p.l2TxOverrides ? toGasOverrides(p.l2TxOverrides) : undefined, } satisfies BuildCtx & { route: WithdrawRoute }; } diff --git a/src/adapters/ethers/resources/withdrawals/index.ts b/src/adapters/ethers/resources/withdrawals/index.ts index c1379c0..86837c5 100644 --- a/src/adapters/ethers/resources/withdrawals/index.ts +++ b/src/adapters/ethers/resources/withdrawals/index.ts @@ -193,7 +193,13 @@ export function createWithdrawalsResource( const managed = new NonceManager(client.getL2Signer()); const from = await managed.getAddress(); - let next = await client.l2.getTransactionCount(from, 'pending'); + let next: number; + if (typeof p.l2TxOverrides?.nonce === 'number') { + next = p.l2TxOverrides.nonce; + } else { + const blockTag = p.l2TxOverrides?.nonce ?? 'pending'; + next = await client.l2.getTransactionCount(from, blockTag); + } for (const step of plan.steps) { step.tx.nonce = next++; diff --git a/src/adapters/viem/resources/deposits/context.ts b/src/adapters/viem/resources/deposits/context.ts index 41e3d54..21e4656 100644 --- a/src/adapters/viem/resources/deposits/context.ts +++ b/src/adapters/viem/resources/deposits/context.ts @@ -3,7 +3,7 @@ import type { ViemClient } from '../../client'; import type { Address, Hex } from '../../../../core/types/primitives'; import type { DepositParams, DepositRoute } from '../../../../core/types/flows/deposits'; import type { CommonCtx } from '../../../../core/types/flows/base'; -import type { TxOverrides } from '../../../../core/types/fees'; +import { type TxGasOverrides, toGasOverrides } from '../../../../core/types/fees'; import type { ResolvedToken, TokensResource } from '../../../../core/types/flows/token'; import type { ContractsResource } from '../contracts'; @@ -21,7 +21,7 @@ export interface BuildCtx extends CommonCtx { l1AssetRouter: Address; - gasOverrides?: TxOverrides; + gasOverrides?: TxGasOverrides; l2GasLimit?: bigint; gasPerPubdata: bigint; operatorTip: bigint; @@ -71,7 +71,7 @@ export async function commonCtx( bridgehub, chainIdL2: BigInt(chainId), sender, - gasOverrides: p.l1TxOverrides, + gasOverrides: p.l1TxOverrides ? toGasOverrides(p.l1TxOverrides) : undefined, l2GasLimit: p.l2GasLimit, gasPerPubdata, operatorTip, diff --git a/src/adapters/viem/resources/deposits/index.ts b/src/adapters/viem/resources/deposits/index.ts index 5fb3efc..af74709 100644 --- a/src/adapters/viem/resources/deposits/index.ts +++ b/src/adapters/viem/resources/deposits/index.ts @@ -188,8 +188,13 @@ export function createDepositsResource( const stepHashes: Record = {}; const from = client.account.address; - // TODO: remove this - let next = await client.l1.getTransactionCount({ address: from, blockTag: 'latest' }); + let next: number; + if (typeof p.l1TxOverrides?.nonce === 'number') { + next = p.l1TxOverrides.nonce; + } else { + const blockTag = p.l1TxOverrides?.nonce ?? 'latest'; + next = await client.l1.getTransactionCount({ address: from, blockTag }); + } for (const step of plan.steps) { // Re-check allowance diff --git a/src/adapters/viem/resources/deposits/services/gas.ts b/src/adapters/viem/resources/deposits/services/gas.ts index 96c3664..9bdc161 100644 --- a/src/adapters/viem/resources/deposits/services/gas.ts +++ b/src/adapters/viem/resources/deposits/services/gas.ts @@ -3,7 +3,7 @@ import { zeroAddress, type TransactionRequest } from 'viem'; import type { BuildCtx } from '../context'; import type { DepositRoute } from '../../../../../core/types/flows/deposits'; -import type { TxOverrides } from '../../../../../core/types/fees'; +import type { TxGasOverrides } from '../../../../../core/types/fees'; import type { Address } from '../../../../../core/types/primitives'; import { quoteL1Gas as coreQuoteL1Gas, @@ -17,7 +17,7 @@ export type { GasQuote }; export type QuoteL1GasInput = { ctx: BuildCtx; tx: TransactionRequest; - overrides?: TxOverrides; + overrides?: TxGasOverrides; fallbackGasLimit?: bigint; }; diff --git a/src/adapters/viem/resources/withdrawals/context.ts b/src/adapters/viem/resources/withdrawals/context.ts index b952098..7f81aee 100644 --- a/src/adapters/viem/resources/withdrawals/context.ts +++ b/src/adapters/viem/resources/withdrawals/context.ts @@ -5,7 +5,7 @@ import type { Address } from '../../../../core/types/primitives'; import { pickWithdrawRoute } from '../../../../core/resources/withdrawals/route'; import { type WithdrawParams, type WithdrawRoute } from '../../../../core/types/flows/withdrawals'; import type { CommonCtx } from '../../../../core/types/flows/base'; -import type { TxOverrides } from '../../../../core/types/fees'; +import { type TxGasOverrides, toGasOverrides } from '../../../../core/types/fees'; import type { Hex } from '../../../../core/types/primitives'; import type { ResolvedToken, TokensResource } from '../../../../core/types/flows/token'; import type { ContractsResource } from '../contracts'; @@ -31,7 +31,7 @@ export interface BuildCtx extends CommonCtx { l2BaseTokenSystem: Address; // L2 gas - gasOverrides?: TxOverrides; + gasOverrides?: TxGasOverrides; } export async function commonCtx( @@ -78,6 +78,6 @@ export async function commonCtx( l2NativeTokenVault, l2BaseTokenSystem, baseIsEth, - gasOverrides: p.l2TxOverrides, + gasOverrides: p.l2TxOverrides ? toGasOverrides(p.l2TxOverrides) : undefined, } satisfies BuildCtx & { route: WithdrawRoute }; } diff --git a/src/adapters/viem/resources/withdrawals/index.ts b/src/adapters/viem/resources/withdrawals/index.ts index 79da474..65ad4fd 100644 --- a/src/adapters/viem/resources/withdrawals/index.ts +++ b/src/adapters/viem/resources/withdrawals/index.ts @@ -190,6 +190,15 @@ export function createWithdrawalsResource( const stepHashes: Record = {}; const l2Wallet = client.getL2Wallet(); + const from = client.account.address; + + let next: number; + if (typeof p.l2TxOverrides?.nonce === 'number') { + next = p.l2TxOverrides.nonce; + } else { + const blockTag = p.l2TxOverrides?.nonce ?? 'pending'; + next = await client.l2.getTransactionCount({ address: from, blockTag }); + } for (const step of plan.steps) { if (p.l2TxOverrides) { @@ -237,6 +246,8 @@ export function createWithdrawalsResource( } : {}; + const nonce = next++; + const baseReq = { address: step.tx.address, abi: step.tx.abi as Abi, @@ -244,6 +255,7 @@ export function createWithdrawalsResource( args: step.tx.args ?? [], account: step.tx.account ?? l2Wallet.account ?? client.account, gas: step.tx.gas, + nonce, ...fee1559, ...(step.tx.dataSuffix ? { dataSuffix: step.tx.dataSuffix } : {}), ...(step.tx.chain ? { chain: step.tx.chain } : {}), diff --git a/src/core/types/fees.ts b/src/core/types/fees.ts index b2a8403..247cd9f 100644 --- a/src/core/types/fees.ts +++ b/src/core/types/fees.ts @@ -49,4 +49,19 @@ export type TxOverrides = { gasLimit: bigint; maxFeePerGas: bigint; maxPriorityFeePerGas?: bigint; + /** Optional nonce override. + * - number: use as the starting nonce directly (skip getTransactionCount). If there are multiple transactions, + * the specified number will be used as a starting nonce and incremented for each transaction. + * - 'latest' | 'pending': call getTransactionCount with the given block tag. + */ + nonce?: number | 'latest' | 'pending'; }; + +/** TxOverrides without the nonce field. */ +export type TxGasOverrides = Omit; + +export function toGasOverrides(overrides: TxOverrides): TxGasOverrides { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { nonce: _, ...gas } = overrides; + return gas; +}