@matterlabs/zksync-js 0.0.1 → 0.0.3

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 (103) 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 +1279 -925
  7. package/dist/adapters/ethers/index.cjs.map +1 -1
  8. package/dist/adapters/ethers/index.d.ts +1 -0
  9. package/dist/adapters/ethers/index.js +9 -8
  10. package/dist/adapters/ethers/resources/contracts/contracts.d.ts +9 -0
  11. package/dist/adapters/ethers/resources/contracts/index.d.ts +2 -0
  12. package/dist/adapters/ethers/resources/contracts/types.d.ts +60 -0
  13. package/dist/adapters/ethers/resources/deposits/context.d.ts +21 -7
  14. package/dist/adapters/ethers/resources/deposits/index.d.ts +3 -1
  15. package/dist/adapters/ethers/resources/deposits/routes/types.d.ts +2 -6
  16. package/dist/adapters/ethers/resources/deposits/services/fee.d.ts +6 -0
  17. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +41 -0
  18. package/dist/adapters/ethers/resources/tokens/index.d.ts +1 -0
  19. package/dist/adapters/ethers/resources/tokens/tokens.d.ts +10 -0
  20. package/dist/adapters/ethers/resources/utils.d.ts +3 -17
  21. package/dist/adapters/ethers/resources/withdrawals/context.d.ts +15 -7
  22. package/dist/adapters/ethers/resources/withdrawals/index.d.ts +3 -1
  23. package/dist/adapters/ethers/resources/withdrawals/routes/types.d.ts +2 -2
  24. package/dist/adapters/ethers/resources/withdrawals/services/fees.d.ts +14 -0
  25. package/dist/adapters/ethers/resources/withdrawals/services/gas.d.ts +12 -0
  26. package/dist/adapters/ethers/sdk.cjs +1388 -1501
  27. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  28. package/dist/adapters/ethers/sdk.d.ts +5 -22
  29. package/dist/adapters/ethers/sdk.js +7 -6
  30. package/dist/adapters/viem/client.cjs.map +1 -1
  31. package/dist/adapters/viem/client.d.ts +1 -1
  32. package/dist/adapters/viem/client.js +4 -5
  33. package/dist/adapters/viem/estimator.d.ts +4 -0
  34. package/dist/adapters/viem/index.cjs +1233 -744
  35. package/dist/adapters/viem/index.cjs.map +1 -1
  36. package/dist/adapters/viem/index.d.ts +3 -0
  37. package/dist/adapters/viem/index.js +8 -8
  38. package/dist/adapters/viem/resources/contracts/contracts.d.ts +9 -0
  39. package/dist/adapters/viem/resources/contracts/index.d.ts +2 -0
  40. package/dist/adapters/viem/resources/contracts/types.d.ts +61 -0
  41. package/dist/adapters/viem/resources/deposits/context.d.ts +21 -7
  42. package/dist/adapters/viem/resources/deposits/index.d.ts +3 -1
  43. package/dist/adapters/viem/resources/deposits/routes/types.d.ts +2 -6
  44. package/dist/adapters/viem/resources/deposits/services/fee.d.ts +6 -0
  45. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +37 -0
  46. package/dist/adapters/viem/resources/tokens/index.d.ts +1 -0
  47. package/dist/adapters/viem/resources/tokens/tokens.d.ts +3 -0
  48. package/dist/adapters/viem/resources/utils.d.ts +3 -19
  49. package/dist/adapters/viem/resources/withdrawals/context.d.ts +14 -9
  50. package/dist/adapters/viem/resources/withdrawals/index.d.ts +3 -1
  51. package/dist/adapters/viem/resources/withdrawals/routes/types.d.ts +12 -2
  52. package/dist/adapters/viem/resources/withdrawals/services/fee.d.ts +17 -0
  53. package/dist/adapters/viem/resources/withdrawals/services/gas.d.ts +12 -0
  54. package/dist/adapters/viem/sdk.cjs +1225 -699
  55. package/dist/adapters/viem/sdk.cjs.map +1 -1
  56. package/dist/adapters/viem/sdk.d.ts +5 -25
  57. package/dist/adapters/viem/sdk.js +6 -6
  58. package/dist/{chunk-3LALBFFE.js → chunk-3MRGU4HV.js} +9 -5
  59. package/dist/{chunk-CGO27P7F.js → chunk-5YWP4CZP.js} +849 -835
  60. package/dist/{chunk-4HLJJKIY.js → chunk-6K6VJQAL.js} +2 -2
  61. package/dist/{chunk-6GCT6TLS.js → chunk-F2ENUV3A.js} +13 -1
  62. package/dist/{chunk-7M4V3FMT.js → chunk-JXUFGIJG.js} +986 -678
  63. package/dist/chunk-LL3WKCFJ.js +231 -0
  64. package/dist/{chunk-Y75OMFK6.js → chunk-M5J2MM2U.js} +351 -1
  65. package/dist/{chunk-263G6636.js → chunk-NCAIVYBR.js} +1 -14
  66. package/dist/{chunk-DI2CJDPZ.js → chunk-NEC2ZKHI.js} +5 -13
  67. package/dist/chunk-NTEIA5KA.js +13 -0
  68. package/dist/chunk-XRE7H466.js +157 -0
  69. package/dist/{chunk-BD2LUO5T.js → chunk-YUK547UF.js} +3 -3
  70. package/dist/core/abi.d.ts +9 -0
  71. package/dist/core/adapters/interfaces.d.ts +25 -0
  72. package/dist/core/codec/ntv.d.ts +48 -0
  73. package/dist/core/constants.cjs +12 -0
  74. package/dist/core/constants.cjs.map +1 -1
  75. package/dist/core/constants.d.ts +6 -0
  76. package/dist/core/constants.js +1 -1
  77. package/dist/core/index.cjs +4508 -1
  78. package/dist/core/index.cjs.map +1 -1
  79. package/dist/core/index.d.ts +2 -0
  80. package/dist/core/index.js +5 -4
  81. package/dist/core/resources/deposits/fee.d.ts +15 -0
  82. package/dist/core/resources/deposits/gas.d.ts +38 -0
  83. package/dist/core/resources/withdrawals/gas.d.ts +14 -0
  84. package/dist/core/types/errors.d.ts +1 -1
  85. package/dist/core/types/fees.d.ts +40 -0
  86. package/dist/core/types/flows/base.d.ts +0 -10
  87. package/dist/core/types/flows/deposits.d.ts +20 -6
  88. package/dist/core/types/flows/route.d.ts +2 -3
  89. package/dist/core/types/flows/token.d.ts +192 -0
  90. package/dist/core/types/flows/withdrawals.d.ts +12 -6
  91. package/dist/core/utils/addr.d.ts +2 -0
  92. package/dist/index.cjs +4520 -1
  93. package/dist/index.cjs.map +1 -1
  94. package/dist/index.d.ts +2 -0
  95. package/dist/index.js +5 -4
  96. package/package.json +5 -1
  97. package/dist/adapters/ethers/resources/token-info.d.ts +0 -31
  98. package/dist/adapters/ethers/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  99. package/dist/adapters/viem/resources/token-info.d.ts +0 -34
  100. package/dist/adapters/viem/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  101. package/dist/chunk-B77GWPO5.js +0 -339
  102. package/dist/core/internal/abi-registry.d.ts +0 -9
  103. package/dist/core/utils/gas.d.ts +0 -13
@@ -3645,6 +3645,48 @@ var MailboxABI = [
3645
3645
  { type: "error", name: "ZeroGasPriceL1TxZKSyncOS", inputs: [] }
3646
3646
  ];
3647
3647
  var Mailbox_default = MailboxABI;
3648
+
3649
+ // src/adapters/viem/resources/deposits/context.ts
3650
+ async function commonCtx(p, client, tokens, contracts) {
3651
+ const { bridgehub, l1AssetRouter } = await contracts.addresses();
3652
+ const chainId = await client.l2.getChainId();
3653
+ const sender = client.account.address;
3654
+ const gasPerPubdata = p.gasPerPubdata ?? 800n;
3655
+ const operatorTip = p.operatorTip ?? 0n;
3656
+ const refundRecipient = p.refundRecipient ?? sender;
3657
+ const resolvedToken = await tokens.resolve(p.token, { chain: "l1" });
3658
+ const baseTokenAssetId = resolvedToken.baseTokenAssetId;
3659
+ const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
3660
+ const baseIsEth = resolvedToken.isChainEthBased;
3661
+ const route = (() => {
3662
+ if (resolvedToken.kind === "eth") {
3663
+ return baseIsEth ? "eth-base" : "eth-nonbase";
3664
+ }
3665
+ if (resolvedToken.kind === "base") {
3666
+ return baseIsEth ? "eth-base" : "erc20-base";
3667
+ }
3668
+ return "erc20-nonbase";
3669
+ })();
3670
+ return {
3671
+ client,
3672
+ tokens,
3673
+ contracts,
3674
+ resolvedToken,
3675
+ baseTokenAssetId,
3676
+ baseTokenL1,
3677
+ baseIsEth,
3678
+ l1AssetRouter,
3679
+ route,
3680
+ bridgehub,
3681
+ chainIdL2: BigInt(chainId),
3682
+ sender,
3683
+ gasOverrides: p.l1TxOverrides,
3684
+ l2GasLimit: p.l2GasLimit,
3685
+ gasPerPubdata,
3686
+ operatorTip,
3687
+ refundRecipient
3688
+ };
3689
+ }
3648
3690
  var k256hex = (s) => `0x${utils.bytesToHex(sha3.keccak_256(utils.utf8ToBytes(s)))}`.toLowerCase();
3649
3691
  var FORMAL_ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
3650
3692
  var ETH_ADDRESS = "0x0000000000000000000000000000000000000001";
@@ -3656,123 +3698,14 @@ var TOPIC_L1_MESSAGE_SENT_NEW = k256hex("L1MessageSent(uint256,bytes32,bytes)");
3656
3698
  var TOPIC_L1_MESSAGE_SENT_LEG = k256hex("L1MessageSent(address,bytes32,bytes)");
3657
3699
  var TOPIC_CANONICAL_ASSIGNED = "0x779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df";
3658
3700
  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
- }
3701
+ var BUFFER = 20n;
3702
+ var TX_OVERHEAD_GAS = 10000n;
3703
+ var TX_MEMORY_OVERHEAD_GAS = 10n;
3704
+ var DEFAULT_PUBDATA_BYTES = 155n;
3705
+ var DEFAULT_ABI_BYTES = 400n;
3706
+ var SAFE_L1_BRIDGE_GAS = 600000n;
3672
3707
 
3673
3708
  // 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
3709
  function encodeSecondBridgeArgs(token, amount, l2Receiver) {
3777
3710
  return viem.encodeAbiParameters(
3778
3711
  [
@@ -3789,58 +3722,17 @@ function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
3789
3722
  function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
3790
3723
  return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
3791
3724
  }
3792
-
3793
- // src/core/utils/addr.ts
3794
- var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
3795
- function isAddressEq(a, b) {
3796
- return a.toLowerCase() === b.toLowerCase();
3797
- }
3798
- function isETH(token) {
3799
- return isAddressEq(token, FORMAL_ETH_ADDRESS) || isAddressEq(token, L2_BASE_TOKEN_ADDRESS) || isAddressEq(token, ETH_ADDRESS);
3800
- }
3801
- function normalizeAddrEq(a, b) {
3802
- if (!a || !b) return false;
3803
- const normalize = (s) => {
3804
- const hasPrefix = s.slice(0, 2).toLowerCase() === "0x";
3805
- const body = hasPrefix ? s.slice(2) : s;
3806
- return `0x${body.toLowerCase()}`;
3807
- };
3808
- return normalize(a) === normalize(b);
3809
- }
3810
-
3811
- // src/core/resources/deposits/route.ts
3812
- async function pickDepositRoute(client, chainIdL2, token) {
3813
- if (isETH(token)) {
3814
- const base2 = await client.baseToken(chainIdL2);
3815
- return isETH(base2) ? "eth-base" : "eth-nonbase";
3816
- }
3817
- const base = await client.baseToken(chainIdL2);
3818
- return normalizeAddrEq(token, base) ? "erc20-base" : "erc20-nonbase";
3819
- }
3820
-
3821
- // src/adapters/viem/resources/deposits/context.ts
3822
- async function commonCtx(p, client) {
3823
- const { bridgehub, l1AssetRouter } = await client.ensureAddresses();
3824
- const chainId = await client.l2.getChainId();
3825
- const sender = client.account.address;
3826
- const fee = await getFeeOverrides(client, p.l1TxOverrides);
3827
- const l2GasLimit = p.l2GasLimit ?? 300000n;
3828
- const gasPerPubdata = p.gasPerPubdata ?? 800n;
3829
- const operatorTip = p.operatorTip ?? 0n;
3830
- const refundRecipient = p.refundRecipient ?? sender;
3831
- const route = await pickDepositRoute(client, BigInt(chainId), p.token);
3725
+ function buildDirectRequestStruct(args) {
3832
3726
  return {
3833
- client,
3834
- l1AssetRouter,
3835
- route,
3836
- bridgehub,
3837
- chainIdL2: BigInt(chainId),
3838
- sender,
3839
- fee,
3840
- l2GasLimit,
3841
- gasPerPubdata,
3842
- operatorTip,
3843
- refundRecipient
3727
+ chainId: args.chainId,
3728
+ l2Contract: args.l2Contract,
3729
+ mintValue: args.mintValue,
3730
+ l2Value: args.l2Value,
3731
+ l2Calldata: "0x",
3732
+ l2GasLimit: args.l2GasLimit,
3733
+ l2GasPerPubdataByteLimit: args.gasPerPubdata,
3734
+ factoryDeps: [],
3735
+ refundRecipient: args.refundRecipient
3844
3736
  };
3845
3737
  }
3846
3738
 
@@ -4055,27 +3947,21 @@ var OP_DEPOSITS = {
4055
3947
  base: {
4056
3948
  assertErc20Asset: "deposits.erc20-base:assertErc20Asset",
4057
3949
  assertMatchesBase: "deposits.erc20-base:assertMatchesBase",
4058
- baseToken: "deposits.erc20-base:baseToken",
4059
3950
  allowance: "deposits.erc20-base:allowance",
4060
- baseCost: "deposits.erc20-base:l2TransactionBaseCost",
4061
3951
  estGas: "deposits.erc20-base:estimateGas"
4062
3952
  },
4063
3953
  nonbase: {
4064
- baseToken: "deposits.erc20-nonbase:baseToken",
4065
3954
  assertNotEthAsset: "deposits.erc20-nonbase:assertNotEthAsset",
4066
- allowance: "deposits.erc20-nonbase:allowance",
4067
- allowanceFees: "deposits.erc20-nonbase:allowanceFeesBaseToken",
4068
- baseCost: "deposits.erc20-nonbase:l2TransactionBaseCost",
4069
3955
  encodeCalldata: "deposits.erc20-nonbase:encodeSecondBridgeErc20Args",
4070
3956
  estGas: "deposits.erc20-nonbase:estimateGas",
4071
- assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken"},
3957
+ assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken",
3958
+ allowanceToken: "deposits.erc20-nonbase:allowanceToken",
3959
+ allowanceBase: "deposits.erc20-nonbase:allowanceBase"
3960
+ },
4072
3961
  eth: {
4073
- baseCost: "deposits.eth:l2TransactionBaseCost",
4074
3962
  estGas: "deposits.eth:estimateGas"
4075
3963
  },
4076
3964
  ethNonBase: {
4077
- baseToken: "deposits.eth-nonbase:baseToken",
4078
- baseCost: "deposits.eth-nonbase:l2TransactionBaseCost",
4079
3965
  allowanceBase: "deposits.eth-nonbase:allowanceBaseToken",
4080
3966
  ethBalance: "deposits.eth-nonbase:getEthBalance",
4081
3967
  encodeCalldata: "deposits.eth-nonbase:encodeSecondBridgeEthArgs",
@@ -4313,7 +4199,7 @@ function createErrorHandlers(resource) {
4313
4199
  function wrap2(operation, fn, opts) {
4314
4200
  return run("INTERNAL", operation, fn, opts);
4315
4201
  }
4316
- function wrapAs9(kind, operation, fn, opts) {
4202
+ function wrapAs10(kind, operation, fn, opts) {
4317
4203
  return run(kind, operation, fn, opts);
4318
4204
  }
4319
4205
  async function toResult2(operation, fn, opts) {
@@ -4334,44 +4220,358 @@ function createErrorHandlers(resource) {
4334
4220
  return { ok: false, error: shaped };
4335
4221
  }
4336
4222
  }
4337
- return { wrap: wrap2, wrapAs: wrapAs9, toResult: toResult2 };
4223
+ return { wrap: wrap2, wrapAs: wrapAs10, toResult: toResult2 };
4338
4224
  }
4339
4225
 
4340
- // src/adapters/viem/resources/deposits/routes/eth.ts
4226
+ // src/core/resources/deposits/gas.ts
4227
+ function makeGasQuote(p) {
4228
+ const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
4229
+ return {
4230
+ gasLimit: p.gasLimit,
4231
+ maxFeePerGas: p.maxFeePerGas,
4232
+ maxPriorityFeePerGas,
4233
+ gasPerPubdata: p.gasPerPubdata,
4234
+ maxCost: p.gasLimit * p.maxFeePerGas
4235
+ };
4236
+ }
4237
+ async function fetchFees(estimator) {
4238
+ try {
4239
+ const fees = await estimator.estimateFeesPerGas();
4240
+ if (fees.maxFeePerGas != null) {
4241
+ return {
4242
+ maxFeePerGas: fees.maxFeePerGas,
4243
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
4244
+ };
4245
+ }
4246
+ if (fees.gasPrice != null) {
4247
+ return {
4248
+ maxFeePerGas: fees.gasPrice,
4249
+ maxPriorityFeePerGas: 0n
4250
+ };
4251
+ }
4252
+ } catch {
4253
+ }
4254
+ try {
4255
+ const gp = await estimator.getGasPrice();
4256
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
4257
+ } catch {
4258
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
4259
+ }
4260
+ }
4261
+ async function quoteL1Gas(input) {
4262
+ const { estimator, tx, overrides, fallbackGasLimit } = input;
4263
+ let market;
4264
+ const getMarket = async () => {
4265
+ if (market) return market;
4266
+ market = await fetchFees(estimator);
4267
+ return market;
4268
+ };
4269
+ const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
4270
+ const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
4271
+ const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
4272
+ if (explicitGasLimit != null) {
4273
+ return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4274
+ }
4275
+ try {
4276
+ const est = await estimator.estimateGas(tx);
4277
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
4278
+ return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
4279
+ } catch (err) {
4280
+ if (fallbackGasLimit != null) {
4281
+ return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4282
+ }
4283
+ console.warn("L1 gas estimation failed", err);
4284
+ return void 0;
4285
+ }
4286
+ }
4287
+ async function quoteL2Gas(input) {
4288
+ const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
4289
+ const market = await fetchFees(estimator);
4290
+ const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
4291
+ const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
4292
+ const explicit = overrideGasLimit ?? txGasLimit;
4293
+ if (explicit != null) {
4294
+ return makeGasQuote({
4295
+ gasLimit: explicit,
4296
+ maxFeePerGas,
4297
+ gasPerPubdata
4298
+ });
4299
+ }
4300
+ if (!tx) {
4301
+ return makeGasQuote({
4302
+ gasLimit: l2GasLimit ?? 0n,
4303
+ maxFeePerGas,
4304
+ gasPerPubdata
4305
+ });
4306
+ }
4307
+ try {
4308
+ const execEstimate = await estimator.estimateGas(tx, stateOverrides);
4309
+ const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
4310
+ const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
4311
+ const pp = gasPerPubdata ?? 800n;
4312
+ const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
4313
+ const pubdataOverhead = pubdataBytes * pp;
4314
+ let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
4315
+ total = total * (100n + BUFFER) / 100n;
4316
+ return makeGasQuote({
4317
+ gasLimit: total,
4318
+ maxFeePerGas,
4319
+ gasPerPubdata: pp
4320
+ });
4321
+ } catch (err) {
4322
+ console.warn("L2 gas estimation failed", err);
4323
+ return makeGasQuote({
4324
+ gasLimit: l2GasLimit ?? 0n,
4325
+ maxFeePerGas,
4326
+ gasPerPubdata
4327
+ });
4328
+ }
4329
+ }
4330
+
4331
+ // src/adapters/viem/estimator.ts
4332
+ function toCoreTx(tx) {
4333
+ return {
4334
+ to: tx.to,
4335
+ from: tx.from,
4336
+ data: tx.data,
4337
+ value: tx.value,
4338
+ gasLimit: tx.gas,
4339
+ maxFeePerGas: tx.maxFeePerGas,
4340
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4341
+ };
4342
+ }
4343
+ function viemToGasEstimator(client) {
4344
+ return {
4345
+ async estimateGas(tx, stateOverrides) {
4346
+ if (stateOverrides) {
4347
+ try {
4348
+ const result = await client.request({
4349
+ method: "eth_estimateGas",
4350
+ params: [
4351
+ {
4352
+ from: tx.from,
4353
+ to: tx.to,
4354
+ data: tx.data,
4355
+ value: tx.value,
4356
+ gas: tx.gasLimit,
4357
+ maxFeePerGas: tx.maxFeePerGas,
4358
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4359
+ },
4360
+ "latest",
4361
+ stateOverrides
4362
+ ]
4363
+ });
4364
+ return BigInt(result);
4365
+ } catch (error) {
4366
+ console.warn(
4367
+ "Failed to estimate gas with state overrides, falling back to standard estimation:",
4368
+ error
4369
+ );
4370
+ }
4371
+ }
4372
+ return await client.estimateGas({
4373
+ account: tx.from,
4374
+ to: tx.to,
4375
+ data: tx.data,
4376
+ value: tx.value,
4377
+ gas: tx.gasLimit,
4378
+ maxFeePerGas: tx.maxFeePerGas,
4379
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
4380
+ });
4381
+ },
4382
+ async estimateFeesPerGas() {
4383
+ try {
4384
+ const fees = await client.estimateFeesPerGas();
4385
+ return {
4386
+ maxFeePerGas: fees.maxFeePerGas,
4387
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas
4388
+ };
4389
+ } catch {
4390
+ }
4391
+ try {
4392
+ const gp = await client.getGasPrice();
4393
+ return { gasPrice: gp };
4394
+ } catch {
4395
+ return {};
4396
+ }
4397
+ },
4398
+ async getGasPrice() {
4399
+ return await client.getGasPrice();
4400
+ },
4401
+ async call(tx) {
4402
+ const res = await client.call({
4403
+ to: tx.to,
4404
+ data: tx.data,
4405
+ value: tx.value,
4406
+ account: tx.from
4407
+ });
4408
+ return res.data ?? "0x";
4409
+ }
4410
+ };
4411
+ }
4412
+
4413
+ // src/adapters/viem/resources/deposits/services/gas.ts
4414
+ async function quoteL1Gas2(input) {
4415
+ const { ctx, tx, overrides, fallbackGasLimit } = input;
4416
+ const estimator = viemToGasEstimator(ctx.client.l1);
4417
+ return quoteL1Gas({
4418
+ estimator,
4419
+ tx: toCoreTx(tx),
4420
+ overrides,
4421
+ fallbackGasLimit
4422
+ });
4423
+ }
4424
+ async function quoteL2Gas2(input) {
4425
+ const { ctx, route, l2TxForModeling, overrideGasLimit } = input;
4426
+ const estimator = viemToGasEstimator(ctx.client.l2);
4427
+ return quoteL2Gas({
4428
+ estimator,
4429
+ route,
4430
+ tx: l2TxForModeling ? toCoreTx(l2TxForModeling) : void 0,
4431
+ gasPerPubdata: ctx.gasPerPubdata,
4432
+ l2GasLimit: ctx.l2GasLimit,
4433
+ // TODO: investigate if this should be passed here; weird viem quirk
4434
+ overrideGasLimit,
4435
+ stateOverrides: input.stateOverrides
4436
+ });
4437
+ }
4438
+ async function determineErc20L2Gas(input) {
4439
+ const { ctx, l1Token } = input;
4440
+ const DEFAULT_SAFE_L2_GAS_LIMIT = 3000000n;
4441
+ if (ctx.l2GasLimit != null) {
4442
+ return quoteL2Gas2({
4443
+ ctx,
4444
+ route: "erc20-nonbase",
4445
+ overrideGasLimit: ctx.l2GasLimit
4446
+ });
4447
+ }
4448
+ try {
4449
+ const l2TokenAddress = ctx.tokens ? await ctx.tokens.toL2Address(l1Token) : await (await ctx.contracts.l2NativeTokenVault()).read.l2TokenAddress([l1Token]);
4450
+ if (l2TokenAddress === viem.zeroAddress) {
4451
+ return quoteL2Gas2({
4452
+ ctx,
4453
+ route: "erc20-nonbase",
4454
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4455
+ });
4456
+ }
4457
+ const modelTx = {
4458
+ to: input.modelTx?.to ?? ctx.sender,
4459
+ from: input.modelTx?.from ?? ctx.sender,
4460
+ data: input.modelTx?.data ?? "0x",
4461
+ value: input.modelTx?.value ?? 0n
4462
+ };
4463
+ const gas = await quoteL2Gas2({
4464
+ ctx,
4465
+ route: "erc20-nonbase",
4466
+ l2TxForModeling: modelTx
4467
+ });
4468
+ if (!gas) {
4469
+ return quoteL2Gas2({
4470
+ ctx,
4471
+ route: "erc20-nonbase",
4472
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4473
+ });
4474
+ }
4475
+ return gas;
4476
+ } catch (err) {
4477
+ console.warn("Failed to determine ERC20 L2 gas; defaulting to safe gas limit.", err);
4478
+ return quoteL2Gas2({
4479
+ ctx,
4480
+ route: "erc20-nonbase",
4481
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
4482
+ });
4483
+ }
4484
+ }
4485
+
4486
+ // src/adapters/viem/resources/deposits/services/fee.ts
4341
4487
  var { wrapAs } = createErrorHandlers("deposits");
4488
+ async function quoteL2BaseCost(input) {
4489
+ const { ctx, l2GasLimit } = input;
4490
+ const estimator = viemToGasEstimator(ctx.client.l1);
4491
+ const fees = await estimator.estimateFeesPerGas();
4492
+ const gasPrice = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
4493
+ return wrapAs(
4494
+ "RPC",
4495
+ "deposits.fees.l2BaseCost",
4496
+ async () => {
4497
+ return await ctx.client.l1.readContract({
4498
+ address: ctx.bridgehub,
4499
+ abi: IBridgehub_default,
4500
+ functionName: "l2TransactionBaseCost",
4501
+ args: [ctx.chainIdL2, gasPrice, l2GasLimit, ctx.gasPerPubdata]
4502
+ });
4503
+ },
4504
+ { ctx: { chainIdL2: ctx.chainIdL2 } }
4505
+ );
4506
+ }
4507
+
4508
+ // src/core/resources/deposits/fee.ts
4509
+ function buildFeeBreakdown(p) {
4510
+ const l1MaxTotal = p.l1Gas?.maxCost ?? 0n;
4511
+ const l2Total = p.l2BaseCost + p.operatorTip;
4512
+ const l1 = {
4513
+ gasLimit: p.l1Gas?.gasLimit ?? 0n,
4514
+ maxFeePerGas: p.l1Gas?.maxFeePerGas ?? 0n,
4515
+ maxPriorityFeePerGas: p.l1Gas?.maxPriorityFeePerGas,
4516
+ maxTotal: l1MaxTotal
4517
+ };
4518
+ const l2 = {
4519
+ total: l2Total,
4520
+ baseCost: p.l2BaseCost,
4521
+ operatorTip: p.operatorTip,
4522
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
4523
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
4524
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas,
4525
+ gasPerPubdata: p.l2Gas?.gasPerPubdata ?? 0n
4526
+ };
4527
+ return {
4528
+ token: p.feeToken,
4529
+ maxTotal: l1MaxTotal + l2Total,
4530
+ mintValue: p.mintValue,
4531
+ l1,
4532
+ l2
4533
+ };
4534
+ }
4535
+
4536
+ // src/adapters/viem/resources/deposits/routes/eth.ts
4537
+ var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
4342
4538
  function routeEthDirect() {
4343
4539
  return {
4344
4540
  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."
4541
+ const l2TxModel = {
4542
+ to: p.to ?? ctx.sender,
4543
+ from: ctx.sender,
4544
+ data: "0x",
4545
+ value: p.amount
4546
+ };
4547
+ const l2GasParams = await quoteL2Gas2({
4548
+ ctx,
4549
+ route: "eth-base",
4550
+ l2TxForModeling: l2TxModel,
4551
+ overrideGasLimit: ctx.l2GasLimit,
4552
+ stateOverrides: {
4553
+ [ctx.sender]: {
4554
+ balance: "0xffffffffffffffffffff"
4555
+ }
4359
4556
  }
4360
- );
4361
- const baseCost = rawBaseCost;
4557
+ });
4558
+ if (!l2GasParams) {
4559
+ throw new Error("Failed to estimate L2 gas for deposit.");
4560
+ }
4561
+ const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
4362
4562
  const l2Contract = p.to ?? ctx.sender;
4363
4563
  const l2Value = p.amount;
4364
4564
  const mintValue = baseCost + ctx.operatorTip + l2Value;
4365
4565
  const req = buildDirectRequestStruct({
4366
4566
  chainId: ctx.chainIdL2,
4367
4567
  mintValue,
4368
- l2GasLimit: ctx.l2GasLimit,
4568
+ l2GasLimit: l2GasParams.gasLimit,
4369
4569
  gasPerPubdata: ctx.gasPerPubdata,
4370
4570
  refundRecipient: ctx.refundRecipient,
4371
4571
  l2Contract,
4372
4572
  l2Value
4373
4573
  });
4374
- const sim = await wrapAs(
4574
+ const sim = await wrapAs2(
4375
4575
  "RPC",
4376
4576
  OP_DEPOSITS.eth.estGas,
4377
4577
  () => ctx.client.l1.simulateContract({
@@ -4387,120 +4587,138 @@ function routeEthDirect() {
4387
4587
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
4388
4588
  }
4389
4589
  );
4390
- const resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
4590
+ const data = viem.encodeFunctionData({
4591
+ abi: sim.request.abi,
4592
+ functionName: sim.request.functionName,
4593
+ args: sim.request.args
4594
+ });
4595
+ const l1TxCandidate = {
4596
+ to: ctx.bridgehub,
4597
+ data,
4598
+ value: mintValue,
4599
+ from: ctx.sender,
4600
+ ...ctx.gasOverrides
4601
+ };
4602
+ const l1Gas = await quoteL1Gas2({
4603
+ ctx,
4604
+ tx: l1TxCandidate,
4605
+ overrides: ctx.gasOverrides
4606
+ });
4391
4607
  const steps = [
4392
4608
  {
4393
4609
  key: "bridgehub:direct",
4394
4610
  kind: "bridgehub:direct",
4395
4611
  description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
4396
- tx: { ...sim.request, ...txFeeOverrides }
4612
+ tx: { ...sim.request, ...l1Gas }
4397
4613
  }
4398
4614
  ];
4615
+ const fees = buildFeeBreakdown({
4616
+ feeToken: ETH_ADDRESS,
4617
+ l1Gas,
4618
+ l2Gas: l2GasParams,
4619
+ l2BaseCost: baseCost,
4620
+ operatorTip: ctx.operatorTip,
4621
+ mintValue
4622
+ });
4399
4623
  return {
4400
4624
  steps,
4401
4625
  approvals: [],
4402
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
4626
+ fees
4403
4627
  };
4404
4628
  }
4405
4629
  };
4406
4630
  }
4407
4631
 
4632
+ // src/core/utils/addr.ts
4633
+ var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
4634
+ function isAddressEq(a, b) {
4635
+ return a.toLowerCase() === b.toLowerCase();
4636
+ }
4637
+ function isETH(token) {
4638
+ return isAddressEq(token, FORMAL_ETH_ADDRESS) || isAddressEq(token, L2_BASE_TOKEN_ADDRESS) || isAddressEq(token, ETH_ADDRESS);
4639
+ }
4640
+ function normalizeAddrEq(a, b) {
4641
+ if (!a || !b) return false;
4642
+ const normalize = (s) => {
4643
+ const hasPrefix = s.slice(0, 2).toLowerCase() === "0x";
4644
+ const body = hasPrefix ? s.slice(2) : s;
4645
+ return `0x${body.toLowerCase()}`;
4646
+ };
4647
+ return normalize(a) === normalize(b);
4648
+ }
4649
+ var hexEq = (a, b) => a.toLowerCase() === b.toLowerCase();
4650
+ var normalizeL1Token = (token) => isAddressEq(token, FORMAL_ETH_ADDRESS) ? ETH_ADDRESS : token;
4651
+
4408
4652
  // 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;
4653
+ var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
4413
4654
  function routeErc20NonBase() {
4414
4655
  return {
4656
+ // TODO: do we even need these validations?
4415
4657
  async preflight(p, ctx) {
4416
- await wrapAs2(
4658
+ await wrapAs3(
4417
4659
  "VALIDATION",
4418
4660
  OP_DEPOSITS.nonbase.assertNotEthAsset,
4419
4661
  () => {
4420
- if (isETH(p.token)) {
4662
+ if (ctx.resolvedToken?.kind === "eth" || isETH(p.token)) {
4421
4663
  throw new Error("erc20-nonbase route requires an ERC-20 token (not ETH).");
4422
4664
  }
4423
4665
  },
4424
4666
  { ctx: { token: p.token } }
4425
4667
  );
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(
4668
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
4669
+ await wrapAs3(
4438
4670
  "VALIDATION",
4439
4671
  OP_DEPOSITS.nonbase.assertNonBaseToken,
4440
4672
  () => {
4441
- if (normalizeAddrEq(baseToken, p.token)) {
4673
+ if (ctx.resolvedToken?.kind === "base" || normalizeAddrEq(baseToken, p.token)) {
4442
4674
  throw new Error("erc20-nonbase route requires a non-base ERC-20 deposit token.");
4443
4675
  }
4444
4676
  },
4445
4677
  { ctx: { depositToken: p.token, baseToken } }
4446
4678
  );
4447
- return;
4448
4679
  },
4449
4680
  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);
4681
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
4682
+ const baseIsEth = ctx.baseIsEth ?? isETH(baseToken);
4683
+ const assetRouter = ctx.l1AssetRouter;
4684
+ const l2Gas = await determineErc20L2Gas({
4685
+ ctx,
4686
+ l1Token: p.token,
4687
+ modelTx: {
4688
+ to: p.to ?? ctx.sender,
4689
+ from: ctx.sender,
4690
+ data: "0x",
4691
+ value: 0n
4692
+ }
4693
+ });
4694
+ if (!l2Gas) throw new Error("Failed to establish L2 gas parameters.");
4695
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
4696
+ const mintValue = l2BaseCost + ctx.operatorTip;
4478
4697
  const approvals = [];
4479
4698
  const steps = [];
4480
- const depositAllowance = await wrapAs2(
4699
+ const depositAllowance = await wrapAs3(
4481
4700
  "CONTRACT",
4482
- OP_DEPOSITS.nonbase.allowance,
4701
+ OP_DEPOSITS.nonbase.allowanceToken,
4483
4702
  () => ctx.client.l1.readContract({
4484
4703
  address: p.token,
4485
4704
  abi: IERC20_default,
4486
4705
  functionName: "allowance",
4487
- args: [ctx.sender, ctx.l1AssetRouter]
4706
+ args: [ctx.sender, assetRouter]
4488
4707
  }),
4489
4708
  {
4490
- ctx: { where: "erc20.allowance", token: p.token, spender: ctx.l1AssetRouter },
4491
- message: "Failed to read ERC-20 allowance for deposit token."
4709
+ ctx: { where: "erc20.allowance", token: p.token, spender: assetRouter },
4710
+ message: "Failed to read deposit-token allowance."
4492
4711
  }
4493
4712
  );
4494
- const needsDepositApprove = depositAllowance < p.amount;
4495
- if (needsDepositApprove) {
4496
- const approveDepReq = await wrapAs2(
4713
+ if (depositAllowance < p.amount) {
4714
+ const approveSim = await wrapAs3(
4497
4715
  "CONTRACT",
4498
4716
  OP_DEPOSITS.nonbase.estGas,
4499
4717
  () => ctx.client.l1.simulateContract({
4500
4718
  address: p.token,
4501
4719
  abi: IERC20_default,
4502
4720
  functionName: "approve",
4503
- args: [ctx.l1AssetRouter, p.amount],
4721
+ args: [assetRouter, p.amount],
4504
4722
  account: ctx.client.account
4505
4723
  }),
4506
4724
  {
@@ -4508,60 +4726,55 @@ function routeErc20NonBase() {
4508
4726
  message: "Failed to simulate deposit token approve."
4509
4727
  }
4510
4728
  );
4511
- approvals.push({ token: p.token, spender: ctx.l1AssetRouter, amount: p.amount });
4729
+ approvals.push({ token: p.token, spender: assetRouter, amount: p.amount });
4512
4730
  steps.push({
4513
- key: `approve:${p.token}:${ctx.l1AssetRouter}`,
4731
+ key: `approve:${p.token}:${assetRouter}`,
4514
4732
  kind: "approve",
4515
4733
  description: `Approve deposit token for amount`,
4516
- tx: { ...approveDepReq.request, ...txFeeOverrides }
4734
+ tx: { ...approveSim.request }
4517
4735
  });
4518
4736
  }
4519
- const baseIsEth = isETH(baseToken);
4520
- let msgValue = 0n;
4521
4737
  if (!baseIsEth) {
4522
- const baseAllowance = await wrapAs2(
4738
+ const baseAllowance = await wrapAs3(
4523
4739
  "CONTRACT",
4524
- OP_DEPOSITS.nonbase.allowanceFees,
4740
+ OP_DEPOSITS.nonbase.allowanceBase,
4525
4741
  () => ctx.client.l1.readContract({
4526
4742
  address: baseToken,
4527
4743
  abi: IERC20_default,
4528
4744
  functionName: "allowance",
4529
- args: [ctx.sender, ctx.l1AssetRouter]
4745
+ args: [ctx.sender, assetRouter]
4530
4746
  }),
4531
4747
  {
4532
- ctx: { where: "erc20.allowance", token: baseToken, spender: ctx.l1AssetRouter },
4748
+ ctx: { where: "erc20.allowance", token: baseToken, spender: assetRouter },
4533
4749
  message: "Failed to read base-token allowance."
4534
4750
  }
4535
4751
  );
4536
4752
  if (baseAllowance < mintValue) {
4537
- const approveBaseReq = await wrapAs2(
4753
+ const approveBaseSim = await wrapAs3(
4538
4754
  "CONTRACT",
4539
4755
  OP_DEPOSITS.nonbase.estGas,
4540
4756
  () => ctx.client.l1.simulateContract({
4541
4757
  address: baseToken,
4542
4758
  abi: IERC20_default,
4543
4759
  functionName: "approve",
4544
- args: [ctx.l1AssetRouter, mintValue],
4760
+ args: [assetRouter, mintValue],
4545
4761
  account: ctx.client.account
4546
4762
  }),
4547
4763
  {
4548
4764
  ctx: { where: "l1.simulateContract", to: baseToken },
4549
- message: "Failed to simulate base-token approve."
4765
+ message: "Failed to simulate base token approve."
4550
4766
  }
4551
4767
  );
4552
- approvals.push({ token: baseToken, spender: ctx.l1AssetRouter, amount: mintValue });
4768
+ approvals.push({ token: baseToken, spender: assetRouter, amount: mintValue });
4553
4769
  steps.push({
4554
- key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4770
+ key: `approve:${baseToken}:${assetRouter}`,
4555
4771
  kind: "approve",
4556
4772
  description: `Approve base token for mintValue`,
4557
- tx: { ...approveBaseReq.request, ...txFeeOverrides }
4773
+ tx: { ...approveBaseSim.request }
4558
4774
  });
4559
4775
  }
4560
- msgValue = 0n;
4561
- } else {
4562
- msgValue = mintValue;
4563
4776
  }
4564
- const secondBridgeCalldata = await wrapAs2(
4777
+ const secondBridgeCalldata = await wrapAs3(
4565
4778
  "INTERNAL",
4566
4779
  OP_DEPOSITS.nonbase.encodeCalldata,
4567
4780
  () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
@@ -4570,44 +4783,60 @@ function routeErc20NonBase() {
4570
4783
  where: "encodeSecondBridgeErc20Args",
4571
4784
  token: p.token,
4572
4785
  amount: p.amount.toString()
4573
- }
4786
+ },
4787
+ message: "Failed to encode bridging calldata."
4574
4788
  }
4575
4789
  );
4576
- const outer = {
4790
+ const requestStruct = {
4577
4791
  chainId: ctx.chainIdL2,
4578
4792
  mintValue,
4579
4793
  l2Value: 0n,
4580
- l2GasLimit: l2GasLimitUsed,
4794
+ l2GasLimit: l2Gas.gasLimit,
4581
4795
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
4582
4796
  refundRecipient: ctx.refundRecipient,
4583
- secondBridgeAddress: ctx.l1AssetRouter,
4797
+ secondBridgeAddress: assetRouter,
4584
4798
  secondBridgeValue: 0n,
4585
4799
  secondBridgeCalldata
4586
4800
  };
4801
+ const msgValue = baseIsEth ? mintValue : 0n;
4802
+ const calldata = viem.encodeFunctionData({
4803
+ abi: IBridgehub_default,
4804
+ functionName: "requestL2TransactionTwoBridges",
4805
+ args: [requestStruct]
4806
+ });
4807
+ const l1TxCandidate = {
4808
+ to: ctx.bridgehub,
4809
+ data: calldata,
4810
+ value: msgValue,
4811
+ from: ctx.sender,
4812
+ ...ctx.gasOverrides
4813
+ };
4814
+ const l1Gas = await quoteL1Gas2({
4815
+ ctx,
4816
+ tx: l1TxCandidate,
4817
+ overrides: ctx.gasOverrides,
4818
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
4819
+ });
4587
4820
  const approvalsNeeded = approvals.length > 0;
4588
4821
  let bridgeTx;
4589
- let resolvedL1GasLimit;
4590
- const gasOverride = txFeeOverrides.gas;
4591
4822
  if (approvalsNeeded) {
4592
4823
  bridgeTx = {
4593
4824
  address: ctx.bridgehub,
4594
4825
  abi: IBridgehub_default,
4595
4826
  functionName: "requestL2TransactionTwoBridges",
4596
- args: [outer],
4827
+ args: [requestStruct],
4597
4828
  value: msgValue,
4598
- account: ctx.client.account,
4599
- ...txFeeOverrides
4829
+ account: ctx.client.account
4600
4830
  };
4601
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
4602
4831
  } else {
4603
- const sim = await wrapAs2(
4832
+ const sim = await wrapAs3(
4604
4833
  "CONTRACT",
4605
4834
  OP_DEPOSITS.nonbase.estGas,
4606
4835
  () => ctx.client.l1.simulateContract({
4607
4836
  address: ctx.bridgehub,
4608
4837
  abi: IBridgehub_default,
4609
4838
  functionName: "requestL2TransactionTwoBridges",
4610
- args: [outer],
4839
+ args: [requestStruct],
4611
4840
  value: msgValue,
4612
4841
  account: ctx.client.account
4613
4842
  }),
@@ -4616,67 +4845,64 @@ function routeErc20NonBase() {
4616
4845
  message: "Failed to simulate two-bridges request."
4617
4846
  }
4618
4847
  );
4619
- bridgeTx = { ...sim.request, ...txFeeOverrides };
4620
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
4848
+ bridgeTx = { ...sim.request };
4849
+ }
4850
+ if (l1Gas) {
4851
+ bridgeTx = {
4852
+ ...bridgeTx,
4853
+ gas: l1Gas.gasLimit,
4854
+ maxFeePerGas: l1Gas.maxFeePerGas,
4855
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
4856
+ };
4621
4857
  }
4622
4858
  steps.push({
4623
- key: "bridgehub:two-bridges:nonbase",
4859
+ key: "bridgehub:two-bridges:erc20-nonbase",
4624
4860
  kind: "bridgehub:two-bridges",
4625
4861
  description: baseIsEth ? "Bridge ERC-20 (fees in ETH) via Bridgehub.requestL2TransactionTwoBridges" : "Bridge ERC-20 (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
4626
4862
  tx: bridgeTx
4627
4863
  });
4864
+ const fees = buildFeeBreakdown({
4865
+ feeToken: baseToken,
4866
+ l1Gas,
4867
+ l2Gas,
4868
+ l2BaseCost,
4869
+ operatorTip: ctx.operatorTip,
4870
+ mintValue
4871
+ });
4628
4872
  return {
4629
4873
  steps,
4630
4874
  approvals,
4631
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
4875
+ fees
4632
4876
  };
4633
4877
  }
4634
4878
  };
4635
4879
  }
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;
4880
+ var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
4642
4881
  function routeEthNonBase() {
4643
4882
  return {
4883
+ // TODO: do we even need these validations?
4644
4884
  async preflight(p, ctx) {
4645
- await wrapAs3(
4885
+ await wrapAs4(
4646
4886
  "VALIDATION",
4647
4887
  OP_DEPOSITS.ethNonBase.assertEthAsset,
4648
4888
  () => {
4649
- if (!isETH(p.token)) {
4889
+ if (ctx.resolvedToken?.kind !== "eth" && !isETH(p.token)) {
4650
4890
  throw new Error("eth-nonbase route requires ETH as the deposit asset.");
4651
4891
  }
4652
4892
  },
4653
4893
  { ctx: { token: p.token } }
4654
4894
  );
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(
4895
+ await wrapAs4(
4670
4896
  "VALIDATION",
4671
4897
  OP_DEPOSITS.ethNonBase.assertNonEthBase,
4672
4898
  () => {
4673
- if (isETH(baseToken)) {
4899
+ if (ctx.baseIsEth) {
4674
4900
  throw new Error("eth-nonbase route requires target chain base token \u2260 ETH.");
4675
4901
  }
4676
4902
  },
4677
- { ctx: { baseToken, chainIdL2: ctx.chainIdL2 } }
4903
+ { ctx: { baseIsEth: ctx.baseIsEth, chainIdL2: ctx.chainIdL2 } }
4678
4904
  );
4679
- const ethBal = await wrapAs3(
4905
+ const ethBal = await wrapAs4(
4680
4906
  "RPC",
4681
4907
  OP_DEPOSITS.ethNonBase.ethBalance,
4682
4908
  () => ctx.client.l1.getBalance({ address: ctx.sender }),
@@ -4685,7 +4911,7 @@ function routeEthNonBase() {
4685
4911
  message: "Failed to read L1 ETH balance."
4686
4912
  }
4687
4913
  );
4688
- await wrapAs3(
4914
+ await wrapAs4(
4689
4915
  "VALIDATION",
4690
4916
  OP_DEPOSITS.ethNonBase.assertEthBalance,
4691
4917
  () => {
@@ -4695,45 +4921,27 @@ function routeEthNonBase() {
4695
4921
  },
4696
4922
  { ctx: { required: p.amount.toString(), balance: ethBal.toString() } }
4697
4923
  );
4698
- return;
4699
4924
  },
4700
4925
  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);
4926
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
4927
+ const l2TxModel = {
4928
+ to: p.to ?? ctx.sender,
4929
+ from: ctx.sender,
4930
+ data: "0x",
4931
+ value: 0n
4932
+ };
4933
+ const l2Gas = await quoteL2Gas2({
4934
+ ctx,
4935
+ route: "eth-nonbase",
4936
+ l2TxForModeling: l2TxModel,
4937
+ overrideGasLimit: ctx.l2GasLimit
4938
+ });
4939
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
4940
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
4941
+ const mintValue = l2BaseCost + ctx.operatorTip;
4734
4942
  const approvals = [];
4735
4943
  const steps = [];
4736
- const allowance = await wrapAs3(
4944
+ const allowance = await wrapAs4(
4737
4945
  "CONTRACT",
4738
4946
  OP_DEPOSITS.ethNonBase.allowanceBase,
4739
4947
  () => ctx.client.l1.readContract({
@@ -4749,7 +4957,7 @@ function routeEthNonBase() {
4749
4957
  );
4750
4958
  const needsApprove = allowance < mintValue;
4751
4959
  if (needsApprove) {
4752
- const approveSim = await wrapAs3(
4960
+ const approveSim = await wrapAs4(
4753
4961
  "CONTRACT",
4754
4962
  OP_DEPOSITS.ethNonBase.estGas,
4755
4963
  () => ctx.client.l1.simulateContract({
@@ -4768,11 +4976,11 @@ function routeEthNonBase() {
4768
4976
  steps.push({
4769
4977
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4770
4978
  kind: "approve",
4771
- description: `Approve base token for mintValue`,
4979
+ description: `Approve base token for fees (mintValue)`,
4772
4980
  tx: { ...approveSim.request }
4773
4981
  });
4774
4982
  }
4775
- const secondBridgeCalldata = await wrapAs3(
4983
+ const secondBridgeCalldata = await wrapAs4(
4776
4984
  "INTERNAL",
4777
4985
  OP_DEPOSITS.ethNonBase.encodeCalldata,
4778
4986
  () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
@@ -4785,11 +4993,11 @@ function routeEthNonBase() {
4785
4993
  message: "Failed to encode ETH bridging calldata."
4786
4994
  }
4787
4995
  );
4788
- const outer = {
4996
+ const requestStruct = {
4789
4997
  chainId: ctx.chainIdL2,
4790
4998
  mintValue,
4791
- l2Value: 0n,
4792
- l2GasLimit: ctx.l2GasLimit,
4999
+ l2Value: p.amount,
5000
+ l2GasLimit: l2Gas.gasLimit,
4793
5001
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
4794
5002
  refundRecipient: ctx.refundRecipient,
4795
5003
  secondBridgeAddress: ctx.l1AssetRouter,
@@ -4797,29 +5005,32 @@ function routeEthNonBase() {
4797
5005
  secondBridgeCalldata
4798
5006
  };
4799
5007
  let bridgeTx;
4800
- let resolvedL1GasLimit;
5008
+ let calldata;
4801
5009
  if (needsApprove) {
4802
5010
  bridgeTx = {
4803
5011
  address: ctx.bridgehub,
4804
5012
  abi: IBridgehub_default,
4805
5013
  functionName: "requestL2TransactionTwoBridges",
4806
- args: [outer],
5014
+ args: [requestStruct],
4807
5015
  value: p.amount,
4808
5016
  // base ≠ ETH ⇒ msg.value == secondBridgeValue
4809
5017
  account: ctx.client.account
4810
5018
  };
4811
- resolvedL1GasLimit = ctx.l2GasLimit;
5019
+ calldata = viem.encodeFunctionData({
5020
+ abi: IBridgehub_default,
5021
+ functionName: "requestL2TransactionTwoBridges",
5022
+ args: [requestStruct]
5023
+ });
4812
5024
  } else {
4813
- const twoBridgesSim = await wrapAs3(
5025
+ const sim = await wrapAs4(
4814
5026
  "CONTRACT",
4815
5027
  OP_DEPOSITS.ethNonBase.estGas,
4816
5028
  () => ctx.client.l1.simulateContract({
4817
5029
  address: ctx.bridgehub,
4818
5030
  abi: IBridgehub_default,
4819
5031
  functionName: "requestL2TransactionTwoBridges",
4820
- args: [outer],
5032
+ args: [requestStruct],
4821
5033
  value: p.amount,
4822
- // base ≠ ETH ⇒ msg.value == secondBridgeValue
4823
5034
  account: ctx.client.account
4824
5035
  }),
4825
5036
  {
@@ -4827,8 +5038,33 @@ function routeEthNonBase() {
4827
5038
  message: "Failed to simulate Bridgehub two-bridges request."
4828
5039
  }
4829
5040
  );
4830
- bridgeTx = { ...twoBridgesSim.request, ...txFeeOverrides };
4831
- resolvedL1GasLimit = twoBridgesSim.request.gas ?? ctx.l2GasLimit;
5041
+ calldata = viem.encodeFunctionData({
5042
+ abi: sim.request.abi,
5043
+ functionName: sim.request.functionName,
5044
+ args: sim.request.args
5045
+ });
5046
+ bridgeTx = { ...sim.request };
5047
+ }
5048
+ const l1TxCandidate = {
5049
+ to: ctx.bridgehub,
5050
+ data: calldata,
5051
+ value: p.amount,
5052
+ from: ctx.sender,
5053
+ ...ctx.gasOverrides
5054
+ };
5055
+ const l1Gas = await quoteL1Gas2({
5056
+ ctx,
5057
+ tx: l1TxCandidate,
5058
+ overrides: ctx.gasOverrides,
5059
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
5060
+ });
5061
+ if (l1Gas) {
5062
+ bridgeTx = {
5063
+ ...bridgeTx,
5064
+ gas: l1Gas.gasLimit,
5065
+ maxFeePerGas: l1Gas.maxFeePerGas,
5066
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
5067
+ };
4832
5068
  }
4833
5069
  steps.push({
4834
5070
  key: "bridgehub:two-bridges:eth-nonbase",
@@ -4836,48 +5072,38 @@ function routeEthNonBase() {
4836
5072
  description: "Bridge ETH (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
4837
5073
  tx: bridgeTx
4838
5074
  });
5075
+ const fees = buildFeeBreakdown({
5076
+ feeToken: baseToken,
5077
+ l1Gas,
5078
+ l2Gas,
5079
+ l2BaseCost,
5080
+ operatorTip: ctx.operatorTip,
5081
+ mintValue
5082
+ });
4839
5083
  return {
4840
5084
  steps,
4841
5085
  approvals,
4842
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5086
+ fees
4843
5087
  };
4844
5088
  }
4845
5089
  };
4846
5090
  }
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;
5091
+ var { wrapAs: wrapAs5 } = createErrorHandlers("deposits");
4853
5092
  function routeErc20Base() {
4854
5093
  return {
4855
5094
  async preflight(p, ctx) {
4856
- await wrapAs4(
5095
+ await wrapAs5(
4857
5096
  "VALIDATION",
4858
5097
  OP_DEPOSITS.base.assertErc20Asset,
4859
5098
  () => {
4860
- if (isETH(p.token)) {
5099
+ if (ctx.resolvedToken?.kind === "eth" || isETH(p.token)) {
4861
5100
  throw new Error("erc20-base route requires an ERC-20 token (not ETH).");
4862
- }
4863
- },
4864
- { ctx: { token: p.token } }
4865
- );
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
- }
5101
+ }
5102
+ },
5103
+ { ctx: { token: p.token } }
4879
5104
  );
4880
- await wrapAs4(
5105
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
5106
+ await wrapAs5(
4881
5107
  "VALIDATION",
4882
5108
  OP_DEPOSITS.base.assertMatchesBase,
4883
5109
  () => {
@@ -4887,45 +5113,27 @@ function routeErc20Base() {
4887
5113
  },
4888
5114
  { ctx: { baseToken, provided: p.token, chainIdL2: ctx.chainIdL2 } }
4889
5115
  );
4890
- return;
4891
5116
  },
4892
5117
  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(
5118
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
5119
+ const l2TxModel = {
5120
+ to: p.to ?? ctx.sender,
5121
+ from: ctx.sender,
5122
+ data: "0x",
5123
+ value: 0n
5124
+ };
5125
+ const l2Gas = await quoteL2Gas2({
5126
+ ctx,
5127
+ route: "erc20-base",
5128
+ l2TxForModeling: l2TxModel,
5129
+ overrideGasLimit: ctx.l2GasLimit
5130
+ });
5131
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
5132
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
5133
+ const mintValue = l2BaseCost + ctx.operatorTip + p.amount;
5134
+ const approvals = [];
5135
+ const steps = [];
5136
+ const allowance = await wrapAs5(
4929
5137
  "CONTRACT",
4930
5138
  OP_DEPOSITS.base.allowance,
4931
5139
  () => ctx.client.l1.readContract({
@@ -4939,11 +5147,9 @@ function routeErc20Base() {
4939
5147
  message: "Failed to read base-token allowance."
4940
5148
  }
4941
5149
  );
4942
- const approvals = [];
4943
- const steps = [];
4944
5150
  const needsApprove = allowance < mintValue;
4945
5151
  if (needsApprove) {
4946
- const approveSim = await wrapAs4(
5152
+ const approveSim = await wrapAs5(
4947
5153
  "CONTRACT",
4948
5154
  OP_DEPOSITS.base.estGas,
4949
5155
  () => ctx.client.l1.simulateContract({
@@ -4963,20 +5169,20 @@ function routeErc20Base() {
4963
5169
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
4964
5170
  kind: "approve",
4965
5171
  description: "Approve base token for mintValue",
4966
- tx: { ...approveSim.request, ...txFeeOverrides }
5172
+ tx: { ...approveSim.request }
4967
5173
  });
4968
5174
  }
4969
5175
  const req = buildDirectRequestStruct({
4970
5176
  chainId: ctx.chainIdL2,
4971
5177
  mintValue,
4972
- l2GasLimit: ctx.l2GasLimit,
5178
+ l2GasLimit: l2Gas.gasLimit,
4973
5179
  gasPerPubdata: ctx.gasPerPubdata,
4974
5180
  refundRecipient: ctx.refundRecipient,
4975
5181
  l2Contract: p.to ?? ctx.sender,
4976
- l2Value
5182
+ l2Value: p.amount
4977
5183
  });
4978
5184
  let bridgeTx;
4979
- let resolvedL1GasLimit;
5185
+ let calldata;
4980
5186
  if (needsApprove) {
4981
5187
  bridgeTx = {
4982
5188
  address: ctx.bridgehub,
@@ -4984,13 +5190,16 @@ function routeErc20Base() {
4984
5190
  functionName: "requestL2TransactionDirect",
4985
5191
  args: [req],
4986
5192
  value: 0n,
4987
- // base is ERC-20 ⇒ msg.value MUST be 0
4988
- account: ctx.client.account,
4989
- ...txFeeOverrides
5193
+ // base token is ERC-20 ⇒ msg.value MUST be 0
5194
+ account: ctx.client.account
4990
5195
  };
4991
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
5196
+ calldata = viem.encodeFunctionData({
5197
+ abi: IBridgehub_default,
5198
+ functionName: "requestL2TransactionDirect",
5199
+ args: [req]
5200
+ });
4992
5201
  } else {
4993
- const sim = await wrapAs4(
5202
+ const sim = await wrapAs5(
4994
5203
  "RPC",
4995
5204
  OP_DEPOSITS.base.estGas,
4996
5205
  () => ctx.client.l1.simulateContract({
@@ -5006,8 +5215,33 @@ function routeErc20Base() {
5006
5215
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
5007
5216
  }
5008
5217
  );
5009
- bridgeTx = { ...sim.request, ...txFeeOverrides };
5010
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
5218
+ calldata = viem.encodeFunctionData({
5219
+ abi: sim.request.abi,
5220
+ functionName: sim.request.functionName,
5221
+ args: sim.request.args
5222
+ });
5223
+ bridgeTx = { ...sim.request };
5224
+ }
5225
+ const l1TxCandidate = {
5226
+ to: ctx.bridgehub,
5227
+ data: calldata,
5228
+ value: 0n,
5229
+ from: ctx.sender,
5230
+ ...ctx.gasOverrides
5231
+ };
5232
+ const l1Gas = await quoteL1Gas2({
5233
+ ctx,
5234
+ tx: l1TxCandidate,
5235
+ overrides: ctx.gasOverrides,
5236
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
5237
+ });
5238
+ if (l1Gas) {
5239
+ bridgeTx = {
5240
+ ...bridgeTx,
5241
+ gas: l1Gas.gasLimit,
5242
+ maxFeePerGas: l1Gas.maxFeePerGas,
5243
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
5244
+ };
5011
5245
  }
5012
5246
  steps.push({
5013
5247
  key: "bridgehub:direct:erc20-base",
@@ -5015,10 +5249,18 @@ function routeErc20Base() {
5015
5249
  description: "Bridge base ERC-20 via Bridgehub.requestL2TransactionDirect",
5016
5250
  tx: bridgeTx
5017
5251
  });
5252
+ const fees = buildFeeBreakdown({
5253
+ feeToken: baseToken,
5254
+ l1Gas,
5255
+ l2Gas,
5256
+ l2BaseCost,
5257
+ operatorTip: ctx.operatorTip,
5258
+ mintValue
5259
+ });
5018
5260
  return {
5019
5261
  steps,
5020
5262
  approvals,
5021
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5263
+ fees
5022
5264
  };
5023
5265
  }
5024
5266
  };
@@ -5099,6 +5341,276 @@ async function waitForL2ExecutionFromL1Tx(l1, l2, l1TxHash) {
5099
5341
  return { l2Receipt, l2TxHash };
5100
5342
  }
5101
5343
 
5344
+ // src/core/codec/ntv.ts
5345
+ function createNTVCodec(deps) {
5346
+ function encodeAssetId(originChainId, ntvAddress, tokenAddress) {
5347
+ const encoded = deps.encode(
5348
+ ["uint256", "address", "address"],
5349
+ [originChainId, ntvAddress, tokenAddress]
5350
+ );
5351
+ return deps.keccak256(encoded);
5352
+ }
5353
+ return {
5354
+ encodeAssetId
5355
+ };
5356
+ }
5357
+
5358
+ // src/adapters/viem/resources/tokens/tokens.ts
5359
+ var { wrapAs: wrapAs6 } = createErrorHandlers("tokens");
5360
+ var ntvCodec = createNTVCodec({
5361
+ encode: (types, values) => viem.encodeAbiParameters(
5362
+ types.map((t, i) => ({ type: t, name: `arg${i}` })),
5363
+ values
5364
+ ),
5365
+ keccak256: (data) => viem.keccak256(data)
5366
+ });
5367
+ function createTokensResource(client) {
5368
+ let l2NtvL1ChainIdPromise = null;
5369
+ let baseTokenAssetIdPromise = null;
5370
+ let wethL1Promise = null;
5371
+ let wethL2Promise = null;
5372
+ async function getL1ChainId() {
5373
+ if (!l2NtvL1ChainIdPromise) {
5374
+ l2NtvL1ChainIdPromise = wrapAs6("INTERNAL", "getL1ChainId", async () => {
5375
+ const { l2NativeTokenVault } = await client.contracts();
5376
+ return await l2NativeTokenVault.read.L1_CHAIN_ID();
5377
+ });
5378
+ }
5379
+ return l2NtvL1ChainIdPromise;
5380
+ }
5381
+ async function getBaseTokenAssetId() {
5382
+ if (!baseTokenAssetIdPromise) {
5383
+ baseTokenAssetIdPromise = wrapAs6("INTERNAL", "baseTokenAssetId", async () => {
5384
+ const { l2NativeTokenVault } = await client.contracts();
5385
+ const assetId = await l2NativeTokenVault.read.BASE_TOKEN_ASSET_ID();
5386
+ return assetId;
5387
+ });
5388
+ }
5389
+ return baseTokenAssetIdPromise;
5390
+ }
5391
+ async function getWethL1() {
5392
+ if (!wethL1Promise) {
5393
+ wethL1Promise = wrapAs6("INTERNAL", "wethL1", async () => {
5394
+ const { l1NativeTokenVault } = await client.contracts();
5395
+ const weth = await l1NativeTokenVault.read.WETH_TOKEN();
5396
+ return weth;
5397
+ });
5398
+ }
5399
+ return wethL1Promise;
5400
+ }
5401
+ async function getWethL2() {
5402
+ if (!wethL2Promise) {
5403
+ wethL2Promise = wrapAs6("INTERNAL", "wethL2", async () => {
5404
+ const { l2NativeTokenVault } = await client.contracts();
5405
+ const weth = await l2NativeTokenVault.read.WETH_TOKEN();
5406
+ return weth;
5407
+ });
5408
+ }
5409
+ return wethL2Promise;
5410
+ }
5411
+ async function toL2Address(l1Token) {
5412
+ return wrapAs6("CONTRACT", "tokens.toL2Address", async () => {
5413
+ const normalized = normalizeL1Token(l1Token);
5414
+ const chainId = BigInt(await client.l2.getChainId());
5415
+ const baseToken = await client.baseToken(chainId);
5416
+ if (isAddressEq(normalized, baseToken)) {
5417
+ return L2_BASE_TOKEN_ADDRESS;
5418
+ }
5419
+ const { l2NativeTokenVault } = await client.contracts();
5420
+ const l2Token = await l2NativeTokenVault.read.l2TokenAddress([normalized]);
5421
+ return l2Token;
5422
+ });
5423
+ }
5424
+ async function toL1Address(l2Token) {
5425
+ return wrapAs6("CONTRACT", "tokens.toL1Address", async () => {
5426
+ if (isAddressEq(l2Token, ETH_ADDRESS)) return ETH_ADDRESS;
5427
+ if (isAddressEq(l2Token, L2_BASE_TOKEN_ADDRESS)) {
5428
+ const chainId = BigInt(await client.l2.getChainId());
5429
+ return await client.baseToken(chainId);
5430
+ }
5431
+ const { l2AssetRouter } = await client.contracts();
5432
+ const l1Token = await l2AssetRouter.read.l1TokenAddress([l2Token]);
5433
+ return l1Token;
5434
+ });
5435
+ }
5436
+ async function assetIdOfL1(l1Token) {
5437
+ return wrapAs6("CONTRACT", "tokens.assetIdOfL1", async () => {
5438
+ const normalized = normalizeL1Token(l1Token);
5439
+ const { l1NativeTokenVault } = await client.contracts();
5440
+ return await l1NativeTokenVault.read.assetId([normalized]);
5441
+ });
5442
+ }
5443
+ async function assetIdOfL2(l2Token) {
5444
+ return wrapAs6("CONTRACT", "tokens.assetIdOfL2", async () => {
5445
+ const { l2NativeTokenVault } = await client.contracts();
5446
+ return await l2NativeTokenVault.read.assetId([l2Token]);
5447
+ });
5448
+ }
5449
+ async function l2TokenFromAssetId(assetId) {
5450
+ return wrapAs6("CONTRACT", "tokens.l2TokenFromAssetId", async () => {
5451
+ const { l2NativeTokenVault } = await client.contracts();
5452
+ return await l2NativeTokenVault.read.tokenAddress([assetId]);
5453
+ });
5454
+ }
5455
+ async function l1TokenFromAssetId(assetId) {
5456
+ return wrapAs6("CONTRACT", "tokens.l1TokenFromAssetId", async () => {
5457
+ const { l1NativeTokenVault } = await client.contracts();
5458
+ return await l1NativeTokenVault.read.tokenAddress([assetId]);
5459
+ });
5460
+ }
5461
+ async function originChainId(assetId) {
5462
+ return wrapAs6("CONTRACT", "tokens.originChainId", async () => {
5463
+ const { l2NativeTokenVault } = await client.contracts();
5464
+ return await l2NativeTokenVault.read.originChainId([assetId]);
5465
+ });
5466
+ }
5467
+ async function baseTokenAssetId() {
5468
+ return getBaseTokenAssetId();
5469
+ }
5470
+ async function isChainEthBased() {
5471
+ return wrapAs6("CONTRACT", "tokens.isChainEthBased", async () => {
5472
+ const baseAssetId = await getBaseTokenAssetId();
5473
+ const l1ChainId = await getL1ChainId();
5474
+ const ethAssetId = ntvCodec.encodeAssetId(
5475
+ l1ChainId,
5476
+ L2_NATIVE_TOKEN_VAULT_ADDRESS,
5477
+ ETH_ADDRESS
5478
+ );
5479
+ return hexEq(baseAssetId, ethAssetId);
5480
+ });
5481
+ }
5482
+ async function wethL1() {
5483
+ return getWethL1();
5484
+ }
5485
+ async function wethL2() {
5486
+ return getWethL2();
5487
+ }
5488
+ async function computeL2BridgedAddress(args) {
5489
+ return wrapAs6("CONTRACT", "tokens.computeL2BridgedAddress", async () => {
5490
+ const normalized = normalizeL1Token(args.l1Token);
5491
+ const { l2NativeTokenVault } = await client.contracts();
5492
+ const predicted = await l2NativeTokenVault.read.calculateCreate2TokenAddress([
5493
+ args.originChainId,
5494
+ normalized
5495
+ ]);
5496
+ return predicted;
5497
+ });
5498
+ }
5499
+ async function resolve(ref, opts) {
5500
+ return wrapAs6("CONTRACT", "tokens.resolve", async () => {
5501
+ let chain;
5502
+ let address;
5503
+ if (typeof ref === "string") {
5504
+ chain = opts?.chain ?? "l1";
5505
+ address = ref;
5506
+ } else {
5507
+ chain = ref.chain;
5508
+ address = ref.address;
5509
+ }
5510
+ let l1;
5511
+ let l2;
5512
+ if (chain === "l1") {
5513
+ l1 = normalizeL1Token(address);
5514
+ l2 = await toL2Address(address);
5515
+ } else {
5516
+ l2 = address;
5517
+ l1 = await toL1Address(address);
5518
+ }
5519
+ const assetId = await assetIdOfL1(l1);
5520
+ const originChainIdVal = await originChainId(assetId);
5521
+ const [baseAssetId, wethL1Addr, wethL2Addr, ethBased] = await Promise.all([
5522
+ baseTokenAssetId(),
5523
+ wethL1(),
5524
+ wethL2(),
5525
+ isChainEthBased()
5526
+ ]);
5527
+ let kind;
5528
+ if (isAddressEq(l1, ETH_ADDRESS)) {
5529
+ kind = "eth";
5530
+ } else if (hexEq(assetId, baseAssetId)) {
5531
+ kind = "base";
5532
+ } else {
5533
+ kind = "erc20";
5534
+ }
5535
+ return {
5536
+ kind,
5537
+ l1,
5538
+ l2,
5539
+ assetId,
5540
+ originChainId: originChainIdVal,
5541
+ isChainEthBased: ethBased,
5542
+ baseTokenAssetId: baseAssetId,
5543
+ wethL1: wethL1Addr,
5544
+ wethL2: wethL2Addr
5545
+ };
5546
+ });
5547
+ }
5548
+ return {
5549
+ resolve,
5550
+ toL2Address,
5551
+ toL1Address,
5552
+ assetIdOfL1,
5553
+ assetIdOfL2,
5554
+ l2TokenFromAssetId,
5555
+ l1TokenFromAssetId,
5556
+ originChainId,
5557
+ baseTokenAssetId,
5558
+ isChainEthBased,
5559
+ wethL1,
5560
+ wethL2,
5561
+ computeL2BridgedAddress
5562
+ };
5563
+ }
5564
+
5565
+ // src/adapters/viem/resources/contracts/contracts.ts
5566
+ function createContractsResource(client) {
5567
+ async function addresses() {
5568
+ return client.ensureAddresses();
5569
+ }
5570
+ async function instances() {
5571
+ return client.contracts();
5572
+ }
5573
+ async function bridgehub() {
5574
+ const { bridgehub: bridgehub2 } = await instances();
5575
+ return bridgehub2;
5576
+ }
5577
+ async function l1AssetRouter() {
5578
+ const { l1AssetRouter: l1AssetRouter2 } = await instances();
5579
+ return l1AssetRouter2;
5580
+ }
5581
+ async function l1NativeTokenVault() {
5582
+ const { l1NativeTokenVault: l1NativeTokenVault2 } = await instances();
5583
+ return l1NativeTokenVault2;
5584
+ }
5585
+ async function l1Nullifier() {
5586
+ const { l1Nullifier: l1Nullifier2 } = await instances();
5587
+ return l1Nullifier2;
5588
+ }
5589
+ async function l2AssetRouter() {
5590
+ const { l2AssetRouter: l2AssetRouter2 } = await instances();
5591
+ return l2AssetRouter2;
5592
+ }
5593
+ async function l2NativeTokenVault() {
5594
+ const { l2NativeTokenVault: l2NativeTokenVault2 } = await instances();
5595
+ return l2NativeTokenVault2;
5596
+ }
5597
+ async function l2BaseTokenSystem() {
5598
+ const { l2BaseTokenSystem: l2BaseTokenSystem2 } = await instances();
5599
+ return l2BaseTokenSystem2;
5600
+ }
5601
+ return {
5602
+ addresses,
5603
+ instances,
5604
+ bridgehub,
5605
+ l1AssetRouter,
5606
+ l1NativeTokenVault,
5607
+ l1Nullifier,
5608
+ l2AssetRouter,
5609
+ l2NativeTokenVault,
5610
+ l2BaseTokenSystem
5611
+ };
5612
+ }
5613
+
5102
5614
  // src/adapters/viem/resources/deposits/index.ts
5103
5615
  var { wrap, toResult } = createErrorHandlers("deposits");
5104
5616
  var ROUTES = {
@@ -5107,37 +5619,26 @@ var ROUTES = {
5107
5619
  "erc20-nonbase": routeErc20NonBase(),
5108
5620
  "erc20-base": routeErc20Base()
5109
5621
  };
5110
- function createDepositsResource(client) {
5622
+ function createDepositsResource(client, tokens, contracts) {
5623
+ const tokensResource = tokens ?? createTokensResource(client);
5624
+ const contractsResource = contracts ?? createContractsResource(client);
5111
5625
  async function buildPlan(p) {
5112
- const ctx = await commonCtx(p, client);
5626
+ const ctx = await commonCtx(p, client, tokensResource, contractsResource);
5113
5627
  const route = ctx.route;
5114
5628
  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();
5629
+ const { steps, approvals, fees } = await ROUTES[route].build(p, ctx);
5128
5630
  return {
5129
5631
  route: ctx.route,
5130
5632
  summary: {
5131
5633
  route: ctx.route,
5132
5634
  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
- }
5635
+ amounts: {
5636
+ transfer: { token: p.token, amount: p.amount }
5637
+ },
5638
+ fees,
5639
+ // Legacy fields (maintained for backward compatibility)
5640
+ baseCost: fees.l2?.baseCost,
5641
+ mintValue: fees.mintValue
5141
5642
  },
5142
5643
  steps
5143
5644
  };
@@ -5212,7 +5713,7 @@ function createDepositsResource(client) {
5212
5713
  step.tx.gas = overrides.gasLimit;
5213
5714
  }
5214
5715
  }
5215
- if (step.tx.gas == null) {
5716
+ if (!p.l1TxOverrides?.gasLimit) {
5216
5717
  try {
5217
5718
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
5218
5719
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -5435,36 +5936,12 @@ function normalizeTokenForRouting(token) {
5435
5936
  function pickWithdrawRoute(args) {
5436
5937
  const tokenNorm = normalizeTokenForRouting(args.token);
5437
5938
  const isL2BaseAlias = tokenNorm.toLowerCase() === L2_BASE_TOKEN_ADDRESS.toLowerCase();
5438
- if (isL2BaseAlias) return args.baseIsEth ? "eth-base" : "eth-nonbase";
5939
+ if (isL2BaseAlias) return "base";
5439
5940
  return "erc20-nonbase";
5440
5941
  }
5441
5942
 
5442
- // src/adapters/viem/resources/token-info.ts
5443
- async function ntvBaseAssetId(l2, ntv) {
5444
- return l2.readContract({
5445
- address: ntv,
5446
- abi: L2NativeTokenVault_default,
5447
- functionName: "BASE_TOKEN_ASSET_ID"
5448
- });
5449
- }
5450
- async function ntvL1ChainId(l2, ntv) {
5451
- return l2.readContract({
5452
- address: ntv,
5453
- abi: L2NativeTokenVault_default,
5454
- functionName: "L1_CHAIN_ID"
5455
- });
5456
- }
5457
- async function isEthBasedChain(l2, ntv) {
5458
- const [baseAssetId, l1ChainId] = await Promise.all([
5459
- ntvBaseAssetId(l2, ntv),
5460
- ntvL1ChainId(l2, ntv)
5461
- ]);
5462
- const ethAssetId = encodeNativeTokenVaultAssetId(l1ChainId, ETH_ADDRESS);
5463
- return baseAssetId.toLowerCase() === ethAssetId.toLowerCase();
5464
- }
5465
-
5466
5943
  // src/adapters/viem/resources/withdrawals/context.ts
5467
- async function commonCtx2(p, client) {
5944
+ async function commonCtx2(p, client, tokens, contracts) {
5468
5945
  const sender = client.account.address;
5469
5946
  const {
5470
5947
  bridgehub,
@@ -5473,18 +5950,20 @@ async function commonCtx2(p, client) {
5473
5950
  l2AssetRouter,
5474
5951
  l2NativeTokenVault,
5475
5952
  l2BaseTokenSystem
5476
- } = await client.ensureAddresses();
5953
+ } = await contracts.addresses();
5477
5954
  const chainIdL2 = BigInt(await client.l2.getChainId());
5478
- 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;
5955
+ const resolvedToken = await tokens.resolve(p.token, { chain: "l2" });
5956
+ const baseTokenAssetId = resolvedToken.baseTokenAssetId;
5957
+ const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
5958
+ const baseIsEth = resolvedToken.isChainEthBased;
5959
+ const route = pickWithdrawRoute({ token: p.token});
5486
5960
  return {
5487
5961
  client,
5962
+ tokens,
5963
+ contracts,
5964
+ resolvedToken,
5965
+ baseTokenAssetId,
5966
+ baseTokenL1,
5488
5967
  bridgehub,
5489
5968
  chainIdL2,
5490
5969
  sender,
@@ -5495,56 +5974,162 @@ async function commonCtx2(p, client) {
5495
5974
  l2NativeTokenVault,
5496
5975
  l2BaseTokenSystem,
5497
5976
  baseIsEth,
5498
- l2GasLimit,
5499
- gasBufferPct,
5500
- fee
5977
+ gasOverrides: p.l2TxOverrides
5978
+ };
5979
+ }
5980
+
5981
+ // src/core/resources/withdrawals/gas.ts
5982
+ function makeGasQuote2(p) {
5983
+ return {
5984
+ gasLimit: p.gasLimit,
5985
+ maxFeePerGas: p.maxFeePerGas,
5986
+ maxPriorityFeePerGas: p.maxPriorityFeePerGas,
5987
+ maxCost: p.gasLimit * p.maxFeePerGas
5988
+ };
5989
+ }
5990
+ async function fetchFees2(estimator) {
5991
+ try {
5992
+ const fees = await estimator.estimateFeesPerGas();
5993
+ if (fees.maxFeePerGas != null) {
5994
+ return {
5995
+ maxFeePerGas: fees.maxFeePerGas,
5996
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
5997
+ };
5998
+ }
5999
+ if (fees.gasPrice != null) {
6000
+ return {
6001
+ maxFeePerGas: fees.gasPrice,
6002
+ maxPriorityFeePerGas: 0n
6003
+ };
6004
+ }
6005
+ } catch {
6006
+ }
6007
+ try {
6008
+ const gp = await estimator.getGasPrice();
6009
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
6010
+ } catch {
6011
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
6012
+ }
6013
+ }
6014
+ async function quoteL2Gas3(input) {
6015
+ const { estimator, tx, overrides } = input;
6016
+ const market = await fetchFees2(estimator);
6017
+ const o = overrides;
6018
+ const maxFeePerGas = o?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : market.maxFeePerGas);
6019
+ const maxPriorityFeePerGas = o?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : market.maxPriorityFeePerGas);
6020
+ const explicitGasLimit = o?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
6021
+ if (explicitGasLimit != null) {
6022
+ return makeGasQuote2({
6023
+ gasLimit: explicitGasLimit,
6024
+ maxFeePerGas,
6025
+ maxPriorityFeePerGas
6026
+ });
6027
+ }
6028
+ try {
6029
+ const est = await estimator.estimateGas(tx);
6030
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
6031
+ return makeGasQuote2({
6032
+ gasLimit: buffered,
6033
+ maxFeePerGas,
6034
+ maxPriorityFeePerGas
6035
+ });
6036
+ } catch (err) {
6037
+ console.warn("Failed to estimate L2 gas for withdrawal.", err);
6038
+ return void 0;
6039
+ }
6040
+ }
6041
+
6042
+ // src/adapters/viem/resources/withdrawals/services/gas.ts
6043
+ async function quoteL2Gas4(input) {
6044
+ const { ctx, tx } = input;
6045
+ const estimator = viemToGasEstimator(ctx.client.l2);
6046
+ return quoteL2Gas3({
6047
+ estimator,
6048
+ tx: toCoreTx(tx),
6049
+ overrides: ctx.gasOverrides
6050
+ });
6051
+ }
6052
+
6053
+ // src/adapters/viem/resources/withdrawals/services/fee.ts
6054
+ function buildFeeBreakdown2(p) {
6055
+ const l2Total = p.l2Gas?.maxCost ?? 0n;
6056
+ const l2 = {
6057
+ total: l2Total,
6058
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
6059
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
6060
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
6061
+ };
6062
+ return {
6063
+ token: p.feeToken,
6064
+ maxTotal: l2Total,
6065
+ l2
5501
6066
  };
5502
6067
  }
5503
6068
 
5504
6069
  // src/adapters/viem/resources/withdrawals/routes/eth.ts
5505
- var { wrapAs: wrapAs5 } = createErrorHandlers("withdrawals");
6070
+ var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
5506
6071
  function routeEthBase() {
5507
6072
  return {
5508
6073
  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
- }),
6074
+ const steps = [];
6075
+ const data = await wrapAs7(
6076
+ "INTERNAL",
6077
+ OP_WITHDRAWALS.eth.encodeWithdraw,
6078
+ () => Promise.resolve(
6079
+ viem.encodeFunctionData({
6080
+ abi: IBaseToken_default,
6081
+ functionName: "withdraw",
6082
+ args: [p.to ?? ctx.sender]
6083
+ })
6084
+ ),
5523
6085
  {
5524
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
5525
- message: "Failed to simulate L2 ETH withdraw."
6086
+ ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
6087
+ message: "Failed to encode ETH withdraw calldata."
5526
6088
  }
5527
6089
  );
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: {} };
6090
+ const L2tx = {
6091
+ to: L2_BASE_TOKEN_ADDRESS,
6092
+ data,
6093
+ value: p.amount,
6094
+ from: ctx.sender
6095
+ };
6096
+ const l2Gas = await quoteL2Gas4({ ctx, tx: L2tx });
6097
+ if (l2Gas) {
6098
+ L2tx.gas = l2Gas.gasLimit;
6099
+ L2tx.maxFeePerGas = l2Gas.maxFeePerGas;
6100
+ L2tx.maxPriorityFeePerGas = l2Gas.maxPriorityFeePerGas;
6101
+ }
6102
+ const tx = {
6103
+ address: L2_BASE_TOKEN_ADDRESS,
6104
+ abi: IBaseToken_default,
6105
+ functionName: "withdraw",
6106
+ args: [p.to ?? ctx.sender],
6107
+ value: p.amount,
6108
+ account: ctx.client.account,
6109
+ ...l2Gas
6110
+ };
6111
+ const fees = buildFeeBreakdown2({
6112
+ feeToken: L2_BASE_TOKEN_ADDRESS,
6113
+ l2Gas
6114
+ });
6115
+ steps.push({
6116
+ key: "l2-base-token:withdraw",
6117
+ kind: "l2-base-token:withdraw",
6118
+ description: "Withdraw ETH via L2 Base Token System",
6119
+ tx
6120
+ });
6121
+ return { steps, approvals: [], fees };
5537
6122
  }
5538
6123
  };
5539
6124
  }
5540
- var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
6125
+ var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
5541
6126
  function routeErc20NonBase2() {
5542
6127
  return {
5543
6128
  // TODO: add preflight validations here
5544
6129
  async build(p, ctx) {
5545
- const toL1 = p.to ?? ctx.sender;
5546
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5547
- const current = await wrapAs6(
6130
+ const steps = [];
6131
+ const approvals = [];
6132
+ const current = await wrapAs8(
5548
6133
  "CONTRACT",
5549
6134
  OP_WITHDRAWALS.erc20.allowance,
5550
6135
  () => ctx.client.l2.readContract({
@@ -5564,12 +6149,26 @@ function routeErc20NonBase2() {
5564
6149
  message: "Failed to read L2 ERC-20 allowance."
5565
6150
  }
5566
6151
  );
5567
- const needsApprove = current < p.amount;
5568
- const steps = [];
5569
- const approvals = [];
5570
- if (needsApprove) {
6152
+ if (current < p.amount) {
5571
6153
  approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
5572
- const approveSim = await wrapAs6(
6154
+ const data = viem.encodeFunctionData({
6155
+ abi: IERC20_default,
6156
+ functionName: "approve",
6157
+ args: [ctx.l2NativeTokenVault, p.amount]
6158
+ });
6159
+ const approveTxCandidate = {
6160
+ to: p.token,
6161
+ data,
6162
+ value: 0n,
6163
+ from: ctx.sender
6164
+ };
6165
+ const approveGas = await quoteL2Gas4({ ctx, tx: approveTxCandidate });
6166
+ if (approveGas) {
6167
+ approveTxCandidate.gas = approveGas.gasLimit;
6168
+ approveTxCandidate.maxFeePerGas = approveGas.maxFeePerGas;
6169
+ approveTxCandidate.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
6170
+ }
6171
+ const approveSim = await wrapAs8(
5573
6172
  "CONTRACT",
5574
6173
  OP_WITHDRAWALS.erc20.estGas,
5575
6174
  () => ctx.client.l2.simulateContract({
@@ -5578,21 +6177,26 @@ function routeErc20NonBase2() {
5578
6177
  functionName: "approve",
5579
6178
  args: [ctx.l2NativeTokenVault, p.amount],
5580
6179
  account: ctx.client.account,
5581
- ...txFeeOverrides
6180
+ ...approveGas
5582
6181
  }),
5583
6182
  {
5584
6183
  ctx: { where: "l2.simulateContract", to: p.token },
5585
6184
  message: "Failed to simulate L2 ERC-20 approve."
5586
6185
  }
5587
6186
  );
6187
+ const { ...approveRequest } = approveSim.request;
6188
+ const approveTx = {
6189
+ ...approveRequest
6190
+ };
5588
6191
  steps.push({
5589
6192
  key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
5590
6193
  kind: "approve:l2",
5591
6194
  description: `Approve ${p.amount} to NativeTokenVault`,
5592
- tx: { ...approveSim.request, ...txFeeOverrides }
6195
+ tx: approveTx
5593
6196
  });
5594
6197
  }
5595
- const ensure = await wrapAs6(
6198
+ const resolved = ctx.resolvedToken ?? (ctx.tokens ? await ctx.tokens.resolve(p.token, { chain: "l2" }) : void 0);
6199
+ const assetId = resolved?.assetId ?? (await wrapAs8(
5596
6200
  "CONTRACT",
5597
6201
  OP_WITHDRAWALS.erc20.ensureRegistered,
5598
6202
  () => ctx.client.l2.simulateContract({
@@ -5606,28 +6210,44 @@ function routeErc20NonBase2() {
5606
6210
  ctx: { where: "L2NativeTokenVault.ensureTokenIsRegistered", token: p.token },
5607
6211
  message: "Failed to ensure token is registered in L2NativeTokenVault."
5608
6212
  }
5609
- );
5610
- const assetId = ensure.result;
6213
+ )).result;
5611
6214
  const assetData = viem.encodeAbiParameters(
5612
6215
  [
5613
6216
  { type: "uint256", name: "amount" },
5614
6217
  { type: "address", name: "l1Receiver" },
5615
6218
  { type: "address", name: "l2Token" }
5616
6219
  ],
5617
- [p.amount, toL1, p.token]
6220
+ [p.amount, p.to ?? ctx.sender, p.token]
5618
6221
  );
6222
+ const withdrawCalldata = viem.encodeFunctionData({
6223
+ abi: IL2AssetRouter_default,
6224
+ functionName: "withdraw",
6225
+ args: [assetId, assetData]
6226
+ });
6227
+ const withdrawTxCandidate = {
6228
+ to: ctx.l2AssetRouter,
6229
+ data: withdrawCalldata,
6230
+ value: 0n,
6231
+ from: ctx.sender
6232
+ };
6233
+ const withdrawGas = await quoteL2Gas4({ ctx, tx: withdrawTxCandidate });
6234
+ if (withdrawGas) {
6235
+ withdrawTxCandidate.gas = withdrawGas.gasLimit;
6236
+ withdrawTxCandidate.maxFeePerGas = withdrawGas.maxFeePerGas;
6237
+ withdrawTxCandidate.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
6238
+ }
5619
6239
  let withdrawTx;
5620
- if (needsApprove) {
6240
+ if (current < p.amount) {
5621
6241
  withdrawTx = {
5622
6242
  address: ctx.l2AssetRouter,
5623
6243
  abi: IL2AssetRouter_default,
5624
6244
  functionName: "withdraw",
5625
6245
  args: [assetId, assetData],
5626
6246
  account: ctx.client.account,
5627
- ...txFeeOverrides
6247
+ ...withdrawGas
5628
6248
  };
5629
6249
  } else {
5630
- const sim = await wrapAs6(
6250
+ const sim = await wrapAs8(
5631
6251
  "CONTRACT",
5632
6252
  OP_WITHDRAWALS.erc20.estGas,
5633
6253
  () => ctx.client.l2.simulateContract({
@@ -5636,14 +6256,18 @@ function routeErc20NonBase2() {
5636
6256
  functionName: "withdraw",
5637
6257
  args: [assetId, assetData],
5638
6258
  account: ctx.client.account,
5639
- ...txFeeOverrides
6259
+ ...withdrawGas
5640
6260
  }),
5641
6261
  {
5642
6262
  ctx: { where: "l2.simulateContract", to: ctx.l2AssetRouter },
5643
6263
  message: "Failed to simulate L2 ERC-20 withdraw."
5644
6264
  }
5645
6265
  );
5646
- withdrawTx = { ...sim.request, ...txFeeOverrides };
6266
+ const { ...withdrawRequest } = sim.request;
6267
+ withdrawTx = {
6268
+ ...withdrawRequest,
6269
+ ...withdrawGas
6270
+ };
5647
6271
  }
5648
6272
  steps.push({
5649
6273
  key: "l2-asset-router:withdraw",
@@ -5651,59 +6275,11 @@ function routeErc20NonBase2() {
5651
6275
  description: "Burn on L2 & send L2\u2192L1 message",
5652
6276
  tx: withdrawTx
5653
6277
  });
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: {} };
6278
+ const fees = buildFeeBreakdown2({
6279
+ feeToken: ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2),
6280
+ l2Gas: withdrawGas
6281
+ });
6282
+ return { steps, approvals, fees };
5707
6283
  }
5708
6284
  };
5709
6285
  }
@@ -5739,7 +6315,7 @@ function messengerLogIndex(raw, opts) {
5739
6315
  }
5740
6316
  return (hits[index] ?? hits[0]).i;
5741
6317
  }
5742
- var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
6318
+ var { wrapAs: wrapAs9 } = createErrorHandlers("withdrawals");
5743
6319
  var IL1NullifierMini = [
5744
6320
  {
5745
6321
  type: "function",
@@ -5756,7 +6332,7 @@ var IL1NullifierMini = [
5756
6332
  function createFinalizationServices(client) {
5757
6333
  return {
5758
6334
  async fetchFinalizeDepositParams(l2TxHash) {
5759
- const parsed = await wrapAs8(
6335
+ const parsed = await wrapAs9(
5760
6336
  "RPC",
5761
6337
  OP_WITHDRAWALS.finalize.fetchParams.receipt,
5762
6338
  () => client.zks.getReceiptWithL2ToL1(l2TxHash),
@@ -5773,7 +6349,7 @@ function createFinalizationServices(client) {
5773
6349
  context: { l2TxHash }
5774
6350
  });
5775
6351
  }
5776
- const ev = await wrapAs8(
6352
+ const ev = await wrapAs9(
5777
6353
  "INTERNAL",
5778
6354
  OP_WITHDRAWALS.finalize.fetchParams.findMessage,
5779
6355
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
@@ -5783,7 +6359,7 @@ function createFinalizationServices(client) {
5783
6359
  message: "Failed to locate L1MessageSent event in L2 receipt."
5784
6360
  }
5785
6361
  );
5786
- const message = await wrapAs8(
6362
+ const message = await wrapAs9(
5787
6363
  "INTERNAL",
5788
6364
  OP_WITHDRAWALS.finalize.fetchParams.decodeMessage,
5789
6365
  () => {
@@ -5795,7 +6371,7 @@ function createFinalizationServices(client) {
5795
6371
  message: "Failed to decode withdrawal message."
5796
6372
  }
5797
6373
  );
5798
- const raw = await wrapAs8(
6374
+ const raw = await wrapAs9(
5799
6375
  "RPC",
5800
6376
  OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
5801
6377
  () => client.zks.getReceiptWithL2ToL1(l2TxHash),
@@ -5812,7 +6388,7 @@ function createFinalizationServices(client) {
5812
6388
  context: { l2TxHash }
5813
6389
  });
5814
6390
  }
5815
- const idx = await wrapAs8(
6391
+ const idx = await wrapAs9(
5816
6392
  "INTERNAL",
5817
6393
  OP_WITHDRAWALS.finalize.fetchParams.messengerIndex,
5818
6394
  () => Promise.resolve(messengerLogIndex(raw, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
@@ -5821,7 +6397,7 @@ function createFinalizationServices(client) {
5821
6397
  message: "Failed to derive messenger log index."
5822
6398
  }
5823
6399
  );
5824
- const proof = await wrapAs8(
6400
+ const proof = await wrapAs9(
5825
6401
  "RPC",
5826
6402
  OP_WITHDRAWALS.finalize.fetchParams.proof,
5827
6403
  () => client.zks.getL2ToL1LogProof(l2TxHash, idx),
@@ -5830,7 +6406,7 @@ function createFinalizationServices(client) {
5830
6406
  message: "Failed to fetch L2\u2192L1 log proof."
5831
6407
  }
5832
6408
  );
5833
- const chainId = await wrapAs8(
6409
+ const chainId = await wrapAs9(
5834
6410
  "RPC",
5835
6411
  OP_WITHDRAWALS.finalize.fetchParams.network,
5836
6412
  () => client.l2.getChainId(),
@@ -5846,7 +6422,7 @@ function createFinalizationServices(client) {
5846
6422
  message,
5847
6423
  merkleProof: proof.proof
5848
6424
  };
5849
- const { l1Nullifier } = await wrapAs8(
6425
+ const { l1Nullifier } = await wrapAs9(
5850
6426
  "INTERNAL",
5851
6427
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
5852
6428
  () => client.ensureAddresses(),
@@ -5858,7 +6434,7 @@ function createFinalizationServices(client) {
5858
6434
  return { params, nullifier: l1Nullifier };
5859
6435
  },
5860
6436
  async simulateFinalizeReadiness(params) {
5861
- const { l1Nullifier } = await wrapAs8(
6437
+ const { l1Nullifier } = await wrapAs9(
5862
6438
  "INTERNAL",
5863
6439
  OP_WITHDRAWALS.finalize.readiness.ensureAddresses,
5864
6440
  () => client.ensureAddresses(),
@@ -5869,7 +6445,7 @@ function createFinalizationServices(client) {
5869
6445
  );
5870
6446
  const done = await (async () => {
5871
6447
  try {
5872
- const result = await wrapAs8(
6448
+ const result = await wrapAs9(
5873
6449
  "RPC",
5874
6450
  OP_WITHDRAWALS.finalize.readiness.isFinalized,
5875
6451
  () => client.l1.readContract({
@@ -5903,7 +6479,7 @@ function createFinalizationServices(client) {
5903
6479
  }
5904
6480
  },
5905
6481
  async isWithdrawalFinalized(key) {
5906
- const { l1Nullifier } = await wrapAs8(
6482
+ const { l1Nullifier } = await wrapAs9(
5907
6483
  "INTERNAL",
5908
6484
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
5909
6485
  () => client.ensureAddresses(),
@@ -5912,7 +6488,7 @@ function createFinalizationServices(client) {
5912
6488
  message: "Failed to ensure L1 Nullifier address."
5913
6489
  }
5914
6490
  );
5915
- return await wrapAs8(
6491
+ return await wrapAs9(
5916
6492
  "RPC",
5917
6493
  OP_WITHDRAWALS.finalize.isFinalized,
5918
6494
  () => client.l1.readContract({
@@ -5928,7 +6504,7 @@ function createFinalizationServices(client) {
5928
6504
  );
5929
6505
  },
5930
6506
  async estimateFinalization(params) {
5931
- const { l1Nullifier } = await wrapAs8(
6507
+ const { l1Nullifier } = await wrapAs9(
5932
6508
  "INTERNAL",
5933
6509
  OP_WITHDRAWALS.finalize.estimate,
5934
6510
  () => client.ensureAddresses(),
@@ -5937,7 +6513,7 @@ function createFinalizationServices(client) {
5937
6513
  message: "Failed to ensure L1 Nullifier address."
5938
6514
  }
5939
6515
  );
5940
- const gasLimit = await wrapAs8(
6516
+ const gasLimit = await wrapAs9(
5941
6517
  "RPC",
5942
6518
  OP_WITHDRAWALS.finalize.estimate,
5943
6519
  () => client.l1.estimateContractGas({
@@ -5961,7 +6537,7 @@ function createFinalizationServices(client) {
5961
6537
  let maxFeePerGas;
5962
6538
  let maxPriorityFeePerGas;
5963
6539
  try {
5964
- const fee = await wrapAs8(
6540
+ const fee = await wrapAs9(
5965
6541
  "RPC",
5966
6542
  OP_WITHDRAWALS.finalize.estimate,
5967
6543
  () => client.l1.estimateFeesPerGas(),
@@ -5980,7 +6556,7 @@ function createFinalizationServices(client) {
5980
6556
  })();
5981
6557
  maxPriorityFeePerGas = fee.maxPriorityFeePerGas ?? 0n;
5982
6558
  } catch {
5983
- const gasPrice = await wrapAs8(
6559
+ const gasPrice = await wrapAs9(
5984
6560
  "RPC",
5985
6561
  OP_WITHDRAWALS.finalize.estimate,
5986
6562
  () => client.l1.getGasPrice(),
@@ -5999,7 +6575,7 @@ function createFinalizationServices(client) {
5999
6575
  };
6000
6576
  },
6001
6577
  async finalizeDeposit(params) {
6002
- const { l1Nullifier } = await wrapAs8(
6578
+ const { l1Nullifier } = await wrapAs9(
6003
6579
  "INTERNAL",
6004
6580
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
6005
6581
  () => client.ensureAddresses(),
@@ -6058,40 +6634,32 @@ function createFinalizationServices(client) {
6058
6634
 
6059
6635
  // src/adapters/viem/resources/withdrawals/index.ts
6060
6636
  var ROUTES2 = {
6061
- "eth-base": routeEthBase(),
6637
+ base: routeEthBase(),
6062
6638
  // BaseTokenSystem.withdraw, chain base = ETH
6063
- "eth-nonbase": routeEthNonBase2(),
6064
- // BaseTokenSystem.withdraw, chain base ≠ ETH
6065
6639
  "erc20-nonbase": routeErc20NonBase2()
6066
6640
  // AssetRouter.withdraw for non-base ERC-20s
6067
6641
  };
6068
- function createWithdrawalsResource(client) {
6642
+ function createWithdrawalsResource(client, tokens, contracts) {
6069
6643
  const svc = createFinalizationServices(client);
6070
6644
  const { wrap: wrap2, toResult: toResult2 } = createErrorHandlers("withdrawals");
6645
+ const tokensResource = tokens ?? createTokensResource(client);
6646
+ const contractsResource = contracts ?? createContractsResource(client);
6071
6647
  async function buildPlan(p) {
6072
- const ctx = await commonCtx2(p, client);
6648
+ const ctx = await commonCtx2(p, client, tokensResource, contractsResource);
6073
6649
  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 = {
6650
+ const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
6651
+ return {
6085
6652
  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
- }
6653
+ summary: {
6654
+ route: ctx.route,
6655
+ approvalsNeeded: approvals,
6656
+ amounts: {
6657
+ transfer: { token: p.token, amount: p.amount }
6658
+ },
6659
+ fees
6660
+ },
6661
+ steps
6093
6662
  };
6094
- return { route: ctx.route, summary, steps };
6095
6663
  }
6096
6664
  const finalizeCache = /* @__PURE__ */ new Map();
6097
6665
  const quote = (p) => wrap2(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
@@ -6125,7 +6693,7 @@ function createWithdrawalsResource(client) {
6125
6693
  }
6126
6694
  if (overrides.gasLimit != null) step.tx.gas = overrides.gasLimit;
6127
6695
  }
6128
- if (step.tx.gas == null) {
6696
+ if (!p.l2TxOverrides?.gasLimit) {
6129
6697
  try {
6130
6698
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
6131
6699
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -6424,55 +6992,13 @@ function createWithdrawalsResource(client) {
6424
6992
 
6425
6993
  // src/adapters/viem/sdk.ts
6426
6994
  function createViemSdk(client) {
6995
+ const tokens = createTokensResource(client);
6996
+ const contracts = createContractsResource(client);
6427
6997
  return {
6428
- deposits: createDepositsResource(client),
6429
- withdrawals: createWithdrawalsResource(client),
6430
- helpers: {
6431
- addresses: () => client.ensureAddresses(),
6432
- contracts: () => client.contracts(),
6433
- async l1AssetRouter() {
6434
- const { l1AssetRouter } = await client.contracts();
6435
- return l1AssetRouter;
6436
- },
6437
- async l1NativeTokenVault() {
6438
- const { l1NativeTokenVault } = await client.contracts();
6439
- return l1NativeTokenVault;
6440
- },
6441
- async l1Nullifier() {
6442
- const { l1Nullifier } = await client.contracts();
6443
- return l1Nullifier;
6444
- },
6445
- async baseToken(chainId) {
6446
- const id = chainId ?? BigInt(await client.l2.getChainId());
6447
- return client.baseToken(id);
6448
- },
6449
- async l2TokenAddress(l1Token) {
6450
- if (isAddressEq(l1Token, FORMAL_ETH_ADDRESS)) {
6451
- return ETH_ADDRESS;
6452
- }
6453
- const base = await client.baseToken(BigInt(await client.l2.getChainId()));
6454
- if (isAddressEq(l1Token, base)) {
6455
- return L2_BASE_TOKEN_ADDRESS;
6456
- }
6457
- const { l2NativeTokenVault } = await client.contracts();
6458
- const addr = await l2NativeTokenVault.read.l2TokenAddress([l1Token]);
6459
- return addr;
6460
- },
6461
- async l1TokenAddress(l2Token) {
6462
- if (isAddressEq(l2Token, FORMAL_ETH_ADDRESS)) {
6463
- return FORMAL_ETH_ADDRESS;
6464
- }
6465
- const { l2AssetRouter } = await client.contracts();
6466
- const addr = await l2AssetRouter.read.l1TokenAddress([l2Token]);
6467
- return addr;
6468
- },
6469
- async assetId(l1Token) {
6470
- const norm = isAddressEq(l1Token, FORMAL_ETH_ADDRESS) ? ETH_ADDRESS : l1Token;
6471
- const { l1NativeTokenVault } = await client.contracts();
6472
- const id = await l1NativeTokenVault.read.assetId([norm]);
6473
- return id;
6474
- }
6475
- }
6998
+ deposits: createDepositsResource(client, tokens, contracts),
6999
+ withdrawals: createWithdrawalsResource(client, tokens, contracts),
7000
+ tokens,
7001
+ contracts
6476
7002
  };
6477
7003
  }
6478
7004