@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
@@ -217,27 +217,21 @@ var OP_DEPOSITS = {
217
217
  base: {
218
218
  assertErc20Asset: "deposits.erc20-base:assertErc20Asset",
219
219
  assertMatchesBase: "deposits.erc20-base:assertMatchesBase",
220
- baseToken: "deposits.erc20-base:baseToken",
221
220
  allowance: "deposits.erc20-base:allowance",
222
- baseCost: "deposits.erc20-base:l2TransactionBaseCost",
223
221
  estGas: "deposits.erc20-base:estimateGas"
224
222
  },
225
223
  nonbase: {
226
- baseToken: "deposits.erc20-nonbase:baseToken",
227
224
  assertNotEthAsset: "deposits.erc20-nonbase:assertNotEthAsset",
228
- allowance: "deposits.erc20-nonbase:allowance",
229
- allowanceFees: "deposits.erc20-nonbase:allowanceFeesBaseToken",
230
- baseCost: "deposits.erc20-nonbase:l2TransactionBaseCost",
231
225
  encodeCalldata: "deposits.erc20-nonbase:encodeSecondBridgeErc20Args",
232
226
  estGas: "deposits.erc20-nonbase:estimateGas",
233
- assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken"},
227
+ assertNonBaseToken: "deposits.erc20-nonbase:assertNonBaseToken",
228
+ allowanceToken: "deposits.erc20-nonbase:allowanceToken",
229
+ allowanceBase: "deposits.erc20-nonbase:allowanceBase"
230
+ },
234
231
  eth: {
235
- baseCost: "deposits.eth:l2TransactionBaseCost",
236
232
  estGas: "deposits.eth:estimateGas"
237
233
  },
238
234
  ethNonBase: {
239
- baseToken: "deposits.eth-nonbase:baseToken",
240
- baseCost: "deposits.eth-nonbase:l2TransactionBaseCost",
241
235
  allowanceBase: "deposits.eth-nonbase:allowanceBaseToken",
242
236
  ethBalance: "deposits.eth-nonbase:getEthBalance",
243
237
  encodeCalldata: "deposits.eth-nonbase:encodeSecondBridgeEthArgs",
@@ -595,8 +589,12 @@ var TOPIC_L1_MESSAGE_SENT_NEW = k256hex("L1MessageSent(uint256,bytes32,bytes)");
595
589
  var TOPIC_L1_MESSAGE_SENT_LEG = k256hex("L1MessageSent(address,bytes32,bytes)");
596
590
  var TOPIC_CANONICAL_ASSIGNED = "0x779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df";
597
591
  var TOPIC_CANONICAL_SUCCESS = "0xe4def01b981193a97a9e81230d7b9f31812ceaf23f864a828a82c687911cb2df";
598
- var L1_FEE_ESTIMATION_COEF_NUMERATOR = 12;
599
- var L1_FEE_ESTIMATION_COEF_DENOMINATOR = 10;
592
+ var BUFFER = 20n;
593
+ var TX_OVERHEAD_GAS = 10000n;
594
+ var TX_MEMORY_OVERHEAD_GAS = 10n;
595
+ var DEFAULT_PUBDATA_BYTES = 155n;
596
+ var DEFAULT_ABI_BYTES = 400n;
597
+ var SAFE_L1_BRIDGE_GAS = 600000n;
600
598
 
601
599
  // src/core/internal/abis/IBridgehub.ts
602
600
  var IBridgehubABI = [
@@ -5195,30 +5193,46 @@ function createViemClient(args) {
5195
5193
  };
5196
5194
  }
5197
5195
 
5198
- // src/core/utils/gas.ts
5199
- function assertNoLegacyGas(overrides) {
5200
- if (!overrides) return;
5201
- if ("gasPrice" in overrides && overrides.gasPrice !== void 0) {
5202
- throw new Error("Legacy gasPrice is not supported; use EIP-1559 fields instead.");
5203
- }
5204
- }
5205
- function assertPriorityFeeBounds(fees) {
5206
- if (fees.maxPriorityFeePerGas > fees.maxFeePerGas) {
5207
- throw new Error("maxPriorityFeePerGas cannot exceed maxFeePerGas.");
5208
- }
5209
- }
5210
-
5211
- // src/adapters/viem/resources/utils.ts
5212
- function encodeNativeTokenVaultAssetId(chainId, address) {
5213
- const encoded = viem.encodeAbiParameters(
5214
- [
5215
- { type: "uint256", name: "originChainId" },
5216
- { type: "address", name: "ntv" },
5217
- { type: "address", name: "token" }
5218
- ],
5219
- [chainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, address]
5220
- );
5221
- return viem.keccak256(encoded);
5196
+ // src/adapters/viem/resources/deposits/context.ts
5197
+ async function commonCtx(p, client, tokens, contracts) {
5198
+ const { bridgehub, l1AssetRouter } = await contracts.addresses();
5199
+ const chainId = await client.l2.getChainId();
5200
+ const sender = client.account.address;
5201
+ const gasPerPubdata = p.gasPerPubdata ?? 800n;
5202
+ const operatorTip = p.operatorTip ?? 0n;
5203
+ const refundRecipient = p.refundRecipient ?? sender;
5204
+ const resolvedToken = await tokens.resolve(p.token, { chain: "l1" });
5205
+ const baseTokenAssetId = resolvedToken.baseTokenAssetId;
5206
+ const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
5207
+ const baseIsEth = resolvedToken.isChainEthBased;
5208
+ const route = (() => {
5209
+ if (resolvedToken.kind === "eth") {
5210
+ return baseIsEth ? "eth-base" : "eth-nonbase";
5211
+ }
5212
+ if (resolvedToken.kind === "base") {
5213
+ return baseIsEth ? "eth-base" : "erc20-base";
5214
+ }
5215
+ return "erc20-nonbase";
5216
+ })();
5217
+ return {
5218
+ client,
5219
+ tokens,
5220
+ contracts,
5221
+ resolvedToken,
5222
+ baseTokenAssetId,
5223
+ baseTokenL1,
5224
+ baseIsEth,
5225
+ l1AssetRouter,
5226
+ route,
5227
+ bridgehub,
5228
+ chainIdL2: BigInt(chainId),
5229
+ sender,
5230
+ gasOverrides: p.l1TxOverrides,
5231
+ l2GasLimit: p.l2GasLimit,
5232
+ gasPerPubdata,
5233
+ operatorTip,
5234
+ refundRecipient
5235
+ };
5222
5236
  }
5223
5237
  function encodeNativeTokenVaultTransferData(amount, receiver, token) {
5224
5238
  return viem.encodeAbiParameters(
@@ -5240,123 +5254,6 @@ function encodeSecondBridgeDataV1(assetId, transferData) {
5240
5254
  );
5241
5255
  return viem.concat(["0x01", data]);
5242
5256
  }
5243
- var encodeNTVAssetId = encodeNativeTokenVaultAssetId;
5244
- var encodeNTVTransferData = encodeNativeTokenVaultTransferData;
5245
- function scaleGasLimit(gasLimit) {
5246
- return gasLimit * BigInt(L1_FEE_ESTIMATION_COEF_NUMERATOR) / BigInt(L1_FEE_ESTIMATION_COEF_DENOMINATOR);
5247
- }
5248
- async function checkBaseCost(baseCost, value) {
5249
- const resolved = await value;
5250
- if (baseCost > resolved) {
5251
- throw new Error(
5252
- `The base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: ${String(baseCost)}, provided value: ${String(resolved)}!`
5253
- );
5254
- }
5255
- }
5256
- async function getFeeOverrides(client, overrides) {
5257
- assertNoLegacyGas(overrides);
5258
- let maxFeePerGasFromProvider;
5259
- let maxPriorityFromProvider;
5260
- let gasPriceFromProvider;
5261
- try {
5262
- const fees = await client.l1.estimateFeesPerGas();
5263
- const { maxFeePerGas: maxFeePerGas2, maxPriorityFeePerGas: maxPriorityFeePerGas2 } = fees;
5264
- if (maxFeePerGas2 != null && maxPriorityFeePerGas2 != null) {
5265
- maxFeePerGasFromProvider = maxFeePerGas2;
5266
- maxPriorityFromProvider = maxPriorityFeePerGas2;
5267
- gasPriceFromProvider = fees.gasPrice ?? maxFeePerGas2;
5268
- } else if (fees.gasPrice != null) {
5269
- gasPriceFromProvider = fees.gasPrice;
5270
- }
5271
- } catch {
5272
- }
5273
- if (gasPriceFromProvider == null) {
5274
- try {
5275
- gasPriceFromProvider = await client.l1.getGasPrice();
5276
- } catch {
5277
- }
5278
- }
5279
- const maxFeePerGas = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider;
5280
- if (maxFeePerGas == null) {
5281
- throw new Error("L1 provider returned no gas price data");
5282
- }
5283
- const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? maxPriorityFromProvider ?? maxFeePerGas;
5284
- assertPriorityFeeBounds({ maxFeePerGas, maxPriorityFeePerGas });
5285
- const gasPriceForBaseCost = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider ?? maxFeePerGas;
5286
- return {
5287
- gasLimit: overrides?.gasLimit,
5288
- maxFeePerGas,
5289
- maxPriorityFeePerGas,
5290
- gasPriceForBaseCost
5291
- };
5292
- }
5293
- async function getL2FeeOverrides(client, overrides) {
5294
- assertNoLegacyGas(overrides);
5295
- let maxFeePerGasFromProvider;
5296
- let maxPriorityFromProvider;
5297
- let gasPriceFromProvider;
5298
- try {
5299
- const fees = await client.l2.estimateFeesPerGas();
5300
- if (fees?.maxFeePerGas != null && fees.maxPriorityFeePerGas != null) {
5301
- maxFeePerGasFromProvider = fees.maxFeePerGas;
5302
- maxPriorityFromProvider = fees.maxPriorityFeePerGas;
5303
- gasPriceFromProvider = fees.gasPrice ?? fees.maxFeePerGas;
5304
- } else if (fees?.gasPrice != null) {
5305
- gasPriceFromProvider = fees.gasPrice;
5306
- }
5307
- } catch {
5308
- }
5309
- if (gasPriceFromProvider == null) {
5310
- try {
5311
- gasPriceFromProvider = await client.l2.getGasPrice();
5312
- } catch {
5313
- }
5314
- }
5315
- const maxFeePerGas = overrides?.maxFeePerGas ?? maxFeePerGasFromProvider ?? gasPriceFromProvider;
5316
- if (maxFeePerGas == null) {
5317
- throw new Error("provider returned no gas price data");
5318
- }
5319
- const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? maxPriorityFromProvider ?? maxFeePerGas;
5320
- assertPriorityFeeBounds({ maxFeePerGas, maxPriorityFeePerGas });
5321
- return {
5322
- gasLimit: overrides?.gasLimit,
5323
- maxFeePerGas,
5324
- maxPriorityFeePerGas
5325
- };
5326
- }
5327
- function buildViemFeeOverrides(fees) {
5328
- return {
5329
- maxFeePerGas: fees.maxFeePerGas,
5330
- maxPriorityFeePerGas: fees.maxPriorityFeePerGas,
5331
- gas: fees.gasLimit
5332
- };
5333
- }
5334
- async function getGasPriceWei(client) {
5335
- try {
5336
- const gp = await client.l1.getGasPrice();
5337
- if (gp != null) return gp;
5338
- } catch {
5339
- }
5340
- try {
5341
- const fees = await client.l1.estimateFeesPerGas();
5342
- if (fees?.maxFeePerGas != null) return fees.maxFeePerGas;
5343
- } catch {
5344
- }
5345
- throw new Error("provider returned no gas price data");
5346
- }
5347
- function buildDirectRequestStruct(args) {
5348
- return {
5349
- chainId: args.chainId,
5350
- l2Contract: args.l2Contract,
5351
- mintValue: args.mintValue,
5352
- l2Value: args.l2Value,
5353
- l2Calldata: "0x",
5354
- l2GasLimit: args.l2GasLimit,
5355
- l2GasPerPubdataByteLimit: args.gasPerPubdata,
5356
- factoryDeps: [],
5357
- refundRecipient: args.refundRecipient
5358
- };
5359
- }
5360
5257
  function encodeSecondBridgeArgs(token, amount, l2Receiver) {
5361
5258
  return viem.encodeAbiParameters(
5362
5259
  [
@@ -5373,58 +5270,17 @@ function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
5373
5270
  function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
5374
5271
  return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
5375
5272
  }
5376
-
5377
- // src/core/utils/addr.ts
5378
- var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
5379
- function isAddressEq(a, b) {
5380
- return a.toLowerCase() === b.toLowerCase();
5381
- }
5382
- function isETH(token) {
5383
- return isAddressEq(token, FORMAL_ETH_ADDRESS) || isAddressEq(token, L2_BASE_TOKEN_ADDRESS) || isAddressEq(token, ETH_ADDRESS);
5384
- }
5385
- function normalizeAddrEq(a, b) {
5386
- if (!a || !b) return false;
5387
- const normalize = (s) => {
5388
- const hasPrefix = s.slice(0, 2).toLowerCase() === "0x";
5389
- const body = hasPrefix ? s.slice(2) : s;
5390
- return `0x${body.toLowerCase()}`;
5391
- };
5392
- return normalize(a) === normalize(b);
5393
- }
5394
-
5395
- // src/core/resources/deposits/route.ts
5396
- async function pickDepositRoute(client, chainIdL2, token) {
5397
- if (isETH(token)) {
5398
- const base2 = await client.baseToken(chainIdL2);
5399
- return isETH(base2) ? "eth-base" : "eth-nonbase";
5400
- }
5401
- const base = await client.baseToken(chainIdL2);
5402
- return normalizeAddrEq(token, base) ? "erc20-base" : "erc20-nonbase";
5403
- }
5404
-
5405
- // src/adapters/viem/resources/deposits/context.ts
5406
- async function commonCtx(p, client) {
5407
- const { bridgehub, l1AssetRouter } = await client.ensureAddresses();
5408
- const chainId = await client.l2.getChainId();
5409
- const sender = client.account.address;
5410
- const fee = await getFeeOverrides(client, p.l1TxOverrides);
5411
- const l2GasLimit = p.l2GasLimit ?? 300000n;
5412
- const gasPerPubdata = p.gasPerPubdata ?? 800n;
5413
- const operatorTip = p.operatorTip ?? 0n;
5414
- const refundRecipient = p.refundRecipient ?? sender;
5415
- const route = await pickDepositRoute(client, BigInt(chainId), p.token);
5273
+ function buildDirectRequestStruct(args) {
5416
5274
  return {
5417
- client,
5418
- l1AssetRouter,
5419
- route,
5420
- bridgehub,
5421
- chainIdL2: BigInt(chainId),
5422
- sender,
5423
- fee,
5424
- l2GasLimit,
5425
- gasPerPubdata,
5426
- operatorTip,
5427
- refundRecipient
5275
+ chainId: args.chainId,
5276
+ l2Contract: args.l2Contract,
5277
+ mintValue: args.mintValue,
5278
+ l2Value: args.l2Value,
5279
+ l2Calldata: "0x",
5280
+ l2GasLimit: args.l2GasLimit,
5281
+ l2GasPerPubdataByteLimit: args.gasPerPubdata,
5282
+ factoryDeps: [],
5283
+ refundRecipient: args.refundRecipient
5428
5284
  };
5429
5285
  }
5430
5286
 
@@ -5579,7 +5435,7 @@ function createErrorHandlers(resource) {
5579
5435
  function wrap2(operation, fn, opts) {
5580
5436
  return run("INTERNAL", operation, fn, opts);
5581
5437
  }
5582
- function wrapAs9(kind, operation, fn, opts) {
5438
+ function wrapAs10(kind, operation, fn, opts) {
5583
5439
  return run(kind, operation, fn, opts);
5584
5440
  }
5585
5441
  async function toResult2(operation, fn, opts) {
@@ -5600,44 +5456,358 @@ function createErrorHandlers(resource) {
5600
5456
  return { ok: false, error: shaped };
5601
5457
  }
5602
5458
  }
5603
- return { wrap: wrap2, wrapAs: wrapAs9, toResult: toResult2 };
5459
+ return { wrap: wrap2, wrapAs: wrapAs10, toResult: toResult2 };
5604
5460
  }
5605
5461
 
5606
- // src/adapters/viem/resources/deposits/routes/eth.ts
5462
+ // src/core/resources/deposits/gas.ts
5463
+ function makeGasQuote(p) {
5464
+ const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
5465
+ return {
5466
+ gasLimit: p.gasLimit,
5467
+ maxFeePerGas: p.maxFeePerGas,
5468
+ maxPriorityFeePerGas,
5469
+ gasPerPubdata: p.gasPerPubdata,
5470
+ maxCost: p.gasLimit * p.maxFeePerGas
5471
+ };
5472
+ }
5473
+ async function fetchFees(estimator) {
5474
+ try {
5475
+ const fees = await estimator.estimateFeesPerGas();
5476
+ if (fees.maxFeePerGas != null) {
5477
+ return {
5478
+ maxFeePerGas: fees.maxFeePerGas,
5479
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
5480
+ };
5481
+ }
5482
+ if (fees.gasPrice != null) {
5483
+ return {
5484
+ maxFeePerGas: fees.gasPrice,
5485
+ maxPriorityFeePerGas: 0n
5486
+ };
5487
+ }
5488
+ } catch {
5489
+ }
5490
+ try {
5491
+ const gp = await estimator.getGasPrice();
5492
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
5493
+ } catch {
5494
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
5495
+ }
5496
+ }
5497
+ async function quoteL1Gas(input) {
5498
+ const { estimator, tx, overrides, fallbackGasLimit } = input;
5499
+ let market;
5500
+ const getMarket = async () => {
5501
+ if (market) return market;
5502
+ market = await fetchFees(estimator);
5503
+ return market;
5504
+ };
5505
+ const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
5506
+ const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
5507
+ const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
5508
+ if (explicitGasLimit != null) {
5509
+ return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
5510
+ }
5511
+ try {
5512
+ const est = await estimator.estimateGas(tx);
5513
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
5514
+ return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
5515
+ } catch (err) {
5516
+ if (fallbackGasLimit != null) {
5517
+ return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
5518
+ }
5519
+ console.warn("L1 gas estimation failed", err);
5520
+ return void 0;
5521
+ }
5522
+ }
5523
+ async function quoteL2Gas(input) {
5524
+ const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
5525
+ const market = await fetchFees(estimator);
5526
+ const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
5527
+ const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
5528
+ const explicit = overrideGasLimit ?? txGasLimit;
5529
+ if (explicit != null) {
5530
+ return makeGasQuote({
5531
+ gasLimit: explicit,
5532
+ maxFeePerGas,
5533
+ gasPerPubdata
5534
+ });
5535
+ }
5536
+ if (!tx) {
5537
+ return makeGasQuote({
5538
+ gasLimit: l2GasLimit ?? 0n,
5539
+ maxFeePerGas,
5540
+ gasPerPubdata
5541
+ });
5542
+ }
5543
+ try {
5544
+ const execEstimate = await estimator.estimateGas(tx, stateOverrides);
5545
+ const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
5546
+ const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
5547
+ const pp = gasPerPubdata ?? 800n;
5548
+ const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
5549
+ const pubdataOverhead = pubdataBytes * pp;
5550
+ let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
5551
+ total = total * (100n + BUFFER) / 100n;
5552
+ return makeGasQuote({
5553
+ gasLimit: total,
5554
+ maxFeePerGas,
5555
+ gasPerPubdata: pp
5556
+ });
5557
+ } catch (err) {
5558
+ console.warn("L2 gas estimation failed", err);
5559
+ return makeGasQuote({
5560
+ gasLimit: l2GasLimit ?? 0n,
5561
+ maxFeePerGas,
5562
+ gasPerPubdata
5563
+ });
5564
+ }
5565
+ }
5566
+
5567
+ // src/adapters/viem/estimator.ts
5568
+ function toCoreTx(tx) {
5569
+ return {
5570
+ to: tx.to,
5571
+ from: tx.from,
5572
+ data: tx.data,
5573
+ value: tx.value,
5574
+ gasLimit: tx.gas,
5575
+ maxFeePerGas: tx.maxFeePerGas,
5576
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5577
+ };
5578
+ }
5579
+ function viemToGasEstimator(client) {
5580
+ return {
5581
+ async estimateGas(tx, stateOverrides) {
5582
+ if (stateOverrides) {
5583
+ try {
5584
+ const result = await client.request({
5585
+ method: "eth_estimateGas",
5586
+ params: [
5587
+ {
5588
+ from: tx.from,
5589
+ to: tx.to,
5590
+ data: tx.data,
5591
+ value: tx.value,
5592
+ gas: tx.gasLimit,
5593
+ maxFeePerGas: tx.maxFeePerGas,
5594
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5595
+ },
5596
+ "latest",
5597
+ stateOverrides
5598
+ ]
5599
+ });
5600
+ return BigInt(result);
5601
+ } catch (error) {
5602
+ console.warn(
5603
+ "Failed to estimate gas with state overrides, falling back to standard estimation:",
5604
+ error
5605
+ );
5606
+ }
5607
+ }
5608
+ return await client.estimateGas({
5609
+ account: tx.from,
5610
+ to: tx.to,
5611
+ data: tx.data,
5612
+ value: tx.value,
5613
+ gas: tx.gasLimit,
5614
+ maxFeePerGas: tx.maxFeePerGas,
5615
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5616
+ });
5617
+ },
5618
+ async estimateFeesPerGas() {
5619
+ try {
5620
+ const fees = await client.estimateFeesPerGas();
5621
+ return {
5622
+ maxFeePerGas: fees.maxFeePerGas,
5623
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas
5624
+ };
5625
+ } catch {
5626
+ }
5627
+ try {
5628
+ const gp = await client.getGasPrice();
5629
+ return { gasPrice: gp };
5630
+ } catch {
5631
+ return {};
5632
+ }
5633
+ },
5634
+ async getGasPrice() {
5635
+ return await client.getGasPrice();
5636
+ },
5637
+ async call(tx) {
5638
+ const res = await client.call({
5639
+ to: tx.to,
5640
+ data: tx.data,
5641
+ value: tx.value,
5642
+ account: tx.from
5643
+ });
5644
+ return res.data ?? "0x";
5645
+ }
5646
+ };
5647
+ }
5648
+
5649
+ // src/adapters/viem/resources/deposits/services/gas.ts
5650
+ async function quoteL1Gas2(input) {
5651
+ const { ctx, tx, overrides, fallbackGasLimit } = input;
5652
+ const estimator = viemToGasEstimator(ctx.client.l1);
5653
+ return quoteL1Gas({
5654
+ estimator,
5655
+ tx: toCoreTx(tx),
5656
+ overrides,
5657
+ fallbackGasLimit
5658
+ });
5659
+ }
5660
+ async function quoteL2Gas2(input) {
5661
+ const { ctx, route, l2TxForModeling, overrideGasLimit } = input;
5662
+ const estimator = viemToGasEstimator(ctx.client.l2);
5663
+ return quoteL2Gas({
5664
+ estimator,
5665
+ route,
5666
+ tx: l2TxForModeling ? toCoreTx(l2TxForModeling) : void 0,
5667
+ gasPerPubdata: ctx.gasPerPubdata,
5668
+ l2GasLimit: ctx.l2GasLimit,
5669
+ // TODO: investigate if this should be passed here; weird viem quirk
5670
+ overrideGasLimit,
5671
+ stateOverrides: input.stateOverrides
5672
+ });
5673
+ }
5674
+ async function determineErc20L2Gas(input) {
5675
+ const { ctx, l1Token } = input;
5676
+ const DEFAULT_SAFE_L2_GAS_LIMIT = 3000000n;
5677
+ if (ctx.l2GasLimit != null) {
5678
+ return quoteL2Gas2({
5679
+ ctx,
5680
+ route: "erc20-nonbase",
5681
+ overrideGasLimit: ctx.l2GasLimit
5682
+ });
5683
+ }
5684
+ try {
5685
+ const l2TokenAddress = ctx.tokens ? await ctx.tokens.toL2Address(l1Token) : await (await ctx.contracts.l2NativeTokenVault()).read.l2TokenAddress([l1Token]);
5686
+ if (l2TokenAddress === viem.zeroAddress) {
5687
+ return quoteL2Gas2({
5688
+ ctx,
5689
+ route: "erc20-nonbase",
5690
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5691
+ });
5692
+ }
5693
+ const modelTx = {
5694
+ to: input.modelTx?.to ?? ctx.sender,
5695
+ from: input.modelTx?.from ?? ctx.sender,
5696
+ data: input.modelTx?.data ?? "0x",
5697
+ value: input.modelTx?.value ?? 0n
5698
+ };
5699
+ const gas = await quoteL2Gas2({
5700
+ ctx,
5701
+ route: "erc20-nonbase",
5702
+ l2TxForModeling: modelTx
5703
+ });
5704
+ if (!gas) {
5705
+ return quoteL2Gas2({
5706
+ ctx,
5707
+ route: "erc20-nonbase",
5708
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5709
+ });
5710
+ }
5711
+ return gas;
5712
+ } catch (err) {
5713
+ console.warn("Failed to determine ERC20 L2 gas; defaulting to safe gas limit.", err);
5714
+ return quoteL2Gas2({
5715
+ ctx,
5716
+ route: "erc20-nonbase",
5717
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5718
+ });
5719
+ }
5720
+ }
5721
+
5722
+ // src/adapters/viem/resources/deposits/services/fee.ts
5607
5723
  var { wrapAs } = createErrorHandlers("deposits");
5724
+ async function quoteL2BaseCost(input) {
5725
+ const { ctx, l2GasLimit } = input;
5726
+ const estimator = viemToGasEstimator(ctx.client.l1);
5727
+ const fees = await estimator.estimateFeesPerGas();
5728
+ const gasPrice = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
5729
+ return wrapAs(
5730
+ "RPC",
5731
+ "deposits.fees.l2BaseCost",
5732
+ async () => {
5733
+ return await ctx.client.l1.readContract({
5734
+ address: ctx.bridgehub,
5735
+ abi: IBridgehub_default,
5736
+ functionName: "l2TransactionBaseCost",
5737
+ args: [ctx.chainIdL2, gasPrice, l2GasLimit, ctx.gasPerPubdata]
5738
+ });
5739
+ },
5740
+ { ctx: { chainIdL2: ctx.chainIdL2 } }
5741
+ );
5742
+ }
5743
+
5744
+ // src/core/resources/deposits/fee.ts
5745
+ function buildFeeBreakdown(p) {
5746
+ const l1MaxTotal = p.l1Gas?.maxCost ?? 0n;
5747
+ const l2Total = p.l2BaseCost + p.operatorTip;
5748
+ const l1 = {
5749
+ gasLimit: p.l1Gas?.gasLimit ?? 0n,
5750
+ maxFeePerGas: p.l1Gas?.maxFeePerGas ?? 0n,
5751
+ maxPriorityFeePerGas: p.l1Gas?.maxPriorityFeePerGas,
5752
+ maxTotal: l1MaxTotal
5753
+ };
5754
+ const l2 = {
5755
+ total: l2Total,
5756
+ baseCost: p.l2BaseCost,
5757
+ operatorTip: p.operatorTip,
5758
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
5759
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
5760
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas,
5761
+ gasPerPubdata: p.l2Gas?.gasPerPubdata ?? 0n
5762
+ };
5763
+ return {
5764
+ token: p.feeToken,
5765
+ maxTotal: l1MaxTotal + l2Total,
5766
+ mintValue: p.mintValue,
5767
+ l1,
5768
+ l2
5769
+ };
5770
+ }
5771
+
5772
+ // src/adapters/viem/resources/deposits/routes/eth.ts
5773
+ var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
5608
5774
  function routeEthDirect() {
5609
5775
  return {
5610
5776
  async build(p, ctx) {
5611
- const { gasPriceForBaseCost } = ctx.fee;
5612
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5613
- const rawBaseCost = await wrapAs(
5614
- "CONTRACT",
5615
- OP_DEPOSITS.eth.baseCost,
5616
- () => ctx.client.l1.readContract({
5617
- address: ctx.bridgehub,
5618
- abi: IBridgehub_default,
5619
- functionName: "l2TransactionBaseCost",
5620
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
5621
- }),
5622
- {
5623
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
5624
- message: "Could not fetch L2 base cost from Bridgehub."
5777
+ const l2TxModel = {
5778
+ to: p.to ?? ctx.sender,
5779
+ from: ctx.sender,
5780
+ data: "0x",
5781
+ value: p.amount
5782
+ };
5783
+ const l2GasParams = await quoteL2Gas2({
5784
+ ctx,
5785
+ route: "eth-base",
5786
+ l2TxForModeling: l2TxModel,
5787
+ overrideGasLimit: ctx.l2GasLimit,
5788
+ stateOverrides: {
5789
+ [ctx.sender]: {
5790
+ balance: "0xffffffffffffffffffff"
5791
+ }
5625
5792
  }
5626
- );
5627
- const baseCost = rawBaseCost;
5793
+ });
5794
+ if (!l2GasParams) {
5795
+ throw new Error("Failed to estimate L2 gas for deposit.");
5796
+ }
5797
+ const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
5628
5798
  const l2Contract = p.to ?? ctx.sender;
5629
5799
  const l2Value = p.amount;
5630
5800
  const mintValue = baseCost + ctx.operatorTip + l2Value;
5631
5801
  const req = buildDirectRequestStruct({
5632
5802
  chainId: ctx.chainIdL2,
5633
5803
  mintValue,
5634
- l2GasLimit: ctx.l2GasLimit,
5804
+ l2GasLimit: l2GasParams.gasLimit,
5635
5805
  gasPerPubdata: ctx.gasPerPubdata,
5636
5806
  refundRecipient: ctx.refundRecipient,
5637
5807
  l2Contract,
5638
5808
  l2Value
5639
5809
  });
5640
- const sim = await wrapAs(
5810
+ const sim = await wrapAs2(
5641
5811
  "RPC",
5642
5812
  OP_DEPOSITS.eth.estGas,
5643
5813
  () => ctx.client.l1.simulateContract({
@@ -5653,120 +5823,138 @@ function routeEthDirect() {
5653
5823
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
5654
5824
  }
5655
5825
  );
5656
- const resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
5826
+ const data = viem.encodeFunctionData({
5827
+ abi: sim.request.abi,
5828
+ functionName: sim.request.functionName,
5829
+ args: sim.request.args
5830
+ });
5831
+ const l1TxCandidate = {
5832
+ to: ctx.bridgehub,
5833
+ data,
5834
+ value: mintValue,
5835
+ from: ctx.sender,
5836
+ ...ctx.gasOverrides
5837
+ };
5838
+ const l1Gas = await quoteL1Gas2({
5839
+ ctx,
5840
+ tx: l1TxCandidate,
5841
+ overrides: ctx.gasOverrides
5842
+ });
5657
5843
  const steps = [
5658
5844
  {
5659
5845
  key: "bridgehub:direct",
5660
5846
  kind: "bridgehub:direct",
5661
5847
  description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
5662
- tx: { ...sim.request, ...txFeeOverrides }
5848
+ tx: { ...sim.request, ...l1Gas }
5663
5849
  }
5664
5850
  ];
5851
+ const fees = buildFeeBreakdown({
5852
+ feeToken: ETH_ADDRESS,
5853
+ l1Gas,
5854
+ l2Gas: l2GasParams,
5855
+ l2BaseCost: baseCost,
5856
+ operatorTip: ctx.operatorTip,
5857
+ mintValue
5858
+ });
5665
5859
  return {
5666
5860
  steps,
5667
5861
  approvals: [],
5668
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5862
+ fees
5669
5863
  };
5670
5864
  }
5671
5865
  };
5672
5866
  }
5673
5867
 
5674
- // src/adapters/viem/resources/deposits/routes/erc20-nonbase.ts
5675
- var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
5676
- var BASE_COST_BUFFER_BPS = 100n;
5677
- var BPS = 10000n;
5678
- var withBuffer = (x) => x * (BPS + BASE_COST_BUFFER_BPS) / BPS;
5679
- function routeErc20NonBase() {
5680
- return {
5681
- async preflight(p, ctx) {
5682
- await wrapAs2(
5683
- "VALIDATION",
5684
- OP_DEPOSITS.nonbase.assertNotEthAsset,
5685
- () => {
5686
- if (isETH(p.token)) {
5687
- throw new Error("erc20-nonbase route requires an ERC-20 token (not ETH).");
5688
- }
5689
- },
5690
- { ctx: { token: p.token } }
5691
- );
5692
- const baseToken = await wrapAs2(
5693
- "CONTRACT",
5694
- OP_DEPOSITS.nonbase.baseToken,
5695
- () => ctx.client.l1.readContract({
5696
- address: ctx.bridgehub,
5697
- abi: IBridgehub_default,
5698
- functionName: "baseToken",
5699
- args: [ctx.chainIdL2]
5700
- }),
5701
- { ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 } }
5868
+ // src/core/utils/addr.ts
5869
+ var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
5870
+ function isAddressEq(a, b) {
5871
+ return a.toLowerCase() === b.toLowerCase();
5872
+ }
5873
+ function isETH(token) {
5874
+ return isAddressEq(token, FORMAL_ETH_ADDRESS) || isAddressEq(token, L2_BASE_TOKEN_ADDRESS) || isAddressEq(token, ETH_ADDRESS);
5875
+ }
5876
+ function normalizeAddrEq(a, b) {
5877
+ if (!a || !b) return false;
5878
+ const normalize = (s) => {
5879
+ const hasPrefix = s.slice(0, 2).toLowerCase() === "0x";
5880
+ const body = hasPrefix ? s.slice(2) : s;
5881
+ return `0x${body.toLowerCase()}`;
5882
+ };
5883
+ return normalize(a) === normalize(b);
5884
+ }
5885
+ var hexEq = (a, b) => a.toLowerCase() === b.toLowerCase();
5886
+ var normalizeL1Token = (token) => isAddressEq(token, FORMAL_ETH_ADDRESS) ? ETH_ADDRESS : token;
5887
+
5888
+ // src/adapters/viem/resources/deposits/routes/erc20-nonbase.ts
5889
+ var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
5890
+ function routeErc20NonBase() {
5891
+ return {
5892
+ // TODO: do we even need these validations?
5893
+ async preflight(p, ctx) {
5894
+ await wrapAs3(
5895
+ "VALIDATION",
5896
+ OP_DEPOSITS.nonbase.assertNotEthAsset,
5897
+ () => {
5898
+ if (ctx.resolvedToken?.kind === "eth" || isETH(p.token)) {
5899
+ throw new Error("erc20-nonbase route requires an ERC-20 token (not ETH).");
5900
+ }
5901
+ },
5902
+ { ctx: { token: p.token } }
5702
5903
  );
5703
- await wrapAs2(
5904
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
5905
+ await wrapAs3(
5704
5906
  "VALIDATION",
5705
5907
  OP_DEPOSITS.nonbase.assertNonBaseToken,
5706
5908
  () => {
5707
- if (normalizeAddrEq(baseToken, p.token)) {
5909
+ if (ctx.resolvedToken?.kind === "base" || normalizeAddrEq(baseToken, p.token)) {
5708
5910
  throw new Error("erc20-nonbase route requires a non-base ERC-20 deposit token.");
5709
5911
  }
5710
5912
  },
5711
5913
  { ctx: { depositToken: p.token, baseToken } }
5712
5914
  );
5713
- return;
5714
5915
  },
5715
5916
  async build(p, ctx) {
5716
- const { gasPriceForBaseCost } = ctx.fee;
5717
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5718
- const baseToken = await wrapAs2(
5719
- "CONTRACT",
5720
- OP_DEPOSITS.nonbase.baseToken,
5721
- () => ctx.client.l1.readContract({
5722
- address: ctx.bridgehub,
5723
- abi: IBridgehub_default,
5724
- functionName: "baseToken",
5725
- args: [ctx.chainIdL2]
5726
- }),
5727
- { ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 } }
5728
- );
5729
- const MIN_L2_GAS_FOR_ERC20 = 2500000n;
5730
- 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;
5731
- const rawBaseCost = await wrapAs2(
5732
- "CONTRACT",
5733
- OP_DEPOSITS.nonbase.baseCost,
5734
- () => ctx.client.l1.readContract({
5735
- address: ctx.bridgehub,
5736
- abi: IBridgehub_default,
5737
- functionName: "l2TransactionBaseCost",
5738
- args: [ctx.chainIdL2, gasPriceForBaseCost, l2GasLimitUsed, ctx.gasPerPubdata]
5739
- }),
5740
- { ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 } }
5741
- );
5742
- const baseCost = rawBaseCost;
5743
- const mintValue = withBuffer(baseCost + ctx.operatorTip);
5917
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
5918
+ const baseIsEth = ctx.baseIsEth ?? isETH(baseToken);
5919
+ const assetRouter = ctx.l1AssetRouter;
5920
+ const l2Gas = await determineErc20L2Gas({
5921
+ ctx,
5922
+ l1Token: p.token,
5923
+ modelTx: {
5924
+ to: p.to ?? ctx.sender,
5925
+ from: ctx.sender,
5926
+ data: "0x",
5927
+ value: 0n
5928
+ }
5929
+ });
5930
+ if (!l2Gas) throw new Error("Failed to establish L2 gas parameters.");
5931
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
5932
+ const mintValue = l2BaseCost + ctx.operatorTip;
5744
5933
  const approvals = [];
5745
5934
  const steps = [];
5746
- const depositAllowance = await wrapAs2(
5935
+ const depositAllowance = await wrapAs3(
5747
5936
  "CONTRACT",
5748
- OP_DEPOSITS.nonbase.allowance,
5937
+ OP_DEPOSITS.nonbase.allowanceToken,
5749
5938
  () => ctx.client.l1.readContract({
5750
5939
  address: p.token,
5751
5940
  abi: IERC20_default,
5752
5941
  functionName: "allowance",
5753
- args: [ctx.sender, ctx.l1AssetRouter]
5942
+ args: [ctx.sender, assetRouter]
5754
5943
  }),
5755
5944
  {
5756
- ctx: { where: "erc20.allowance", token: p.token, spender: ctx.l1AssetRouter },
5757
- message: "Failed to read ERC-20 allowance for deposit token."
5945
+ ctx: { where: "erc20.allowance", token: p.token, spender: assetRouter },
5946
+ message: "Failed to read deposit-token allowance."
5758
5947
  }
5759
5948
  );
5760
- const needsDepositApprove = depositAllowance < p.amount;
5761
- if (needsDepositApprove) {
5762
- const approveDepReq = await wrapAs2(
5949
+ if (depositAllowance < p.amount) {
5950
+ const approveSim = await wrapAs3(
5763
5951
  "CONTRACT",
5764
5952
  OP_DEPOSITS.nonbase.estGas,
5765
5953
  () => ctx.client.l1.simulateContract({
5766
5954
  address: p.token,
5767
5955
  abi: IERC20_default,
5768
5956
  functionName: "approve",
5769
- args: [ctx.l1AssetRouter, p.amount],
5957
+ args: [assetRouter, p.amount],
5770
5958
  account: ctx.client.account
5771
5959
  }),
5772
5960
  {
@@ -5774,60 +5962,55 @@ function routeErc20NonBase() {
5774
5962
  message: "Failed to simulate deposit token approve."
5775
5963
  }
5776
5964
  );
5777
- approvals.push({ token: p.token, spender: ctx.l1AssetRouter, amount: p.amount });
5965
+ approvals.push({ token: p.token, spender: assetRouter, amount: p.amount });
5778
5966
  steps.push({
5779
- key: `approve:${p.token}:${ctx.l1AssetRouter}`,
5967
+ key: `approve:${p.token}:${assetRouter}`,
5780
5968
  kind: "approve",
5781
5969
  description: `Approve deposit token for amount`,
5782
- tx: { ...approveDepReq.request, ...txFeeOverrides }
5970
+ tx: { ...approveSim.request }
5783
5971
  });
5784
5972
  }
5785
- const baseIsEth = isETH(baseToken);
5786
- let msgValue = 0n;
5787
5973
  if (!baseIsEth) {
5788
- const baseAllowance = await wrapAs2(
5974
+ const baseAllowance = await wrapAs3(
5789
5975
  "CONTRACT",
5790
- OP_DEPOSITS.nonbase.allowanceFees,
5976
+ OP_DEPOSITS.nonbase.allowanceBase,
5791
5977
  () => ctx.client.l1.readContract({
5792
5978
  address: baseToken,
5793
5979
  abi: IERC20_default,
5794
5980
  functionName: "allowance",
5795
- args: [ctx.sender, ctx.l1AssetRouter]
5981
+ args: [ctx.sender, assetRouter]
5796
5982
  }),
5797
5983
  {
5798
- ctx: { where: "erc20.allowance", token: baseToken, spender: ctx.l1AssetRouter },
5984
+ ctx: { where: "erc20.allowance", token: baseToken, spender: assetRouter },
5799
5985
  message: "Failed to read base-token allowance."
5800
5986
  }
5801
5987
  );
5802
5988
  if (baseAllowance < mintValue) {
5803
- const approveBaseReq = await wrapAs2(
5989
+ const approveBaseSim = await wrapAs3(
5804
5990
  "CONTRACT",
5805
5991
  OP_DEPOSITS.nonbase.estGas,
5806
5992
  () => ctx.client.l1.simulateContract({
5807
5993
  address: baseToken,
5808
5994
  abi: IERC20_default,
5809
5995
  functionName: "approve",
5810
- args: [ctx.l1AssetRouter, mintValue],
5996
+ args: [assetRouter, mintValue],
5811
5997
  account: ctx.client.account
5812
5998
  }),
5813
5999
  {
5814
6000
  ctx: { where: "l1.simulateContract", to: baseToken },
5815
- message: "Failed to simulate base-token approve."
6001
+ message: "Failed to simulate base token approve."
5816
6002
  }
5817
6003
  );
5818
- approvals.push({ token: baseToken, spender: ctx.l1AssetRouter, amount: mintValue });
6004
+ approvals.push({ token: baseToken, spender: assetRouter, amount: mintValue });
5819
6005
  steps.push({
5820
- key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6006
+ key: `approve:${baseToken}:${assetRouter}`,
5821
6007
  kind: "approve",
5822
6008
  description: `Approve base token for mintValue`,
5823
- tx: { ...approveBaseReq.request, ...txFeeOverrides }
6009
+ tx: { ...approveBaseSim.request }
5824
6010
  });
5825
6011
  }
5826
- msgValue = 0n;
5827
- } else {
5828
- msgValue = mintValue;
5829
6012
  }
5830
- const secondBridgeCalldata = await wrapAs2(
6013
+ const secondBridgeCalldata = await wrapAs3(
5831
6014
  "INTERNAL",
5832
6015
  OP_DEPOSITS.nonbase.encodeCalldata,
5833
6016
  () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
@@ -5836,44 +6019,60 @@ function routeErc20NonBase() {
5836
6019
  where: "encodeSecondBridgeErc20Args",
5837
6020
  token: p.token,
5838
6021
  amount: p.amount.toString()
5839
- }
6022
+ },
6023
+ message: "Failed to encode bridging calldata."
5840
6024
  }
5841
6025
  );
5842
- const outer = {
6026
+ const requestStruct = {
5843
6027
  chainId: ctx.chainIdL2,
5844
6028
  mintValue,
5845
6029
  l2Value: 0n,
5846
- l2GasLimit: l2GasLimitUsed,
6030
+ l2GasLimit: l2Gas.gasLimit,
5847
6031
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
5848
6032
  refundRecipient: ctx.refundRecipient,
5849
- secondBridgeAddress: ctx.l1AssetRouter,
6033
+ secondBridgeAddress: assetRouter,
5850
6034
  secondBridgeValue: 0n,
5851
6035
  secondBridgeCalldata
5852
6036
  };
6037
+ const msgValue = baseIsEth ? mintValue : 0n;
6038
+ const calldata = viem.encodeFunctionData({
6039
+ abi: IBridgehub_default,
6040
+ functionName: "requestL2TransactionTwoBridges",
6041
+ args: [requestStruct]
6042
+ });
6043
+ const l1TxCandidate = {
6044
+ to: ctx.bridgehub,
6045
+ data: calldata,
6046
+ value: msgValue,
6047
+ from: ctx.sender,
6048
+ ...ctx.gasOverrides
6049
+ };
6050
+ const l1Gas = await quoteL1Gas2({
6051
+ ctx,
6052
+ tx: l1TxCandidate,
6053
+ overrides: ctx.gasOverrides,
6054
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6055
+ });
5853
6056
  const approvalsNeeded = approvals.length > 0;
5854
6057
  let bridgeTx;
5855
- let resolvedL1GasLimit;
5856
- const gasOverride = txFeeOverrides.gas;
5857
6058
  if (approvalsNeeded) {
5858
6059
  bridgeTx = {
5859
6060
  address: ctx.bridgehub,
5860
6061
  abi: IBridgehub_default,
5861
6062
  functionName: "requestL2TransactionTwoBridges",
5862
- args: [outer],
6063
+ args: [requestStruct],
5863
6064
  value: msgValue,
5864
- account: ctx.client.account,
5865
- ...txFeeOverrides
6065
+ account: ctx.client.account
5866
6066
  };
5867
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
5868
6067
  } else {
5869
- const sim = await wrapAs2(
6068
+ const sim = await wrapAs3(
5870
6069
  "CONTRACT",
5871
6070
  OP_DEPOSITS.nonbase.estGas,
5872
6071
  () => ctx.client.l1.simulateContract({
5873
6072
  address: ctx.bridgehub,
5874
6073
  abi: IBridgehub_default,
5875
6074
  functionName: "requestL2TransactionTwoBridges",
5876
- args: [outer],
6075
+ args: [requestStruct],
5877
6076
  value: msgValue,
5878
6077
  account: ctx.client.account
5879
6078
  }),
@@ -5882,67 +6081,64 @@ function routeErc20NonBase() {
5882
6081
  message: "Failed to simulate two-bridges request."
5883
6082
  }
5884
6083
  );
5885
- bridgeTx = { ...sim.request, ...txFeeOverrides };
5886
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
6084
+ bridgeTx = { ...sim.request };
6085
+ }
6086
+ if (l1Gas) {
6087
+ bridgeTx = {
6088
+ ...bridgeTx,
6089
+ gas: l1Gas.gasLimit,
6090
+ maxFeePerGas: l1Gas.maxFeePerGas,
6091
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6092
+ };
5887
6093
  }
5888
6094
  steps.push({
5889
- key: "bridgehub:two-bridges:nonbase",
6095
+ key: "bridgehub:two-bridges:erc20-nonbase",
5890
6096
  kind: "bridgehub:two-bridges",
5891
6097
  description: baseIsEth ? "Bridge ERC-20 (fees in ETH) via Bridgehub.requestL2TransactionTwoBridges" : "Bridge ERC-20 (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
5892
6098
  tx: bridgeTx
5893
6099
  });
6100
+ const fees = buildFeeBreakdown({
6101
+ feeToken: baseToken,
6102
+ l1Gas,
6103
+ l2Gas,
6104
+ l2BaseCost,
6105
+ operatorTip: ctx.operatorTip,
6106
+ mintValue
6107
+ });
5894
6108
  return {
5895
6109
  steps,
5896
6110
  approvals,
5897
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6111
+ fees
5898
6112
  };
5899
6113
  }
5900
6114
  };
5901
6115
  }
5902
-
5903
- // src/adapters/viem/resources/deposits/routes/eth-nonbase.ts
5904
- var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
5905
- var BASE_COST_BUFFER_BPS2 = 100n;
5906
- var BPS2 = 10000n;
5907
- var withBuffer2 = (x) => x * (BPS2 + BASE_COST_BUFFER_BPS2) / BPS2;
6116
+ var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
5908
6117
  function routeEthNonBase() {
5909
6118
  return {
6119
+ // TODO: do we even need these validations?
5910
6120
  async preflight(p, ctx) {
5911
- await wrapAs3(
6121
+ await wrapAs4(
5912
6122
  "VALIDATION",
5913
6123
  OP_DEPOSITS.ethNonBase.assertEthAsset,
5914
6124
  () => {
5915
- if (!isETH(p.token)) {
6125
+ if (ctx.resolvedToken?.kind !== "eth" && !isETH(p.token)) {
5916
6126
  throw new Error("eth-nonbase route requires ETH as the deposit asset.");
5917
6127
  }
5918
6128
  },
5919
6129
  { ctx: { token: p.token } }
5920
6130
  );
5921
- const baseToken = await wrapAs3(
5922
- "CONTRACT",
5923
- OP_DEPOSITS.ethNonBase.baseToken,
5924
- () => ctx.client.l1.readContract({
5925
- address: ctx.bridgehub,
5926
- abi: IBridgehub_default,
5927
- functionName: "baseToken",
5928
- args: [ctx.chainIdL2]
5929
- }),
5930
- {
5931
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
5932
- message: "Failed to read base token."
5933
- }
5934
- );
5935
- await wrapAs3(
6131
+ await wrapAs4(
5936
6132
  "VALIDATION",
5937
6133
  OP_DEPOSITS.ethNonBase.assertNonEthBase,
5938
6134
  () => {
5939
- if (isETH(baseToken)) {
6135
+ if (ctx.baseIsEth) {
5940
6136
  throw new Error("eth-nonbase route requires target chain base token \u2260 ETH.");
5941
6137
  }
5942
6138
  },
5943
- { ctx: { baseToken, chainIdL2: ctx.chainIdL2 } }
6139
+ { ctx: { baseIsEth: ctx.baseIsEth, chainIdL2: ctx.chainIdL2 } }
5944
6140
  );
5945
- const ethBal = await wrapAs3(
6141
+ const ethBal = await wrapAs4(
5946
6142
  "RPC",
5947
6143
  OP_DEPOSITS.ethNonBase.ethBalance,
5948
6144
  () => ctx.client.l1.getBalance({ address: ctx.sender }),
@@ -5951,7 +6147,7 @@ function routeEthNonBase() {
5951
6147
  message: "Failed to read L1 ETH balance."
5952
6148
  }
5953
6149
  );
5954
- await wrapAs3(
6150
+ await wrapAs4(
5955
6151
  "VALIDATION",
5956
6152
  OP_DEPOSITS.ethNonBase.assertEthBalance,
5957
6153
  () => {
@@ -5961,45 +6157,27 @@ function routeEthNonBase() {
5961
6157
  },
5962
6158
  { ctx: { required: p.amount.toString(), balance: ethBal.toString() } }
5963
6159
  );
5964
- return;
5965
6160
  },
5966
6161
  async build(p, ctx) {
5967
- const { gasPriceForBaseCost } = ctx.fee;
5968
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
5969
- const baseToken = await wrapAs3(
5970
- "CONTRACT",
5971
- OP_DEPOSITS.ethNonBase.baseToken,
5972
- () => ctx.client.l1.readContract({
5973
- address: ctx.bridgehub,
5974
- abi: IBridgehub_default,
5975
- functionName: "baseToken",
5976
- args: [ctx.chainIdL2]
5977
- }),
5978
- {
5979
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
5980
- message: "Failed to read base token."
5981
- }
5982
- );
5983
- const rawBaseCost = await wrapAs3(
5984
- "CONTRACT",
5985
- OP_DEPOSITS.ethNonBase.baseCost,
5986
- () => ctx.client.l1.readContract({
5987
- address: ctx.bridgehub,
5988
- abi: IBridgehub_default,
5989
- functionName: "l2TransactionBaseCost",
5990
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
5991
- }),
5992
- {
5993
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
5994
- message: "Could not fetch L2 base cost."
5995
- }
5996
- );
5997
- const baseCost = BigInt(rawBaseCost);
5998
- const mintValueRaw = baseCost + ctx.operatorTip;
5999
- const mintValue = withBuffer2(mintValueRaw);
6162
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
6163
+ const l2TxModel = {
6164
+ to: p.to ?? ctx.sender,
6165
+ from: ctx.sender,
6166
+ data: "0x",
6167
+ value: 0n
6168
+ };
6169
+ const l2Gas = await quoteL2Gas2({
6170
+ ctx,
6171
+ route: "eth-nonbase",
6172
+ l2TxForModeling: l2TxModel,
6173
+ overrideGasLimit: ctx.l2GasLimit
6174
+ });
6175
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
6176
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
6177
+ const mintValue = l2BaseCost + ctx.operatorTip;
6000
6178
  const approvals = [];
6001
6179
  const steps = [];
6002
- const allowance = await wrapAs3(
6180
+ const allowance = await wrapAs4(
6003
6181
  "CONTRACT",
6004
6182
  OP_DEPOSITS.ethNonBase.allowanceBase,
6005
6183
  () => ctx.client.l1.readContract({
@@ -6015,7 +6193,7 @@ function routeEthNonBase() {
6015
6193
  );
6016
6194
  const needsApprove = allowance < mintValue;
6017
6195
  if (needsApprove) {
6018
- const approveSim = await wrapAs3(
6196
+ const approveSim = await wrapAs4(
6019
6197
  "CONTRACT",
6020
6198
  OP_DEPOSITS.ethNonBase.estGas,
6021
6199
  () => ctx.client.l1.simulateContract({
@@ -6034,11 +6212,11 @@ function routeEthNonBase() {
6034
6212
  steps.push({
6035
6213
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6036
6214
  kind: "approve",
6037
- description: `Approve base token for mintValue`,
6215
+ description: `Approve base token for fees (mintValue)`,
6038
6216
  tx: { ...approveSim.request }
6039
6217
  });
6040
6218
  }
6041
- const secondBridgeCalldata = await wrapAs3(
6219
+ const secondBridgeCalldata = await wrapAs4(
6042
6220
  "INTERNAL",
6043
6221
  OP_DEPOSITS.ethNonBase.encodeCalldata,
6044
6222
  () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
@@ -6051,11 +6229,11 @@ function routeEthNonBase() {
6051
6229
  message: "Failed to encode ETH bridging calldata."
6052
6230
  }
6053
6231
  );
6054
- const outer = {
6232
+ const requestStruct = {
6055
6233
  chainId: ctx.chainIdL2,
6056
6234
  mintValue,
6057
- l2Value: 0n,
6058
- l2GasLimit: ctx.l2GasLimit,
6235
+ l2Value: p.amount,
6236
+ l2GasLimit: l2Gas.gasLimit,
6059
6237
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
6060
6238
  refundRecipient: ctx.refundRecipient,
6061
6239
  secondBridgeAddress: ctx.l1AssetRouter,
@@ -6063,29 +6241,32 @@ function routeEthNonBase() {
6063
6241
  secondBridgeCalldata
6064
6242
  };
6065
6243
  let bridgeTx;
6066
- let resolvedL1GasLimit;
6244
+ let calldata;
6067
6245
  if (needsApprove) {
6068
6246
  bridgeTx = {
6069
6247
  address: ctx.bridgehub,
6070
6248
  abi: IBridgehub_default,
6071
6249
  functionName: "requestL2TransactionTwoBridges",
6072
- args: [outer],
6250
+ args: [requestStruct],
6073
6251
  value: p.amount,
6074
6252
  // base ≠ ETH ⇒ msg.value == secondBridgeValue
6075
6253
  account: ctx.client.account
6076
6254
  };
6077
- resolvedL1GasLimit = ctx.l2GasLimit;
6255
+ calldata = viem.encodeFunctionData({
6256
+ abi: IBridgehub_default,
6257
+ functionName: "requestL2TransactionTwoBridges",
6258
+ args: [requestStruct]
6259
+ });
6078
6260
  } else {
6079
- const twoBridgesSim = await wrapAs3(
6261
+ const sim = await wrapAs4(
6080
6262
  "CONTRACT",
6081
6263
  OP_DEPOSITS.ethNonBase.estGas,
6082
6264
  () => ctx.client.l1.simulateContract({
6083
6265
  address: ctx.bridgehub,
6084
6266
  abi: IBridgehub_default,
6085
6267
  functionName: "requestL2TransactionTwoBridges",
6086
- args: [outer],
6268
+ args: [requestStruct],
6087
6269
  value: p.amount,
6088
- // base ≠ ETH ⇒ msg.value == secondBridgeValue
6089
6270
  account: ctx.client.account
6090
6271
  }),
6091
6272
  {
@@ -6093,8 +6274,33 @@ function routeEthNonBase() {
6093
6274
  message: "Failed to simulate Bridgehub two-bridges request."
6094
6275
  }
6095
6276
  );
6096
- bridgeTx = { ...twoBridgesSim.request, ...txFeeOverrides };
6097
- resolvedL1GasLimit = twoBridgesSim.request.gas ?? ctx.l2GasLimit;
6277
+ calldata = viem.encodeFunctionData({
6278
+ abi: sim.request.abi,
6279
+ functionName: sim.request.functionName,
6280
+ args: sim.request.args
6281
+ });
6282
+ bridgeTx = { ...sim.request };
6283
+ }
6284
+ const l1TxCandidate = {
6285
+ to: ctx.bridgehub,
6286
+ data: calldata,
6287
+ value: p.amount,
6288
+ from: ctx.sender,
6289
+ ...ctx.gasOverrides
6290
+ };
6291
+ const l1Gas = await quoteL1Gas2({
6292
+ ctx,
6293
+ tx: l1TxCandidate,
6294
+ overrides: ctx.gasOverrides,
6295
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6296
+ });
6297
+ if (l1Gas) {
6298
+ bridgeTx = {
6299
+ ...bridgeTx,
6300
+ gas: l1Gas.gasLimit,
6301
+ maxFeePerGas: l1Gas.maxFeePerGas,
6302
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6303
+ };
6098
6304
  }
6099
6305
  steps.push({
6100
6306
  key: "bridgehub:two-bridges:eth-nonbase",
@@ -6102,48 +6308,38 @@ function routeEthNonBase() {
6102
6308
  description: "Bridge ETH (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
6103
6309
  tx: bridgeTx
6104
6310
  });
6311
+ const fees = buildFeeBreakdown({
6312
+ feeToken: baseToken,
6313
+ l1Gas,
6314
+ l2Gas,
6315
+ l2BaseCost,
6316
+ operatorTip: ctx.operatorTip,
6317
+ mintValue
6318
+ });
6105
6319
  return {
6106
6320
  steps,
6107
6321
  approvals,
6108
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6322
+ fees
6109
6323
  };
6110
6324
  }
6111
6325
  };
6112
6326
  }
6113
-
6114
- // src/adapters/viem/resources/deposits/routes/erc20-base.ts
6115
- var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
6116
- var BASE_COST_BUFFER_BPS3 = 100n;
6117
- var BPS3 = 10000n;
6118
- var withBuffer3 = (x) => x * (BPS3 + BASE_COST_BUFFER_BPS3) / BPS3;
6327
+ var { wrapAs: wrapAs5 } = createErrorHandlers("deposits");
6119
6328
  function routeErc20Base() {
6120
6329
  return {
6121
6330
  async preflight(p, ctx) {
6122
- await wrapAs4(
6331
+ await wrapAs5(
6123
6332
  "VALIDATION",
6124
6333
  OP_DEPOSITS.base.assertErc20Asset,
6125
6334
  () => {
6126
- if (isETH(p.token)) {
6335
+ if (ctx.resolvedToken?.kind === "eth" || isETH(p.token)) {
6127
6336
  throw new Error("erc20-base route requires an ERC-20 token (not ETH).");
6128
6337
  }
6129
6338
  },
6130
6339
  { ctx: { token: p.token } }
6131
6340
  );
6132
- const baseToken = await wrapAs4(
6133
- "CONTRACT",
6134
- OP_DEPOSITS.base.baseToken,
6135
- () => ctx.client.l1.readContract({
6136
- address: ctx.bridgehub,
6137
- abi: IBridgehub_default,
6138
- functionName: "baseToken",
6139
- args: [ctx.chainIdL2]
6140
- }),
6141
- {
6142
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
6143
- message: "Failed to read base token."
6144
- }
6145
- );
6146
- await wrapAs4(
6341
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
6342
+ await wrapAs5(
6147
6343
  "VALIDATION",
6148
6344
  OP_DEPOSITS.base.assertMatchesBase,
6149
6345
  () => {
@@ -6153,45 +6349,27 @@ function routeErc20Base() {
6153
6349
  },
6154
6350
  { ctx: { baseToken, provided: p.token, chainIdL2: ctx.chainIdL2 } }
6155
6351
  );
6156
- return;
6157
6352
  },
6158
6353
  async build(p, ctx) {
6159
- const { gasPriceForBaseCost } = ctx.fee;
6160
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
6161
- const gasOverride = txFeeOverrides.gas;
6162
- const baseToken = await wrapAs4(
6163
- "CONTRACT",
6164
- OP_DEPOSITS.base.baseToken,
6165
- () => ctx.client.l1.readContract({
6166
- address: ctx.bridgehub,
6167
- abi: IBridgehub_default,
6168
- functionName: "baseToken",
6169
- args: [ctx.chainIdL2]
6170
- }),
6171
- {
6172
- ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 },
6173
- message: "Failed to read base token."
6174
- }
6175
- );
6176
- const rawBaseCost = await wrapAs4(
6177
- "CONTRACT",
6178
- OP_DEPOSITS.base.baseCost,
6179
- () => ctx.client.l1.readContract({
6180
- address: ctx.bridgehub,
6181
- abi: IBridgehub_default,
6182
- functionName: "l2TransactionBaseCost",
6183
- args: [ctx.chainIdL2, gasPriceForBaseCost, ctx.l2GasLimit, ctx.gasPerPubdata]
6184
- }),
6185
- {
6186
- ctx: { where: "l2TransactionBaseCost", chainIdL2: ctx.chainIdL2 },
6187
- message: "Could not fetch L2 base cost from Bridgehub."
6188
- }
6189
- );
6190
- const baseCost = rawBaseCost;
6191
- const l2Value = p.amount;
6192
- const rawMintValue = baseCost + ctx.operatorTip + l2Value;
6193
- const mintValue = withBuffer3(rawMintValue);
6194
- const allowance = await wrapAs4(
6354
+ const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
6355
+ const l2TxModel = {
6356
+ to: p.to ?? ctx.sender,
6357
+ from: ctx.sender,
6358
+ data: "0x",
6359
+ value: 0n
6360
+ };
6361
+ const l2Gas = await quoteL2Gas2({
6362
+ ctx,
6363
+ route: "erc20-base",
6364
+ l2TxForModeling: l2TxModel,
6365
+ overrideGasLimit: ctx.l2GasLimit
6366
+ });
6367
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
6368
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
6369
+ const mintValue = l2BaseCost + ctx.operatorTip + p.amount;
6370
+ const approvals = [];
6371
+ const steps = [];
6372
+ const allowance = await wrapAs5(
6195
6373
  "CONTRACT",
6196
6374
  OP_DEPOSITS.base.allowance,
6197
6375
  () => ctx.client.l1.readContract({
@@ -6205,11 +6383,9 @@ function routeErc20Base() {
6205
6383
  message: "Failed to read base-token allowance."
6206
6384
  }
6207
6385
  );
6208
- const approvals = [];
6209
- const steps = [];
6210
6386
  const needsApprove = allowance < mintValue;
6211
6387
  if (needsApprove) {
6212
- const approveSim = await wrapAs4(
6388
+ const approveSim = await wrapAs5(
6213
6389
  "CONTRACT",
6214
6390
  OP_DEPOSITS.base.estGas,
6215
6391
  () => ctx.client.l1.simulateContract({
@@ -6229,20 +6405,20 @@ function routeErc20Base() {
6229
6405
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6230
6406
  kind: "approve",
6231
6407
  description: "Approve base token for mintValue",
6232
- tx: { ...approveSim.request, ...txFeeOverrides }
6408
+ tx: { ...approveSim.request }
6233
6409
  });
6234
6410
  }
6235
6411
  const req = buildDirectRequestStruct({
6236
6412
  chainId: ctx.chainIdL2,
6237
6413
  mintValue,
6238
- l2GasLimit: ctx.l2GasLimit,
6414
+ l2GasLimit: l2Gas.gasLimit,
6239
6415
  gasPerPubdata: ctx.gasPerPubdata,
6240
6416
  refundRecipient: ctx.refundRecipient,
6241
6417
  l2Contract: p.to ?? ctx.sender,
6242
- l2Value
6418
+ l2Value: p.amount
6243
6419
  });
6244
6420
  let bridgeTx;
6245
- let resolvedL1GasLimit;
6421
+ let calldata;
6246
6422
  if (needsApprove) {
6247
6423
  bridgeTx = {
6248
6424
  address: ctx.bridgehub,
@@ -6250,13 +6426,16 @@ function routeErc20Base() {
6250
6426
  functionName: "requestL2TransactionDirect",
6251
6427
  args: [req],
6252
6428
  value: 0n,
6253
- // base is ERC-20 ⇒ msg.value MUST be 0
6254
- account: ctx.client.account,
6255
- ...txFeeOverrides
6429
+ // base token is ERC-20 ⇒ msg.value MUST be 0
6430
+ account: ctx.client.account
6256
6431
  };
6257
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
6432
+ calldata = viem.encodeFunctionData({
6433
+ abi: IBridgehub_default,
6434
+ functionName: "requestL2TransactionDirect",
6435
+ args: [req]
6436
+ });
6258
6437
  } else {
6259
- const sim = await wrapAs4(
6438
+ const sim = await wrapAs5(
6260
6439
  "RPC",
6261
6440
  OP_DEPOSITS.base.estGas,
6262
6441
  () => ctx.client.l1.simulateContract({
@@ -6272,8 +6451,33 @@ function routeErc20Base() {
6272
6451
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
6273
6452
  }
6274
6453
  );
6275
- bridgeTx = { ...sim.request, ...txFeeOverrides };
6276
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
6454
+ calldata = viem.encodeFunctionData({
6455
+ abi: sim.request.abi,
6456
+ functionName: sim.request.functionName,
6457
+ args: sim.request.args
6458
+ });
6459
+ bridgeTx = { ...sim.request };
6460
+ }
6461
+ const l1TxCandidate = {
6462
+ to: ctx.bridgehub,
6463
+ data: calldata,
6464
+ value: 0n,
6465
+ from: ctx.sender,
6466
+ ...ctx.gasOverrides
6467
+ };
6468
+ const l1Gas = await quoteL1Gas2({
6469
+ ctx,
6470
+ tx: l1TxCandidate,
6471
+ overrides: ctx.gasOverrides,
6472
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6473
+ });
6474
+ if (l1Gas) {
6475
+ bridgeTx = {
6476
+ ...bridgeTx,
6477
+ gas: l1Gas.gasLimit,
6478
+ maxFeePerGas: l1Gas.maxFeePerGas,
6479
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6480
+ };
6277
6481
  }
6278
6482
  steps.push({
6279
6483
  key: "bridgehub:direct:erc20-base",
@@ -6281,10 +6485,18 @@ function routeErc20Base() {
6281
6485
  description: "Bridge base ERC-20 via Bridgehub.requestL2TransactionDirect",
6282
6486
  tx: bridgeTx
6283
6487
  });
6488
+ const fees = buildFeeBreakdown({
6489
+ feeToken: baseToken,
6490
+ l1Gas,
6491
+ l2Gas,
6492
+ l2BaseCost,
6493
+ operatorTip: ctx.operatorTip,
6494
+ mintValue
6495
+ });
6284
6496
  return {
6285
6497
  steps,
6286
6498
  approvals,
6287
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6499
+ fees
6288
6500
  };
6289
6501
  }
6290
6502
  };
@@ -6365,6 +6577,276 @@ async function waitForL2ExecutionFromL1Tx(l1, l2, l1TxHash) {
6365
6577
  return { l2Receipt, l2TxHash };
6366
6578
  }
6367
6579
 
6580
+ // src/core/codec/ntv.ts
6581
+ function createNTVCodec(deps) {
6582
+ function encodeAssetId(originChainId, ntvAddress, tokenAddress) {
6583
+ const encoded = deps.encode(
6584
+ ["uint256", "address", "address"],
6585
+ [originChainId, ntvAddress, tokenAddress]
6586
+ );
6587
+ return deps.keccak256(encoded);
6588
+ }
6589
+ return {
6590
+ encodeAssetId
6591
+ };
6592
+ }
6593
+
6594
+ // src/adapters/viem/resources/tokens/tokens.ts
6595
+ var { wrapAs: wrapAs6 } = createErrorHandlers("tokens");
6596
+ var ntvCodec = createNTVCodec({
6597
+ encode: (types, values) => viem.encodeAbiParameters(
6598
+ types.map((t, i) => ({ type: t, name: `arg${i}` })),
6599
+ values
6600
+ ),
6601
+ keccak256: (data) => viem.keccak256(data)
6602
+ });
6603
+ function createTokensResource(client) {
6604
+ let l2NtvL1ChainIdPromise = null;
6605
+ let baseTokenAssetIdPromise = null;
6606
+ let wethL1Promise = null;
6607
+ let wethL2Promise = null;
6608
+ async function getL1ChainId() {
6609
+ if (!l2NtvL1ChainIdPromise) {
6610
+ l2NtvL1ChainIdPromise = wrapAs6("INTERNAL", "getL1ChainId", async () => {
6611
+ const { l2NativeTokenVault } = await client.contracts();
6612
+ return await l2NativeTokenVault.read.L1_CHAIN_ID();
6613
+ });
6614
+ }
6615
+ return l2NtvL1ChainIdPromise;
6616
+ }
6617
+ async function getBaseTokenAssetId() {
6618
+ if (!baseTokenAssetIdPromise) {
6619
+ baseTokenAssetIdPromise = wrapAs6("INTERNAL", "baseTokenAssetId", async () => {
6620
+ const { l2NativeTokenVault } = await client.contracts();
6621
+ const assetId = await l2NativeTokenVault.read.BASE_TOKEN_ASSET_ID();
6622
+ return assetId;
6623
+ });
6624
+ }
6625
+ return baseTokenAssetIdPromise;
6626
+ }
6627
+ async function getWethL1() {
6628
+ if (!wethL1Promise) {
6629
+ wethL1Promise = wrapAs6("INTERNAL", "wethL1", async () => {
6630
+ const { l1NativeTokenVault } = await client.contracts();
6631
+ const weth = await l1NativeTokenVault.read.WETH_TOKEN();
6632
+ return weth;
6633
+ });
6634
+ }
6635
+ return wethL1Promise;
6636
+ }
6637
+ async function getWethL2() {
6638
+ if (!wethL2Promise) {
6639
+ wethL2Promise = wrapAs6("INTERNAL", "wethL2", async () => {
6640
+ const { l2NativeTokenVault } = await client.contracts();
6641
+ const weth = await l2NativeTokenVault.read.WETH_TOKEN();
6642
+ return weth;
6643
+ });
6644
+ }
6645
+ return wethL2Promise;
6646
+ }
6647
+ async function toL2Address(l1Token) {
6648
+ return wrapAs6("CONTRACT", "tokens.toL2Address", async () => {
6649
+ const normalized = normalizeL1Token(l1Token);
6650
+ const chainId = BigInt(await client.l2.getChainId());
6651
+ const baseToken = await client.baseToken(chainId);
6652
+ if (isAddressEq(normalized, baseToken)) {
6653
+ return L2_BASE_TOKEN_ADDRESS;
6654
+ }
6655
+ const { l2NativeTokenVault } = await client.contracts();
6656
+ const l2Token = await l2NativeTokenVault.read.l2TokenAddress([normalized]);
6657
+ return l2Token;
6658
+ });
6659
+ }
6660
+ async function toL1Address(l2Token) {
6661
+ return wrapAs6("CONTRACT", "tokens.toL1Address", async () => {
6662
+ if (isAddressEq(l2Token, ETH_ADDRESS)) return ETH_ADDRESS;
6663
+ if (isAddressEq(l2Token, L2_BASE_TOKEN_ADDRESS)) {
6664
+ const chainId = BigInt(await client.l2.getChainId());
6665
+ return await client.baseToken(chainId);
6666
+ }
6667
+ const { l2AssetRouter } = await client.contracts();
6668
+ const l1Token = await l2AssetRouter.read.l1TokenAddress([l2Token]);
6669
+ return l1Token;
6670
+ });
6671
+ }
6672
+ async function assetIdOfL1(l1Token) {
6673
+ return wrapAs6("CONTRACT", "tokens.assetIdOfL1", async () => {
6674
+ const normalized = normalizeL1Token(l1Token);
6675
+ const { l1NativeTokenVault } = await client.contracts();
6676
+ return await l1NativeTokenVault.read.assetId([normalized]);
6677
+ });
6678
+ }
6679
+ async function assetIdOfL2(l2Token) {
6680
+ return wrapAs6("CONTRACT", "tokens.assetIdOfL2", async () => {
6681
+ const { l2NativeTokenVault } = await client.contracts();
6682
+ return await l2NativeTokenVault.read.assetId([l2Token]);
6683
+ });
6684
+ }
6685
+ async function l2TokenFromAssetId(assetId) {
6686
+ return wrapAs6("CONTRACT", "tokens.l2TokenFromAssetId", async () => {
6687
+ const { l2NativeTokenVault } = await client.contracts();
6688
+ return await l2NativeTokenVault.read.tokenAddress([assetId]);
6689
+ });
6690
+ }
6691
+ async function l1TokenFromAssetId(assetId) {
6692
+ return wrapAs6("CONTRACT", "tokens.l1TokenFromAssetId", async () => {
6693
+ const { l1NativeTokenVault } = await client.contracts();
6694
+ return await l1NativeTokenVault.read.tokenAddress([assetId]);
6695
+ });
6696
+ }
6697
+ async function originChainId(assetId) {
6698
+ return wrapAs6("CONTRACT", "tokens.originChainId", async () => {
6699
+ const { l2NativeTokenVault } = await client.contracts();
6700
+ return await l2NativeTokenVault.read.originChainId([assetId]);
6701
+ });
6702
+ }
6703
+ async function baseTokenAssetId() {
6704
+ return getBaseTokenAssetId();
6705
+ }
6706
+ async function isChainEthBased() {
6707
+ return wrapAs6("CONTRACT", "tokens.isChainEthBased", async () => {
6708
+ const baseAssetId = await getBaseTokenAssetId();
6709
+ const l1ChainId = await getL1ChainId();
6710
+ const ethAssetId = ntvCodec.encodeAssetId(
6711
+ l1ChainId,
6712
+ L2_NATIVE_TOKEN_VAULT_ADDRESS,
6713
+ ETH_ADDRESS
6714
+ );
6715
+ return hexEq(baseAssetId, ethAssetId);
6716
+ });
6717
+ }
6718
+ async function wethL1() {
6719
+ return getWethL1();
6720
+ }
6721
+ async function wethL2() {
6722
+ return getWethL2();
6723
+ }
6724
+ async function computeL2BridgedAddress(args) {
6725
+ return wrapAs6("CONTRACT", "tokens.computeL2BridgedAddress", async () => {
6726
+ const normalized = normalizeL1Token(args.l1Token);
6727
+ const { l2NativeTokenVault } = await client.contracts();
6728
+ const predicted = await l2NativeTokenVault.read.calculateCreate2TokenAddress([
6729
+ args.originChainId,
6730
+ normalized
6731
+ ]);
6732
+ return predicted;
6733
+ });
6734
+ }
6735
+ async function resolve(ref, opts) {
6736
+ return wrapAs6("CONTRACT", "tokens.resolve", async () => {
6737
+ let chain;
6738
+ let address;
6739
+ if (typeof ref === "string") {
6740
+ chain = opts?.chain ?? "l1";
6741
+ address = ref;
6742
+ } else {
6743
+ chain = ref.chain;
6744
+ address = ref.address;
6745
+ }
6746
+ let l1;
6747
+ let l2;
6748
+ if (chain === "l1") {
6749
+ l1 = normalizeL1Token(address);
6750
+ l2 = await toL2Address(address);
6751
+ } else {
6752
+ l2 = address;
6753
+ l1 = await toL1Address(address);
6754
+ }
6755
+ const assetId = await assetIdOfL1(l1);
6756
+ const originChainIdVal = await originChainId(assetId);
6757
+ const [baseAssetId, wethL1Addr, wethL2Addr, ethBased] = await Promise.all([
6758
+ baseTokenAssetId(),
6759
+ wethL1(),
6760
+ wethL2(),
6761
+ isChainEthBased()
6762
+ ]);
6763
+ let kind;
6764
+ if (isAddressEq(l1, ETH_ADDRESS)) {
6765
+ kind = "eth";
6766
+ } else if (hexEq(assetId, baseAssetId)) {
6767
+ kind = "base";
6768
+ } else {
6769
+ kind = "erc20";
6770
+ }
6771
+ return {
6772
+ kind,
6773
+ l1,
6774
+ l2,
6775
+ assetId,
6776
+ originChainId: originChainIdVal,
6777
+ isChainEthBased: ethBased,
6778
+ baseTokenAssetId: baseAssetId,
6779
+ wethL1: wethL1Addr,
6780
+ wethL2: wethL2Addr
6781
+ };
6782
+ });
6783
+ }
6784
+ return {
6785
+ resolve,
6786
+ toL2Address,
6787
+ toL1Address,
6788
+ assetIdOfL1,
6789
+ assetIdOfL2,
6790
+ l2TokenFromAssetId,
6791
+ l1TokenFromAssetId,
6792
+ originChainId,
6793
+ baseTokenAssetId,
6794
+ isChainEthBased,
6795
+ wethL1,
6796
+ wethL2,
6797
+ computeL2BridgedAddress
6798
+ };
6799
+ }
6800
+
6801
+ // src/adapters/viem/resources/contracts/contracts.ts
6802
+ function createContractsResource(client) {
6803
+ async function addresses() {
6804
+ return client.ensureAddresses();
6805
+ }
6806
+ async function instances() {
6807
+ return client.contracts();
6808
+ }
6809
+ async function bridgehub() {
6810
+ const { bridgehub: bridgehub2 } = await instances();
6811
+ return bridgehub2;
6812
+ }
6813
+ async function l1AssetRouter() {
6814
+ const { l1AssetRouter: l1AssetRouter2 } = await instances();
6815
+ return l1AssetRouter2;
6816
+ }
6817
+ async function l1NativeTokenVault() {
6818
+ const { l1NativeTokenVault: l1NativeTokenVault2 } = await instances();
6819
+ return l1NativeTokenVault2;
6820
+ }
6821
+ async function l1Nullifier() {
6822
+ const { l1Nullifier: l1Nullifier2 } = await instances();
6823
+ return l1Nullifier2;
6824
+ }
6825
+ async function l2AssetRouter() {
6826
+ const { l2AssetRouter: l2AssetRouter2 } = await instances();
6827
+ return l2AssetRouter2;
6828
+ }
6829
+ async function l2NativeTokenVault() {
6830
+ const { l2NativeTokenVault: l2NativeTokenVault2 } = await instances();
6831
+ return l2NativeTokenVault2;
6832
+ }
6833
+ async function l2BaseTokenSystem() {
6834
+ const { l2BaseTokenSystem: l2BaseTokenSystem2 } = await instances();
6835
+ return l2BaseTokenSystem2;
6836
+ }
6837
+ return {
6838
+ addresses,
6839
+ instances,
6840
+ bridgehub,
6841
+ l1AssetRouter,
6842
+ l1NativeTokenVault,
6843
+ l1Nullifier,
6844
+ l2AssetRouter,
6845
+ l2NativeTokenVault,
6846
+ l2BaseTokenSystem
6847
+ };
6848
+ }
6849
+
6368
6850
  // src/adapters/viem/resources/deposits/index.ts
6369
6851
  var { wrap, toResult } = createErrorHandlers("deposits");
6370
6852
  var ROUTES = {
@@ -6373,37 +6855,26 @@ var ROUTES = {
6373
6855
  "erc20-nonbase": routeErc20NonBase(),
6374
6856
  "erc20-base": routeErc20Base()
6375
6857
  };
6376
- function createDepositsResource(client) {
6858
+ function createDepositsResource(client, tokens, contracts) {
6859
+ const tokensResource = tokens ?? createTokensResource(client);
6860
+ const contractsResource = contracts ?? createContractsResource(client);
6377
6861
  async function buildPlan(p) {
6378
- const ctx = await commonCtx(p, client);
6862
+ const ctx = await commonCtx(p, client, tokensResource, contractsResource);
6379
6863
  const route = ctx.route;
6380
6864
  await ROUTES[route].preflight?.(p, ctx);
6381
- const { steps, approvals, quoteExtras } = await ROUTES[route].build(p, ctx);
6382
- const { baseCost, mintValue } = quoteExtras;
6383
- const fallbackGasLimit = quoteExtras.l1GasLimit;
6384
- const resolveGasLimit = () => {
6385
- if (ctx.fee.gasLimit != null) return ctx.fee.gasLimit;
6386
- for (let i = steps.length - 1; i >= 0; i--) {
6387
- const candidate = steps[i].tx.gas;
6388
- if (candidate != null) return candidate;
6389
- }
6390
- if (fallbackGasLimit != null) return fallbackGasLimit;
6391
- return ctx.l2GasLimit;
6392
- };
6393
- const gasLimit = resolveGasLimit();
6865
+ const { steps, approvals, fees } = await ROUTES[route].build(p, ctx);
6394
6866
  return {
6395
6867
  route: ctx.route,
6396
6868
  summary: {
6397
6869
  route: ctx.route,
6398
6870
  approvalsNeeded: approvals,
6399
- baseCost,
6400
- mintValue,
6401
- gasPerPubdata: ctx.gasPerPubdata,
6402
- fees: {
6403
- gasLimit,
6404
- maxFeePerGas: ctx.fee.maxFeePerGas,
6405
- maxPriorityFeePerGas: ctx.fee.maxPriorityFeePerGas
6406
- }
6871
+ amounts: {
6872
+ transfer: { token: p.token, amount: p.amount }
6873
+ },
6874
+ fees,
6875
+ // Legacy fields (maintained for backward compatibility)
6876
+ baseCost: fees.l2?.baseCost,
6877
+ mintValue: fees.mintValue
6407
6878
  },
6408
6879
  steps
6409
6880
  };
@@ -6478,7 +6949,7 @@ function createDepositsResource(client) {
6478
6949
  step.tx.gas = overrides.gasLimit;
6479
6950
  }
6480
6951
  }
6481
- if (step.tx.gas == null) {
6952
+ if (!p.l1TxOverrides?.gasLimit) {
6482
6953
  try {
6483
6954
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
6484
6955
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -6701,36 +7172,12 @@ function normalizeTokenForRouting(token) {
6701
7172
  function pickWithdrawRoute(args) {
6702
7173
  const tokenNorm = normalizeTokenForRouting(args.token);
6703
7174
  const isL2BaseAlias = tokenNorm.toLowerCase() === L2_BASE_TOKEN_ADDRESS.toLowerCase();
6704
- if (isL2BaseAlias) return args.baseIsEth ? "eth-base" : "eth-nonbase";
7175
+ if (isL2BaseAlias) return "base";
6705
7176
  return "erc20-nonbase";
6706
7177
  }
6707
7178
 
6708
- // src/adapters/viem/resources/token-info.ts
6709
- async function ntvBaseAssetId(l2, ntv) {
6710
- return l2.readContract({
6711
- address: ntv,
6712
- abi: L2NativeTokenVault_default,
6713
- functionName: "BASE_TOKEN_ASSET_ID"
6714
- });
6715
- }
6716
- async function ntvL1ChainId(l2, ntv) {
6717
- return l2.readContract({
6718
- address: ntv,
6719
- abi: L2NativeTokenVault_default,
6720
- functionName: "L1_CHAIN_ID"
6721
- });
6722
- }
6723
- async function isEthBasedChain(l2, ntv) {
6724
- const [baseAssetId, l1ChainId] = await Promise.all([
6725
- ntvBaseAssetId(l2, ntv),
6726
- ntvL1ChainId(l2, ntv)
6727
- ]);
6728
- const ethAssetId = encodeNativeTokenVaultAssetId(l1ChainId, ETH_ADDRESS);
6729
- return baseAssetId.toLowerCase() === ethAssetId.toLowerCase();
6730
- }
6731
-
6732
7179
  // src/adapters/viem/resources/withdrawals/context.ts
6733
- async function commonCtx2(p, client) {
7180
+ async function commonCtx2(p, client, tokens, contracts) {
6734
7181
  const sender = client.account.address;
6735
7182
  const {
6736
7183
  bridgehub,
@@ -6739,18 +7186,20 @@ async function commonCtx2(p, client) {
6739
7186
  l2AssetRouter,
6740
7187
  l2NativeTokenVault,
6741
7188
  l2BaseTokenSystem
6742
- } = await client.ensureAddresses();
7189
+ } = await contracts.addresses();
6743
7190
  const chainIdL2 = BigInt(await client.l2.getChainId());
6744
- const baseIsEth = await isEthBasedChain(client.l2, l2NativeTokenVault);
6745
- const fee = await getL2FeeOverrides(client, p.l2TxOverrides);
6746
- const route = pickWithdrawRoute({
6747
- token: p.token,
6748
- baseIsEth
6749
- });
6750
- const l2GasLimit = p.l2GasLimit ?? 300000n;
6751
- const gasBufferPct = 15;
7191
+ const resolvedToken = await tokens.resolve(p.token, { chain: "l2" });
7192
+ const baseTokenAssetId = resolvedToken.baseTokenAssetId;
7193
+ const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
7194
+ const baseIsEth = resolvedToken.isChainEthBased;
7195
+ const route = pickWithdrawRoute({ token: p.token});
6752
7196
  return {
6753
7197
  client,
7198
+ tokens,
7199
+ contracts,
7200
+ resolvedToken,
7201
+ baseTokenAssetId,
7202
+ baseTokenL1,
6754
7203
  bridgehub,
6755
7204
  chainIdL2,
6756
7205
  sender,
@@ -6761,56 +7210,162 @@ async function commonCtx2(p, client) {
6761
7210
  l2NativeTokenVault,
6762
7211
  l2BaseTokenSystem,
6763
7212
  baseIsEth,
6764
- l2GasLimit,
6765
- gasBufferPct,
6766
- fee
7213
+ gasOverrides: p.l2TxOverrides
7214
+ };
7215
+ }
7216
+
7217
+ // src/core/resources/withdrawals/gas.ts
7218
+ function makeGasQuote2(p) {
7219
+ return {
7220
+ gasLimit: p.gasLimit,
7221
+ maxFeePerGas: p.maxFeePerGas,
7222
+ maxPriorityFeePerGas: p.maxPriorityFeePerGas,
7223
+ maxCost: p.gasLimit * p.maxFeePerGas
7224
+ };
7225
+ }
7226
+ async function fetchFees2(estimator) {
7227
+ try {
7228
+ const fees = await estimator.estimateFeesPerGas();
7229
+ if (fees.maxFeePerGas != null) {
7230
+ return {
7231
+ maxFeePerGas: fees.maxFeePerGas,
7232
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
7233
+ };
7234
+ }
7235
+ if (fees.gasPrice != null) {
7236
+ return {
7237
+ maxFeePerGas: fees.gasPrice,
7238
+ maxPriorityFeePerGas: 0n
7239
+ };
7240
+ }
7241
+ } catch {
7242
+ }
7243
+ try {
7244
+ const gp = await estimator.getGasPrice();
7245
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
7246
+ } catch {
7247
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
7248
+ }
7249
+ }
7250
+ async function quoteL2Gas3(input) {
7251
+ const { estimator, tx, overrides } = input;
7252
+ const market = await fetchFees2(estimator);
7253
+ const o = overrides;
7254
+ const maxFeePerGas = o?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : market.maxFeePerGas);
7255
+ const maxPriorityFeePerGas = o?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : market.maxPriorityFeePerGas);
7256
+ const explicitGasLimit = o?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
7257
+ if (explicitGasLimit != null) {
7258
+ return makeGasQuote2({
7259
+ gasLimit: explicitGasLimit,
7260
+ maxFeePerGas,
7261
+ maxPriorityFeePerGas
7262
+ });
7263
+ }
7264
+ try {
7265
+ const est = await estimator.estimateGas(tx);
7266
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
7267
+ return makeGasQuote2({
7268
+ gasLimit: buffered,
7269
+ maxFeePerGas,
7270
+ maxPriorityFeePerGas
7271
+ });
7272
+ } catch (err) {
7273
+ console.warn("Failed to estimate L2 gas for withdrawal.", err);
7274
+ return void 0;
7275
+ }
7276
+ }
7277
+
7278
+ // src/adapters/viem/resources/withdrawals/services/gas.ts
7279
+ async function quoteL2Gas4(input) {
7280
+ const { ctx, tx } = input;
7281
+ const estimator = viemToGasEstimator(ctx.client.l2);
7282
+ return quoteL2Gas3({
7283
+ estimator,
7284
+ tx: toCoreTx(tx),
7285
+ overrides: ctx.gasOverrides
7286
+ });
7287
+ }
7288
+
7289
+ // src/adapters/viem/resources/withdrawals/services/fee.ts
7290
+ function buildFeeBreakdown2(p) {
7291
+ const l2Total = p.l2Gas?.maxCost ?? 0n;
7292
+ const l2 = {
7293
+ total: l2Total,
7294
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
7295
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
7296
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
7297
+ };
7298
+ return {
7299
+ token: p.feeToken,
7300
+ maxTotal: l2Total,
7301
+ l2
6767
7302
  };
6768
7303
  }
6769
7304
 
6770
7305
  // src/adapters/viem/resources/withdrawals/routes/eth.ts
6771
- var { wrapAs: wrapAs5 } = createErrorHandlers("withdrawals");
7306
+ var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
6772
7307
  function routeEthBase() {
6773
7308
  return {
6774
7309
  async build(p, ctx) {
6775
- const toL1 = p.to ?? ctx.sender;
6776
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
6777
- const sim = await wrapAs5(
6778
- "CONTRACT",
6779
- OP_WITHDRAWALS.eth.estGas,
6780
- () => ctx.client.l2.simulateContract({
6781
- address: L2_BASE_TOKEN_ADDRESS,
6782
- abi: IBaseToken_default,
6783
- functionName: "withdraw",
6784
- args: [toL1],
6785
- value: p.amount,
6786
- account: ctx.client.account,
6787
- ...txFeeOverrides
6788
- }),
7310
+ const steps = [];
7311
+ const data = await wrapAs7(
7312
+ "INTERNAL",
7313
+ OP_WITHDRAWALS.eth.encodeWithdraw,
7314
+ () => Promise.resolve(
7315
+ viem.encodeFunctionData({
7316
+ abi: IBaseToken_default,
7317
+ functionName: "withdraw",
7318
+ args: [p.to ?? ctx.sender]
7319
+ })
7320
+ ),
6789
7321
  {
6790
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
6791
- message: "Failed to simulate L2 ETH withdraw."
7322
+ ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
7323
+ message: "Failed to encode ETH withdraw calldata."
6792
7324
  }
6793
7325
  );
6794
- const steps = [
6795
- {
6796
- key: "l2-base-token:withdraw",
6797
- kind: "l2-base-token:withdraw",
6798
- description: "Withdraw ETH via L2 Base Token System",
6799
- tx: { ...sim.request, ...txFeeOverrides }
6800
- }
6801
- ];
6802
- return { steps, approvals: [], quoteExtras: {} };
7326
+ const L2tx = {
7327
+ to: L2_BASE_TOKEN_ADDRESS,
7328
+ data,
7329
+ value: p.amount,
7330
+ from: ctx.sender
7331
+ };
7332
+ const l2Gas = await quoteL2Gas4({ ctx, tx: L2tx });
7333
+ if (l2Gas) {
7334
+ L2tx.gas = l2Gas.gasLimit;
7335
+ L2tx.maxFeePerGas = l2Gas.maxFeePerGas;
7336
+ L2tx.maxPriorityFeePerGas = l2Gas.maxPriorityFeePerGas;
7337
+ }
7338
+ const tx = {
7339
+ address: L2_BASE_TOKEN_ADDRESS,
7340
+ abi: IBaseToken_default,
7341
+ functionName: "withdraw",
7342
+ args: [p.to ?? ctx.sender],
7343
+ value: p.amount,
7344
+ account: ctx.client.account,
7345
+ ...l2Gas
7346
+ };
7347
+ const fees = buildFeeBreakdown2({
7348
+ feeToken: L2_BASE_TOKEN_ADDRESS,
7349
+ l2Gas
7350
+ });
7351
+ steps.push({
7352
+ key: "l2-base-token:withdraw",
7353
+ kind: "l2-base-token:withdraw",
7354
+ description: "Withdraw ETH via L2 Base Token System",
7355
+ tx
7356
+ });
7357
+ return { steps, approvals: [], fees };
6803
7358
  }
6804
7359
  };
6805
7360
  }
6806
- var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
7361
+ var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
6807
7362
  function routeErc20NonBase2() {
6808
7363
  return {
6809
7364
  // TODO: add preflight validations here
6810
7365
  async build(p, ctx) {
6811
- const toL1 = p.to ?? ctx.sender;
6812
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
6813
- const current = await wrapAs6(
7366
+ const steps = [];
7367
+ const approvals = [];
7368
+ const current = await wrapAs8(
6814
7369
  "CONTRACT",
6815
7370
  OP_WITHDRAWALS.erc20.allowance,
6816
7371
  () => ctx.client.l2.readContract({
@@ -6830,12 +7385,26 @@ function routeErc20NonBase2() {
6830
7385
  message: "Failed to read L2 ERC-20 allowance."
6831
7386
  }
6832
7387
  );
6833
- const needsApprove = current < p.amount;
6834
- const steps = [];
6835
- const approvals = [];
6836
- if (needsApprove) {
7388
+ if (current < p.amount) {
6837
7389
  approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
6838
- const approveSim = await wrapAs6(
7390
+ const data = viem.encodeFunctionData({
7391
+ abi: IERC20_default,
7392
+ functionName: "approve",
7393
+ args: [ctx.l2NativeTokenVault, p.amount]
7394
+ });
7395
+ const approveTxCandidate = {
7396
+ to: p.token,
7397
+ data,
7398
+ value: 0n,
7399
+ from: ctx.sender
7400
+ };
7401
+ const approveGas = await quoteL2Gas4({ ctx, tx: approveTxCandidate });
7402
+ if (approveGas) {
7403
+ approveTxCandidate.gas = approveGas.gasLimit;
7404
+ approveTxCandidate.maxFeePerGas = approveGas.maxFeePerGas;
7405
+ approveTxCandidate.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
7406
+ }
7407
+ const approveSim = await wrapAs8(
6839
7408
  "CONTRACT",
6840
7409
  OP_WITHDRAWALS.erc20.estGas,
6841
7410
  () => ctx.client.l2.simulateContract({
@@ -6844,21 +7413,26 @@ function routeErc20NonBase2() {
6844
7413
  functionName: "approve",
6845
7414
  args: [ctx.l2NativeTokenVault, p.amount],
6846
7415
  account: ctx.client.account,
6847
- ...txFeeOverrides
7416
+ ...approveGas
6848
7417
  }),
6849
7418
  {
6850
7419
  ctx: { where: "l2.simulateContract", to: p.token },
6851
7420
  message: "Failed to simulate L2 ERC-20 approve."
6852
7421
  }
6853
7422
  );
7423
+ const { ...approveRequest } = approveSim.request;
7424
+ const approveTx = {
7425
+ ...approveRequest
7426
+ };
6854
7427
  steps.push({
6855
7428
  key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
6856
7429
  kind: "approve:l2",
6857
7430
  description: `Approve ${p.amount} to NativeTokenVault`,
6858
- tx: { ...approveSim.request, ...txFeeOverrides }
7431
+ tx: approveTx
6859
7432
  });
6860
7433
  }
6861
- const ensure = await wrapAs6(
7434
+ const resolved = ctx.resolvedToken ?? (ctx.tokens ? await ctx.tokens.resolve(p.token, { chain: "l2" }) : void 0);
7435
+ const assetId = resolved?.assetId ?? (await wrapAs8(
6862
7436
  "CONTRACT",
6863
7437
  OP_WITHDRAWALS.erc20.ensureRegistered,
6864
7438
  () => ctx.client.l2.simulateContract({
@@ -6872,28 +7446,44 @@ function routeErc20NonBase2() {
6872
7446
  ctx: { where: "L2NativeTokenVault.ensureTokenIsRegistered", token: p.token },
6873
7447
  message: "Failed to ensure token is registered in L2NativeTokenVault."
6874
7448
  }
6875
- );
6876
- const assetId = ensure.result;
7449
+ )).result;
6877
7450
  const assetData = viem.encodeAbiParameters(
6878
7451
  [
6879
7452
  { type: "uint256", name: "amount" },
6880
7453
  { type: "address", name: "l1Receiver" },
6881
7454
  { type: "address", name: "l2Token" }
6882
7455
  ],
6883
- [p.amount, toL1, p.token]
7456
+ [p.amount, p.to ?? ctx.sender, p.token]
6884
7457
  );
7458
+ const withdrawCalldata = viem.encodeFunctionData({
7459
+ abi: IL2AssetRouter_default,
7460
+ functionName: "withdraw",
7461
+ args: [assetId, assetData]
7462
+ });
7463
+ const withdrawTxCandidate = {
7464
+ to: ctx.l2AssetRouter,
7465
+ data: withdrawCalldata,
7466
+ value: 0n,
7467
+ from: ctx.sender
7468
+ };
7469
+ const withdrawGas = await quoteL2Gas4({ ctx, tx: withdrawTxCandidate });
7470
+ if (withdrawGas) {
7471
+ withdrawTxCandidate.gas = withdrawGas.gasLimit;
7472
+ withdrawTxCandidate.maxFeePerGas = withdrawGas.maxFeePerGas;
7473
+ withdrawTxCandidate.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
7474
+ }
6885
7475
  let withdrawTx;
6886
- if (needsApprove) {
7476
+ if (current < p.amount) {
6887
7477
  withdrawTx = {
6888
7478
  address: ctx.l2AssetRouter,
6889
7479
  abi: IL2AssetRouter_default,
6890
7480
  functionName: "withdraw",
6891
7481
  args: [assetId, assetData],
6892
7482
  account: ctx.client.account,
6893
- ...txFeeOverrides
7483
+ ...withdrawGas
6894
7484
  };
6895
7485
  } else {
6896
- const sim = await wrapAs6(
7486
+ const sim = await wrapAs8(
6897
7487
  "CONTRACT",
6898
7488
  OP_WITHDRAWALS.erc20.estGas,
6899
7489
  () => ctx.client.l2.simulateContract({
@@ -6902,14 +7492,18 @@ function routeErc20NonBase2() {
6902
7492
  functionName: "withdraw",
6903
7493
  args: [assetId, assetData],
6904
7494
  account: ctx.client.account,
6905
- ...txFeeOverrides
7495
+ ...withdrawGas
6906
7496
  }),
6907
7497
  {
6908
7498
  ctx: { where: "l2.simulateContract", to: ctx.l2AssetRouter },
6909
7499
  message: "Failed to simulate L2 ERC-20 withdraw."
6910
7500
  }
6911
7501
  );
6912
- withdrawTx = { ...sim.request, ...txFeeOverrides };
7502
+ const { ...withdrawRequest } = sim.request;
7503
+ withdrawTx = {
7504
+ ...withdrawRequest,
7505
+ ...withdrawGas
7506
+ };
6913
7507
  }
6914
7508
  steps.push({
6915
7509
  key: "l2-asset-router:withdraw",
@@ -6917,59 +7511,11 @@ function routeErc20NonBase2() {
6917
7511
  description: "Burn on L2 & send L2\u2192L1 message",
6918
7512
  tx: withdrawTx
6919
7513
  });
6920
- return { steps, approvals, quoteExtras: {} };
6921
- }
6922
- };
6923
- }
6924
-
6925
- // src/adapters/viem/resources/withdrawals/routes/eth-nonbase.ts
6926
- var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
6927
- function routeEthNonBase2() {
6928
- return {
6929
- async preflight(p, ctx) {
6930
- await wrapAs7(
6931
- "VALIDATION",
6932
- OP_WITHDRAWALS.ethNonBase.assertNonEthBase,
6933
- () => {
6934
- if (p.token.toLowerCase() !== L2_BASE_TOKEN_ADDRESS.toLowerCase()) {
6935
- throw new Error("eth-nonbase route requires the L2 base-token alias (0x\u2026800A).");
6936
- }
6937
- if (ctx.baseIsEth) {
6938
- throw new Error("eth-nonbase route requires chain base \u2260 ETH.");
6939
- }
6940
- },
6941
- { ctx: { token: p.token, baseIsEth: ctx.baseIsEth } }
6942
- );
6943
- },
6944
- async build(p, ctx) {
6945
- const toL1 = p.to ?? ctx.sender;
6946
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
6947
- const sim = await wrapAs7(
6948
- "CONTRACT",
6949
- OP_WITHDRAWALS.ethNonBase.estGas,
6950
- () => ctx.client.l2.simulateContract({
6951
- address: L2_BASE_TOKEN_ADDRESS,
6952
- abi: IBaseToken_default,
6953
- functionName: "withdraw",
6954
- args: [toL1],
6955
- value: p.amount,
6956
- account: ctx.client.account,
6957
- ...txFeeOverrides
6958
- }),
6959
- {
6960
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
6961
- message: "Failed to simulate L2 base-token withdraw."
6962
- }
6963
- );
6964
- const steps = [
6965
- {
6966
- key: "l2-base-token:withdraw",
6967
- kind: "l2-base-token:withdraw",
6968
- description: "Withdraw base token via L2 Base Token System (base \u2260 ETH)",
6969
- tx: { ...sim.request, ...txFeeOverrides }
6970
- }
6971
- ];
6972
- return { steps, approvals: [], quoteExtras: {} };
7514
+ const fees = buildFeeBreakdown2({
7515
+ feeToken: ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2),
7516
+ l2Gas: withdrawGas
7517
+ });
7518
+ return { steps, approvals, fees };
6973
7519
  }
6974
7520
  };
6975
7521
  }
@@ -7005,7 +7551,7 @@ function messengerLogIndex(raw, opts) {
7005
7551
  }
7006
7552
  return (hits[index] ?? hits[0]).i;
7007
7553
  }
7008
- var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
7554
+ var { wrapAs: wrapAs9 } = createErrorHandlers("withdrawals");
7009
7555
  var IL1NullifierMini = [
7010
7556
  {
7011
7557
  type: "function",
@@ -7022,7 +7568,7 @@ var IL1NullifierMini = [
7022
7568
  function createFinalizationServices(client) {
7023
7569
  return {
7024
7570
  async fetchFinalizeDepositParams(l2TxHash) {
7025
- const parsed = await wrapAs8(
7571
+ const parsed = await wrapAs9(
7026
7572
  "RPC",
7027
7573
  OP_WITHDRAWALS.finalize.fetchParams.receipt,
7028
7574
  () => client.zks.getReceiptWithL2ToL1(l2TxHash),
@@ -7039,7 +7585,7 @@ function createFinalizationServices(client) {
7039
7585
  context: { l2TxHash }
7040
7586
  });
7041
7587
  }
7042
- const ev = await wrapAs8(
7588
+ const ev = await wrapAs9(
7043
7589
  "INTERNAL",
7044
7590
  OP_WITHDRAWALS.finalize.fetchParams.findMessage,
7045
7591
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
@@ -7049,7 +7595,7 @@ function createFinalizationServices(client) {
7049
7595
  message: "Failed to locate L1MessageSent event in L2 receipt."
7050
7596
  }
7051
7597
  );
7052
- const message = await wrapAs8(
7598
+ const message = await wrapAs9(
7053
7599
  "INTERNAL",
7054
7600
  OP_WITHDRAWALS.finalize.fetchParams.decodeMessage,
7055
7601
  () => {
@@ -7061,7 +7607,7 @@ function createFinalizationServices(client) {
7061
7607
  message: "Failed to decode withdrawal message."
7062
7608
  }
7063
7609
  );
7064
- const raw = await wrapAs8(
7610
+ const raw = await wrapAs9(
7065
7611
  "RPC",
7066
7612
  OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
7067
7613
  () => client.zks.getReceiptWithL2ToL1(l2TxHash),
@@ -7078,7 +7624,7 @@ function createFinalizationServices(client) {
7078
7624
  context: { l2TxHash }
7079
7625
  });
7080
7626
  }
7081
- const idx = await wrapAs8(
7627
+ const idx = await wrapAs9(
7082
7628
  "INTERNAL",
7083
7629
  OP_WITHDRAWALS.finalize.fetchParams.messengerIndex,
7084
7630
  () => Promise.resolve(messengerLogIndex(raw, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
@@ -7087,7 +7633,7 @@ function createFinalizationServices(client) {
7087
7633
  message: "Failed to derive messenger log index."
7088
7634
  }
7089
7635
  );
7090
- const proof = await wrapAs8(
7636
+ const proof = await wrapAs9(
7091
7637
  "RPC",
7092
7638
  OP_WITHDRAWALS.finalize.fetchParams.proof,
7093
7639
  () => client.zks.getL2ToL1LogProof(l2TxHash, idx),
@@ -7096,7 +7642,7 @@ function createFinalizationServices(client) {
7096
7642
  message: "Failed to fetch L2\u2192L1 log proof."
7097
7643
  }
7098
7644
  );
7099
- const chainId = await wrapAs8(
7645
+ const chainId = await wrapAs9(
7100
7646
  "RPC",
7101
7647
  OP_WITHDRAWALS.finalize.fetchParams.network,
7102
7648
  () => client.l2.getChainId(),
@@ -7112,7 +7658,7 @@ function createFinalizationServices(client) {
7112
7658
  message,
7113
7659
  merkleProof: proof.proof
7114
7660
  };
7115
- const { l1Nullifier } = await wrapAs8(
7661
+ const { l1Nullifier } = await wrapAs9(
7116
7662
  "INTERNAL",
7117
7663
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
7118
7664
  () => client.ensureAddresses(),
@@ -7124,7 +7670,7 @@ function createFinalizationServices(client) {
7124
7670
  return { params, nullifier: l1Nullifier };
7125
7671
  },
7126
7672
  async simulateFinalizeReadiness(params) {
7127
- const { l1Nullifier } = await wrapAs8(
7673
+ const { l1Nullifier } = await wrapAs9(
7128
7674
  "INTERNAL",
7129
7675
  OP_WITHDRAWALS.finalize.readiness.ensureAddresses,
7130
7676
  () => client.ensureAddresses(),
@@ -7135,7 +7681,7 @@ function createFinalizationServices(client) {
7135
7681
  );
7136
7682
  const done = await (async () => {
7137
7683
  try {
7138
- const result = await wrapAs8(
7684
+ const result = await wrapAs9(
7139
7685
  "RPC",
7140
7686
  OP_WITHDRAWALS.finalize.readiness.isFinalized,
7141
7687
  () => client.l1.readContract({
@@ -7169,7 +7715,7 @@ function createFinalizationServices(client) {
7169
7715
  }
7170
7716
  },
7171
7717
  async isWithdrawalFinalized(key) {
7172
- const { l1Nullifier } = await wrapAs8(
7718
+ const { l1Nullifier } = await wrapAs9(
7173
7719
  "INTERNAL",
7174
7720
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
7175
7721
  () => client.ensureAddresses(),
@@ -7178,7 +7724,7 @@ function createFinalizationServices(client) {
7178
7724
  message: "Failed to ensure L1 Nullifier address."
7179
7725
  }
7180
7726
  );
7181
- return await wrapAs8(
7727
+ return await wrapAs9(
7182
7728
  "RPC",
7183
7729
  OP_WITHDRAWALS.finalize.isFinalized,
7184
7730
  () => client.l1.readContract({
@@ -7194,7 +7740,7 @@ function createFinalizationServices(client) {
7194
7740
  );
7195
7741
  },
7196
7742
  async estimateFinalization(params) {
7197
- const { l1Nullifier } = await wrapAs8(
7743
+ const { l1Nullifier } = await wrapAs9(
7198
7744
  "INTERNAL",
7199
7745
  OP_WITHDRAWALS.finalize.estimate,
7200
7746
  () => client.ensureAddresses(),
@@ -7203,7 +7749,7 @@ function createFinalizationServices(client) {
7203
7749
  message: "Failed to ensure L1 Nullifier address."
7204
7750
  }
7205
7751
  );
7206
- const gasLimit = await wrapAs8(
7752
+ const gasLimit = await wrapAs9(
7207
7753
  "RPC",
7208
7754
  OP_WITHDRAWALS.finalize.estimate,
7209
7755
  () => client.l1.estimateContractGas({
@@ -7227,7 +7773,7 @@ function createFinalizationServices(client) {
7227
7773
  let maxFeePerGas;
7228
7774
  let maxPriorityFeePerGas;
7229
7775
  try {
7230
- const fee = await wrapAs8(
7776
+ const fee = await wrapAs9(
7231
7777
  "RPC",
7232
7778
  OP_WITHDRAWALS.finalize.estimate,
7233
7779
  () => client.l1.estimateFeesPerGas(),
@@ -7246,7 +7792,7 @@ function createFinalizationServices(client) {
7246
7792
  })();
7247
7793
  maxPriorityFeePerGas = fee.maxPriorityFeePerGas ?? 0n;
7248
7794
  } catch {
7249
- const gasPrice = await wrapAs8(
7795
+ const gasPrice = await wrapAs9(
7250
7796
  "RPC",
7251
7797
  OP_WITHDRAWALS.finalize.estimate,
7252
7798
  () => client.l1.getGasPrice(),
@@ -7265,7 +7811,7 @@ function createFinalizationServices(client) {
7265
7811
  };
7266
7812
  },
7267
7813
  async finalizeDeposit(params) {
7268
- const { l1Nullifier } = await wrapAs8(
7814
+ const { l1Nullifier } = await wrapAs9(
7269
7815
  "INTERNAL",
7270
7816
  OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
7271
7817
  () => client.ensureAddresses(),
@@ -7324,40 +7870,32 @@ function createFinalizationServices(client) {
7324
7870
 
7325
7871
  // src/adapters/viem/resources/withdrawals/index.ts
7326
7872
  var ROUTES2 = {
7327
- "eth-base": routeEthBase(),
7873
+ base: routeEthBase(),
7328
7874
  // BaseTokenSystem.withdraw, chain base = ETH
7329
- "eth-nonbase": routeEthNonBase2(),
7330
- // BaseTokenSystem.withdraw, chain base ≠ ETH
7331
7875
  "erc20-nonbase": routeErc20NonBase2()
7332
7876
  // AssetRouter.withdraw for non-base ERC-20s
7333
7877
  };
7334
- function createWithdrawalsResource(client) {
7878
+ function createWithdrawalsResource(client, tokens, contracts) {
7335
7879
  const svc = createFinalizationServices(client);
7336
7880
  const { wrap: wrap2, toResult: toResult2 } = createErrorHandlers("withdrawals");
7881
+ const tokensResource = tokens ?? createTokensResource(client);
7882
+ const contractsResource = contracts ?? createContractsResource(client);
7337
7883
  async function buildPlan(p) {
7338
- const ctx = await commonCtx2(p, client);
7884
+ const ctx = await commonCtx2(p, client, tokensResource, contractsResource);
7339
7885
  await ROUTES2[ctx.route].preflight?.(p, ctx);
7340
- const { steps, approvals } = await ROUTES2[ctx.route].build(p, ctx);
7341
- const resolveGasLimit = () => {
7342
- if (ctx.fee.gasLimit != null) return ctx.fee.gasLimit;
7343
- for (let i = steps.length - 1; i >= 0; i--) {
7344
- const candidate = steps[i].tx.gas;
7345
- if (candidate != null) return candidate;
7346
- }
7347
- return void 0;
7348
- };
7349
- const gasLimit = resolveGasLimit();
7350
- const summary = {
7886
+ const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
7887
+ return {
7351
7888
  route: ctx.route,
7352
- approvalsNeeded: approvals,
7353
- suggestedL2GasLimit: ctx.l2GasLimit,
7354
- fees: {
7355
- gasLimit,
7356
- maxFeePerGas: ctx.fee.maxFeePerGas,
7357
- maxPriorityFeePerGas: ctx.fee.maxPriorityFeePerGas
7358
- }
7889
+ summary: {
7890
+ route: ctx.route,
7891
+ approvalsNeeded: approvals,
7892
+ amounts: {
7893
+ transfer: { token: p.token, amount: p.amount }
7894
+ },
7895
+ fees
7896
+ },
7897
+ steps
7359
7898
  };
7360
- return { route: ctx.route, summary, steps };
7361
7899
  }
7362
7900
  const finalizeCache = /* @__PURE__ */ new Map();
7363
7901
  const quote = (p) => wrap2(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
@@ -7391,7 +7929,7 @@ function createWithdrawalsResource(client) {
7391
7929
  }
7392
7930
  if (overrides.gasLimit != null) step.tx.gas = overrides.gasLimit;
7393
7931
  }
7394
- if (step.tx.gas == null) {
7932
+ if (!p.l2TxOverrides?.gasLimit) {
7395
7933
  try {
7396
7934
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
7397
7935
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -7690,83 +8228,34 @@ function createWithdrawalsResource(client) {
7690
8228
 
7691
8229
  // src/adapters/viem/sdk.ts
7692
8230
  function createViemSdk(client) {
8231
+ const tokens = createTokensResource(client);
8232
+ const contracts = createContractsResource(client);
7693
8233
  return {
7694
- deposits: createDepositsResource(client),
7695
- withdrawals: createWithdrawalsResource(client),
7696
- helpers: {
7697
- addresses: () => client.ensureAddresses(),
7698
- contracts: () => client.contracts(),
7699
- async l1AssetRouter() {
7700
- const { l1AssetRouter } = await client.contracts();
7701
- return l1AssetRouter;
7702
- },
7703
- async l1NativeTokenVault() {
7704
- const { l1NativeTokenVault } = await client.contracts();
7705
- return l1NativeTokenVault;
7706
- },
7707
- async l1Nullifier() {
7708
- const { l1Nullifier } = await client.contracts();
7709
- return l1Nullifier;
7710
- },
7711
- async baseToken(chainId) {
7712
- const id = chainId ?? BigInt(await client.l2.getChainId());
7713
- return client.baseToken(id);
7714
- },
7715
- async l2TokenAddress(l1Token) {
7716
- if (isAddressEq(l1Token, FORMAL_ETH_ADDRESS)) {
7717
- return ETH_ADDRESS;
7718
- }
7719
- const base = await client.baseToken(BigInt(await client.l2.getChainId()));
7720
- if (isAddressEq(l1Token, base)) {
7721
- return L2_BASE_TOKEN_ADDRESS;
7722
- }
7723
- const { l2NativeTokenVault } = await client.contracts();
7724
- const addr = await l2NativeTokenVault.read.l2TokenAddress([l1Token]);
7725
- return addr;
7726
- },
7727
- async l1TokenAddress(l2Token) {
7728
- if (isAddressEq(l2Token, FORMAL_ETH_ADDRESS)) {
7729
- return FORMAL_ETH_ADDRESS;
7730
- }
7731
- const { l2AssetRouter } = await client.contracts();
7732
- const addr = await l2AssetRouter.read.l1TokenAddress([l2Token]);
7733
- return addr;
7734
- },
7735
- async assetId(l1Token) {
7736
- const norm = isAddressEq(l1Token, FORMAL_ETH_ADDRESS) ? ETH_ADDRESS : l1Token;
7737
- const { l1NativeTokenVault } = await client.contracts();
7738
- const id = await l1NativeTokenVault.read.assetId([norm]);
7739
- return id;
7740
- }
7741
- }
8234
+ deposits: createDepositsResource(client, tokens, contracts),
8235
+ withdrawals: createWithdrawalsResource(client, tokens, contracts),
8236
+ tokens,
8237
+ contracts
7742
8238
  };
7743
8239
  }
7744
8240
 
7745
8241
  exports.buildDirectRequestStruct = buildDirectRequestStruct;
7746
- exports.buildViemFeeOverrides = buildViemFeeOverrides;
7747
- exports.checkBaseCost = checkBaseCost;
7748
8242
  exports.classifyReadinessFromRevert = classifyReadinessFromRevert;
7749
8243
  exports.createClient = createViemClient;
8244
+ exports.createContractsResource = createContractsResource;
7750
8245
  exports.createDepositsResource = createDepositsResource;
7751
8246
  exports.createErrorHandlers = createErrorHandlers;
7752
8247
  exports.createFinalizationServices = createFinalizationServices;
8248
+ exports.createTokensResource = createTokensResource;
7753
8249
  exports.createViemClient = createViemClient;
7754
8250
  exports.createViemSdk = createViemSdk;
7755
8251
  exports.createWithdrawalsResource = createWithdrawalsResource;
7756
8252
  exports.decodeRevert = decodeRevert;
7757
- exports.encodeNTVAssetId = encodeNTVAssetId;
7758
- exports.encodeNTVTransferData = encodeNTVTransferData;
7759
- exports.encodeNativeTokenVaultAssetId = encodeNativeTokenVaultAssetId;
7760
8253
  exports.encodeNativeTokenVaultTransferData = encodeNativeTokenVaultTransferData;
7761
8254
  exports.encodeSecondBridgeArgs = encodeSecondBridgeArgs;
7762
8255
  exports.encodeSecondBridgeDataV1 = encodeSecondBridgeDataV1;
7763
8256
  exports.encodeSecondBridgeErc20Args = encodeSecondBridgeErc20Args;
7764
8257
  exports.encodeSecondBridgeEthArgs = encodeSecondBridgeEthArgs;
7765
- exports.getFeeOverrides = getFeeOverrides;
7766
- exports.getGasPriceWei = getGasPriceWei;
7767
- exports.getL2FeeOverrides = getL2FeeOverrides;
7768
8258
  exports.registerErrorAbi = registerErrorAbi;
7769
- exports.scaleGasLimit = scaleGasLimit;
7770
8259
  exports.toZKsyncError = toZKsyncError;
7771
8260
  //# sourceMappingURL=index.cjs.map
7772
8261
  //# sourceMappingURL=index.cjs.map