Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/adapters/ethers/resources/deposits/routes/erc20-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createErrorHandlers } from '../../../errors/error-ops';
import { OP_DEPOSITS } from '../../../../../core/types';
import { normalizeAddrEq, isETH } from '../../../../../core/utils/addr';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';
import { quoteL1Gas, quoteL2Gas } from '../services/gas.ts';
import { quoteL1Gas, quoteL2Gas, fetchL1MarketFees } from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

Expand Down Expand Up @@ -70,8 +70,15 @@ export function routeErc20Base(): DepositRouteStrategy {
// TODO: proper error handling
if (!l2GasParams) throw new Error('Failed to estimate L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
const baseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2GasParams.gasLimit,
precomputedMarket: l1Market,
});
const mintValue = baseCost + ctx.operatorTip + p.amount;

// --- Approvals ---
Expand Down Expand Up @@ -135,6 +142,7 @@ export function routeErc20Base(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});
if (l1GasParams) {
l1TxCandidate.gasLimit = l1GasParams.gasLimit;
Expand Down
12 changes: 10 additions & 2 deletions src/adapters/ethers/resources/deposits/routes/erc20-nonbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { isETH } from '../../../../../core/utils/addr';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

import { quoteL2BaseCost } from '../services/fee.ts';
import { quoteL1Gas, determineErc20L2Gas } from '../services/gas.ts';
import { quoteL1Gas, determineErc20L2Gas, fetchL1MarketFees } from '../services/gas.ts';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';

// error handling
Expand Down Expand Up @@ -59,8 +59,15 @@ export function routeErc20NonBase(): DepositRouteStrategy {
// TODO: proper error handling with error envelope
if (!l2GasParams) throw new Error('Failed to establish L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
const baseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2GasParams.gasLimit,
precomputedMarket: l1Market,
});
const mintValue = baseCost + ctx.operatorTip;

// -- Approvals --
Expand Down Expand Up @@ -165,6 +172,7 @@ export function routeErc20NonBase(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});
if (l1GasParams) {
l1TxCandidate.gasLimit = l1GasParams.gasLimit;
Expand Down
12 changes: 10 additions & 2 deletions src/adapters/ethers/resources/deposits/routes/eth-nonbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { ApprovalNeed, PlanStep } from '../../../../../core/types/flows/bas
import { createErrorHandlers } from '../../../errors/error-ops';
import { OP_DEPOSITS } from '../../../../../core/types';
import { isETH } from '../../../../../core/utils/addr';
import { quoteL1Gas, quoteL2Gas } from '../services/gas.ts';
import { quoteL1Gas, quoteL2Gas, fetchL1MarketFees } from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';
Expand Down Expand Up @@ -87,8 +87,15 @@ export function routeEthNonBase(): DepositRouteStrategy {
});
if (!l2GasParams) throw new Error('Failed to estimate L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
const baseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2GasParams.gasLimit,
precomputedMarket: l1Market,
});
const mintValue = baseCost + ctx.operatorTip;

// --- Approvals ---
Expand Down Expand Up @@ -163,6 +170,7 @@ export function routeEthNonBase(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});
if (l1GasParams) {
l1TxCandidate.gasLimit = l1GasParams.gasLimit;
Expand Down
13 changes: 11 additions & 2 deletions src/adapters/ethers/resources/deposits/routes/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { buildDirectRequestStruct } from '../../utils';
import type { PlanStep } from '../../../../../core/types/flows/base';
import { ETH_ADDRESS } from '../../../../../core/constants.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { quoteL1Gas, quoteL2Gas } from '../services/gas.ts';
import { quoteL1Gas, quoteL2Gas, fetchL1MarketFees } from '../services/gas.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

// ETH deposit route via Bridgehub.requestL2TransactionDirect
Expand Down Expand Up @@ -43,8 +43,16 @@ export function routeEthDirect(): DepositRouteStrategy {
throw new Error('Failed to estimate L2 gas for deposit.');
}

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas to avoid
// redundant eth_feeHistory / eth_gasPrice RPC calls.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
const baseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2GasParams.gasLimit,
precomputedMarket: l1Market,
});
const mintValue = baseCost + ctx.operatorTip + p.amount;

const req = buildDirectRequestStruct({
Expand All @@ -71,6 +79,7 @@ export function routeEthDirect(): DepositRouteStrategy {
ctx,
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
precomputedMarket: l1Market,
});
if (l1GasParams) {
l1TxCandidate.gasLimit = l1GasParams.gasLimit;
Expand Down
6 changes: 5 additions & 1 deletion src/adapters/ethers/resources/deposits/services/fee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { BuildCtx } from '../context';
import {
quoteL2BaseCost as coreQuoteL2BaseCost,
type AbiEncoder,
type MarketFees,
} from '../../../../../core/resources/deposits/gas';
import { ethersToGasEstimator } from '../../../../ethers/estimator';
import { createErrorHandlers } from '../../../errors/error-ops';
Expand All @@ -14,6 +15,8 @@ const { wrapAs } = createErrorHandlers('deposits');
export type QuoteL2BaseCostInput = {
ctx: BuildCtx;
l2GasLimit: bigint;
/** Pre-fetched market fees to skip a redundant L1 fee RPC call. */
precomputedMarket?: MarketFees;
};

const encode: AbiEncoder = (abi, fn, args) => {
Expand All @@ -23,7 +26,7 @@ const encode: AbiEncoder = (abi, fn, args) => {
// Quotes the L2 base cost for a deposit transaction.
// Calls `l2TransactionBaseCost` on Bridgehub contract.
export async function quoteL2BaseCost(input: QuoteL2BaseCostInput): Promise<bigint> {
const { ctx, l2GasLimit } = input;
const { ctx, l2GasLimit, precomputedMarket } = input;
const estimator = ethersToGasEstimator(ctx.client.l1);

return wrapAs(
Expand All @@ -37,6 +40,7 @@ export async function quoteL2BaseCost(input: QuoteL2BaseCostInput): Promise<bigi
chainIdL2: ctx.chainIdL2,
l2GasLimit,
gasPerPubdata: ctx.gasPerPubdata,
precomputedMarket,
}),
{ ctx: { chainIdL2: ctx.chainIdL2 } },
);
Expand Down
17 changes: 15 additions & 2 deletions src/adapters/ethers/resources/deposits/services/gas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ import type { Address } from '../../../../../core/types/primitives';
import {
quoteL1Gas as coreQuoteL1Gas,
quoteL2Gas as coreQuoteL2Gas,
fetchFees,
type GasQuote,
type MarketFees,
} from '../../../../../core/resources/deposits/gas';
import { ethersToGasEstimator, toCoreTx } from '../../../../ethers/estimator';

export type { GasQuote };
export type { GasQuote, MarketFees };

export type QuoteL1GasInput = {
ctx: BuildCtx;
tx: TransactionRequest;
overrides?: TxGasOverrides;
fallbackGasLimit?: bigint;
/** Pre-fetched market fees to skip a redundant L1 fee RPC call. */
precomputedMarket?: MarketFees;
};

export type QuoteL2GasInput = {
Expand All @@ -38,18 +42,27 @@ export type ResolveErc20L2GasLimitInput = {
/* Public API */
/* -------------------------------------------------------------------------- */

/**
* Fetch L1 market fees once so callers can share them across multiple quote functions.
* Avoids redundant RPC calls when both quoteL2BaseCost and quoteL1Gas are called.
*/
export async function fetchL1MarketFees(ctx: BuildCtx): Promise<MarketFees> {
return fetchFees(ethersToGasEstimator(ctx.client.l1));
}

/**
* Quote L1 gas for a deposit transaction.
*/
export async function quoteL1Gas(input: QuoteL1GasInput): Promise<GasQuote | undefined> {
const { ctx, tx, overrides, fallbackGasLimit } = input;
const { ctx, tx, overrides, fallbackGasLimit, precomputedMarket } = input;
const estimator = ethersToGasEstimator(ctx.client.l1);

return coreQuoteL1Gas({
estimator,
tx: toCoreTx(tx),
overrides,
fallbackGasLimit,
precomputedMarket,
});
}

Expand Down
23 changes: 2 additions & 21 deletions src/adapters/ethers/resources/withdrawals/services/finalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,32 +107,13 @@ export function createFinalizationServices(client: EthersClient): FinalizationSe
},
);

// Fetch raw receipt again
const raw = await wrapAs(
'RPC',
OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
() => client.zks.getReceiptWithL2ToL1(l2TxHash),
{
ctx: { where: 'getReceiptWithL2ToL1 (raw)', l2TxHash },
message: 'Failed to fetch raw L2 receipt.',
},
);
if (!raw) {
throw createError('STATE', {
resource: 'withdrawals',
operation: OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
message: 'Raw L2 receipt not found.',
context: { l2TxHash },
});
}

const idx = await wrapAs(
'INTERNAL',
OP_WITHDRAWALS.finalize.fetchParams.messengerIndex,
() =>
Promise.resolve(messengerLogIndex(raw, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
Promise.resolve(messengerLogIndex(parsed, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
{
ctx: { where: 'derive messenger log index', l2TxHash, receipt: raw },
ctx: { where: 'derive messenger log index', l2TxHash, receipt: parsed },
message: 'Failed to derive messenger log index.',
},
);
Expand Down
12 changes: 10 additions & 2 deletions src/adapters/viem/resources/deposits/routes/erc20-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { OP_DEPOSITS } from '../../../../../core/types';
import { normalizeAddrEq, isETH } from '../../../../../core/utils/addr';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';

import { quoteL2Gas, quoteL1Gas } from '../services/gas.ts';
import { quoteL2Gas, quoteL1Gas, fetchL1MarketFees, marketToGasPrice } from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

Expand Down Expand Up @@ -66,8 +66,15 @@ export function routeErc20Base(): DepositRouteStrategy {

if (!l2Gas) throw new Error('Failed to estimate L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
const l2BaseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2Gas.gasLimit,
precomputedGasPrice: marketToGasPrice(l1Market),
});
const mintValue = l2BaseCost + ctx.operatorTip + p.amount;

// -- Approvals --
Expand Down Expand Up @@ -188,6 +195,7 @@ export function routeErc20Base(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});

if (l1Gas) {
Expand Down
17 changes: 15 additions & 2 deletions src/adapters/viem/resources/deposits/routes/erc20-nonbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import { OP_DEPOSITS } from '../../../../../core/types';
import { isETH, normalizeAddrEq } from '../../../../../core/utils/addr';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';

import { quoteL1Gas, determineErc20L2Gas } from '../services/gas.ts';
import {
quoteL1Gas,
determineErc20L2Gas,
fetchL1MarketFees,
marketToGasPrice,
} from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

Expand Down Expand Up @@ -69,8 +74,15 @@ export function routeErc20NonBase(): DepositRouteStrategy {

if (!l2Gas) throw new Error('Failed to establish L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
const l2BaseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2Gas.gasLimit,
precomputedGasPrice: marketToGasPrice(l1Market),
});
const mintValue = l2BaseCost + ctx.operatorTip;

// -- Approvals --
Expand Down Expand Up @@ -212,6 +224,7 @@ export function routeErc20NonBase(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});

const approvalsNeeded = approvals.length > 0;
Expand Down
12 changes: 10 additions & 2 deletions src/adapters/viem/resources/deposits/routes/eth-nonbase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { OP_DEPOSITS } from '../../../../../core/types';
import { isETH } from '../../../../../core/utils/addr';
import { SAFE_L1_BRIDGE_GAS } from '../../../../../core/constants.ts';

import { quoteL2Gas, quoteL1Gas } from '../services/gas.ts';
import { quoteL2Gas, quoteL1Gas, fetchL1MarketFees, marketToGasPrice } from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';

Expand Down Expand Up @@ -84,8 +84,15 @@ export function routeEthNonBase(): DepositRouteStrategy {

if (!l2Gas) throw new Error('Failed to estimate L2 gas parameters.');

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
const l2BaseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2Gas.gasLimit,
precomputedGasPrice: marketToGasPrice(l1Market),
});
const mintValue = l2BaseCost + ctx.operatorTip;

// -- Approvals --
Expand Down Expand Up @@ -222,6 +229,7 @@ export function routeEthNonBase(): DepositRouteStrategy {
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
fallbackGasLimit: SAFE_L1_BRIDGE_GAS,
precomputedMarket: l1Market,
});

if (l1Gas) {
Expand Down
13 changes: 11 additions & 2 deletions src/adapters/viem/resources/deposits/routes/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { buildDirectRequestStruct } from '../../utils';
import { IBridgehubABI } from '../../../../../core/abi.ts';
import { createErrorHandlers } from '../../../errors/error-ops';
import { OP_DEPOSITS } from '../../../../../core/types';
import { quoteL2Gas, quoteL1Gas } from '../services/gas.ts';
import { quoteL2Gas, quoteL1Gas, fetchL1MarketFees, marketToGasPrice } from '../services/gas.ts';
import { quoteL2BaseCost } from '../services/fee.ts';
import { ETH_ADDRESS } from '../../../../../core/constants.ts';
import { buildFeeBreakdown } from '../../../../../core/resources/deposits/fee.ts';
Expand Down Expand Up @@ -43,8 +43,16 @@ export function routeEthDirect(): DepositRouteStrategy {
throw new Error('Failed to estimate L2 gas for deposit.');
}

// Pre-fetch L1 market fees once; reused by quoteL2BaseCost and quoteL1Gas to avoid
// redundant eth_feeHistory / eth_gasPrice RPC calls.
const l1Market = await fetchL1MarketFees(ctx);

// L2TransactionBase cost
const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
const baseCost = await quoteL2BaseCost({
ctx,
l2GasLimit: l2GasParams.gasLimit,
precomputedGasPrice: marketToGasPrice(l1Market),
});

const l2Contract = p.to ?? ctx.sender;
const l2Value = p.amount;
Expand Down Expand Up @@ -97,6 +105,7 @@ export function routeEthDirect(): DepositRouteStrategy {
ctx,
tx: l1TxCandidate,
overrides: ctx.gasOverrides,
precomputedMarket: l1Market,
});

const steps: PlanStep<ViemPlanWriteRequest>[] = [
Expand Down
Loading