@matterlabs/zksync-js 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +12 -12
  2. package/dist/adapters/ethers/client.cjs +642 -1
  3. package/dist/adapters/ethers/client.cjs.map +1 -1
  4. package/dist/adapters/ethers/client.js +6 -5
  5. package/dist/adapters/ethers/estimator.d.ts +4 -0
  6. package/dist/adapters/ethers/index.cjs +934 -801
  7. package/dist/adapters/ethers/index.cjs.map +1 -1
  8. package/dist/adapters/ethers/index.js +9 -8
  9. package/dist/adapters/ethers/resources/deposits/context.d.ts +5 -5
  10. package/dist/adapters/ethers/resources/deposits/routes/types.d.ts +2 -6
  11. package/dist/adapters/ethers/resources/deposits/services/fee.d.ts +6 -0
  12. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +40 -0
  13. package/dist/adapters/ethers/resources/utils.d.ts +4 -15
  14. package/dist/adapters/ethers/resources/withdrawals/context.d.ts +4 -4
  15. package/dist/adapters/ethers/resources/withdrawals/routes/types.d.ts +2 -2
  16. package/dist/adapters/ethers/resources/withdrawals/services/fees.d.ts +14 -0
  17. package/dist/adapters/ethers/resources/withdrawals/services/gas.d.ts +12 -0
  18. package/dist/adapters/ethers/sdk.cjs +947 -1292
  19. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  20. package/dist/adapters/ethers/sdk.js +7 -6
  21. package/dist/adapters/viem/client.cjs.map +1 -1
  22. package/dist/adapters/viem/client.d.ts +1 -1
  23. package/dist/adapters/viem/client.js +4 -5
  24. package/dist/adapters/viem/estimator.d.ts +4 -0
  25. package/dist/adapters/viem/index.cjs +944 -662
  26. package/dist/adapters/viem/index.cjs.map +1 -1
  27. package/dist/adapters/viem/index.js +8 -8
  28. package/dist/adapters/viem/resources/deposits/context.d.ts +5 -5
  29. package/dist/adapters/viem/resources/deposits/routes/types.d.ts +2 -6
  30. package/dist/adapters/viem/resources/deposits/services/fee.d.ts +6 -0
  31. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +36 -0
  32. package/dist/adapters/viem/resources/utils.d.ts +3 -16
  33. package/dist/adapters/viem/resources/withdrawals/context.d.ts +3 -6
  34. package/dist/adapters/viem/resources/withdrawals/routes/types.d.ts +12 -2
  35. package/dist/adapters/viem/resources/withdrawals/services/fee.d.ts +17 -0
  36. package/dist/adapters/viem/resources/withdrawals/services/gas.d.ts +12 -0
  37. package/dist/adapters/viem/sdk.cjs +877 -563
  38. package/dist/adapters/viem/sdk.cjs.map +1 -1
  39. package/dist/adapters/viem/sdk.d.ts +1 -1
  40. package/dist/adapters/viem/sdk.js +6 -6
  41. package/dist/{chunk-3LALBFFE.js → chunk-3MRGU4HV.js} +9 -5
  42. package/dist/{chunk-4HLJJKIY.js → chunk-6K6VJQAL.js} +2 -2
  43. package/dist/{chunk-CGO27P7F.js → chunk-BCCKWWOX.js} +540 -741
  44. package/dist/{chunk-6GCT6TLS.js → chunk-F2ENUV3A.js} +13 -1
  45. package/dist/{chunk-DI2CJDPZ.js → chunk-HLUANWGN.js} +2 -2
  46. package/dist/{chunk-Y75OMFK6.js → chunk-M5J2MM2U.js} +351 -1
  47. package/dist/{chunk-263G6636.js → chunk-NCAIVYBR.js} +1 -14
  48. package/dist/{chunk-7M4V3FMT.js → chunk-OC6ZVLSP.js} +669 -559
  49. package/dist/chunk-QJS6ETEE.js +217 -0
  50. package/dist/chunk-XRE7H466.js +157 -0
  51. package/dist/{chunk-BD2LUO5T.js → chunk-YUK547UF.js} +3 -3
  52. package/dist/core/abi.d.ts +9 -0
  53. package/dist/core/adapters/interfaces.d.ts +25 -0
  54. package/dist/core/constants.cjs +12 -0
  55. package/dist/core/constants.cjs.map +1 -1
  56. package/dist/core/constants.d.ts +6 -0
  57. package/dist/core/constants.js +1 -1
  58. package/dist/core/index.cjs +4504 -1
  59. package/dist/core/index.cjs.map +1 -1
  60. package/dist/core/index.d.ts +1 -0
  61. package/dist/core/index.js +4 -4
  62. package/dist/core/resources/deposits/fee.d.ts +15 -0
  63. package/dist/core/resources/deposits/gas.d.ts +38 -0
  64. package/dist/core/resources/withdrawals/gas.d.ts +14 -0
  65. package/dist/core/types/errors.d.ts +1 -1
  66. package/dist/core/types/fees.d.ts +40 -0
  67. package/dist/core/types/flows/base.d.ts +0 -10
  68. package/dist/core/types/flows/deposits.d.ts +20 -6
  69. package/dist/core/types/flows/route.d.ts +2 -3
  70. package/dist/core/types/flows/withdrawals.d.ts +12 -6
  71. package/dist/index.cjs +4516 -1
  72. package/dist/index.cjs.map +1 -1
  73. package/dist/index.d.ts +1 -0
  74. package/dist/index.js +4 -4
  75. package/package.json +5 -1
  76. package/dist/adapters/ethers/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  77. package/dist/adapters/viem/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  78. package/dist/chunk-B77GWPO5.js +0 -339
  79. package/dist/core/internal/abi-registry.d.ts +0 -9
  80. package/dist/core/utils/gas.d.ts +0 -13
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var viem = require('viem');
4
3
  var sha3 = require('@noble/hashes/sha3');
5
4
  var utils = require('@noble/hashes/utils');
5
+ var viem = require('viem');
6
6
 
7
7
  // src/core/internal/abis/IBridgehub.ts
8
8
  var IBridgehubABI = [
@@ -3656,139 +3656,12 @@ var TOPIC_L1_MESSAGE_SENT_NEW = k256hex("L1MessageSent(uint256,bytes32,bytes)");
3656
3656
  var TOPIC_L1_MESSAGE_SENT_LEG = k256hex("L1MessageSent(address,bytes32,bytes)");
3657
3657
  var TOPIC_CANONICAL_ASSIGNED = "0x779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df";
3658
3658
  var TOPIC_CANONICAL_SUCCESS = "0xe4def01b981193a97a9e81230d7b9f31812ceaf23f864a828a82c687911cb2df";
3659
-
3660
- // src/core/utils/gas.ts
3661
- function assertNoLegacyGas(overrides) {
3662
- if (!overrides) return;
3663
- if ("gasPrice" in overrides && overrides.gasPrice !== void 0) {
3664
- throw new Error("Legacy gasPrice is not supported; use EIP-1559 fields instead.");
3665
- }
3666
- }
3667
- function assertPriorityFeeBounds(fees) {
3668
- if (fees.maxPriorityFeePerGas > fees.maxFeePerGas) {
3669
- throw new Error("maxPriorityFeePerGas cannot exceed maxFeePerGas.");
3670
- }
3671
- }
3672
-
3673
- // src/adapters/viem/resources/utils.ts
3674
- function encodeNativeTokenVaultAssetId(chainId, address) {
3675
- const encoded = viem.encodeAbiParameters(
3676
- [
3677
- { type: "uint256", name: "originChainId" },
3678
- { type: "address", name: "ntv" },
3679
- { type: "address", name: "token" }
3680
- ],
3681
- [chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, address]
3682
- );
3683
- return viem.keccak256(encoded);
3684
- }
3685
- async function getFeeOverrides(client, overrides) {
3686
- assertNoLegacyGas(overrides);
3687
- let maxFeePerGasFromProvider;
3688
- let maxPriorityFromProvider;
3689
- let gasPriceFromProvider;
3690
- try {
3691
- const fees = await client.l1.estimateFeesPerGas();
3692
- const { maxFeePerGas: maxFeePerGas2, maxPriorityFeePerGas: maxPriorityFeePerGas2 } = fees;
3693
- if (maxFeePerGas2 != null && maxPriorityFeePerGas2 != null) {
3694
- maxFeePerGasFromProvider = maxFeePerGas2;
3695
- maxPriorityFromProvider = maxPriorityFeePerGas2;
3696
- gasPriceFromProvider = fees.gasPrice ?? maxFeePerGas2;
3697
- } else if (fees.gasPrice != null) {
3698
- gasPriceFromProvider = fees.gasPrice;
3699
- }
3700
- } catch {
3701
- }
3702
- if (gasPriceFromProvider == null) {
3703
- try {
3704
- gasPriceFromProvider = await client.l1.getGasPrice();
3705
- } catch {
3706
- }
3707
- }
3708
- const maxFeePerGas = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider;
3709
- if (maxFeePerGas == null) {
3710
- throw new Error("L1 provider returned no gas price data");
3711
- }
3712
- const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? maxPriorityFromProvider ?? maxFeePerGas;
3713
- assertPriorityFeeBounds({ maxFeePerGas, maxPriorityFeePerGas });
3714
- const gasPriceForBaseCost = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider ?? maxFeePerGas;
3715
- return {
3716
- gasLimit: overrides?.gasLimit,
3717
- maxFeePerGas,
3718
- maxPriorityFeePerGas,
3719
- gasPriceForBaseCost
3720
- };
3721
- }
3722
- async function getL2FeeOverrides(client, overrides) {
3723
- assertNoLegacyGas(overrides);
3724
- let maxFeePerGasFromProvider;
3725
- let maxPriorityFromProvider;
3726
- let gasPriceFromProvider;
3727
- try {
3728
- const fees = await client.l2.estimateFeesPerGas();
3729
- if (fees?.maxFeePerGas != null && fees.maxPriorityFeePerGas != null) {
3730
- maxFeePerGasFromProvider = fees.maxFeePerGas;
3731
- maxPriorityFromProvider = fees.maxPriorityFeePerGas;
3732
- gasPriceFromProvider = fees.gasPrice ?? fees.maxFeePerGas;
3733
- } else if (fees?.gasPrice != null) {
3734
- gasPriceFromProvider = fees.gasPrice;
3735
- }
3736
- } catch {
3737
- }
3738
- if (gasPriceFromProvider == null) {
3739
- try {
3740
- gasPriceFromProvider = await client.l2.getGasPrice();
3741
- } catch {
3742
- }
3743
- }
3744
- const maxFeePerGas = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider;
3745
- if (maxFeePerGas == null) {
3746
- throw new Error("provider returned no gas price data");
3747
- }
3748
- const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? maxPriorityFromProvider ?? maxFeePerGas;
3749
- assertPriorityFeeBounds({ maxFeePerGas, maxPriorityFeePerGas });
3750
- return {
3751
- gasLimit: overrides?.gasLimit,
3752
- maxFeePerGas,
3753
- maxPriorityFeePerGas
3754
- };
3755
- }
3756
- function buildViemFeeOverrides(fees) {
3757
- return {
3758
- maxFeePerGas: fees.maxFeePerGas,
3759
- maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
3760
- gas: fees.gasLimit
3761
- };
3762
- }
3763
- function buildDirectRequestStruct(args) {
3764
- return {
3765
- chainId: args.chainId,
3766
- l2Contract: args.l2Contract,
3767
- mintValue: args.mintValue,
3768
- l2Value: args.l2Value,
3769
- l2Calldata: "0x",
3770
- l2GasLimit: args.l2GasLimit,
3771
- l2GasPerPubdataByteLimit: args.gasPerPubdata,
3772
- factoryDeps: [],
3773
- refundRecipient: args.refundRecipient
3774
- };
3775
- }
3776
- function encodeSecondBridgeArgs(token, amount, l2Receiver) {
3777
- return viem.encodeAbiParameters(
3778
- [
3779
- { type: "address", name: "token" },
3780
- { type: "uint256", name: "amount" },
3781
- { type: "address", name: "l2Receiver" }
3782
- ],
3783
- [token, amount, l2Receiver]
3784
- );
3785
- }
3786
- function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
3787
- return encodeSecondBridgeArgs(token, amount, l2Receiver);
3788
- }
3789
- function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
3790
- return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
3791
- }
3659
+ var BUFFER = 20n;
3660
+ var TX_OVERHEAD_GAS = 10000n;
3661
+ var TX_MEMORY_OVERHEAD_GAS = 10n;
3662
+ var DEFAULT_PUBDATA_BYTES = 155n;
3663
+ var DEFAULT_ABI_BYTES = 400n;
3664
+ var SAFE_L1_BRIDGE_GAS = 600000n;
3792
3665
 
3793
3666
  // src/core/utils/addr.ts
3794
3667
  var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
@@ -3823,8 +3696,6 @@ async function commonCtx(p, client) {
3823
3696
  const { bridgehub, l1AssetRouter } = await client.ensureAddresses();
3824
3697
  const chainId = await client.l2.getChainId();
3825
3698
  const sender = client.account.address;
3826
- const fee = await getFeeOverrides(client, p.l1TxOverrides);
3827
- const l2GasLimit = p.l2GasLimit ?? 300000n;
3828
3699
  const gasPerPubdata = p.gasPerPubdata ?? 800n;
3829
3700
  const operatorTip = p.operatorTip ?? 0n;
3830
3701
  const refundRecipient = p.refundRecipient ?? sender;
@@ -3836,13 +3707,53 @@ async function commonCtx(p, client) {
3836
3707
  bridgehub,
3837
3708
  chainIdL2: BigInt(chainId),
3838
3709
  sender,
3839
- fee,
3840
- l2GasLimit,
3710
+ gasOverrides: p.l1TxOverrides,
3711
+ l2GasLimit: p.l2GasLimit,
3841
3712
  gasPerPubdata,
3842
3713
  operatorTip,
3843
3714
  refundRecipient
3844
3715
  };
3845
3716
  }
3717
+ function encodeNativeTokenVaultAssetId(chainId, address) {
3718
+ const encoded = viem.encodeAbiParameters(
3719
+ [
3720
+ { type: "uint256", name: "originChainId" },
3721
+ { type: "address", name: "ntv" },
3722
+ { type: "address", name: "token" }
3723
+ ],
3724
+ [chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, address]
3725
+ );
3726
+ return viem.keccak256(encoded);
3727
+ }
3728
+ function encodeSecondBridgeArgs(token, amount, l2Receiver) {
3729
+ return viem.encodeAbiParameters(
3730
+ [
3731
+ { type: "address", name: "token" },
3732
+ { type: "uint256", name: "amount" },
3733
+ { type: "address", name: "l2Receiver" }
3734
+ ],
3735
+ [token, amount, l2Receiver]
3736
+ );
3737
+ }
3738
+ function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
3739
+ return encodeSecondBridgeArgs(token, amount, l2Receiver);
3740
+ }
3741
+ function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
3742
+ return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
3743
+ }
3744
+ function buildDirectRequestStruct(args) {
3745
+ return {
3746
+ chainId: args.chainId,
3747
+ l2Contract: args.l2Contract,
3748
+ mintValue: args.mintValue,
3749
+ l2Value: args.l2Value,
3750
+ l2Calldata: "0x",
3751
+ l2GasLimit: args.l2GasLimit,
3752
+ l2GasPerPubdataByteLimit: args.gasPerPubdata,
3753
+ factoryDeps: [],
3754
+ refundRecipient: args.refundRecipient
3755
+ };
3756
+ }
3846
3757
 
3847
3758
  // src/core/errors/formatter.ts
3848
3759
  function elideMiddle(s, max = 96) {
@@ -4055,27 +3966,21 @@ var OP_DEPOSITS = {
4055
3966
  base: {
4056
3967
  assertErc20Asset: "deposits.erc20-base:assertErc20Asset",
4057
3968
  assertMatchesBase: "deposits.erc20-base:assertMatchesBase",
4058
- baseToken: "deposits.erc20-base:baseToken",
4059
3969
  allowance: "deposits.erc20-base:allowance",
4060
- baseCost: "deposits.erc20-base:l2TransactionBaseCost",
4061
3970
  estGas: "deposits.erc20-base:estimateGas"
4062
3971
  },
4063
3972
  nonbase: {
4064
- baseToken: "deposits.erc20-nonbase:baseToken",
4065
3973
  assertNotEthAsset: "deposits.erc20-nonbase:assertNotEthAsset",
4066
- allowance: "deposits.erc20-nonbase:allowance",
4067
- allowanceFees: "deposits.erc20-nonbase:allowanceFeesBaseToken",
4068
- baseCost: "deposits.erc20-nonbase:l2TransactionBaseCost",
4069
3974
  encodeCalldata: "deposits.erc20-nonbase:encodeSecondBridgeErc20Args",
4070
3975
  estGas: "deposits.erc20-nonbase:estimateGas",
4071
- assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken"},
3976
+ assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken",
3977
+ allowanceToken: "deposits.erc20-nonbase:allowanceToken",
3978
+ allowanceBase: "deposits.erc20-nonbase:allowanceBase"
3979
+ },
4072
3980
  eth: {
4073
- baseCost: "deposits.eth:l2TransactionBaseCost",
4074
3981
  estGas: "deposits.eth:estimateGas"
4075
3982
  },
4076
3983
  ethNonBase: {
4077
- baseToken: "deposits.eth-nonbase:baseToken",
4078
- baseCost: "deposits.eth-nonbase:l2TransactionBaseCost",
4079
3984
  allowanceBase: "deposits.eth-nonbase:allowanceBaseToken",
4080
3985
  ethBalance: "deposits.eth-nonbase:getEthBalance",
4081
3986
  encodeCalldata: "deposits.eth-nonbase:encodeSecondBridgeEthArgs",
@@ -4337,41 +4242,361 @@ function createErrorHandlers(resource) {
4337
4242
  return { wrap: wrap2, wrapAs: wrapAs9, toResult: toResult2 };
4338
4243
  }
4339
4244
 
4340
- // src/adapters/viem/resources/deposits/routes/eth.ts
4245
+ // src/core/resources/deposits/gas.ts
4246
+ function makeGasQuote(p) {
4247
+ const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
4248
+ return {
4249
+ gasLimit: p.gasLimit,
4250
+ maxFeePerGas: p.maxFeePerGas,
4251
+ maxPriorityFeePerGas,
4252
+ gasPerPubdata: p.gasPerPubdata,
4253
+ maxCost: p.gasLimit * p.maxFeePerGas
4254
+ };
4255
+ }
4256
+ async function fetchFees(estimator) {
4257
+ try {
4258
+ const fees = await estimator.estimateFeesPerGas();
4259
+ if (fees.maxFeePerGas != null) {
4260
+ return {
4261
+ maxFeePerGas: fees.maxFeePerGas,
4262
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
4263
+ };
4264
+ }
4265
+ if (fees.gasPrice != null) {
4266
+ return {
4267
+ maxFeePerGas: fees.gasPrice,
4268
+ maxPriorityFeePerGas: 0n
4269
+ };
4270
+ }
4271
+ } catch {
4272
+ }
4273
+ try {
4274
+ const gp = await estimator.getGasPrice();
4275
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
4276
+ } catch {
4277
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
4278
+ }
4279
+ }
4280
+ async function quoteL1Gas(input) {
4281
+ const { estimator, tx, overrides, fallbackGasLimit } = input;
4282
+ let market;
4283
+ const getMarket = async () => {
4284
+ if (market) return market;
4285
+ market = await fetchFees(estimator);
4286
+ return market;
4287
+ };
4288
+ const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
4289
+ const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
4290
+ const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
4291
+ if (explicitGasLimit != null) {
4292
+ return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4293
+ }
4294
+ try {
4295
+ const est = await estimator.estimateGas(tx);
4296
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
4297
+ return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
4298
+ } catch (err) {
4299
+ if (fallbackGasLimit != null) {
4300
+ return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4301
+ }
4302
+ console.warn("L1 gas estimation failed", err);
4303
+ return void 0;
4304
+ }
4305
+ }
4306
+ async function quoteL2Gas(input) {
4307
+ const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
4308
+ const market = await fetchFees(estimator);
4309
+ const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
4310
+ const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
4311
+ const explicit = overrideGasLimit ?? txGasLimit;
4312
+ if (explicit != null) {
4313
+ return makeGasQuote({
4314
+ gasLimit: explicit,
4315
+ maxFeePerGas,
4316
+ gasPerPubdata
4317
+ });
4318
+ }
4319
+ if (!tx) {
4320
+ return makeGasQuote({
4321
+ gasLimit: l2GasLimit ?? 0n,
4322
+ maxFeePerGas,
4323
+ gasPerPubdata
4324
+ });
4325
+ }
4326
+ try {
4327
+ const execEstimate = await estimator.estimateGas(tx, stateOverrides);
4328
+ const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
4329
+ const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
4330
+ const pp = gasPerPubdata ?? 800n;
4331
+ const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
4332
+ const pubdataOverhead = pubdataBytes * pp;
4333
+ let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
4334
+ total = total * (100n + BUFFER) / 100n;
4335
+ return makeGasQuote({
4336
+ gasLimit: total,
4337
+ maxFeePerGas,
4338
+ gasPerPubdata: pp
4339
+ });
4340
+ } catch (err) {
4341
+ console.warn("L2 gas estimation failed", err);
4342
+ return makeGasQuote({
4343
+ gasLimit: l2GasLimit ?? 0n,
4344
+ maxFeePerGas,
4345
+ gasPerPubdata
4346
+ });
4347
+ }
4348
+ }
4349
+
4350
+ // src/adapters/viem/estimator.ts
4351
+ function toCoreTx(tx) {
4352
+ return {
4353
+ to: tx.to,
4354
+ from: tx.from,
4355
+ data: tx.data,
4356
+ value: tx.value,
4357
+ gasLimit: tx.gas,
4358
+ maxFeePerGas: tx.maxFeePerGas,
4359
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4360
+ };
4361
+ }
4362
+ function viemToGasEstimator(client) {
4363
+ return {
4364
+ async estimateGas(tx, stateOverrides) {
4365
+ if (stateOverrides) {
4366
+ try {
4367
+ const result = await client.request({
4368
+ method: "eth_estimateGas",
4369
+ params: [
4370
+ {
4371
+ from: tx.from,
4372
+ to: tx.to,
4373
+ data: tx.data,
4374
+ value: tx.value,
4375
+ gas: tx.gasLimit,
4376
+ maxFeePerGas: tx.maxFeePerGas,
4377
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4378
+ },
4379
+ "latest",
4380
+ stateOverrides
4381
+ ]
4382
+ });
4383
+ return BigInt(result);
4384
+ } catch (error) {
4385
+ console.warn(
4386
+ "Failed to estimate gas with state overrides, falling back to standard estimation:",
4387
+ error
4388
+ );
4389
+ }
4390
+ }
4391
+ return await client.estimateGas({
4392
+ account: tx.from,
4393
+ to: tx.to,
4394
+ data: tx.data,
4395
+ value: tx.value,
4396
+ gas: tx.gasLimit,
4397
+ maxFeePerGas: tx.maxFeePerGas,
4398
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4399
+ });
4400
+ },
4401
+ async estimateFeesPerGas() {
4402
+ try {
4403
+ const fees = await client.estimateFeesPerGas();
4404
+ return {
4405
+ maxFeePerGas: fees.maxFeePerGas,
4406
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas
4407
+ };
4408
+ } catch {
4409
+ }
4410
+ try {
4411
+ const gp = await client.getGasPrice();
4412
+ return { gasPrice: gp };
4413
+ } catch {
4414
+ return {};
4415
+ }
4416
+ },
4417
+ async getGasPrice() {
4418
+ return await client.getGasPrice();
4419
+ },
4420
+ async call(tx) {
4421
+ const res = await client.call({
4422
+ to: tx.to,
4423
+ data: tx.data,
4424
+ value: tx.value,
4425
+ account: tx.from
4426
+ });
4427
+ return res.data ?? "0x";
4428
+ }
4429
+ };
4430
+ }
4431
+
4432
+ // src/adapters/viem/resources/deposits/services/gas.ts
4433
+ async function quoteL1Gas2(input) {
4434
+ const { ctx, tx, overrides, fallbackGasLimit } = input;
4435
+ const estimator = viemToGasEstimator(ctx.client.l1);
4436
+ return quoteL1Gas({
4437
+ estimator,
4438
+ tx: toCoreTx(tx),
4439
+ overrides,
4440
+ fallbackGasLimit
4441
+ });
4442
+ }
4443
+ async function quoteL2Gas2(input) {
4444
+ const { ctx, route, l2TxForModeling, overrideGasLimit } = input;
4445
+ const estimator = viemToGasEstimator(ctx.client.l2);
4446
+ return quoteL2Gas({
4447
+ estimator,
4448
+ route,
4449
+ tx: l2TxForModeling ? toCoreTx(l2TxForModeling) : void 0,
4450
+ gasPerPubdata: ctx.gasPerPubdata,
4451
+ l2GasLimit: ctx.l2GasLimit,
4452
+ // TODO: investigate if this should be passed here; weird viem quirk
4453
+ overrideGasLimit,
4454
+ stateOverrides: input.stateOverrides
4455
+ });
4456
+ }
4457
+ async function determineErc20L2Gas(input) {
4458
+ const { ctx, l1Token } = input;
4459
+ const DEFAULT_SAFE_L2_GAS_LIMIT = 3000000n;
4460
+ if (ctx.l2GasLimit != null) {
4461
+ return quoteL2Gas2({
4462
+ ctx,
4463
+ route: "erc20-nonbase",
4464
+ overrideGasLimit: ctx.l2GasLimit
4465
+ });
4466
+ }
4467
+ try {
4468
+ const l2NativeTokenVault = (await ctx.client.contracts()).l2NativeTokenVault;
4469
+ const l2TokenAddress = await ctx.client.l2.readContract({
4470
+ address: l2NativeTokenVault.address,
4471
+ abi: l2NativeTokenVault.abi,
4472
+ functionName: "l2TokenAddress",
4473
+ args: [l1Token]
4474
+ });
4475
+ if (l2TokenAddress === viem.zeroAddress) {
4476
+ return quoteL2Gas2({
4477
+ ctx,
4478
+ route: "erc20-nonbase",
4479
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4480
+ });
4481
+ }
4482
+ const modelTx = {
4483
+ to: input.modelTx?.to ?? ctx.sender,
4484
+ from: input.modelTx?.from ?? ctx.sender,
4485
+ data: input.modelTx?.data ?? "0x",
4486
+ value: input.modelTx?.value ?? 0n
4487
+ };
4488
+ const gas = await quoteL2Gas2({
4489
+ ctx,
4490
+ route: "erc20-nonbase",
4491
+ l2TxForModeling: modelTx
4492
+ });
4493
+ if (!gas) {
4494
+ return quoteL2Gas2({
4495
+ ctx,
4496
+ route: "erc20-nonbase",
4497
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4498
+ });
4499
+ }
4500
+ return gas;
4501
+ } catch (err) {
4502
+ console.warn("Failed to determine ERC20 L2 gas; defaulting to safe gas limit.", err);
4503
+ return quoteL2Gas2({
4504
+ ctx,
4505
+ route: "erc20-nonbase",
4506
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4507
+ });
4508
+ }
4509
+ }
4510
+
4511
+ // src/adapters/viem/resources/deposits/services/fee.ts
4341
4512
  var { wrapAs } = createErrorHandlers("deposits");
4513
+ async function quoteL2BaseCost(input) {
4514
+ const { ctx, l2GasLimit } = input;
4515
+ const estimator = viemToGasEstimator(ctx.client.l1);
4516
+ const fees = await estimator.estimateFeesPerGas();
4517
+ const gasPrice = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
4518
+ return wrapAs(
4519
+ "RPC",
4520
+ "deposits.fees.l2BaseCost",
4521
+ async () => {
4522
+ return await ctx.client.l1.readContract({
4523
+ address: ctx.bridgehub,
4524
+ abi: IBridgehub_default,
4525
+ functionName: "l2TransactionBaseCost",
4526
+ args: [ctx.chainIdL2, gasPrice, l2GasLimit, ctx.gasPerPubdata]
4527
+ });
4528
+ },
4529
+ { ctx: { chainIdL2: ctx.chainIdL2 } }
4530
+ );
4531
+ }
4532
+
4533
+ // src/core/resources/deposits/fee.ts
4534
+ function buildFeeBreakdown(p) {
4535
+ const l1MaxTotal = p.l1Gas?.maxCost ?? 0n;
4536
+ const l2Total = p.l2BaseCost + p.operatorTip;
4537
+ const l1 = {
4538
+ gasLimit: p.l1Gas?.gasLimit ?? 0n,
4539
+ maxFeePerGas: p.l1Gas?.maxFeePerGas ?? 0n,
4540
+ maxPriorityFeePerGas: p.l1Gas?.maxPriorityFeePerGas,
4541
+ maxTotal: l1MaxTotal
4542
+ };
4543
+ const l2 = {
4544
+ total: l2Total,
4545
+ baseCost: p.l2BaseCost,
4546
+ operatorTip: p.operatorTip,
4547
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
4548
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
4549
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas,
4550
+ gasPerPubdata: p.l2Gas?.gasPerPubdata ?? 0n
4551
+ };
4552
+ return {
4553
+ token: p.feeToken,
4554
+ maxTotal: l1MaxTotal + l2Total,
4555
+ mintValue: p.mintValue,
4556
+ l1,
4557
+ l2
4558
+ };
4559
+ }
4560
+
4561
+ // src/adapters/viem/resources/deposits/routes/eth.ts
4562
+ var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
4342
4563
  function routeEthDirect() {
4343
4564
  return {
4344
4565
  async build(p, ctx) {
4345
- const { gasPriceForBaseCost } = ctx.fee;
4346
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
4347
- const rawBaseCost = await wrapAs(
4348
- "CONTRACT",
4349
- OP_DEPOSITS.eth.baseCost,
4350
- () => ctx.client.l1.readContract({
4351
- address: ctx.bridgehub,
4352
- abi: IBridgehub_default,
4353
- functionName: "l2TransactionBaseCost",
4354
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
4355
- }),
4356
- {
4357
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
4358
- message: "Could not fetch L2 base cost from Bridgehub."
4566
+ const l2TxModel = {
4567
+ to: p.to ?? ctx.sender,
4568
+ from: ctx.sender,
4569
+ data: "0x",
4570
+ value: p.amount
4571
+ };
4572
+ const l2GasParams = await quoteL2Gas2({
4573
+ ctx,
4574
+ route: "eth-base",
4575
+ l2TxForModeling: l2TxModel,
4576
+ overrideGasLimit: ctx.l2GasLimit,
4577
+ stateOverrides: {
4578
+ [ctx.sender]: {
4579
+ balance: "0xffffffffffffffffffff"
4580
+ }
4359
4581
  }
4360
- );
4361
- const baseCost = rawBaseCost;
4582
+ });
4583
+ if (!l2GasParams) {
4584
+ throw new Error("Failed to estimate L2 gas for deposit.");
4585
+ }
4586
+ const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
4362
4587
  const l2Contract = p.to ?? ctx.sender;
4363
4588
  const l2Value = p.amount;
4364
4589
  const mintValue = baseCost + ctx.operatorTip + l2Value;
4365
4590
  const req = buildDirectRequestStruct({
4366
4591
  chainId: ctx.chainIdL2,
4367
4592
  mintValue,
4368
- l2GasLimit: ctx.l2GasLimit,
4593
+ l2GasLimit: l2GasParams.gasLimit,
4369
4594
  gasPerPubdata: ctx.gasPerPubdata,
4370
4595
  refundRecipient: ctx.refundRecipient,
4371
4596
  l2Contract,
4372
4597
  l2Value
4373
4598
  });
4374
- const sim = await wrapAs(
4599
+ const sim = await wrapAs2(
4375
4600
  "RPC",
4376
4601
  OP_DEPOSITS.eth.estGas,
4377
4602
  () => ctx.client.l1.simulateContract({
@@ -4387,33 +4612,53 @@ function routeEthDirect() {
4387
4612
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
4388
4613
  }
4389
4614
  );
4390
- const resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
4615
+ const data = viem.encodeFunctionData({
4616
+ abi: sim.request.abi,
4617
+ functionName: sim.request.functionName,
4618
+ args: sim.request.args
4619
+ });
4620
+ const l1TxCandidate = {
4621
+ to: ctx.bridgehub,
4622
+ data,
4623
+ value: mintValue,
4624
+ from: ctx.sender,
4625
+ ...ctx.gasOverrides
4626
+ };
4627
+ const l1Gas = await quoteL1Gas2({
4628
+ ctx,
4629
+ tx: l1TxCandidate,
4630
+ overrides: ctx.gasOverrides
4631
+ });
4391
4632
  const steps = [
4392
4633
  {
4393
4634
  key: "bridgehub:direct",
4394
4635
  kind: "bridgehub:direct",
4395
4636
  description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
4396
- tx: { ...sim.request, ...txFeeOverrides }
4637
+ tx: { ...sim.request, ...l1Gas }
4397
4638
  }
4398
4639
  ];
4640
+ const fees = buildFeeBreakdown({
4641
+ feeToken: ETH_ADDRESS,
4642
+ l1Gas,
4643
+ l2Gas: l2GasParams,
4644
+ l2BaseCost: baseCost,
4645
+ operatorTip: ctx.operatorTip,
4646
+ mintValue
4647
+ });
4399
4648
  return {
4400
4649
  steps,
4401
4650
  approvals: [],
4402
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
4651
+ fees
4403
4652
  };
4404
4653
  }
4405
4654
  };
4406
4655
  }
4407
-
4408
- // src/adapters/viem/resources/deposits/routes/erc20-nonbase.ts
4409
- var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
4410
- var BASE_COST_BUFFER_BPS = 100n;
4411
- var BPS = 10000n;
4412
- var withBuffer = (x) => x * (BPS + BASE_COST_BUFFER_BPS) / BPS;
4656
+ var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
4413
4657
  function routeErc20NonBase() {
4414
4658
  return {
4659
+ // TODO: do we even need these validations?
4415
4660
  async preflight(p, ctx) {
4416
- await wrapAs2(
4661
+ await wrapAs3(
4417
4662
  "VALIDATION",
4418
4663
  OP_DEPOSITS.nonbase.assertNotEthAsset,
4419
4664
  () => {
@@ -4423,18 +4668,8 @@ function routeErc20NonBase() {
4423
4668
  },
4424
4669
  { ctx: { token: p.token } }
4425
4670
  );
4426
- const baseToken = await wrapAs2(
4427
- "CONTRACT",
4428
- OP_DEPOSITS.nonbase.baseToken,
4429
- () => ctx.client.l1.readContract({
4430
- address: ctx.bridgehub,
4431
- abi: IBridgehub_default,
4432
- functionName: "baseToken",
4433
- args: [ctx.chainIdL2]
4434
- }),
4435
- { ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 } }
4436
- );
4437
- await wrapAs2(
4671
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
4672
+ await wrapAs3(
4438
4673
  "VALIDATION",
4439
4674
  OP_DEPOSITS.nonbase.assertNonBaseToken,
4440
4675
  () => {
@@ -4444,63 +4679,49 @@ function routeErc20NonBase() {
4444
4679
  },
4445
4680
  { ctx: { depositToken: p.token, baseToken } }
4446
4681
  );
4447
- return;
4448
4682
  },
4449
4683
  async build(p, ctx) {
4450
- const { gasPriceForBaseCost } = ctx.fee;
4451
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
4452
- const baseToken = await wrapAs2(
4453
- "CONTRACT",
4454
- OP_DEPOSITS.nonbase.baseToken,
4455
- () => ctx.client.l1.readContract({
4456
- address: ctx.bridgehub,
4457
- abi: IBridgehub_default,
4458
- functionName: "baseToken",
4459
- args: [ctx.chainIdL2]
4460
- }),
4461
- { ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 } }
4462
- );
4463
- const MIN_L2_GAS_FOR_ERC20 = 2500000n;
4464
- const l2GasLimitUsed = ctx.l2GasLimit && ctx.l2GasLimit > 0n ? ctx.l2GasLimit < MIN_L2_GAS_FOR_ERC20 ? MIN_L2_GAS_FOR_ERC20 : ctx.l2GasLimit : MIN_L2_GAS_FOR_ERC20;
4465
- const rawBaseCost = await wrapAs2(
4466
- "CONTRACT",
4467
- OP_DEPOSITS.nonbase.baseCost,
4468
- () => ctx.client.l1.readContract({
4469
- address: ctx.bridgehub,
4470
- abi: IBridgehub_default,
4471
- functionName: "l2TransactionBaseCost",
4472
- args: [ctx.chainIdL2, gasPriceForBaseCost, l2GasLimitUsed, ctx.gasPerPubdata]
4473
- }),
4474
- { ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 } }
4475
- );
4476
- const baseCost = rawBaseCost;
4477
- const mintValue = withBuffer(baseCost + ctx.operatorTip);
4684
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
4685
+ const baseIsEth = isETH(baseToken);
4686
+ const assetRouter = ctx.l1AssetRouter;
4687
+ const l2Gas = await determineErc20L2Gas({
4688
+ ctx,
4689
+ l1Token: p.token,
4690
+ modelTx: {
4691
+ to: p.to ?? ctx.sender,
4692
+ from: ctx.sender,
4693
+ data: "0x",
4694
+ value: 0n
4695
+ }
4696
+ });
4697
+ if (!l2Gas) throw new Error("Failed to establish L2 gas parameters.");
4698
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
4699
+ const mintValue = l2BaseCost + ctx.operatorTip;
4478
4700
  const approvals = [];
4479
4701
  const steps = [];
4480
- const depositAllowance = await wrapAs2(
4702
+ const depositAllowance = await wrapAs3(
4481
4703
  "CONTRACT",
4482
- OP_DEPOSITS.nonbase.allowance,
4704
+ OP_DEPOSITS.nonbase.allowanceToken,
4483
4705
  () => ctx.client.l1.readContract({
4484
4706
  address: p.token,
4485
4707
  abi: IERC20_default,
4486
4708
  functionName: "allowance",
4487
- args: [ctx.sender, ctx.l1AssetRouter]
4709
+ args: [ctx.sender, assetRouter]
4488
4710
  }),
4489
4711
  {
4490
- ctx: { where: "erc20.allowance", token: p.token, spender: ctx.l1AssetRouter },
4491
- message: "Failed to read ERC-20 allowance for deposit token."
4712
+ ctx: { where: "erc20.allowance", token: p.token, spender: assetRouter },
4713
+ message: "Failed to read deposit-token allowance."
4492
4714
  }
4493
4715
  );
4494
- const needsDepositApprove = depositAllowance < p.amount;
4495
- if (needsDepositApprove) {
4496
- const approveDepReq = await wrapAs2(
4716
+ if (depositAllowance < p.amount) {
4717
+ const approveSim = await wrapAs3(
4497
4718
  "CONTRACT",
4498
4719
  OP_DEPOSITS.nonbase.estGas,
4499
4720
  () => ctx.client.l1.simulateContract({
4500
4721
  address: p.token,
4501
4722
  abi: IERC20_default,
4502
4723
  functionName: "approve",
4503
- args: [ctx.l1AssetRouter, p.amount],
4724
+ args: [assetRouter, p.amount],
4504
4725
  account: ctx.client.account
4505
4726
  }),
4506
4727
  {
@@ -4508,60 +4729,55 @@ function routeErc20NonBase() {
4508
4729
  message: "Failed to simulate deposit token approve."
4509
4730
  }
4510
4731
  );
4511
- approvals.push({ token: p.token, spender: ctx.l1AssetRouter, amount: p.amount });
4732
+ approvals.push({ token: p.token, spender: assetRouter, amount: p.amount });
4512
4733
  steps.push({
4513
- key: `approve:${p.token}:${ctx.l1AssetRouter}`,
4734
+ key: `approve:${p.token}:${assetRouter}`,
4514
4735
  kind: "approve",
4515
4736
  description: `Approve deposit token for amount`,
4516
- tx: { ...approveDepReq.request, ...txFeeOverrides }
4737
+ tx: { ...approveSim.request }
4517
4738
  });
4518
4739
  }
4519
- const baseIsEth = isETH(baseToken);
4520
- let msgValue = 0n;
4521
4740
  if (!baseIsEth) {
4522
- const baseAllowance = await wrapAs2(
4741
+ const baseAllowance = await wrapAs3(
4523
4742
  "CONTRACT",
4524
- OP_DEPOSITS.nonbase.allowanceFees,
4743
+ OP_DEPOSITS.nonbase.allowanceBase,
4525
4744
  () => ctx.client.l1.readContract({
4526
4745
  address: baseToken,
4527
4746
  abi: IERC20_default,
4528
4747
  functionName: "allowance",
4529
- args: [ctx.sender, ctx.l1AssetRouter]
4748
+ args: [ctx.sender, assetRouter]
4530
4749
  }),
4531
4750
  {
4532
- ctx: { where: "erc20.allowance", token: baseToken, spender: ctx.l1AssetRouter },
4751
+ ctx: { where: "erc20.allowance", token: baseToken, spender: assetRouter },
4533
4752
  message: "Failed to read base-token allowance."
4534
4753
  }
4535
4754
  );
4536
4755
  if (baseAllowance < mintValue) {
4537
- const approveBaseReq = await wrapAs2(
4756
+ const approveBaseSim = await wrapAs3(
4538
4757
  "CONTRACT",
4539
4758
  OP_DEPOSITS.nonbase.estGas,
4540
4759
  () => ctx.client.l1.simulateContract({
4541
4760
  address: baseToken,
4542
4761
  abi: IERC20_default,
4543
4762
  functionName: "approve",
4544
- args: [ctx.l1AssetRouter, mintValue],
4763
+ args: [assetRouter, mintValue],
4545
4764
  account: ctx.client.account
4546
4765
  }),
4547
4766
  {
4548
4767
  ctx: { where: "l1.simulateContract", to: baseToken },
4549
- message: "Failed to simulate base-token approve."
4768
+ message: "Failed to simulate base token approve."
4550
4769
  }
4551
4770
  );
4552
- approvals.push({ token: baseToken, spender: ctx.l1AssetRouter, amount: mintValue });
4771
+ approvals.push({ token: baseToken, spender: assetRouter, amount: mintValue });
4553
4772
  steps.push({
4554
- key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4773
+ key: `approve:${baseToken}:${assetRouter}`,
4555
4774
  kind: "approve",
4556
4775
  description: `Approve base token for mintValue`,
4557
- tx: { ...approveBaseReq.request, ...txFeeOverrides }
4776
+ tx: { ...approveBaseSim.request }
4558
4777
  });
4559
4778
  }
4560
- msgValue = 0n;
4561
- } else {
4562
- msgValue = mintValue;
4563
4779
  }
4564
- const secondBridgeCalldata = await wrapAs2(
4780
+ const secondBridgeCalldata = await wrapAs3(
4565
4781
  "INTERNAL",
4566
4782
  OP_DEPOSITS.nonbase.encodeCalldata,
4567
4783
  () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
@@ -4570,44 +4786,60 @@ function routeErc20NonBase() {
4570
4786
  where: "encodeSecondBridgeErc20Args",
4571
4787
  token: p.token,
4572
4788
  amount: p.amount.toString()
4573
- }
4789
+ },
4790
+ message: "Failed to encode bridging calldata."
4574
4791
  }
4575
4792
  );
4576
- const outer = {
4793
+ const requestStruct = {
4577
4794
  chainId: ctx.chainIdL2,
4578
4795
  mintValue,
4579
4796
  l2Value: 0n,
4580
- l2GasLimit: l2GasLimitUsed,
4797
+ l2GasLimit: l2Gas.gasLimit,
4581
4798
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
4582
4799
  refundRecipient: ctx.refundRecipient,
4583
- secondBridgeAddress: ctx.l1AssetRouter,
4800
+ secondBridgeAddress: assetRouter,
4584
4801
  secondBridgeValue: 0n,
4585
4802
  secondBridgeCalldata
4586
4803
  };
4804
+ const msgValue = baseIsEth ? mintValue : 0n;
4805
+ const calldata = viem.encodeFunctionData({
4806
+ abi: IBridgehub_default,
4807
+ functionName: "requestL2TransactionTwoBridges",
4808
+ args: [requestStruct]
4809
+ });
4810
+ const l1TxCandidate = {
4811
+ to: ctx.bridgehub,
4812
+ data: calldata,
4813
+ value: msgValue,
4814
+ from: ctx.sender,
4815
+ ...ctx.gasOverrides
4816
+ };
4817
+ const l1Gas = await quoteL1Gas2({
4818
+ ctx,
4819
+ tx: l1TxCandidate,
4820
+ overrides: ctx.gasOverrides,
4821
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
4822
+ });
4587
4823
  const approvalsNeeded = approvals.length > 0;
4588
4824
  let bridgeTx;
4589
- let resolvedL1GasLimit;
4590
- const gasOverride = txFeeOverrides.gas;
4591
4825
  if (approvalsNeeded) {
4592
4826
  bridgeTx = {
4593
4827
  address: ctx.bridgehub,
4594
4828
  abi: IBridgehub_default,
4595
4829
  functionName: "requestL2TransactionTwoBridges",
4596
- args: [outer],
4830
+ args: [requestStruct],
4597
4831
  value: msgValue,
4598
- account: ctx.client.account,
4599
- ...txFeeOverrides
4832
+ account: ctx.client.account
4600
4833
  };
4601
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
4602
4834
  } else {
4603
- const sim = await wrapAs2(
4835
+ const sim = await wrapAs3(
4604
4836
  "CONTRACT",
4605
4837
  OP_DEPOSITS.nonbase.estGas,
4606
4838
  () => ctx.client.l1.simulateContract({
4607
4839
  address: ctx.bridgehub,
4608
4840
  abi: IBridgehub_default,
4609
4841
  functionName: "requestL2TransactionTwoBridges",
4610
- args: [outer],
4842
+ args: [requestStruct],
4611
4843
  value: msgValue,
4612
4844
  account: ctx.client.account
4613
4845
  }),
@@ -4616,33 +4848,44 @@ function routeErc20NonBase() {
4616
4848
  message: "Failed to simulate two-bridges request."
4617
4849
  }
4618
4850
  );
4619
- bridgeTx = { ...sim.request, ...txFeeOverrides };
4620
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
4851
+ bridgeTx = { ...sim.request };
4852
+ }
4853
+ if (l1Gas) {
4854
+ bridgeTx = {
4855
+ ...bridgeTx,
4856
+ gas: l1Gas.gasLimit,
4857
+ maxFeePerGas: l1Gas.maxFeePerGas,
4858
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
4859
+ };
4621
4860
  }
4622
4861
  steps.push({
4623
- key: "bridgehub:two-bridges:nonbase",
4862
+ key: "bridgehub:two-bridges:erc20-nonbase",
4624
4863
  kind: "bridgehub:two-bridges",
4625
4864
  description: baseIsEth ? "Bridge ERC-20 (fees in ETH) via Bridgehub.requestL2TransactionTwoBridges" : "Bridge ERC-20 (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
4626
4865
  tx: bridgeTx
4627
4866
  });
4867
+ const fees = buildFeeBreakdown({
4868
+ feeToken: baseToken,
4869
+ l1Gas,
4870
+ l2Gas,
4871
+ l2BaseCost,
4872
+ operatorTip: ctx.operatorTip,
4873
+ mintValue
4874
+ });
4628
4875
  return {
4629
4876
  steps,
4630
4877
  approvals,
4631
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
4878
+ fees
4632
4879
  };
4633
4880
  }
4634
4881
  };
4635
4882
  }
4636
-
4637
- // src/adapters/viem/resources/deposits/routes/eth-nonbase.ts
4638
- var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
4639
- var BASE_COST_BUFFER_BPS2 = 100n;
4640
- var BPS2 = 10000n;
4641
- var withBuffer2 = (x) => x * (BPS2 + BASE_COST_BUFFER_BPS2) / BPS2;
4883
+ var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
4642
4884
  function routeEthNonBase() {
4643
4885
  return {
4886
+ // TODO: do we even need these validations?
4644
4887
  async preflight(p, ctx) {
4645
- await wrapAs3(
4888
+ await wrapAs4(
4646
4889
  "VALIDATION",
4647
4890
  OP_DEPOSITS.ethNonBase.assertEthAsset,
4648
4891
  () => {
@@ -4652,21 +4895,8 @@ function routeEthNonBase() {
4652
4895
  },
4653
4896
  { ctx: { token: p.token } }
4654
4897
  );
4655
- const baseToken = await wrapAs3(
4656
- "CONTRACT",
4657
- OP_DEPOSITS.ethNonBase.baseToken,
4658
- () => ctx.client.l1.readContract({
4659
- address: ctx.bridgehub,
4660
- abi: IBridgehub_default,
4661
- functionName: "baseToken",
4662
- args: [ctx.chainIdL2]
4663
- }),
4664
- {
4665
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
4666
- message: "Failed to read base token."
4667
- }
4668
- );
4669
- await wrapAs3(
4898
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
4899
+ await wrapAs4(
4670
4900
  "VALIDATION",
4671
4901
  OP_DEPOSITS.ethNonBase.assertNonEthBase,
4672
4902
  () => {
@@ -4676,7 +4906,7 @@ function routeEthNonBase() {
4676
4906
  },
4677
4907
  { ctx: { baseToken, chainIdL2: ctx.chainIdL2 } }
4678
4908
  );
4679
- const ethBal = await wrapAs3(
4909
+ const ethBal = await wrapAs4(
4680
4910
  "RPC",
4681
4911
  OP_DEPOSITS.ethNonBase.ethBalance,
4682
4912
  () => ctx.client.l1.getBalance({ address: ctx.sender }),
@@ -4685,7 +4915,7 @@ function routeEthNonBase() {
4685
4915
  message: "Failed to read L1 ETH balance."
4686
4916
  }
4687
4917
  );
4688
- await wrapAs3(
4918
+ await wrapAs4(
4689
4919
  "VALIDATION",
4690
4920
  OP_DEPOSITS.ethNonBase.assertEthBalance,
4691
4921
  () => {
@@ -4695,45 +4925,27 @@ function routeEthNonBase() {
4695
4925
  },
4696
4926
  { ctx: { required: p.amount.toString(), balance: ethBal.toString() } }
4697
4927
  );
4698
- return;
4699
4928
  },
4700
4929
  async build(p, ctx) {
4701
- const { gasPriceForBaseCost } = ctx.fee;
4702
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
4703
- const baseToken = await wrapAs3(
4704
- "CONTRACT",
4705
- OP_DEPOSITS.ethNonBase.baseToken,
4706
- () => ctx.client.l1.readContract({
4707
- address: ctx.bridgehub,
4708
- abi: IBridgehub_default,
4709
- functionName: "baseToken",
4710
- args: [ctx.chainIdL2]
4711
- }),
4712
- {
4713
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
4714
- message: "Failed to read base token."
4715
- }
4716
- );
4717
- const rawBaseCost = await wrapAs3(
4718
- "CONTRACT",
4719
- OP_DEPOSITS.ethNonBase.baseCost,
4720
- () => ctx.client.l1.readContract({
4721
- address: ctx.bridgehub,
4722
- abi: IBridgehub_default,
4723
- functionName: "l2TransactionBaseCost",
4724
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
4725
- }),
4726
- {
4727
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
4728
- message: "Could not fetch L2 base cost."
4729
- }
4730
- );
4731
- const baseCost = BigInt(rawBaseCost);
4732
- const mintValueRaw = baseCost + ctx.operatorTip;
4733
- const mintValue = withBuffer2(mintValueRaw);
4930
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
4931
+ const l2TxModel = {
4932
+ to: p.to ?? ctx.sender,
4933
+ from: ctx.sender,
4934
+ data: "0x",
4935
+ value: 0n
4936
+ };
4937
+ const l2Gas = await quoteL2Gas2({
4938
+ ctx,
4939
+ route: "eth-nonbase",
4940
+ l2TxForModeling: l2TxModel,
4941
+ overrideGasLimit: ctx.l2GasLimit
4942
+ });
4943
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
4944
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
4945
+ const mintValue = l2BaseCost + ctx.operatorTip;
4734
4946
  const approvals = [];
4735
4947
  const steps = [];
4736
- const allowance = await wrapAs3(
4948
+ const allowance = await wrapAs4(
4737
4949
  "CONTRACT",
4738
4950
  OP_DEPOSITS.ethNonBase.allowanceBase,
4739
4951
  () => ctx.client.l1.readContract({
@@ -4749,7 +4961,7 @@ function routeEthNonBase() {
4749
4961
  );
4750
4962
  const needsApprove = allowance < mintValue;
4751
4963
  if (needsApprove) {
4752
- const approveSim = await wrapAs3(
4964
+ const approveSim = await wrapAs4(
4753
4965
  "CONTRACT",
4754
4966
  OP_DEPOSITS.ethNonBase.estGas,
4755
4967
  () => ctx.client.l1.simulateContract({
@@ -4768,11 +4980,11 @@ function routeEthNonBase() {
4768
4980
  steps.push({
4769
4981
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4770
4982
  kind: "approve",
4771
- description: `Approve base token for mintValue`,
4983
+ description: `Approve base token for fees (mintValue)`,
4772
4984
  tx: { ...approveSim.request }
4773
4985
  });
4774
4986
  }
4775
- const secondBridgeCalldata = await wrapAs3(
4987
+ const secondBridgeCalldata = await wrapAs4(
4776
4988
  "INTERNAL",
4777
4989
  OP_DEPOSITS.ethNonBase.encodeCalldata,
4778
4990
  () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
@@ -4785,11 +4997,11 @@ function routeEthNonBase() {
4785
4997
  message: "Failed to encode ETH bridging calldata."
4786
4998
  }
4787
4999
  );
4788
- const outer = {
5000
+ const requestStruct = {
4789
5001
  chainId: ctx.chainIdL2,
4790
5002
  mintValue,
4791
- l2Value: 0n,
4792
- l2GasLimit: ctx.l2GasLimit,
5003
+ l2Value: p.amount,
5004
+ l2GasLimit: l2Gas.gasLimit,
4793
5005
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
4794
5006
  refundRecipient: ctx.refundRecipient,
4795
5007
  secondBridgeAddress: ctx.l1AssetRouter,
@@ -4797,29 +5009,32 @@ function routeEthNonBase() {
4797
5009
  secondBridgeCalldata
4798
5010
  };
4799
5011
  let bridgeTx;
4800
- let resolvedL1GasLimit;
5012
+ let calldata;
4801
5013
  if (needsApprove) {
4802
5014
  bridgeTx = {
4803
5015
  address: ctx.bridgehub,
4804
5016
  abi: IBridgehub_default,
4805
5017
  functionName: "requestL2TransactionTwoBridges",
4806
- args: [outer],
5018
+ args: [requestStruct],
4807
5019
  value: p.amount,
4808
5020
  // base ≠ ETH ⇒ msg.value == secondBridgeValue
4809
5021
  account: ctx.client.account
4810
5022
  };
4811
- resolvedL1GasLimit = ctx.l2GasLimit;
5023
+ calldata = viem.encodeFunctionData({
5024
+ abi: IBridgehub_default,
5025
+ functionName: "requestL2TransactionTwoBridges",
5026
+ args: [requestStruct]
5027
+ });
4812
5028
  } else {
4813
- const twoBridgesSim = await wrapAs3(
5029
+ const sim = await wrapAs4(
4814
5030
  "CONTRACT",
4815
5031
  OP_DEPOSITS.ethNonBase.estGas,
4816
5032
  () => ctx.client.l1.simulateContract({
4817
5033
  address: ctx.bridgehub,
4818
5034
  abi: IBridgehub_default,
4819
5035
  functionName: "requestL2TransactionTwoBridges",
4820
- args: [outer],
5036
+ args: [requestStruct],
4821
5037
  value: p.amount,
4822
- // base ≠ ETH ⇒ msg.value == secondBridgeValue
4823
5038
  account: ctx.client.account
4824
5039
  }),
4825
5040
  {
@@ -4827,8 +5042,33 @@ function routeEthNonBase() {
4827
5042
  message: "Failed to simulate Bridgehub two-bridges request."
4828
5043
  }
4829
5044
  );
4830
- bridgeTx = { ...twoBridgesSim.request, ...txFeeOverrides };
4831
- resolvedL1GasLimit = twoBridgesSim.request.gas ?? ctx.l2GasLimit;
5045
+ calldata = viem.encodeFunctionData({
5046
+ abi: sim.request.abi,
5047
+ functionName: sim.request.functionName,
5048
+ args: sim.request.args
5049
+ });
5050
+ bridgeTx = { ...sim.request };
5051
+ }
5052
+ const l1TxCandidate = {
5053
+ to: ctx.bridgehub,
5054
+ data: calldata,
5055
+ value: p.amount,
5056
+ from: ctx.sender,
5057
+ ...ctx.gasOverrides
5058
+ };
5059
+ const l1Gas = await quoteL1Gas2({
5060
+ ctx,
5061
+ tx: l1TxCandidate,
5062
+ overrides: ctx.gasOverrides,
5063
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
5064
+ });
5065
+ if (l1Gas) {
5066
+ bridgeTx = {
5067
+ ...bridgeTx,
5068
+ gas: l1Gas.gasLimit,
5069
+ maxFeePerGas: l1Gas.maxFeePerGas,
5070
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
5071
+ };
4832
5072
  }
4833
5073
  steps.push({
4834
5074
  key: "bridgehub:two-bridges:eth-nonbase",
@@ -4836,24 +5076,27 @@ function routeEthNonBase() {
4836
5076
  description: "Bridge ETH (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
4837
5077
  tx: bridgeTx
4838
5078
  });
5079
+ const fees = buildFeeBreakdown({
5080
+ feeToken: baseToken,
5081
+ l1Gas,
5082
+ l2Gas,
5083
+ l2BaseCost,
5084
+ operatorTip: ctx.operatorTip,
5085
+ mintValue
5086
+ });
4839
5087
  return {
4840
5088
  steps,
4841
5089
  approvals,
4842
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5090
+ fees
4843
5091
  };
4844
5092
  }
4845
5093
  };
4846
5094
  }
4847
-
4848
- // src/adapters/viem/resources/deposits/routes/erc20-base.ts
4849
- var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
4850
- var BASE_COST_BUFFER_BPS3 = 100n;
4851
- var BPS3 = 10000n;
4852
- var withBuffer3 = (x) => x * (BPS3 + BASE_COST_BUFFER_BPS3) / BPS3;
5095
+ var { wrapAs: wrapAs5 } = createErrorHandlers("deposits");
4853
5096
  function routeErc20Base() {
4854
5097
  return {
4855
5098
  async preflight(p, ctx) {
4856
- await wrapAs4(
5099
+ await wrapAs5(
4857
5100
  "VALIDATION",
4858
5101
  OP_DEPOSITS.base.assertErc20Asset,
4859
5102
  () => {
@@ -4863,21 +5106,8 @@ function routeErc20Base() {
4863
5106
  },
4864
5107
  { ctx: { token: p.token } }
4865
5108
  );
4866
- const baseToken = await wrapAs4(
4867
- "CONTRACT",
4868
- OP_DEPOSITS.base.baseToken,
4869
- () => ctx.client.l1.readContract({
4870
- address: ctx.bridgehub,
4871
- abi: IBridgehub_default,
4872
- functionName: "baseToken",
4873
- args: [ctx.chainIdL2]
4874
- }),
4875
- {
4876
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
4877
- message: "Failed to read base token."
4878
- }
4879
- );
4880
- await wrapAs4(
5109
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
5110
+ await wrapAs5(
4881
5111
  "VALIDATION",
4882
5112
  OP_DEPOSITS.base.assertMatchesBase,
4883
5113
  () => {
@@ -4887,45 +5117,27 @@ function routeErc20Base() {
4887
5117
  },
4888
5118
  { ctx: { baseToken, provided: p.token, chainIdL2: ctx.chainIdL2 } }
4889
5119
  );
4890
- return;
4891
5120
  },
4892
5121
  async build(p, ctx) {
4893
- const { gasPriceForBaseCost } = ctx.fee;
4894
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
4895
- const gasOverride = txFeeOverrides.gas;
4896
- const baseToken = await wrapAs4(
4897
- "CONTRACT",
4898
- OP_DEPOSITS.base.baseToken,
4899
- () => ctx.client.l1.readContract({
4900
- address: ctx.bridgehub,
4901
- abi: IBridgehub_default,
4902
- functionName: "baseToken",
4903
- args: [ctx.chainIdL2]
4904
- }),
4905
- {
4906
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
4907
- message: "Failed to read base token."
4908
- }
4909
- );
4910
- const rawBaseCost = await wrapAs4(
4911
- "CONTRACT",
4912
- OP_DEPOSITS.base.baseCost,
4913
- () => ctx.client.l1.readContract({
4914
- address: ctx.bridgehub,
4915
- abi: IBridgehub_default,
4916
- functionName: "l2TransactionBaseCost",
4917
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
4918
- }),
4919
- {
4920
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
4921
- message: "Could not fetch L2 base cost from Bridgehub."
4922
- }
4923
- );
4924
- const baseCost = rawBaseCost;
4925
- const l2Value = p.amount;
4926
- const rawMintValue = baseCost + ctx.operatorTip + l2Value;
4927
- const mintValue = withBuffer3(rawMintValue);
4928
- const allowance = await wrapAs4(
5122
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
5123
+ const l2TxModel = {
5124
+ to: p.to ?? ctx.sender,
5125
+ from: ctx.sender,
5126
+ data: "0x",
5127
+ value: 0n
5128
+ };
5129
+ const l2Gas = await quoteL2Gas2({
5130
+ ctx,
5131
+ route: "erc20-base",
5132
+ l2TxForModeling: l2TxModel,
5133
+ overrideGasLimit: ctx.l2GasLimit
5134
+ });
5135
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
5136
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
5137
+ const mintValue = l2BaseCost + ctx.operatorTip + p.amount;
5138
+ const approvals = [];
5139
+ const steps = [];
5140
+ const allowance = await wrapAs5(
4929
5141
  "CONTRACT",
4930
5142
  OP_DEPOSITS.base.allowance,
4931
5143
  () => ctx.client.l1.readContract({
@@ -4939,11 +5151,9 @@ function routeErc20Base() {
4939
5151
  message: "Failed to read base-token allowance."
4940
5152
  }
4941
5153
  );
4942
- const approvals = [];
4943
- const steps = [];
4944
5154
  const needsApprove = allowance < mintValue;
4945
5155
  if (needsApprove) {
4946
- const approveSim = await wrapAs4(
5156
+ const approveSim = await wrapAs5(
4947
5157
  "CONTRACT",
4948
5158
  OP_DEPOSITS.base.estGas,
4949
5159
  () => ctx.client.l1.simulateContract({
@@ -4963,20 +5173,20 @@ function routeErc20Base() {
4963
5173
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4964
5174
  kind: "approve",
4965
5175
  description: "Approve base token for mintValue",
4966
- tx: { ...approveSim.request, ...txFeeOverrides }
5176
+ tx: { ...approveSim.request }
4967
5177
  });
4968
5178
  }
4969
5179
  const req = buildDirectRequestStruct({
4970
5180
  chainId: ctx.chainIdL2,
4971
5181
  mintValue,
4972
- l2GasLimit: ctx.l2GasLimit,
5182
+ l2GasLimit: l2Gas.gasLimit,
4973
5183
  gasPerPubdata: ctx.gasPerPubdata,
4974
5184
  refundRecipient: ctx.refundRecipient,
4975
5185
  l2Contract: p.to ?? ctx.sender,
4976
- l2Value
5186
+ l2Value: p.amount
4977
5187
  });
4978
5188
  let bridgeTx;
4979
- let resolvedL1GasLimit;
5189
+ let calldata;
4980
5190
  if (needsApprove) {
4981
5191
  bridgeTx = {
4982
5192
  address: ctx.bridgehub,
@@ -4984,13 +5194,16 @@ function routeErc20Base() {
4984
5194
  functionName: "requestL2TransactionDirect",
4985
5195
  args: [req],
4986
5196
  value: 0n,
4987
- // base is ERC-20 ⇒ msg.value MUST be 0
4988
- account: ctx.client.account,
4989
- ...txFeeOverrides
5197
+ // base token is ERC-20 ⇒ msg.value MUST be 0
5198
+ account: ctx.client.account
4990
5199
  };
4991
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
5200
+ calldata = viem.encodeFunctionData({
5201
+ abi: IBridgehub_default,
5202
+ functionName: "requestL2TransactionDirect",
5203
+ args: [req]
5204
+ });
4992
5205
  } else {
4993
- const sim = await wrapAs4(
5206
+ const sim = await wrapAs5(
4994
5207
  "RPC",
4995
5208
  OP_DEPOSITS.base.estGas,
4996
5209
  () => ctx.client.l1.simulateContract({
@@ -5006,8 +5219,33 @@ function routeErc20Base() {
5006
5219
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
5007
5220
  }
5008
5221
  );
5009
- bridgeTx = { ...sim.request, ...txFeeOverrides };
5010
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
5222
+ calldata = viem.encodeFunctionData({
5223
+ abi: sim.request.abi,
5224
+ functionName: sim.request.functionName,
5225
+ args: sim.request.args
5226
+ });
5227
+ bridgeTx = { ...sim.request };
5228
+ }
5229
+ const l1TxCandidate = {
5230
+ to: ctx.bridgehub,
5231
+ data: calldata,
5232
+ value: 0n,
5233
+ from: ctx.sender,
5234
+ ...ctx.gasOverrides
5235
+ };
5236
+ const l1Gas = await quoteL1Gas2({
5237
+ ctx,
5238
+ tx: l1TxCandidate,
5239
+ overrides: ctx.gasOverrides,
5240
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
5241
+ });
5242
+ if (l1Gas) {
5243
+ bridgeTx = {
5244
+ ...bridgeTx,
5245
+ gas: l1Gas.gasLimit,
5246
+ maxFeePerGas: l1Gas.maxFeePerGas,
5247
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
5248
+ };
5011
5249
  }
5012
5250
  steps.push({
5013
5251
  key: "bridgehub:direct:erc20-base",
@@ -5015,10 +5253,18 @@ function routeErc20Base() {
5015
5253
  description: "Bridge base ERC-20 via Bridgehub.requestL2TransactionDirect",
5016
5254
  tx: bridgeTx
5017
5255
  });
5256
+ const fees = buildFeeBreakdown({
5257
+ feeToken: baseToken,
5258
+ l1Gas,
5259
+ l2Gas,
5260
+ l2BaseCost,
5261
+ operatorTip: ctx.operatorTip,
5262
+ mintValue
5263
+ });
5018
5264
  return {
5019
5265
  steps,
5020
5266
  approvals,
5021
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5267
+ fees
5022
5268
  };
5023
5269
  }
5024
5270
  };
@@ -5112,32 +5358,19 @@ function createDepositsResource(client) {
5112
5358
  const ctx = await commonCtx(p, client);
5113
5359
  const route = ctx.route;
5114
5360
  await ROUTES[route].preflight?.(p, ctx);
5115
- const { steps, approvals, quoteExtras } = await ROUTES[route].build(p, ctx);
5116
- const { baseCost, mintValue } = quoteExtras;
5117
- const fallbackGasLimit = quoteExtras.l1GasLimit;
5118
- const resolveGasLimit = () => {
5119
- if (ctx.fee.gasLimit != null) return ctx.fee.gasLimit;
5120
- for (let i = steps.length - 1; i >= 0; i--) {
5121
- const candidate = steps[i].tx.gas;
5122
- if (candidate != null) return candidate;
5123
- }
5124
- if (fallbackGasLimit != null) return fallbackGasLimit;
5125
- return ctx.l2GasLimit;
5126
- };
5127
- const gasLimit = resolveGasLimit();
5361
+ const { steps, approvals, fees } = await ROUTES[route].build(p, ctx);
5128
5362
  return {
5129
5363
  route: ctx.route,
5130
5364
  summary: {
5131
5365
  route: ctx.route,
5132
5366
  approvalsNeeded: approvals,
5133
- baseCost,
5134
- mintValue,
5135
- gasPerPubdata: ctx.gasPerPubdata,
5136
- fees: {
5137
- gasLimit,
5138
- maxFeePerGas: ctx.fee.maxFeePerGas,
5139
- maxPriorityFeePerGas: ctx.fee.maxPriorityFeePerGas
5140
- }
5367
+ amounts: {
5368
+ transfer: { token: p.token, amount: p.amount }
5369
+ },
5370
+ fees,
5371
+ // Legacy fields (maintained for backward compatibility)
5372
+ baseCost: fees.l2?.baseCost,
5373
+ mintValue: fees.mintValue
5141
5374
  },
5142
5375
  steps
5143
5376
  };
@@ -5212,7 +5445,7 @@ function createDepositsResource(client) {
5212
5445
  step.tx.gas = overrides.gasLimit;
5213
5446
  }
5214
5447
  }
5215
- if (step.tx.gas == null) {
5448
+ if (!p.l1TxOverrides?.gasLimit) {
5216
5449
  try {
5217
5450
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
5218
5451
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -5435,7 +5668,7 @@ function normalizeTokenForRouting(token) {
5435
5668
  function pickWithdrawRoute(args) {
5436
5669
  const tokenNorm = normalizeTokenForRouting(args.token);
5437
5670
  const isL2BaseAlias = tokenNorm.toLowerCase() === L2_BASE_TOKEN_ADDRESS.toLowerCase();
5438
- if (isL2BaseAlias) return args.baseIsEth ? "eth-base" : "eth-nonbase";
5671
+ if (isL2BaseAlias) return "base";
5439
5672
  return "erc20-nonbase";
5440
5673
  }
5441
5674
 
@@ -5476,13 +5709,7 @@ async function commonCtx2(p, client) {
5476
5709
  } = await client.ensureAddresses();
5477
5710
  const chainIdL2 = BigInt(await client.l2.getChainId());
5478
5711
  const baseIsEth = await isEthBasedChain(client.l2, l2NativeTokenVault);
5479
- const fee = await getL2FeeOverrides(client, p.l2TxOverrides);
5480
- const route = pickWithdrawRoute({
5481
- token: p.token,
5482
- baseIsEth
5483
- });
5484
- const l2GasLimit = p.l2GasLimit ?? 300000n;
5485
- const gasBufferPct = 15;
5712
+ const route = pickWithdrawRoute({ token: p.token});
5486
5713
  return {
5487
5714
  client,
5488
5715
  bridgehub,
@@ -5495,56 +5722,162 @@ async function commonCtx2(p, client) {
5495
5722
  l2NativeTokenVault,
5496
5723
  l2BaseTokenSystem,
5497
5724
  baseIsEth,
5498
- l2GasLimit,
5499
- gasBufferPct,
5500
- fee
5725
+ gasOverrides: p.l2TxOverrides
5726
+ };
5727
+ }
5728
+
5729
+ // src/core/resources/withdrawals/gas.ts
5730
+ function makeGasQuote2(p) {
5731
+ return {
5732
+ gasLimit: p.gasLimit,
5733
+ maxFeePerGas: p.maxFeePerGas,
5734
+ maxPriorityFeePerGas: p.maxPriorityFeePerGas,
5735
+ maxCost: p.gasLimit * p.maxFeePerGas
5736
+ };
5737
+ }
5738
+ async function fetchFees2(estimator) {
5739
+ try {
5740
+ const fees = await estimator.estimateFeesPerGas();
5741
+ if (fees.maxFeePerGas != null) {
5742
+ return {
5743
+ maxFeePerGas: fees.maxFeePerGas,
5744
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
5745
+ };
5746
+ }
5747
+ if (fees.gasPrice != null) {
5748
+ return {
5749
+ maxFeePerGas: fees.gasPrice,
5750
+ maxPriorityFeePerGas: 0n
5751
+ };
5752
+ }
5753
+ } catch {
5754
+ }
5755
+ try {
5756
+ const gp = await estimator.getGasPrice();
5757
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
5758
+ } catch {
5759
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
5760
+ }
5761
+ }
5762
+ async function quoteL2Gas3(input) {
5763
+ const { estimator, tx, overrides } = input;
5764
+ const market = await fetchFees2(estimator);
5765
+ const o = overrides;
5766
+ const maxFeePerGas = o?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : market.maxFeePerGas);
5767
+ const maxPriorityFeePerGas = o?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : market.maxPriorityFeePerGas);
5768
+ const explicitGasLimit = o?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
5769
+ if (explicitGasLimit != null) {
5770
+ return makeGasQuote2({
5771
+ gasLimit: explicitGasLimit,
5772
+ maxFeePerGas,
5773
+ maxPriorityFeePerGas
5774
+ });
5775
+ }
5776
+ try {
5777
+ const est = await estimator.estimateGas(tx);
5778
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
5779
+ return makeGasQuote2({
5780
+ gasLimit: buffered,
5781
+ maxFeePerGas,
5782
+ maxPriorityFeePerGas
5783
+ });
5784
+ } catch (err) {
5785
+ console.warn("Failed to estimate L2 gas for withdrawal.", err);
5786
+ return void 0;
5787
+ }
5788
+ }
5789
+
5790
+ // src/adapters/viem/resources/withdrawals/services/gas.ts
5791
+ async function quoteL2Gas4(input) {
5792
+ const { ctx, tx } = input;
5793
+ const estimator = viemToGasEstimator(ctx.client.l2);
5794
+ return quoteL2Gas3({
5795
+ estimator,
5796
+ tx: toCoreTx(tx),
5797
+ overrides: ctx.gasOverrides
5798
+ });
5799
+ }
5800
+
5801
+ // src/adapters/viem/resources/withdrawals/services/fee.ts
5802
+ function buildFeeBreakdown2(p) {
5803
+ const l2Total = p.l2Gas?.maxCost ?? 0n;
5804
+ const l2 = {
5805
+ total: l2Total,
5806
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
5807
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
5808
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
5809
+ };
5810
+ return {
5811
+ token: p.feeToken,
5812
+ maxTotal: l2Total,
5813
+ l2
5501
5814
  };
5502
5815
  }
5503
5816
 
5504
5817
  // src/adapters/viem/resources/withdrawals/routes/eth.ts
5505
- var { wrapAs: wrapAs5 } = createErrorHandlers("withdrawals");
5818
+ var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
5506
5819
  function routeEthBase() {
5507
5820
  return {
5508
5821
  async build(p, ctx) {
5509
- const toL1 = p.to ?? ctx.sender;
5510
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5511
- const sim = await wrapAs5(
5512
- "CONTRACT",
5513
- OP_WITHDRAWALS.eth.estGas,
5514
- () => ctx.client.l2.simulateContract({
5515
- address: L2_BASE_TOKEN_ADDRESS,
5516
- abi: IBaseToken_default,
5517
- functionName: "withdraw",
5518
- args: [toL1],
5519
- value: p.amount,
5520
- account: ctx.client.account,
5521
- ...txFeeOverrides
5522
- }),
5822
+ const steps = [];
5823
+ const data = await wrapAs6(
5824
+ "INTERNAL",
5825
+ OP_WITHDRAWALS.eth.encodeWithdraw,
5826
+ () => Promise.resolve(
5827
+ viem.encodeFunctionData({
5828
+ abi: IBaseToken_default,
5829
+ functionName: "withdraw",
5830
+ args: [p.to ?? ctx.sender]
5831
+ })
5832
+ ),
5523
5833
  {
5524
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
5525
- message: "Failed to simulate L2 ETH withdraw."
5834
+ ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
5835
+ message: "Failed to encode ETH withdraw calldata."
5526
5836
  }
5527
5837
  );
5528
- const steps = [
5529
- {
5530
- key: "l2-base-token:withdraw",
5531
- kind: "l2-base-token:withdraw",
5532
- description: "Withdraw ETH via L2 Base Token System",
5533
- tx: { ...sim.request, ...txFeeOverrides }
5534
- }
5535
- ];
5536
- return { steps, approvals: [], quoteExtras: {} };
5838
+ const L2tx = {
5839
+ to: L2_BASE_TOKEN_ADDRESS,
5840
+ data,
5841
+ value: p.amount,
5842
+ from: ctx.sender
5843
+ };
5844
+ const l2Gas = await quoteL2Gas4({ ctx, tx: L2tx });
5845
+ if (l2Gas) {
5846
+ L2tx.gas = l2Gas.gasLimit;
5847
+ L2tx.maxFeePerGas = l2Gas.maxFeePerGas;
5848
+ L2tx.maxPriorityFeePerGas = l2Gas.maxPriorityFeePerGas;
5849
+ }
5850
+ const tx = {
5851
+ address: L2_BASE_TOKEN_ADDRESS,
5852
+ abi: IBaseToken_default,
5853
+ functionName: "withdraw",
5854
+ args: [p.to ?? ctx.sender],
5855
+ value: p.amount,
5856
+ account: ctx.client.account,
5857
+ ...l2Gas
5858
+ };
5859
+ const fees = buildFeeBreakdown2({
5860
+ feeToken: L2_BASE_TOKEN_ADDRESS,
5861
+ l2Gas
5862
+ });
5863
+ steps.push({
5864
+ key: "l2-base-token:withdraw",
5865
+ kind: "l2-base-token:withdraw",
5866
+ description: "Withdraw ETH via L2 Base Token System",
5867
+ tx
5868
+ });
5869
+ return { steps, approvals: [], fees };
5537
5870
  }
5538
5871
  };
5539
5872
  }
5540
- var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
5873
+ var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
5541
5874
  function routeErc20NonBase2() {
5542
5875
  return {
5543
5876
  // TODO: add preflight validations here
5544
5877
  async build(p, ctx) {
5545
- const toL1 = p.to ?? ctx.sender;
5546
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5547
- const current = await wrapAs6(
5878
+ const steps = [];
5879
+ const approvals = [];
5880
+ const current = await wrapAs7(
5548
5881
  "CONTRACT",
5549
5882
  OP_WITHDRAWALS.erc20.allowance,
5550
5883
  () => ctx.client.l2.readContract({
@@ -5564,12 +5897,26 @@ function routeErc20NonBase2() {
5564
5897
  message: "Failed to read L2 ERC-20 allowance."
5565
5898
  }
5566
5899
  );
5567
- const needsApprove = current < p.amount;
5568
- const steps = [];
5569
- const approvals = [];
5570
- if (needsApprove) {
5900
+ if (current < p.amount) {
5571
5901
  approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
5572
- const approveSim = await wrapAs6(
5902
+ const data = viem.encodeFunctionData({
5903
+ abi: IERC20_default,
5904
+ functionName: "approve",
5905
+ args: [ctx.l2NativeTokenVault, p.amount]
5906
+ });
5907
+ const approveTxCandidate = {
5908
+ to: p.token,
5909
+ data,
5910
+ value: 0n,
5911
+ from: ctx.sender
5912
+ };
5913
+ const approveGas = await quoteL2Gas4({ ctx, tx: approveTxCandidate });
5914
+ if (approveGas) {
5915
+ approveTxCandidate.gas = approveGas.gasLimit;
5916
+ approveTxCandidate.maxFeePerGas = approveGas.maxFeePerGas;
5917
+ approveTxCandidate.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
5918
+ }
5919
+ const approveSim = await wrapAs7(
5573
5920
  "CONTRACT",
5574
5921
  OP_WITHDRAWALS.erc20.estGas,
5575
5922
  () => ctx.client.l2.simulateContract({
@@ -5578,21 +5925,25 @@ function routeErc20NonBase2() {
5578
5925
  functionName: "approve",
5579
5926
  args: [ctx.l2NativeTokenVault, p.amount],
5580
5927
  account: ctx.client.account,
5581
- ...txFeeOverrides
5928
+ ...approveGas
5582
5929
  }),
5583
5930
  {
5584
5931
  ctx: { where: "l2.simulateContract", to: p.token },
5585
5932
  message: "Failed to simulate L2 ERC-20 approve."
5586
5933
  }
5587
5934
  );
5935
+ const { ...approveRequest } = approveSim.request;
5936
+ const approveTx = {
5937
+ ...approveRequest
5938
+ };
5588
5939
  steps.push({
5589
5940
  key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
5590
5941
  kind: "approve:l2",
5591
5942
  description: `Approve ${p.amount} to NativeTokenVault`,
5592
- tx: { ...approveSim.request, ...txFeeOverrides }
5943
+ tx: approveTx
5593
5944
  });
5594
5945
  }
5595
- const ensure = await wrapAs6(
5946
+ const ensure = await wrapAs7(
5596
5947
  "CONTRACT",
5597
5948
  OP_WITHDRAWALS.erc20.ensureRegistered,
5598
5949
  () => ctx.client.l2.simulateContract({
@@ -5614,20 +5965,37 @@ function routeErc20NonBase2() {
5614
5965
  { type: "address", name: "l1Receiver" },
5615
5966
  { type: "address", name: "l2Token" }
5616
5967
  ],
5617
- [p.amount, toL1, p.token]
5968
+ [p.amount, p.to ?? ctx.sender, p.token]
5618
5969
  );
5970
+ const withdrawCalldata = viem.encodeFunctionData({
5971
+ abi: IL2AssetRouter_default,
5972
+ functionName: "withdraw",
5973
+ args: [assetId, assetData]
5974
+ });
5975
+ const withdrawTxCandidate = {
5976
+ to: ctx.l2AssetRouter,
5977
+ data: withdrawCalldata,
5978
+ value: 0n,
5979
+ from: ctx.sender
5980
+ };
5981
+ const withdrawGas = await quoteL2Gas4({ ctx, tx: withdrawTxCandidate });
5982
+ if (withdrawGas) {
5983
+ withdrawTxCandidate.gas = withdrawGas.gasLimit;
5984
+ withdrawTxCandidate.maxFeePerGas = withdrawGas.maxFeePerGas;
5985
+ withdrawTxCandidate.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
5986
+ }
5619
5987
  let withdrawTx;
5620
- if (needsApprove) {
5988
+ if (current < p.amount) {
5621
5989
  withdrawTx = {
5622
5990
  address: ctx.l2AssetRouter,
5623
5991
  abi: IL2AssetRouter_default,
5624
5992
  functionName: "withdraw",
5625
5993
  args: [assetId, assetData],
5626
5994
  account: ctx.client.account,
5627
- ...txFeeOverrides
5995
+ ...withdrawGas
5628
5996
  };
5629
5997
  } else {
5630
- const sim = await wrapAs6(
5998
+ const sim = await wrapAs7(
5631
5999
  "CONTRACT",
5632
6000
  OP_WITHDRAWALS.erc20.estGas,
5633
6001
  () => ctx.client.l2.simulateContract({
@@ -5636,14 +6004,18 @@ function routeErc20NonBase2() {
5636
6004
  functionName: "withdraw",
5637
6005
  args: [assetId, assetData],
5638
6006
  account: ctx.client.account,
5639
- ...txFeeOverrides
6007
+ ...withdrawGas
5640
6008
  }),
5641
6009
  {
5642
6010
  ctx: { where: "l2.simulateContract", to: ctx.l2AssetRouter },
5643
6011
  message: "Failed to simulate L2 ERC-20 withdraw."
5644
6012
  }
5645
6013
  );
5646
- withdrawTx = { ...sim.request, ...txFeeOverrides };
6014
+ const { ...withdrawRequest } = sim.request;
6015
+ withdrawTx = {
6016
+ ...withdrawRequest,
6017
+ ...withdrawGas
6018
+ };
5647
6019
  }
5648
6020
  steps.push({
5649
6021
  key: "l2-asset-router:withdraw",
@@ -5651,59 +6023,11 @@ function routeErc20NonBase2() {
5651
6023
  description: "Burn on L2 & send L2\u2192L1 message",
5652
6024
  tx: withdrawTx
5653
6025
  });
5654
- return { steps, approvals, quoteExtras: {} };
5655
- }
5656
- };
5657
- }
5658
-
5659
- // src/adapters/viem/resources/withdrawals/routes/eth-nonbase.ts
5660
- var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
5661
- function routeEthNonBase2() {
5662
- return {
5663
- async preflight(p, ctx) {
5664
- await wrapAs7(
5665
- "VALIDATION",
5666
- OP_WITHDRAWALS.ethNonBase.assertNonEthBase,
5667
- () => {
5668
- if (p.token.toLowerCase() !== L2_BASE_TOKEN_ADDRESS.toLowerCase()) {
5669
- throw new Error("eth-nonbase route requires the L2 base-token alias (0x\u2026800A).");
5670
- }
5671
- if (ctx.baseIsEth) {
5672
- throw new Error("eth-nonbase route requires chain base \u2260 ETH.");
5673
- }
5674
- },
5675
- { ctx: { token: p.token, baseIsEth: ctx.baseIsEth } }
5676
- );
5677
- },
5678
- async build(p, ctx) {
5679
- const toL1 = p.to ?? ctx.sender;
5680
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5681
- const sim = await wrapAs7(
5682
- "CONTRACT",
5683
- OP_WITHDRAWALS.ethNonBase.estGas,
5684
- () => ctx.client.l2.simulateContract({
5685
- address: L2_BASE_TOKEN_ADDRESS,
5686
- abi: IBaseToken_default,
5687
- functionName: "withdraw",
5688
- args: [toL1],
5689
- value: p.amount,
5690
- account: ctx.client.account,
5691
- ...txFeeOverrides
5692
- }),
5693
- {
5694
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
5695
- message: "Failed to simulate L2 base-token withdraw."
5696
- }
5697
- );
5698
- const steps = [
5699
- {
5700
- key: "l2-base-token:withdraw",
5701
- kind: "l2-base-token:withdraw",
5702
- description: "Withdraw base token via L2 Base Token System (base \u2260 ETH)",
5703
- tx: { ...sim.request, ...txFeeOverrides }
5704
- }
5705
- ];
5706
- return { steps, approvals: [], quoteExtras: {} };
6026
+ const fees = buildFeeBreakdown2({
6027
+ feeToken: await ctx.client.baseToken(ctx.chainIdL2),
6028
+ l2Gas: withdrawGas
6029
+ });
6030
+ return { steps, approvals, fees };
5707
6031
  }
5708
6032
  };
5709
6033
  }
@@ -6058,10 +6382,8 @@ function createFinalizationServices(client) {
6058
6382
 
6059
6383
  // src/adapters/viem/resources/withdrawals/index.ts
6060
6384
  var ROUTES2 = {
6061
- "eth-base": routeEthBase(),
6385
+ base: routeEthBase(),
6062
6386
  // BaseTokenSystem.withdraw, chain base = ETH
6063
- "eth-nonbase": routeEthNonBase2(),
6064
- // BaseTokenSystem.withdraw, chain base ≠ ETH
6065
6387
  "erc20-nonbase": routeErc20NonBase2()
6066
6388
  // AssetRouter.withdraw for non-base ERC-20s
6067
6389
  };
@@ -6071,27 +6393,19 @@ function createWithdrawalsResource(client) {
6071
6393
  async function buildPlan(p) {
6072
6394
  const ctx = await commonCtx2(p, client);
6073
6395
  await ROUTES2[ctx.route].preflight?.(p, ctx);
6074
- const { steps, approvals } = await ROUTES2[ctx.route].build(p, ctx);
6075
- const resolveGasLimit = () => {
6076
- if (ctx.fee.gasLimit != null) return ctx.fee.gasLimit;
6077
- for (let i = steps.length - 1; i >= 0; i--) {
6078
- const candidate = steps[i].tx.gas;
6079
- if (candidate != null) return candidate;
6080
- }
6081
- return void 0;
6082
- };
6083
- const gasLimit = resolveGasLimit();
6084
- const summary = {
6396
+ const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
6397
+ return {
6085
6398
  route: ctx.route,
6086
- approvalsNeeded: approvals,
6087
- suggestedL2GasLimit: ctx.l2GasLimit,
6088
- fees: {
6089
- gasLimit,
6090
- maxFeePerGas: ctx.fee.maxFeePerGas,
6091
- maxPriorityFeePerGas: ctx.fee.maxPriorityFeePerGas
6092
- }
6399
+ summary: {
6400
+ route: ctx.route,
6401
+ approvalsNeeded: approvals,
6402
+ amounts: {
6403
+ transfer: { token: p.token, amount: p.amount }
6404
+ },
6405
+ fees
6406
+ },
6407
+ steps
6093
6408
  };
6094
- return { route: ctx.route, summary, steps };
6095
6409
  }
6096
6410
  const finalizeCache = /* @__PURE__ */ new Map();
6097
6411
  const quote = (p) => wrap2(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
@@ -6125,7 +6439,7 @@ function createWithdrawalsResource(client) {
6125
6439
  }
6126
6440
  if (overrides.gasLimit != null) step.tx.gas = overrides.gasLimit;
6127
6441
  }
6128
- if (step.tx.gas == null) {
6442
+ if (!p.l2TxOverrides?.gasLimit) {
6129
6443
  try {
6130
6444
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
6131
6445
  maxFeePerGas: step.tx.maxFeePerGas,