@matterlabs/zksync-js 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +12 -12
  2. package/dist/adapters/ethers/client.cjs +642 -1
  3. package/dist/adapters/ethers/client.cjs.map +1 -1
  4. package/dist/adapters/ethers/client.js +6 -5
  5. package/dist/adapters/ethers/estimator.d.ts +4 -0
  6. package/dist/adapters/ethers/index.cjs +934 -801
  7. package/dist/adapters/ethers/index.cjs.map +1 -1
  8. package/dist/adapters/ethers/index.js +9 -8
  9. package/dist/adapters/ethers/resources/deposits/context.d.ts +5 -5
  10. package/dist/adapters/ethers/resources/deposits/routes/types.d.ts +2 -6
  11. package/dist/adapters/ethers/resources/deposits/services/fee.d.ts +6 -0
  12. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +40 -0
  13. package/dist/adapters/ethers/resources/utils.d.ts +4 -15
  14. package/dist/adapters/ethers/resources/withdrawals/context.d.ts +4 -4
  15. package/dist/adapters/ethers/resources/withdrawals/routes/types.d.ts +2 -2
  16. package/dist/adapters/ethers/resources/withdrawals/services/fees.d.ts +14 -0
  17. package/dist/adapters/ethers/resources/withdrawals/services/gas.d.ts +12 -0
  18. package/dist/adapters/ethers/sdk.cjs +947 -1292
  19. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  20. package/dist/adapters/ethers/sdk.js +7 -6
  21. package/dist/adapters/viem/client.cjs.map +1 -1
  22. package/dist/adapters/viem/client.d.ts +1 -1
  23. package/dist/adapters/viem/client.js +4 -5
  24. package/dist/adapters/viem/estimator.d.ts +4 -0
  25. package/dist/adapters/viem/index.cjs +944 -662
  26. package/dist/adapters/viem/index.cjs.map +1 -1
  27. package/dist/adapters/viem/index.js +8 -8
  28. package/dist/adapters/viem/resources/deposits/context.d.ts +5 -5
  29. package/dist/adapters/viem/resources/deposits/routes/types.d.ts +2 -6
  30. package/dist/adapters/viem/resources/deposits/services/fee.d.ts +6 -0
  31. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +36 -0
  32. package/dist/adapters/viem/resources/utils.d.ts +3 -16
  33. package/dist/adapters/viem/resources/withdrawals/context.d.ts +3 -6
  34. package/dist/adapters/viem/resources/withdrawals/routes/types.d.ts +12 -2
  35. package/dist/adapters/viem/resources/withdrawals/services/fee.d.ts +17 -0
  36. package/dist/adapters/viem/resources/withdrawals/services/gas.d.ts +12 -0
  37. package/dist/adapters/viem/sdk.cjs +877 -563
  38. package/dist/adapters/viem/sdk.cjs.map +1 -1
  39. package/dist/adapters/viem/sdk.d.ts +1 -1
  40. package/dist/adapters/viem/sdk.js +6 -6
  41. package/dist/{chunk-3LALBFFE.js → chunk-3MRGU4HV.js} +9 -5
  42. package/dist/{chunk-4HLJJKIY.js → chunk-6K6VJQAL.js} +2 -2
  43. package/dist/{chunk-CGO27P7F.js → chunk-BCCKWWOX.js} +540 -741
  44. package/dist/{chunk-6GCT6TLS.js → chunk-F2ENUV3A.js} +13 -1
  45. package/dist/{chunk-DI2CJDPZ.js → chunk-HLUANWGN.js} +2 -2
  46. package/dist/{chunk-Y75OMFK6.js → chunk-M5J2MM2U.js} +351 -1
  47. package/dist/{chunk-263G6636.js → chunk-NCAIVYBR.js} +1 -14
  48. package/dist/{chunk-7M4V3FMT.js → chunk-OC6ZVLSP.js} +669 -559
  49. package/dist/chunk-QJS6ETEE.js +217 -0
  50. package/dist/chunk-XRE7H466.js +157 -0
  51. package/dist/{chunk-BD2LUO5T.js → chunk-YUK547UF.js} +3 -3
  52. package/dist/core/abi.d.ts +9 -0
  53. package/dist/core/adapters/interfaces.d.ts +25 -0
  54. package/dist/core/constants.cjs +12 -0
  55. package/dist/core/constants.cjs.map +1 -1
  56. package/dist/core/constants.d.ts +6 -0
  57. package/dist/core/constants.js +1 -1
  58. package/dist/core/index.cjs +4504 -1
  59. package/dist/core/index.cjs.map +1 -1
  60. package/dist/core/index.d.ts +1 -0
  61. package/dist/core/index.js +4 -4
  62. package/dist/core/resources/deposits/fee.d.ts +15 -0
  63. package/dist/core/resources/deposits/gas.d.ts +38 -0
  64. package/dist/core/resources/withdrawals/gas.d.ts +14 -0
  65. package/dist/core/types/errors.d.ts +1 -1
  66. package/dist/core/types/fees.d.ts +40 -0
  67. package/dist/core/types/flows/base.d.ts +0 -10
  68. package/dist/core/types/flows/deposits.d.ts +20 -6
  69. package/dist/core/types/flows/route.d.ts +2 -3
  70. package/dist/core/types/flows/withdrawals.d.ts +12 -6
  71. package/dist/index.cjs +4516 -1
  72. package/dist/index.cjs.map +1 -1
  73. package/dist/index.d.ts +1 -0
  74. package/dist/index.js +4 -4
  75. package/package.json +5 -1
  76. package/dist/adapters/ethers/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  77. package/dist/adapters/viem/resources/withdrawals/routes/eth-nonbase.d.ts +0 -2
  78. package/dist/chunk-B77GWPO5.js +0 -339
  79. package/dist/core/internal/abi-registry.d.ts +0 -9
  80. package/dist/core/utils/gas.d.ts +0 -13
@@ -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,20 +5193,57 @@ 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
- }
5196
+ // src/core/utils/addr.ts
5197
+ var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
5198
+ function isAddressEq(a, b) {
5199
+ return a.toLowerCase() === b.toLowerCase();
5200
+ }
5201
+ function isETH(token) {
5202
+ return isAddressEq(token, FORMAL_ETH_ADDRESS) || isAddressEq(token, L2_BASE_TOKEN_ADDRESS) || isAddressEq(token, ETH_ADDRESS);
5203
+ }
5204
+ function normalizeAddrEq(a, b) {
5205
+ if (!a || !b) return false;
5206
+ const normalize = (s) => {
5207
+ const hasPrefix = s.slice(0, 2).toLowerCase() === "0x";
5208
+ const body = hasPrefix ? s.slice(2) : s;
5209
+ return `0x${body.toLowerCase()}`;
5210
+ };
5211
+ return normalize(a) === normalize(b);
5204
5212
  }
5205
- function assertPriorityFeeBounds(fees) {
5206
- if (fees.maxPriorityFeePerGas > fees.maxFeePerGas) {
5207
- throw new Error("maxPriorityFeePerGas cannot exceed maxFeePerGas.");
5213
+
5214
+ // src/core/resources/deposits/route.ts
5215
+ async function pickDepositRoute(client, chainIdL2, token) {
5216
+ if (isETH(token)) {
5217
+ const base2 = await client.baseToken(chainIdL2);
5218
+ return isETH(base2) ? "eth-base" : "eth-nonbase";
5208
5219
  }
5220
+ const base = await client.baseToken(chainIdL2);
5221
+ return normalizeAddrEq(token, base) ? "erc20-base" : "erc20-nonbase";
5209
5222
  }
5210
5223
 
5211
- // src/adapters/viem/resources/utils.ts
5224
+ // src/adapters/viem/resources/deposits/context.ts
5225
+ async function commonCtx(p, client) {
5226
+ const { bridgehub, l1AssetRouter } = await client.ensureAddresses();
5227
+ const chainId = await client.l2.getChainId();
5228
+ const sender = client.account.address;
5229
+ const gasPerPubdata = p.gasPerPubdata ?? 800n;
5230
+ const operatorTip = p.operatorTip ?? 0n;
5231
+ const refundRecipient = p.refundRecipient ?? sender;
5232
+ const route = await pickDepositRoute(client, BigInt(chainId), p.token);
5233
+ return {
5234
+ client,
5235
+ l1AssetRouter,
5236
+ route,
5237
+ bridgehub,
5238
+ chainIdL2: BigInt(chainId),
5239
+ sender,
5240
+ gasOverrides: p.l1TxOverrides,
5241
+ l2GasLimit: p.l2GasLimit,
5242
+ gasPerPubdata,
5243
+ operatorTip,
5244
+ refundRecipient
5245
+ };
5246
+ }
5212
5247
  function encodeNativeTokenVaultAssetId(chainId, address) {
5213
5248
  const encoded = viem.encodeAbiParameters(
5214
5249
  [
@@ -5242,121 +5277,6 @@ function encodeSecondBridgeDataV1(assetId, transferData) {
5242
5277
  }
5243
5278
  var encodeNTVAssetId = encodeNativeTokenVaultAssetId;
5244
5279
  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
5280
  function encodeSecondBridgeArgs(token, amount, l2Receiver) {
5361
5281
  return viem.encodeAbiParameters(
5362
5282
  [
@@ -5373,58 +5293,17 @@ function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
5373
5293
  function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
5374
5294
  return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
5375
5295
  }
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);
5296
+ function buildDirectRequestStruct(args) {
5416
5297
  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
5298
+ chainId: args.chainId,
5299
+ l2Contract: args.l2Contract,
5300
+ mintValue: args.mintValue,
5301
+ l2Value: args.l2Value,
5302
+ l2Calldata: "0x",
5303
+ l2GasLimit: args.l2GasLimit,
5304
+ l2GasPerPubdataByteLimit: args.gasPerPubdata,
5305
+ factoryDeps: [],
5306
+ refundRecipient: args.refundRecipient
5428
5307
  };
5429
5308
  }
5430
5309
 
@@ -5603,170 +5482,486 @@ function createErrorHandlers(resource) {
5603
5482
  return { wrap: wrap2, wrapAs: wrapAs9, toResult: toResult2 };
5604
5483
  }
5605
5484
 
5606
- // src/adapters/viem/resources/deposits/routes/eth.ts
5607
- var { wrapAs } = createErrorHandlers("deposits");
5608
- function routeEthDirect() {
5485
+ // src/core/resources/deposits/gas.ts
5486
+ function makeGasQuote(p) {
5487
+ const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
5609
5488
  return {
5610
- 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."
5625
- }
5626
- );
5627
- const baseCost = rawBaseCost;
5628
- const l2Contract = p.to ?? ctx.sender;
5629
- const l2Value = p.amount;
5630
- const mintValue = baseCost + ctx.operatorTip + l2Value;
5631
- const req = buildDirectRequestStruct({
5632
- chainId: ctx.chainIdL2,
5633
- mintValue,
5634
- l2GasLimit: ctx.l2GasLimit,
5635
- gasPerPubdata: ctx.gasPerPubdata,
5636
- refundRecipient: ctx.refundRecipient,
5637
- l2Contract,
5638
- l2Value
5639
- });
5640
- const sim = await wrapAs(
5641
- "RPC",
5642
- OP_DEPOSITS.eth.estGas,
5643
- () => ctx.client.l1.simulateContract({
5644
- address: ctx.bridgehub,
5645
- abi: IBridgehub_default,
5646
- functionName: "requestL2TransactionDirect",
5647
- args: [req],
5648
- value: mintValue,
5649
- account: ctx.client.account
5650
- }),
5651
- {
5652
- ctx: { where: "l1.simulateContract", to: ctx.bridgehub },
5653
- message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
5654
- }
5655
- );
5656
- const resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
5657
- const steps = [
5658
- {
5659
- key: "bridgehub:direct",
5660
- kind: "bridgehub:direct",
5661
- description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
5662
- tx: { ...sim.request, ...txFeeOverrides }
5663
- }
5664
- ];
5489
+ gasLimit: p.gasLimit,
5490
+ maxFeePerGas: p.maxFeePerGas,
5491
+ maxPriorityFeePerGas,
5492
+ gasPerPubdata: p.gasPerPubdata,
5493
+ maxCost: p.gasLimit * p.maxFeePerGas
5494
+ };
5495
+ }
5496
+ async function fetchFees(estimator) {
5497
+ try {
5498
+ const fees = await estimator.estimateFeesPerGas();
5499
+ if (fees.maxFeePerGas != null) {
5665
5500
  return {
5666
- steps,
5667
- approvals: [],
5668
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
5501
+ maxFeePerGas: fees.maxFeePerGas,
5502
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
5503
+ };
5504
+ }
5505
+ if (fees.gasPrice != null) {
5506
+ return {
5507
+ maxFeePerGas: fees.gasPrice,
5508
+ maxPriorityFeePerGas: 0n
5669
5509
  };
5670
5510
  }
5511
+ } catch {
5512
+ }
5513
+ try {
5514
+ const gp = await estimator.getGasPrice();
5515
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
5516
+ } catch {
5517
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
5518
+ }
5519
+ }
5520
+ async function quoteL1Gas(input) {
5521
+ const { estimator, tx, overrides, fallbackGasLimit } = input;
5522
+ let market;
5523
+ const getMarket = async () => {
5524
+ if (market) return market;
5525
+ market = await fetchFees(estimator);
5526
+ return market;
5671
5527
  };
5528
+ const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
5529
+ const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
5530
+ const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
5531
+ if (explicitGasLimit != null) {
5532
+ return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
5533
+ }
5534
+ try {
5535
+ const est = await estimator.estimateGas(tx);
5536
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
5537
+ return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
5538
+ } catch (err) {
5539
+ if (fallbackGasLimit != null) {
5540
+ return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
5541
+ }
5542
+ console.warn("L1 gas estimation failed", err);
5543
+ return void 0;
5544
+ }
5545
+ }
5546
+ async function quoteL2Gas(input) {
5547
+ const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
5548
+ const market = await fetchFees(estimator);
5549
+ const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
5550
+ const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
5551
+ const explicit = overrideGasLimit ?? txGasLimit;
5552
+ if (explicit != null) {
5553
+ return makeGasQuote({
5554
+ gasLimit: explicit,
5555
+ maxFeePerGas,
5556
+ gasPerPubdata
5557
+ });
5558
+ }
5559
+ if (!tx) {
5560
+ return makeGasQuote({
5561
+ gasLimit: l2GasLimit ?? 0n,
5562
+ maxFeePerGas,
5563
+ gasPerPubdata
5564
+ });
5565
+ }
5566
+ try {
5567
+ const execEstimate = await estimator.estimateGas(tx, stateOverrides);
5568
+ const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
5569
+ const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
5570
+ const pp = gasPerPubdata ?? 800n;
5571
+ const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
5572
+ const pubdataOverhead = pubdataBytes * pp;
5573
+ let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
5574
+ total = total * (100n + BUFFER) / 100n;
5575
+ return makeGasQuote({
5576
+ gasLimit: total,
5577
+ maxFeePerGas,
5578
+ gasPerPubdata: pp
5579
+ });
5580
+ } catch (err) {
5581
+ console.warn("L2 gas estimation failed", err);
5582
+ return makeGasQuote({
5583
+ gasLimit: l2GasLimit ?? 0n,
5584
+ maxFeePerGas,
5585
+ gasPerPubdata
5586
+ });
5587
+ }
5672
5588
  }
5673
5589
 
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() {
5590
+ // src/adapters/viem/estimator.ts
5591
+ function toCoreTx(tx) {
5680
5592
  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 } }
5702
- );
5703
- await wrapAs2(
5704
- "VALIDATION",
5705
- OP_DEPOSITS.nonbase.assertNonBaseToken,
5706
- () => {
5707
- if (normalizeAddrEq(baseToken, p.token)) {
5708
- throw new Error("erc20-nonbase route requires a non-base ERC-20 deposit token.");
5709
- }
5710
- },
5711
- { ctx: { depositToken: p.token, baseToken } }
5712
- );
5713
- return;
5593
+ to: tx.to,
5594
+ from: tx.from,
5595
+ data: tx.data,
5596
+ value: tx.value,
5597
+ gasLimit: tx.gas,
5598
+ maxFeePerGas: tx.maxFeePerGas,
5599
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5600
+ };
5601
+ }
5602
+ function viemToGasEstimator(client) {
5603
+ return {
5604
+ async estimateGas(tx, stateOverrides) {
5605
+ if (stateOverrides) {
5606
+ try {
5607
+ const result = await client.request({
5608
+ method: "eth_estimateGas",
5609
+ params: [
5610
+ {
5611
+ from: tx.from,
5612
+ to: tx.to,
5613
+ data: tx.data,
5614
+ value: tx.value,
5615
+ gas: tx.gasLimit,
5616
+ maxFeePerGas: tx.maxFeePerGas,
5617
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5618
+ },
5619
+ "latest",
5620
+ stateOverrides
5621
+ ]
5622
+ });
5623
+ return BigInt(result);
5624
+ } catch (error) {
5625
+ console.warn(
5626
+ "Failed to estimate gas with state overrides, falling back to standard estimation:",
5627
+ error
5628
+ );
5629
+ }
5630
+ }
5631
+ return await client.estimateGas({
5632
+ account: tx.from,
5633
+ to: tx.to,
5634
+ data: tx.data,
5635
+ value: tx.value,
5636
+ gas: tx.gasLimit,
5637
+ maxFeePerGas: tx.maxFeePerGas,
5638
+ maxPriorityFeePerGas: tx.maxPriorityFeePerGas
5639
+ });
5714
5640
  },
5641
+ async estimateFeesPerGas() {
5642
+ try {
5643
+ const fees = await client.estimateFeesPerGas();
5644
+ return {
5645
+ maxFeePerGas: fees.maxFeePerGas,
5646
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas
5647
+ };
5648
+ } catch {
5649
+ }
5650
+ try {
5651
+ const gp = await client.getGasPrice();
5652
+ return { gasPrice: gp };
5653
+ } catch {
5654
+ return {};
5655
+ }
5656
+ },
5657
+ async getGasPrice() {
5658
+ return await client.getGasPrice();
5659
+ },
5660
+ async call(tx) {
5661
+ const res = await client.call({
5662
+ to: tx.to,
5663
+ data: tx.data,
5664
+ value: tx.value,
5665
+ account: tx.from
5666
+ });
5667
+ return res.data ?? "0x";
5668
+ }
5669
+ };
5670
+ }
5671
+
5672
+ // src/adapters/viem/resources/deposits/services/gas.ts
5673
+ async function quoteL1Gas2(input) {
5674
+ const { ctx, tx, overrides, fallbackGasLimit } = input;
5675
+ const estimator = viemToGasEstimator(ctx.client.l1);
5676
+ return quoteL1Gas({
5677
+ estimator,
5678
+ tx: toCoreTx(tx),
5679
+ overrides,
5680
+ fallbackGasLimit
5681
+ });
5682
+ }
5683
+ async function quoteL2Gas2(input) {
5684
+ const { ctx, route, l2TxForModeling, overrideGasLimit } = input;
5685
+ const estimator = viemToGasEstimator(ctx.client.l2);
5686
+ return quoteL2Gas({
5687
+ estimator,
5688
+ route,
5689
+ tx: l2TxForModeling ? toCoreTx(l2TxForModeling) : void 0,
5690
+ gasPerPubdata: ctx.gasPerPubdata,
5691
+ l2GasLimit: ctx.l2GasLimit,
5692
+ // TODO: investigate if this should be passed here; weird viem quirk
5693
+ overrideGasLimit,
5694
+ stateOverrides: input.stateOverrides
5695
+ });
5696
+ }
5697
+ async function determineErc20L2Gas(input) {
5698
+ const { ctx, l1Token } = input;
5699
+ const DEFAULT_SAFE_L2_GAS_LIMIT = 3000000n;
5700
+ if (ctx.l2GasLimit != null) {
5701
+ return quoteL2Gas2({
5702
+ ctx,
5703
+ route: "erc20-nonbase",
5704
+ overrideGasLimit: ctx.l2GasLimit
5705
+ });
5706
+ }
5707
+ try {
5708
+ const l2NativeTokenVault = (await ctx.client.contracts()).l2NativeTokenVault;
5709
+ const l2TokenAddress = await ctx.client.l2.readContract({
5710
+ address: l2NativeTokenVault.address,
5711
+ abi: l2NativeTokenVault.abi,
5712
+ functionName: "l2TokenAddress",
5713
+ args: [l1Token]
5714
+ });
5715
+ if (l2TokenAddress === viem.zeroAddress) {
5716
+ return quoteL2Gas2({
5717
+ ctx,
5718
+ route: "erc20-nonbase",
5719
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5720
+ });
5721
+ }
5722
+ const modelTx = {
5723
+ to: input.modelTx?.to ?? ctx.sender,
5724
+ from: input.modelTx?.from ?? ctx.sender,
5725
+ data: input.modelTx?.data ?? "0x",
5726
+ value: input.modelTx?.value ?? 0n
5727
+ };
5728
+ const gas = await quoteL2Gas2({
5729
+ ctx,
5730
+ route: "erc20-nonbase",
5731
+ l2TxForModeling: modelTx
5732
+ });
5733
+ if (!gas) {
5734
+ return quoteL2Gas2({
5735
+ ctx,
5736
+ route: "erc20-nonbase",
5737
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5738
+ });
5739
+ }
5740
+ return gas;
5741
+ } catch (err) {
5742
+ console.warn("Failed to determine ERC20 L2 gas; defaulting to safe gas limit.", err);
5743
+ return quoteL2Gas2({
5744
+ ctx,
5745
+ route: "erc20-nonbase",
5746
+ overrideGasLimit: DEFAULT_SAFE_L2_GAS_LIMIT
5747
+ });
5748
+ }
5749
+ }
5750
+
5751
+ // src/adapters/viem/resources/deposits/services/fee.ts
5752
+ var { wrapAs } = createErrorHandlers("deposits");
5753
+ async function quoteL2BaseCost(input) {
5754
+ const { ctx, l2GasLimit } = input;
5755
+ const estimator = viemToGasEstimator(ctx.client.l1);
5756
+ const fees = await estimator.estimateFeesPerGas();
5757
+ const gasPrice = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
5758
+ return wrapAs(
5759
+ "RPC",
5760
+ "deposits.fees.l2BaseCost",
5761
+ async () => {
5762
+ return await ctx.client.l1.readContract({
5763
+ address: ctx.bridgehub,
5764
+ abi: IBridgehub_default,
5765
+ functionName: "l2TransactionBaseCost",
5766
+ args: [ctx.chainIdL2, gasPrice, l2GasLimit, ctx.gasPerPubdata]
5767
+ });
5768
+ },
5769
+ { ctx: { chainIdL2: ctx.chainIdL2 } }
5770
+ );
5771
+ }
5772
+
5773
+ // src/core/resources/deposits/fee.ts
5774
+ function buildFeeBreakdown(p) {
5775
+ const l1MaxTotal = p.l1Gas?.maxCost ?? 0n;
5776
+ const l2Total = p.l2BaseCost + p.operatorTip;
5777
+ const l1 = {
5778
+ gasLimit: p.l1Gas?.gasLimit ?? 0n,
5779
+ maxFeePerGas: p.l1Gas?.maxFeePerGas ?? 0n,
5780
+ maxPriorityFeePerGas: p.l1Gas?.maxPriorityFeePerGas,
5781
+ maxTotal: l1MaxTotal
5782
+ };
5783
+ const l2 = {
5784
+ total: l2Total,
5785
+ baseCost: p.l2BaseCost,
5786
+ operatorTip: p.operatorTip,
5787
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
5788
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
5789
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas,
5790
+ gasPerPubdata: p.l2Gas?.gasPerPubdata ?? 0n
5791
+ };
5792
+ return {
5793
+ token: p.feeToken,
5794
+ maxTotal: l1MaxTotal + l2Total,
5795
+ mintValue: p.mintValue,
5796
+ l1,
5797
+ l2
5798
+ };
5799
+ }
5800
+
5801
+ // src/adapters/viem/resources/deposits/routes/eth.ts
5802
+ var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
5803
+ function routeEthDirect() {
5804
+ return {
5715
5805
  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({
5806
+ const l2TxModel = {
5807
+ to: p.to ?? ctx.sender,
5808
+ from: ctx.sender,
5809
+ data: "0x",
5810
+ value: p.amount
5811
+ };
5812
+ const l2GasParams = await quoteL2Gas2({
5813
+ ctx,
5814
+ route: "eth-base",
5815
+ l2TxForModeling: l2TxModel,
5816
+ overrideGasLimit: ctx.l2GasLimit,
5817
+ stateOverrides: {
5818
+ [ctx.sender]: {
5819
+ balance: "0xffffffffffffffffffff"
5820
+ }
5821
+ }
5822
+ });
5823
+ if (!l2GasParams) {
5824
+ throw new Error("Failed to estimate L2 gas for deposit.");
5825
+ }
5826
+ const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
5827
+ const l2Contract = p.to ?? ctx.sender;
5828
+ const l2Value = p.amount;
5829
+ const mintValue = baseCost + ctx.operatorTip + l2Value;
5830
+ const req = buildDirectRequestStruct({
5831
+ chainId: ctx.chainIdL2,
5832
+ mintValue,
5833
+ l2GasLimit: l2GasParams.gasLimit,
5834
+ gasPerPubdata: ctx.gasPerPubdata,
5835
+ refundRecipient: ctx.refundRecipient,
5836
+ l2Contract,
5837
+ l2Value
5838
+ });
5839
+ const sim = await wrapAs2(
5840
+ "RPC",
5841
+ OP_DEPOSITS.eth.estGas,
5842
+ () => ctx.client.l1.simulateContract({
5722
5843
  address: ctx.bridgehub,
5723
5844
  abi: IBridgehub_default,
5724
- functionName: "baseToken",
5725
- args: [ctx.chainIdL2]
5845
+ functionName: "requestL2TransactionDirect",
5846
+ args: [req],
5847
+ value: mintValue,
5848
+ account: ctx.client.account
5726
5849
  }),
5727
- { ctx: { where: "bridgehub.baseToken", chainIdL2: ctx.chainIdL2 } }
5850
+ {
5851
+ ctx: { where: "l1.simulateContract", to: ctx.bridgehub },
5852
+ message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
5853
+ }
5728
5854
  );
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 } }
5855
+ const data = viem.encodeFunctionData({
5856
+ abi: sim.request.abi,
5857
+ functionName: sim.request.functionName,
5858
+ args: sim.request.args
5859
+ });
5860
+ const l1TxCandidate = {
5861
+ to: ctx.bridgehub,
5862
+ data,
5863
+ value: mintValue,
5864
+ from: ctx.sender,
5865
+ ...ctx.gasOverrides
5866
+ };
5867
+ const l1Gas = await quoteL1Gas2({
5868
+ ctx,
5869
+ tx: l1TxCandidate,
5870
+ overrides: ctx.gasOverrides
5871
+ });
5872
+ const steps = [
5873
+ {
5874
+ key: "bridgehub:direct",
5875
+ kind: "bridgehub:direct",
5876
+ description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
5877
+ tx: { ...sim.request, ...l1Gas }
5878
+ }
5879
+ ];
5880
+ const fees = buildFeeBreakdown({
5881
+ feeToken: ETH_ADDRESS,
5882
+ l1Gas,
5883
+ l2Gas: l2GasParams,
5884
+ l2BaseCost: baseCost,
5885
+ operatorTip: ctx.operatorTip,
5886
+ mintValue
5887
+ });
5888
+ return {
5889
+ steps,
5890
+ approvals: [],
5891
+ fees
5892
+ };
5893
+ }
5894
+ };
5895
+ }
5896
+ var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
5897
+ function routeErc20NonBase() {
5898
+ return {
5899
+ // TODO: do we even need these validations?
5900
+ async preflight(p, ctx) {
5901
+ await wrapAs3(
5902
+ "VALIDATION",
5903
+ OP_DEPOSITS.nonbase.assertNotEthAsset,
5904
+ () => {
5905
+ if (isETH(p.token)) {
5906
+ throw new Error("erc20-nonbase route requires an ERC-20 token (not ETH).");
5907
+ }
5908
+ },
5909
+ { ctx: { token: p.token } }
5910
+ );
5911
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
5912
+ await wrapAs3(
5913
+ "VALIDATION",
5914
+ OP_DEPOSITS.nonbase.assertNonBaseToken,
5915
+ () => {
5916
+ if (normalizeAddrEq(baseToken, p.token)) {
5917
+ throw new Error("erc20-nonbase route requires a non-base ERC-20 deposit token.");
5918
+ }
5919
+ },
5920
+ { ctx: { depositToken: p.token, baseToken } }
5741
5921
  );
5742
- const baseCost = rawBaseCost;
5743
- const mintValue = withBuffer(baseCost + ctx.operatorTip);
5922
+ },
5923
+ async build(p, ctx) {
5924
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
5925
+ const baseIsEth = isETH(baseToken);
5926
+ const assetRouter = ctx.l1AssetRouter;
5927
+ const l2Gas = await determineErc20L2Gas({
5928
+ ctx,
5929
+ l1Token: p.token,
5930
+ modelTx: {
5931
+ to: p.to ?? ctx.sender,
5932
+ from: ctx.sender,
5933
+ data: "0x",
5934
+ value: 0n
5935
+ }
5936
+ });
5937
+ if (!l2Gas) throw new Error("Failed to establish L2 gas parameters.");
5938
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
5939
+ const mintValue = l2BaseCost + ctx.operatorTip;
5744
5940
  const approvals = [];
5745
5941
  const steps = [];
5746
- const depositAllowance = await wrapAs2(
5942
+ const depositAllowance = await wrapAs3(
5747
5943
  "CONTRACT",
5748
- OP_DEPOSITS.nonbase.allowance,
5944
+ OP_DEPOSITS.nonbase.allowanceToken,
5749
5945
  () => ctx.client.l1.readContract({
5750
5946
  address: p.token,
5751
5947
  abi: IERC20_default,
5752
5948
  functionName: "allowance",
5753
- args: [ctx.sender, ctx.l1AssetRouter]
5949
+ args: [ctx.sender, assetRouter]
5754
5950
  }),
5755
5951
  {
5756
- ctx: { where: "erc20.allowance", token: p.token, spender: ctx.l1AssetRouter },
5757
- message: "Failed to read ERC-20 allowance for deposit token."
5952
+ ctx: { where: "erc20.allowance", token: p.token, spender: assetRouter },
5953
+ message: "Failed to read deposit-token allowance."
5758
5954
  }
5759
5955
  );
5760
- const needsDepositApprove = depositAllowance < p.amount;
5761
- if (needsDepositApprove) {
5762
- const approveDepReq = await wrapAs2(
5956
+ if (depositAllowance < p.amount) {
5957
+ const approveSim = await wrapAs3(
5763
5958
  "CONTRACT",
5764
5959
  OP_DEPOSITS.nonbase.estGas,
5765
5960
  () => ctx.client.l1.simulateContract({
5766
5961
  address: p.token,
5767
5962
  abi: IERC20_default,
5768
5963
  functionName: "approve",
5769
- args: [ctx.l1AssetRouter, p.amount],
5964
+ args: [assetRouter, p.amount],
5770
5965
  account: ctx.client.account
5771
5966
  }),
5772
5967
  {
@@ -5774,60 +5969,55 @@ function routeErc20NonBase() {
5774
5969
  message: "Failed to simulate deposit token approve."
5775
5970
  }
5776
5971
  );
5777
- approvals.push({ token: p.token, spender: ctx.l1AssetRouter, amount: p.amount });
5972
+ approvals.push({ token: p.token, spender: assetRouter, amount: p.amount });
5778
5973
  steps.push({
5779
- key: `approve:${p.token}:${ctx.l1AssetRouter}`,
5974
+ key: `approve:${p.token}:${assetRouter}`,
5780
5975
  kind: "approve",
5781
5976
  description: `Approve deposit token for amount`,
5782
- tx: { ...approveDepReq.request, ...txFeeOverrides }
5977
+ tx: { ...approveSim.request }
5783
5978
  });
5784
5979
  }
5785
- const baseIsEth = isETH(baseToken);
5786
- let msgValue = 0n;
5787
5980
  if (!baseIsEth) {
5788
- const baseAllowance = await wrapAs2(
5981
+ const baseAllowance = await wrapAs3(
5789
5982
  "CONTRACT",
5790
- OP_DEPOSITS.nonbase.allowanceFees,
5983
+ OP_DEPOSITS.nonbase.allowanceBase,
5791
5984
  () => ctx.client.l1.readContract({
5792
5985
  address: baseToken,
5793
5986
  abi: IERC20_default,
5794
5987
  functionName: "allowance",
5795
- args: [ctx.sender, ctx.l1AssetRouter]
5988
+ args: [ctx.sender, assetRouter]
5796
5989
  }),
5797
5990
  {
5798
- ctx: { where: "erc20.allowance", token: baseToken, spender: ctx.l1AssetRouter },
5991
+ ctx: { where: "erc20.allowance", token: baseToken, spender: assetRouter },
5799
5992
  message: "Failed to read base-token allowance."
5800
5993
  }
5801
5994
  );
5802
5995
  if (baseAllowance < mintValue) {
5803
- const approveBaseReq = await wrapAs2(
5996
+ const approveBaseSim = await wrapAs3(
5804
5997
  "CONTRACT",
5805
5998
  OP_DEPOSITS.nonbase.estGas,
5806
5999
  () => ctx.client.l1.simulateContract({
5807
6000
  address: baseToken,
5808
6001
  abi: IERC20_default,
5809
6002
  functionName: "approve",
5810
- args: [ctx.l1AssetRouter, mintValue],
6003
+ args: [assetRouter, mintValue],
5811
6004
  account: ctx.client.account
5812
6005
  }),
5813
6006
  {
5814
6007
  ctx: { where: "l1.simulateContract", to: baseToken },
5815
- message: "Failed to simulate base-token approve."
6008
+ message: "Failed to simulate base token approve."
5816
6009
  }
5817
6010
  );
5818
- approvals.push({ token: baseToken, spender: ctx.l1AssetRouter, amount: mintValue });
6011
+ approvals.push({ token: baseToken, spender: assetRouter, amount: mintValue });
5819
6012
  steps.push({
5820
- key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6013
+ key: `approve:${baseToken}:${assetRouter}`,
5821
6014
  kind: "approve",
5822
6015
  description: `Approve base token for mintValue`,
5823
- tx: { ...approveBaseReq.request, ...txFeeOverrides }
6016
+ tx: { ...approveBaseSim.request }
5824
6017
  });
5825
6018
  }
5826
- msgValue = 0n;
5827
- } else {
5828
- msgValue = mintValue;
5829
6019
  }
5830
- const secondBridgeCalldata = await wrapAs2(
6020
+ const secondBridgeCalldata = await wrapAs3(
5831
6021
  "INTERNAL",
5832
6022
  OP_DEPOSITS.nonbase.encodeCalldata,
5833
6023
  () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
@@ -5836,44 +6026,60 @@ function routeErc20NonBase() {
5836
6026
  where: "encodeSecondBridgeErc20Args",
5837
6027
  token: p.token,
5838
6028
  amount: p.amount.toString()
5839
- }
6029
+ },
6030
+ message: "Failed to encode bridging calldata."
5840
6031
  }
5841
6032
  );
5842
- const outer = {
6033
+ const requestStruct = {
5843
6034
  chainId: ctx.chainIdL2,
5844
6035
  mintValue,
5845
6036
  l2Value: 0n,
5846
- l2GasLimit: l2GasLimitUsed,
6037
+ l2GasLimit: l2Gas.gasLimit,
5847
6038
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
5848
6039
  refundRecipient: ctx.refundRecipient,
5849
- secondBridgeAddress: ctx.l1AssetRouter,
6040
+ secondBridgeAddress: assetRouter,
5850
6041
  secondBridgeValue: 0n,
5851
6042
  secondBridgeCalldata
5852
6043
  };
6044
+ const msgValue = baseIsEth ? mintValue : 0n;
6045
+ const calldata = viem.encodeFunctionData({
6046
+ abi: IBridgehub_default,
6047
+ functionName: "requestL2TransactionTwoBridges",
6048
+ args: [requestStruct]
6049
+ });
6050
+ const l1TxCandidate = {
6051
+ to: ctx.bridgehub,
6052
+ data: calldata,
6053
+ value: msgValue,
6054
+ from: ctx.sender,
6055
+ ...ctx.gasOverrides
6056
+ };
6057
+ const l1Gas = await quoteL1Gas2({
6058
+ ctx,
6059
+ tx: l1TxCandidate,
6060
+ overrides: ctx.gasOverrides,
6061
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6062
+ });
5853
6063
  const approvalsNeeded = approvals.length > 0;
5854
6064
  let bridgeTx;
5855
- let resolvedL1GasLimit;
5856
- const gasOverride = txFeeOverrides.gas;
5857
6065
  if (approvalsNeeded) {
5858
6066
  bridgeTx = {
5859
6067
  address: ctx.bridgehub,
5860
6068
  abi: IBridgehub_default,
5861
6069
  functionName: "requestL2TransactionTwoBridges",
5862
- args: [outer],
6070
+ args: [requestStruct],
5863
6071
  value: msgValue,
5864
- account: ctx.client.account,
5865
- ...txFeeOverrides
6072
+ account: ctx.client.account
5866
6073
  };
5867
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
5868
6074
  } else {
5869
- const sim = await wrapAs2(
6075
+ const sim = await wrapAs3(
5870
6076
  "CONTRACT",
5871
6077
  OP_DEPOSITS.nonbase.estGas,
5872
6078
  () => ctx.client.l1.simulateContract({
5873
6079
  address: ctx.bridgehub,
5874
6080
  abi: IBridgehub_default,
5875
6081
  functionName: "requestL2TransactionTwoBridges",
5876
- args: [outer],
6082
+ args: [requestStruct],
5877
6083
  value: msgValue,
5878
6084
  account: ctx.client.account
5879
6085
  }),
@@ -5882,33 +6088,44 @@ function routeErc20NonBase() {
5882
6088
  message: "Failed to simulate two-bridges request."
5883
6089
  }
5884
6090
  );
5885
- bridgeTx = { ...sim.request, ...txFeeOverrides };
5886
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
6091
+ bridgeTx = { ...sim.request };
6092
+ }
6093
+ if (l1Gas) {
6094
+ bridgeTx = {
6095
+ ...bridgeTx,
6096
+ gas: l1Gas.gasLimit,
6097
+ maxFeePerGas: l1Gas.maxFeePerGas,
6098
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6099
+ };
5887
6100
  }
5888
6101
  steps.push({
5889
- key: "bridgehub:two-bridges:nonbase",
6102
+ key: "bridgehub:two-bridges:erc20-nonbase",
5890
6103
  kind: "bridgehub:two-bridges",
5891
6104
  description: baseIsEth ? "Bridge ERC-20 (fees in ETH) via Bridgehub.requestL2TransactionTwoBridges" : "Bridge ERC-20 (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
5892
6105
  tx: bridgeTx
5893
6106
  });
6107
+ const fees = buildFeeBreakdown({
6108
+ feeToken: baseToken,
6109
+ l1Gas,
6110
+ l2Gas,
6111
+ l2BaseCost,
6112
+ operatorTip: ctx.operatorTip,
6113
+ mintValue
6114
+ });
5894
6115
  return {
5895
6116
  steps,
5896
6117
  approvals,
5897
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6118
+ fees
5898
6119
  };
5899
6120
  }
5900
6121
  };
5901
6122
  }
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;
6123
+ var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
5908
6124
  function routeEthNonBase() {
5909
6125
  return {
6126
+ // TODO: do we even need these validations?
5910
6127
  async preflight(p, ctx) {
5911
- await wrapAs3(
6128
+ await wrapAs4(
5912
6129
  "VALIDATION",
5913
6130
  OP_DEPOSITS.ethNonBase.assertEthAsset,
5914
6131
  () => {
@@ -5918,21 +6135,8 @@ function routeEthNonBase() {
5918
6135
  },
5919
6136
  { ctx: { token: p.token } }
5920
6137
  );
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(
6138
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
6139
+ await wrapAs4(
5936
6140
  "VALIDATION",
5937
6141
  OP_DEPOSITS.ethNonBase.assertNonEthBase,
5938
6142
  () => {
@@ -5942,7 +6146,7 @@ function routeEthNonBase() {
5942
6146
  },
5943
6147
  { ctx: { baseToken, chainIdL2: ctx.chainIdL2 } }
5944
6148
  );
5945
- const ethBal = await wrapAs3(
6149
+ const ethBal = await wrapAs4(
5946
6150
  "RPC",
5947
6151
  OP_DEPOSITS.ethNonBase.ethBalance,
5948
6152
  () => ctx.client.l1.getBalance({ address: ctx.sender }),
@@ -5951,7 +6155,7 @@ function routeEthNonBase() {
5951
6155
  message: "Failed to read L1 ETH balance."
5952
6156
  }
5953
6157
  );
5954
- await wrapAs3(
6158
+ await wrapAs4(
5955
6159
  "VALIDATION",
5956
6160
  OP_DEPOSITS.ethNonBase.assertEthBalance,
5957
6161
  () => {
@@ -5961,45 +6165,27 @@ function routeEthNonBase() {
5961
6165
  },
5962
6166
  { ctx: { required: p.amount.toString(), balance: ethBal.toString() } }
5963
6167
  );
5964
- return;
5965
6168
  },
5966
6169
  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);
6170
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
6171
+ const l2TxModel = {
6172
+ to: p.to ?? ctx.sender,
6173
+ from: ctx.sender,
6174
+ data: "0x",
6175
+ value: 0n
6176
+ };
6177
+ const l2Gas = await quoteL2Gas2({
6178
+ ctx,
6179
+ route: "eth-nonbase",
6180
+ l2TxForModeling: l2TxModel,
6181
+ overrideGasLimit: ctx.l2GasLimit
6182
+ });
6183
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
6184
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
6185
+ const mintValue = l2BaseCost + ctx.operatorTip;
6000
6186
  const approvals = [];
6001
6187
  const steps = [];
6002
- const allowance = await wrapAs3(
6188
+ const allowance = await wrapAs4(
6003
6189
  "CONTRACT",
6004
6190
  OP_DEPOSITS.ethNonBase.allowanceBase,
6005
6191
  () => ctx.client.l1.readContract({
@@ -6015,7 +6201,7 @@ function routeEthNonBase() {
6015
6201
  );
6016
6202
  const needsApprove = allowance < mintValue;
6017
6203
  if (needsApprove) {
6018
- const approveSim = await wrapAs3(
6204
+ const approveSim = await wrapAs4(
6019
6205
  "CONTRACT",
6020
6206
  OP_DEPOSITS.ethNonBase.estGas,
6021
6207
  () => ctx.client.l1.simulateContract({
@@ -6034,11 +6220,11 @@ function routeEthNonBase() {
6034
6220
  steps.push({
6035
6221
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6036
6222
  kind: "approve",
6037
- description: `Approve base token for mintValue`,
6223
+ description: `Approve base token for fees (mintValue)`,
6038
6224
  tx: { ...approveSim.request }
6039
6225
  });
6040
6226
  }
6041
- const secondBridgeCalldata = await wrapAs3(
6227
+ const secondBridgeCalldata = await wrapAs4(
6042
6228
  "INTERNAL",
6043
6229
  OP_DEPOSITS.ethNonBase.encodeCalldata,
6044
6230
  () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
@@ -6051,11 +6237,11 @@ function routeEthNonBase() {
6051
6237
  message: "Failed to encode ETH bridging calldata."
6052
6238
  }
6053
6239
  );
6054
- const outer = {
6240
+ const requestStruct = {
6055
6241
  chainId: ctx.chainIdL2,
6056
6242
  mintValue,
6057
- l2Value: 0n,
6058
- l2GasLimit: ctx.l2GasLimit,
6243
+ l2Value: p.amount,
6244
+ l2GasLimit: l2Gas.gasLimit,
6059
6245
  l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
6060
6246
  refundRecipient: ctx.refundRecipient,
6061
6247
  secondBridgeAddress: ctx.l1AssetRouter,
@@ -6063,29 +6249,32 @@ function routeEthNonBase() {
6063
6249
  secondBridgeCalldata
6064
6250
  };
6065
6251
  let bridgeTx;
6066
- let resolvedL1GasLimit;
6252
+ let calldata;
6067
6253
  if (needsApprove) {
6068
6254
  bridgeTx = {
6069
6255
  address: ctx.bridgehub,
6070
6256
  abi: IBridgehub_default,
6071
6257
  functionName: "requestL2TransactionTwoBridges",
6072
- args: [outer],
6258
+ args: [requestStruct],
6073
6259
  value: p.amount,
6074
6260
  // base ≠ ETH ⇒ msg.value == secondBridgeValue
6075
6261
  account: ctx.client.account
6076
6262
  };
6077
- resolvedL1GasLimit = ctx.l2GasLimit;
6263
+ calldata = viem.encodeFunctionData({
6264
+ abi: IBridgehub_default,
6265
+ functionName: "requestL2TransactionTwoBridges",
6266
+ args: [requestStruct]
6267
+ });
6078
6268
  } else {
6079
- const twoBridgesSim = await wrapAs3(
6269
+ const sim = await wrapAs4(
6080
6270
  "CONTRACT",
6081
6271
  OP_DEPOSITS.ethNonBase.estGas,
6082
6272
  () => ctx.client.l1.simulateContract({
6083
6273
  address: ctx.bridgehub,
6084
6274
  abi: IBridgehub_default,
6085
6275
  functionName: "requestL2TransactionTwoBridges",
6086
- args: [outer],
6276
+ args: [requestStruct],
6087
6277
  value: p.amount,
6088
- // base ≠ ETH ⇒ msg.value == secondBridgeValue
6089
6278
  account: ctx.client.account
6090
6279
  }),
6091
6280
  {
@@ -6093,8 +6282,33 @@ function routeEthNonBase() {
6093
6282
  message: "Failed to simulate Bridgehub two-bridges request."
6094
6283
  }
6095
6284
  );
6096
- bridgeTx = { ...twoBridgesSim.request, ...txFeeOverrides };
6097
- resolvedL1GasLimit = twoBridgesSim.request.gas ?? ctx.l2GasLimit;
6285
+ calldata = viem.encodeFunctionData({
6286
+ abi: sim.request.abi,
6287
+ functionName: sim.request.functionName,
6288
+ args: sim.request.args
6289
+ });
6290
+ bridgeTx = { ...sim.request };
6291
+ }
6292
+ const l1TxCandidate = {
6293
+ to: ctx.bridgehub,
6294
+ data: calldata,
6295
+ value: p.amount,
6296
+ from: ctx.sender,
6297
+ ...ctx.gasOverrides
6298
+ };
6299
+ const l1Gas = await quoteL1Gas2({
6300
+ ctx,
6301
+ tx: l1TxCandidate,
6302
+ overrides: ctx.gasOverrides,
6303
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6304
+ });
6305
+ if (l1Gas) {
6306
+ bridgeTx = {
6307
+ ...bridgeTx,
6308
+ gas: l1Gas.gasLimit,
6309
+ maxFeePerGas: l1Gas.maxFeePerGas,
6310
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6311
+ };
6098
6312
  }
6099
6313
  steps.push({
6100
6314
  key: "bridgehub:two-bridges:eth-nonbase",
@@ -6102,24 +6316,27 @@ function routeEthNonBase() {
6102
6316
  description: "Bridge ETH (fees in base ERC-20) via Bridgehub.requestL2TransactionTwoBridges",
6103
6317
  tx: bridgeTx
6104
6318
  });
6319
+ const fees = buildFeeBreakdown({
6320
+ feeToken: baseToken,
6321
+ l1Gas,
6322
+ l2Gas,
6323
+ l2BaseCost,
6324
+ operatorTip: ctx.operatorTip,
6325
+ mintValue
6326
+ });
6105
6327
  return {
6106
6328
  steps,
6107
6329
  approvals,
6108
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6330
+ fees
6109
6331
  };
6110
6332
  }
6111
6333
  };
6112
6334
  }
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;
6335
+ var { wrapAs: wrapAs5 } = createErrorHandlers("deposits");
6119
6336
  function routeErc20Base() {
6120
6337
  return {
6121
6338
  async preflight(p, ctx) {
6122
- await wrapAs4(
6339
+ await wrapAs5(
6123
6340
  "VALIDATION",
6124
6341
  OP_DEPOSITS.base.assertErc20Asset,
6125
6342
  () => {
@@ -6129,21 +6346,8 @@ function routeErc20Base() {
6129
6346
  },
6130
6347
  { ctx: { token: p.token } }
6131
6348
  );
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(
6349
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
6350
+ await wrapAs5(
6147
6351
  "VALIDATION",
6148
6352
  OP_DEPOSITS.base.assertMatchesBase,
6149
6353
  () => {
@@ -6153,45 +6357,27 @@ function routeErc20Base() {
6153
6357
  },
6154
6358
  { ctx: { baseToken, provided: p.token, chainIdL2: ctx.chainIdL2 } }
6155
6359
  );
6156
- return;
6157
6360
  },
6158
6361
  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(
6362
+ const baseToken = await ctx.client.baseToken(ctx.chainIdL2);
6363
+ const l2TxModel = {
6364
+ to: p.to ?? ctx.sender,
6365
+ from: ctx.sender,
6366
+ data: "0x",
6367
+ value: 0n
6368
+ };
6369
+ const l2Gas = await quoteL2Gas2({
6370
+ ctx,
6371
+ route: "erc20-base",
6372
+ l2TxForModeling: l2TxModel,
6373
+ overrideGasLimit: ctx.l2GasLimit
6374
+ });
6375
+ if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
6376
+ const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
6377
+ const mintValue = l2BaseCost + ctx.operatorTip + p.amount;
6378
+ const approvals = [];
6379
+ const steps = [];
6380
+ const allowance = await wrapAs5(
6195
6381
  "CONTRACT",
6196
6382
  OP_DEPOSITS.base.allowance,
6197
6383
  () => ctx.client.l1.readContract({
@@ -6205,11 +6391,9 @@ function routeErc20Base() {
6205
6391
  message: "Failed to read base-token allowance."
6206
6392
  }
6207
6393
  );
6208
- const approvals = [];
6209
- const steps = [];
6210
6394
  const needsApprove = allowance < mintValue;
6211
6395
  if (needsApprove) {
6212
- const approveSim = await wrapAs4(
6396
+ const approveSim = await wrapAs5(
6213
6397
  "CONTRACT",
6214
6398
  OP_DEPOSITS.base.estGas,
6215
6399
  () => ctx.client.l1.simulateContract({
@@ -6229,20 +6413,20 @@ function routeErc20Base() {
6229
6413
  key: `approve:${baseToken}:${ctx.l1AssetRouter}`,
6230
6414
  kind: "approve",
6231
6415
  description: "Approve base token for mintValue",
6232
- tx: { ...approveSim.request, ...txFeeOverrides }
6416
+ tx: { ...approveSim.request }
6233
6417
  });
6234
6418
  }
6235
6419
  const req = buildDirectRequestStruct({
6236
6420
  chainId: ctx.chainIdL2,
6237
6421
  mintValue,
6238
- l2GasLimit: ctx.l2GasLimit,
6422
+ l2GasLimit: l2Gas.gasLimit,
6239
6423
  gasPerPubdata: ctx.gasPerPubdata,
6240
6424
  refundRecipient: ctx.refundRecipient,
6241
6425
  l2Contract: p.to ?? ctx.sender,
6242
- l2Value
6426
+ l2Value: p.amount
6243
6427
  });
6244
6428
  let bridgeTx;
6245
- let resolvedL1GasLimit;
6429
+ let calldata;
6246
6430
  if (needsApprove) {
6247
6431
  bridgeTx = {
6248
6432
  address: ctx.bridgehub,
@@ -6250,13 +6434,16 @@ function routeErc20Base() {
6250
6434
  functionName: "requestL2TransactionDirect",
6251
6435
  args: [req],
6252
6436
  value: 0n,
6253
- // base is ERC-20 ⇒ msg.value MUST be 0
6254
- account: ctx.client.account,
6255
- ...txFeeOverrides
6437
+ // base token is ERC-20 ⇒ msg.value MUST be 0
6438
+ account: ctx.client.account
6256
6439
  };
6257
- resolvedL1GasLimit = gasOverride ?? ctx.l2GasLimit;
6440
+ calldata = viem.encodeFunctionData({
6441
+ abi: IBridgehub_default,
6442
+ functionName: "requestL2TransactionDirect",
6443
+ args: [req]
6444
+ });
6258
6445
  } else {
6259
- const sim = await wrapAs4(
6446
+ const sim = await wrapAs5(
6260
6447
  "RPC",
6261
6448
  OP_DEPOSITS.base.estGas,
6262
6449
  () => ctx.client.l1.simulateContract({
@@ -6272,8 +6459,33 @@ function routeErc20Base() {
6272
6459
  message: "Failed to simulate Bridgehub.requestL2TransactionDirect."
6273
6460
  }
6274
6461
  );
6275
- bridgeTx = { ...sim.request, ...txFeeOverrides };
6276
- resolvedL1GasLimit = sim.request.gas ?? ctx.l2GasLimit;
6462
+ calldata = viem.encodeFunctionData({
6463
+ abi: sim.request.abi,
6464
+ functionName: sim.request.functionName,
6465
+ args: sim.request.args
6466
+ });
6467
+ bridgeTx = { ...sim.request };
6468
+ }
6469
+ const l1TxCandidate = {
6470
+ to: ctx.bridgehub,
6471
+ data: calldata,
6472
+ value: 0n,
6473
+ from: ctx.sender,
6474
+ ...ctx.gasOverrides
6475
+ };
6476
+ const l1Gas = await quoteL1Gas2({
6477
+ ctx,
6478
+ tx: l1TxCandidate,
6479
+ overrides: ctx.gasOverrides,
6480
+ fallbackGasLimit: SAFE_L1_BRIDGE_GAS
6481
+ });
6482
+ if (l1Gas) {
6483
+ bridgeTx = {
6484
+ ...bridgeTx,
6485
+ gas: l1Gas.gasLimit,
6486
+ maxFeePerGas: l1Gas.maxFeePerGas,
6487
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
6488
+ };
6277
6489
  }
6278
6490
  steps.push({
6279
6491
  key: "bridgehub:direct:erc20-base",
@@ -6281,10 +6493,18 @@ function routeErc20Base() {
6281
6493
  description: "Bridge base ERC-20 via Bridgehub.requestL2TransactionDirect",
6282
6494
  tx: bridgeTx
6283
6495
  });
6496
+ const fees = buildFeeBreakdown({
6497
+ feeToken: baseToken,
6498
+ l1Gas,
6499
+ l2Gas,
6500
+ l2BaseCost,
6501
+ operatorTip: ctx.operatorTip,
6502
+ mintValue
6503
+ });
6284
6504
  return {
6285
6505
  steps,
6286
6506
  approvals,
6287
- quoteExtras: { baseCost, mintValue, l1GasLimit: resolvedL1GasLimit }
6507
+ fees
6288
6508
  };
6289
6509
  }
6290
6510
  };
@@ -6378,32 +6598,19 @@ function createDepositsResource(client) {
6378
6598
  const ctx = await commonCtx(p, client);
6379
6599
  const route = ctx.route;
6380
6600
  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();
6601
+ const { steps, approvals, fees } = await ROUTES[route].build(p, ctx);
6394
6602
  return {
6395
6603
  route: ctx.route,
6396
6604
  summary: {
6397
6605
  route: ctx.route,
6398
6606
  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
- }
6607
+ amounts: {
6608
+ transfer: { token: p.token, amount: p.amount }
6609
+ },
6610
+ fees,
6611
+ // Legacy fields (maintained for backward compatibility)
6612
+ baseCost: fees.l2?.baseCost,
6613
+ mintValue: fees.mintValue
6407
6614
  },
6408
6615
  steps
6409
6616
  };
@@ -6478,7 +6685,7 @@ function createDepositsResource(client) {
6478
6685
  step.tx.gas = overrides.gasLimit;
6479
6686
  }
6480
6687
  }
6481
- if (step.tx.gas == null) {
6688
+ if (!p.l1TxOverrides?.gasLimit) {
6482
6689
  try {
6483
6690
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
6484
6691
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -6701,7 +6908,7 @@ function normalizeTokenForRouting(token) {
6701
6908
  function pickWithdrawRoute(args) {
6702
6909
  const tokenNorm = normalizeTokenForRouting(args.token);
6703
6910
  const isL2BaseAlias = tokenNorm.toLowerCase() === L2_BASE_TOKEN_ADDRESS.toLowerCase();
6704
- if (isL2BaseAlias) return args.baseIsEth ? "eth-base" : "eth-nonbase";
6911
+ if (isL2BaseAlias) return "base";
6705
6912
  return "erc20-nonbase";
6706
6913
  }
6707
6914
 
@@ -6742,13 +6949,7 @@ async function commonCtx2(p, client) {
6742
6949
  } = await client.ensureAddresses();
6743
6950
  const chainIdL2 = BigInt(await client.l2.getChainId());
6744
6951
  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;
6952
+ const route = pickWithdrawRoute({ token: p.token});
6752
6953
  return {
6753
6954
  client,
6754
6955
  bridgehub,
@@ -6761,56 +6962,162 @@ async function commonCtx2(p, client) {
6761
6962
  l2NativeTokenVault,
6762
6963
  l2BaseTokenSystem,
6763
6964
  baseIsEth,
6764
- l2GasLimit,
6765
- gasBufferPct,
6766
- fee
6965
+ gasOverrides: p.l2TxOverrides
6966
+ };
6967
+ }
6968
+
6969
+ // src/core/resources/withdrawals/gas.ts
6970
+ function makeGasQuote2(p) {
6971
+ return {
6972
+ gasLimit: p.gasLimit,
6973
+ maxFeePerGas: p.maxFeePerGas,
6974
+ maxPriorityFeePerGas: p.maxPriorityFeePerGas,
6975
+ maxCost: p.gasLimit * p.maxFeePerGas
6976
+ };
6977
+ }
6978
+ async function fetchFees2(estimator) {
6979
+ try {
6980
+ const fees = await estimator.estimateFeesPerGas();
6981
+ if (fees.maxFeePerGas != null) {
6982
+ return {
6983
+ maxFeePerGas: fees.maxFeePerGas,
6984
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
6985
+ };
6986
+ }
6987
+ if (fees.gasPrice != null) {
6988
+ return {
6989
+ maxFeePerGas: fees.gasPrice,
6990
+ maxPriorityFeePerGas: 0n
6991
+ };
6992
+ }
6993
+ } catch {
6994
+ }
6995
+ try {
6996
+ const gp = await estimator.getGasPrice();
6997
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
6998
+ } catch {
6999
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
7000
+ }
7001
+ }
7002
+ async function quoteL2Gas3(input) {
7003
+ const { estimator, tx, overrides } = input;
7004
+ const market = await fetchFees2(estimator);
7005
+ const o = overrides;
7006
+ const maxFeePerGas = o?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : market.maxFeePerGas);
7007
+ const maxPriorityFeePerGas = o?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : market.maxPriorityFeePerGas);
7008
+ const explicitGasLimit = o?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
7009
+ if (explicitGasLimit != null) {
7010
+ return makeGasQuote2({
7011
+ gasLimit: explicitGasLimit,
7012
+ maxFeePerGas,
7013
+ maxPriorityFeePerGas
7014
+ });
7015
+ }
7016
+ try {
7017
+ const est = await estimator.estimateGas(tx);
7018
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
7019
+ return makeGasQuote2({
7020
+ gasLimit: buffered,
7021
+ maxFeePerGas,
7022
+ maxPriorityFeePerGas
7023
+ });
7024
+ } catch (err) {
7025
+ console.warn("Failed to estimate L2 gas for withdrawal.", err);
7026
+ return void 0;
7027
+ }
7028
+ }
7029
+
7030
+ // src/adapters/viem/resources/withdrawals/services/gas.ts
7031
+ async function quoteL2Gas4(input) {
7032
+ const { ctx, tx } = input;
7033
+ const estimator = viemToGasEstimator(ctx.client.l2);
7034
+ return quoteL2Gas3({
7035
+ estimator,
7036
+ tx: toCoreTx(tx),
7037
+ overrides: ctx.gasOverrides
7038
+ });
7039
+ }
7040
+
7041
+ // src/adapters/viem/resources/withdrawals/services/fee.ts
7042
+ function buildFeeBreakdown2(p) {
7043
+ const l2Total = p.l2Gas?.maxCost ?? 0n;
7044
+ const l2 = {
7045
+ total: l2Total,
7046
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
7047
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
7048
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
7049
+ };
7050
+ return {
7051
+ token: p.feeToken,
7052
+ maxTotal: l2Total,
7053
+ l2
6767
7054
  };
6768
7055
  }
6769
7056
 
6770
7057
  // src/adapters/viem/resources/withdrawals/routes/eth.ts
6771
- var { wrapAs: wrapAs5 } = createErrorHandlers("withdrawals");
7058
+ var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
6772
7059
  function routeEthBase() {
6773
7060
  return {
6774
7061
  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
- }),
7062
+ const steps = [];
7063
+ const data = await wrapAs6(
7064
+ "INTERNAL",
7065
+ OP_WITHDRAWALS.eth.encodeWithdraw,
7066
+ () => Promise.resolve(
7067
+ viem.encodeFunctionData({
7068
+ abi: IBaseToken_default,
7069
+ functionName: "withdraw",
7070
+ args: [p.to ?? ctx.sender]
7071
+ })
7072
+ ),
6789
7073
  {
6790
- ctx: { where: "l2.simulateContract", to: L2_BASE_TOKEN_ADDRESS },
6791
- message: "Failed to simulate L2 ETH withdraw."
7074
+ ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
7075
+ message: "Failed to encode ETH withdraw calldata."
6792
7076
  }
6793
7077
  );
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: {} };
7078
+ const L2tx = {
7079
+ to: L2_BASE_TOKEN_ADDRESS,
7080
+ data,
7081
+ value: p.amount,
7082
+ from: ctx.sender
7083
+ };
7084
+ const l2Gas = await quoteL2Gas4({ ctx, tx: L2tx });
7085
+ if (l2Gas) {
7086
+ L2tx.gas = l2Gas.gasLimit;
7087
+ L2tx.maxFeePerGas = l2Gas.maxFeePerGas;
7088
+ L2tx.maxPriorityFeePerGas = l2Gas.maxPriorityFeePerGas;
7089
+ }
7090
+ const tx = {
7091
+ address: L2_BASE_TOKEN_ADDRESS,
7092
+ abi: IBaseToken_default,
7093
+ functionName: "withdraw",
7094
+ args: [p.to ?? ctx.sender],
7095
+ value: p.amount,
7096
+ account: ctx.client.account,
7097
+ ...l2Gas
7098
+ };
7099
+ const fees = buildFeeBreakdown2({
7100
+ feeToken: L2_BASE_TOKEN_ADDRESS,
7101
+ l2Gas
7102
+ });
7103
+ steps.push({
7104
+ key: "l2-base-token:withdraw",
7105
+ kind: "l2-base-token:withdraw",
7106
+ description: "Withdraw ETH via L2 Base Token System",
7107
+ tx
7108
+ });
7109
+ return { steps, approvals: [], fees };
6803
7110
  }
6804
7111
  };
6805
7112
  }
6806
- var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
7113
+ var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
6807
7114
  function routeErc20NonBase2() {
6808
7115
  return {
6809
7116
  // TODO: add preflight validations here
6810
7117
  async build(p, ctx) {
6811
- const toL1 = p.to ?? ctx.sender;
6812
- const txFeeOverrides = buildViemFeeOverrides(ctx.fee);
6813
- const current = await wrapAs6(
7118
+ const steps = [];
7119
+ const approvals = [];
7120
+ const current = await wrapAs7(
6814
7121
  "CONTRACT",
6815
7122
  OP_WITHDRAWALS.erc20.allowance,
6816
7123
  () => ctx.client.l2.readContract({
@@ -6830,12 +7137,26 @@ function routeErc20NonBase2() {
6830
7137
  message: "Failed to read L2 ERC-20 allowance."
6831
7138
  }
6832
7139
  );
6833
- const needsApprove = current < p.amount;
6834
- const steps = [];
6835
- const approvals = [];
6836
- if (needsApprove) {
7140
+ if (current < p.amount) {
6837
7141
  approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
6838
- const approveSim = await wrapAs6(
7142
+ const data = viem.encodeFunctionData({
7143
+ abi: IERC20_default,
7144
+ functionName: "approve",
7145
+ args: [ctx.l2NativeTokenVault, p.amount]
7146
+ });
7147
+ const approveTxCandidate = {
7148
+ to: p.token,
7149
+ data,
7150
+ value: 0n,
7151
+ from: ctx.sender
7152
+ };
7153
+ const approveGas = await quoteL2Gas4({ ctx, tx: approveTxCandidate });
7154
+ if (approveGas) {
7155
+ approveTxCandidate.gas = approveGas.gasLimit;
7156
+ approveTxCandidate.maxFeePerGas = approveGas.maxFeePerGas;
7157
+ approveTxCandidate.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
7158
+ }
7159
+ const approveSim = await wrapAs7(
6839
7160
  "CONTRACT",
6840
7161
  OP_WITHDRAWALS.erc20.estGas,
6841
7162
  () => ctx.client.l2.simulateContract({
@@ -6844,21 +7165,25 @@ function routeErc20NonBase2() {
6844
7165
  functionName: "approve",
6845
7166
  args: [ctx.l2NativeTokenVault, p.amount],
6846
7167
  account: ctx.client.account,
6847
- ...txFeeOverrides
7168
+ ...approveGas
6848
7169
  }),
6849
7170
  {
6850
7171
  ctx: { where: "l2.simulateContract", to: p.token },
6851
7172
  message: "Failed to simulate L2 ERC-20 approve."
6852
7173
  }
6853
7174
  );
7175
+ const { ...approveRequest } = approveSim.request;
7176
+ const approveTx = {
7177
+ ...approveRequest
7178
+ };
6854
7179
  steps.push({
6855
7180
  key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
6856
7181
  kind: "approve:l2",
6857
7182
  description: `Approve ${p.amount} to NativeTokenVault`,
6858
- tx: { ...approveSim.request, ...txFeeOverrides }
7183
+ tx: approveTx
6859
7184
  });
6860
7185
  }
6861
- const ensure = await wrapAs6(
7186
+ const ensure = await wrapAs7(
6862
7187
  "CONTRACT",
6863
7188
  OP_WITHDRAWALS.erc20.ensureRegistered,
6864
7189
  () => ctx.client.l2.simulateContract({
@@ -6880,20 +7205,37 @@ function routeErc20NonBase2() {
6880
7205
  { type: "address", name: "l1Receiver" },
6881
7206
  { type: "address", name: "l2Token" }
6882
7207
  ],
6883
- [p.amount, toL1, p.token]
7208
+ [p.amount, p.to ?? ctx.sender, p.token]
6884
7209
  );
7210
+ const withdrawCalldata = viem.encodeFunctionData({
7211
+ abi: IL2AssetRouter_default,
7212
+ functionName: "withdraw",
7213
+ args: [assetId, assetData]
7214
+ });
7215
+ const withdrawTxCandidate = {
7216
+ to: ctx.l2AssetRouter,
7217
+ data: withdrawCalldata,
7218
+ value: 0n,
7219
+ from: ctx.sender
7220
+ };
7221
+ const withdrawGas = await quoteL2Gas4({ ctx, tx: withdrawTxCandidate });
7222
+ if (withdrawGas) {
7223
+ withdrawTxCandidate.gas = withdrawGas.gasLimit;
7224
+ withdrawTxCandidate.maxFeePerGas = withdrawGas.maxFeePerGas;
7225
+ withdrawTxCandidate.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
7226
+ }
6885
7227
  let withdrawTx;
6886
- if (needsApprove) {
7228
+ if (current < p.amount) {
6887
7229
  withdrawTx = {
6888
7230
  address: ctx.l2AssetRouter,
6889
7231
  abi: IL2AssetRouter_default,
6890
7232
  functionName: "withdraw",
6891
7233
  args: [assetId, assetData],
6892
7234
  account: ctx.client.account,
6893
- ...txFeeOverrides
7235
+ ...withdrawGas
6894
7236
  };
6895
7237
  } else {
6896
- const sim = await wrapAs6(
7238
+ const sim = await wrapAs7(
6897
7239
  "CONTRACT",
6898
7240
  OP_WITHDRAWALS.erc20.estGas,
6899
7241
  () => ctx.client.l2.simulateContract({
@@ -6902,14 +7244,18 @@ function routeErc20NonBase2() {
6902
7244
  functionName: "withdraw",
6903
7245
  args: [assetId, assetData],
6904
7246
  account: ctx.client.account,
6905
- ...txFeeOverrides
7247
+ ...withdrawGas
6906
7248
  }),
6907
7249
  {
6908
7250
  ctx: { where: "l2.simulateContract", to: ctx.l2AssetRouter },
6909
7251
  message: "Failed to simulate L2 ERC-20 withdraw."
6910
7252
  }
6911
7253
  );
6912
- withdrawTx = { ...sim.request, ...txFeeOverrides };
7254
+ const { ...withdrawRequest } = sim.request;
7255
+ withdrawTx = {
7256
+ ...withdrawRequest,
7257
+ ...withdrawGas
7258
+ };
6913
7259
  }
6914
7260
  steps.push({
6915
7261
  key: "l2-asset-router:withdraw",
@@ -6917,59 +7263,11 @@ function routeErc20NonBase2() {
6917
7263
  description: "Burn on L2 & send L2\u2192L1 message",
6918
7264
  tx: withdrawTx
6919
7265
  });
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: {} };
7266
+ const fees = buildFeeBreakdown2({
7267
+ feeToken: await ctx.client.baseToken(ctx.chainIdL2),
7268
+ l2Gas: withdrawGas
7269
+ });
7270
+ return { steps, approvals, fees };
6973
7271
  }
6974
7272
  };
6975
7273
  }
@@ -7324,10 +7622,8 @@ function createFinalizationServices(client) {
7324
7622
 
7325
7623
  // src/adapters/viem/resources/withdrawals/index.ts
7326
7624
  var ROUTES2 = {
7327
- "eth-base": routeEthBase(),
7625
+ base: routeEthBase(),
7328
7626
  // BaseTokenSystem.withdraw, chain base = ETH
7329
- "eth-nonbase": routeEthNonBase2(),
7330
- // BaseTokenSystem.withdraw, chain base ≠ ETH
7331
7627
  "erc20-nonbase": routeErc20NonBase2()
7332
7628
  // AssetRouter.withdraw for non-base ERC-20s
7333
7629
  };
@@ -7337,27 +7633,19 @@ function createWithdrawalsResource(client) {
7337
7633
  async function buildPlan(p) {
7338
7634
  const ctx = await commonCtx2(p, client);
7339
7635
  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 = {
7636
+ const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
7637
+ return {
7351
7638
  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
- }
7639
+ summary: {
7640
+ route: ctx.route,
7641
+ approvalsNeeded: approvals,
7642
+ amounts: {
7643
+ transfer: { token: p.token, amount: p.amount }
7644
+ },
7645
+ fees
7646
+ },
7647
+ steps
7359
7648
  };
7360
- return { route: ctx.route, summary, steps };
7361
7649
  }
7362
7650
  const finalizeCache = /* @__PURE__ */ new Map();
7363
7651
  const quote = (p) => wrap2(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
@@ -7391,7 +7679,7 @@ function createWithdrawalsResource(client) {
7391
7679
  }
7392
7680
  if (overrides.gasLimit != null) step.tx.gas = overrides.gasLimit;
7393
7681
  }
7394
- if (step.tx.gas == null) {
7682
+ if (!p.l2TxOverrides?.gasLimit) {
7395
7683
  try {
7396
7684
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
7397
7685
  maxFeePerGas: step.tx.maxFeePerGas,
@@ -7743,8 +8031,6 @@ function createViemSdk(client) {
7743
8031
  }
7744
8032
 
7745
8033
  exports.buildDirectRequestStruct = buildDirectRequestStruct;
7746
- exports.buildViemFeeOverrides = buildViemFeeOverrides;
7747
- exports.checkBaseCost = checkBaseCost;
7748
8034
  exports.classifyReadinessFromRevert = classifyReadinessFromRevert;
7749
8035
  exports.createClient = createViemClient;
7750
8036
  exports.createDepositsResource = createDepositsResource;
@@ -7762,11 +8048,7 @@ exports.encodeSecondBridgeArgs = encodeSecondBridgeArgs;
7762
8048
  exports.encodeSecondBridgeDataV1 = encodeSecondBridgeDataV1;
7763
8049
  exports.encodeSecondBridgeErc20Args = encodeSecondBridgeErc20Args;
7764
8050
  exports.encodeSecondBridgeEthArgs = encodeSecondBridgeEthArgs;
7765
- exports.getFeeOverrides = getFeeOverrides;
7766
- exports.getGasPriceWei = getGasPriceWei;
7767
- exports.getL2FeeOverrides = getL2FeeOverrides;
7768
8051
  exports.registerErrorAbi = registerErrorAbi;
7769
- exports.scaleGasLimit = scaleGasLimit;
7770
8052
  exports.toZKsyncError = toZKsyncError;
7771
8053
  //# sourceMappingURL=index.cjs.map
7772
8054
  //# sourceMappingURL=index.cjs.map