@matterlabs/zksync-js 0.0.13 → 0.0.15

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 (96) hide show
  1. package/dist/adapters/ethers/client.cjs +13 -4
  2. package/dist/adapters/ethers/client.cjs.map +1 -1
  3. package/dist/adapters/ethers/client.d.ts +1 -2
  4. package/dist/adapters/ethers/client.js +6 -6
  5. package/dist/adapters/ethers/index.cjs +607 -259
  6. package/dist/adapters/ethers/index.cjs.map +1 -1
  7. package/dist/adapters/ethers/index.js +9 -9
  8. package/dist/adapters/ethers/resources/deposits/routes/priority.d.ts +12 -0
  9. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +4 -0
  10. package/dist/adapters/ethers/resources/interop/index.d.ts +14 -14
  11. package/dist/adapters/ethers/resources/interop/resolvers.d.ts +3 -8
  12. package/dist/adapters/ethers/resources/interop/routes/types.d.ts +2 -1
  13. package/dist/adapters/ethers/resources/interop/services/erc20.d.ts +10 -0
  14. package/dist/adapters/ethers/resources/interop/services/fee.d.ts +12 -0
  15. package/dist/adapters/ethers/resources/interop/services/finalization/index.d.ts +1 -1
  16. package/dist/adapters/ethers/resources/interop/services/finalization/polling.d.ts +1 -1
  17. package/dist/adapters/ethers/resources/interop/services/gas.d.ts +12 -0
  18. package/dist/adapters/ethers/resources/interop/types.d.ts +6 -14
  19. package/dist/adapters/ethers/sdk.cjs +1008 -259
  20. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  21. package/dist/adapters/ethers/sdk.d.ts +6 -1
  22. package/dist/adapters/ethers/sdk.js +7 -7
  23. package/dist/adapters/viem/client.cjs +795 -7
  24. package/dist/adapters/viem/client.cjs.map +1 -1
  25. package/dist/adapters/viem/client.d.ts +6 -1
  26. package/dist/adapters/viem/client.js +6 -6
  27. package/dist/adapters/viem/index.cjs +6490 -2799
  28. package/dist/adapters/viem/index.cjs.map +1 -1
  29. package/dist/adapters/viem/index.d.ts +5 -0
  30. package/dist/adapters/viem/index.js +9 -9
  31. package/dist/adapters/viem/resources/deposits/routes/priority.d.ts +13 -0
  32. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +4 -0
  33. package/dist/adapters/viem/resources/interop/address.d.ts +18 -0
  34. package/dist/adapters/viem/resources/interop/attributes/resource.d.ts +6 -0
  35. package/dist/adapters/viem/resources/interop/context.d.ts +31 -0
  36. package/dist/adapters/viem/resources/interop/index.d.ts +62 -0
  37. package/dist/adapters/viem/resources/interop/resolvers.d.ts +4 -0
  38. package/dist/adapters/viem/resources/interop/routes/direct.d.ts +2 -0
  39. package/dist/adapters/viem/resources/interop/routes/indirect.d.ts +2 -0
  40. package/dist/adapters/viem/resources/interop/routes/types.d.ts +23 -0
  41. package/dist/adapters/viem/resources/interop/services/erc20.d.ts +25 -0
  42. package/dist/adapters/viem/resources/interop/services/fee.d.ts +12 -0
  43. package/dist/adapters/viem/resources/interop/services/finalization/bundle.d.ts +15 -0
  44. package/dist/adapters/viem/resources/interop/services/finalization/data-fetchers.d.ts +17 -0
  45. package/dist/adapters/viem/resources/interop/services/finalization/decoders.d.ts +11 -0
  46. package/dist/adapters/viem/resources/interop/services/finalization/index.d.ts +13 -0
  47. package/dist/adapters/viem/resources/interop/services/finalization/polling.d.ts +7 -0
  48. package/dist/adapters/viem/resources/interop/services/finalization/status.d.ts +5 -0
  49. package/dist/adapters/viem/resources/interop/services/finalization/topics.d.ts +4 -0
  50. package/dist/adapters/viem/resources/interop/services/gas.d.ts +12 -0
  51. package/dist/adapters/viem/resources/interop/services/starter-data.d.ts +6 -0
  52. package/dist/adapters/viem/resources/interop/types.d.ts +8 -0
  53. package/dist/adapters/viem/sdk.cjs +6401 -2758
  54. package/dist/adapters/viem/sdk.cjs.map +1 -1
  55. package/dist/adapters/viem/sdk.d.ts +8 -1
  56. package/dist/adapters/viem/sdk.js +7 -7
  57. package/dist/{chunk-E3KP7XCG.js → chunk-3HHUZXSV.js} +1 -1
  58. package/dist/{chunk-EDWBCPO3.js → chunk-4PZCNTQ3.js} +1387 -71
  59. package/dist/{chunk-UDBRUBEK.js → chunk-65HAYKVL.js} +2 -2
  60. package/dist/chunk-BWKWWLY4.js +9 -0
  61. package/dist/{chunk-JHO2UQ5F.js → chunk-HGB3DOV2.js} +445 -554
  62. package/dist/{chunk-HI64OOAR.js → chunk-HVHMLAYH.js} +1 -1
  63. package/dist/{chunk-2RIARDXZ.js → chunk-JHRYNLZG.js} +65 -7
  64. package/dist/{chunk-RI73VJSH.js → chunk-JXR5V5YK.js} +463 -27
  65. package/dist/chunk-K2UVKMLN.js +658 -0
  66. package/dist/{chunk-53MC5BR2.js → chunk-MDPX5LNW.js} +1 -1
  67. package/dist/{chunk-QQ2OR434.js → chunk-MT4X5FEO.js} +18 -2
  68. package/dist/{chunk-R5WRFPK2.js → chunk-MZBKM3GH.js} +4 -4
  69. package/dist/{chunk-5R7L5NM5.js → chunk-YIWXIP2M.js} +10 -2
  70. package/dist/core/constants.cjs +17 -1
  71. package/dist/core/constants.cjs.map +1 -1
  72. package/dist/core/constants.d.ts +9 -1
  73. package/dist/core/constants.js +1 -1
  74. package/dist/core/index.cjs +52 -24
  75. package/dist/core/index.cjs.map +1 -1
  76. package/dist/core/index.js +5 -5
  77. package/dist/core/internal/abis/IERC7786Attributes.d.ts +21 -11
  78. package/dist/core/internal/abis/IInteropCenter.d.ts +4 -0
  79. package/dist/core/resources/deposits/chains.d.ts +1 -0
  80. package/dist/core/resources/deposits/gas.d.ts +7 -0
  81. package/dist/core/resources/deposits/priority.d.ts +41 -0
  82. package/dist/core/resources/interop/attributes/bundle.d.ts +1 -0
  83. package/dist/core/resources/interop/attributes/resource.d.ts +1 -0
  84. package/dist/core/resources/interop/plan.d.ts +11 -3
  85. package/dist/core/resources/interop/protocol.d.ts +3 -0
  86. package/dist/core/rpc/types.d.ts +1 -0
  87. package/dist/core/rpc/zks.d.ts +5 -1
  88. package/dist/core/types/errors.d.ts +5 -0
  89. package/dist/core/types/flows/interop.d.ts +11 -20
  90. package/dist/core/types/primitives.d.ts +2 -0
  91. package/dist/index.cjs +69 -25
  92. package/dist/index.cjs.map +1 -1
  93. package/dist/index.js +5 -5
  94. package/package.json +1 -1
  95. package/dist/chunk-4S4XDA4N.js +0 -415
  96. package/dist/chunk-5L6EYUJB.js +0 -237
@@ -1,9 +1,9 @@
1
- import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-UDBRUBEK.js';
2
- import { createNTVCodec, toGasOverrides, buildFeeBreakdown, quoteL2Gas, quoteL2BaseCost, quoteL1Gas, quoteL2Gas2 } from './chunk-5L6EYUJB.js';
3
- import { findL1MessageSentLog, messengerLogIndex, isL1MessageSentLog, pickWithdrawRoute } from './chunk-E3KP7XCG.js';
4
- import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, OP_INTEROP, IInteropHandler_default, IERC7786Attributes_default, isBigint, isHash, IInteropCenter_default, sleep, isETH, normalizeAddrEq, isNumber, isAddress, isHash66Array, IInteropRootStorage_default, L2NativeTokenVault_default, assertNever } from './chunk-RI73VJSH.js';
5
- import { ETH_ADDRESS, TOPIC_CANONICAL_ASSIGNED, TOPIC_CANONICAL_SUCCESS, L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_INTEROP_CENTER_ADDRESS, BUNDLE_IDENTIFIER, L2_INTEROP_ROOT_STORAGE_ADDRESS, SAFE_L1_BRIDGE_GAS, FORMAL_ETH_ADDRESS } from './chunk-QQ2OR434.js';
6
- import { Interface, AbiCoder, ethers, getBytes, Contract, NonceManager, JsonRpcProvider, isError, getAddress, concat, hexlify, toBeArray, toBeHex } from 'ethers';
1
+ import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-65HAYKVL.js';
2
+ import { createNTVCodec, resolveCreateDepositL1GasLimit, isInteropFinalizationInfo, DEFAULT_POLL_MS, DEFAULT_TIMEOUT_MS, resolveIdsFromWaitable, parseBundleReceiptInfo, buildFinalizationInfo, parseBundleSentFromReceipt, createAttributesResource, pickInteropRoute, ZERO_HASH, toGasOverrides, applyPriorityL2GasLimitBuffer, buildFeeBreakdown, buildIndirectBundle, preflightIndirect, buildDirectBundle, preflightDirect, derivePriorityTxGasBreakdown, quoteL2Gas, quoteL2BaseCost, quoteL1Gas, derivePriorityBodyGasEstimateCap, quoteL2Gas2, assertProtocolVersion } from './chunk-K2UVKMLN.js';
3
+ import { findL1MessageSentLog, messengerLogIndex, pickWithdrawRoute } from './chunk-3HHUZXSV.js';
4
+ import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, OP_INTEROP, IInteropHandler_default, IERC7786Attributes_default, IInteropCenter_default, sleep, isETH, normalizeAddrEq, IInteropRootStorage_default, IL2AssetRouter_default, L2NativeTokenVault_default, assertNever } from './chunk-JXR5V5YK.js';
5
+ import { ETH_ADDRESS, TOPIC_CANONICAL_ASSIGNED, TOPIC_CANONICAL_SUCCESS, L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, BUFFER, L2_INTEROP_ROOT_STORAGE_ADDRESS, SAFE_L1_BRIDGE_GAS, L2_ASSET_ROUTER_ADDRESS, FORMAL_ETH_ADDRESS } from './chunk-MT4X5FEO.js';
6
+ import { Interface, keccak256, AbiCoder, ethers, getBytes, Contract, NonceManager, JsonRpcProvider, getAddress, concat, hexlify, toBeArray, toBeHex, isError } from 'ethers';
7
7
 
8
8
  var I_BRIDGEHUB = new Interface([
9
9
  "event NewPriorityRequest(uint256 indexed chainId, address indexed sender, bytes32 txHash, uint256 txId, bytes data)"
@@ -279,8 +279,22 @@ async function determineNonBaseL2Gas(input) {
279
279
  try {
280
280
  const l2TokenAddress = input.knownL2Token ?? (ctx.tokens ? await ctx.tokens.toL2Address(l1Token) : await (await ctx.contracts.l2NativeTokenVault()).l2TokenAddress(l1Token));
281
281
  if (l2TokenAddress === ZERO_L2_TOKEN_ADDRESS) {
282
+ if (input.undeployedGasLimit != null) {
283
+ return quoteL2Gas3({
284
+ ctx,
285
+ route,
286
+ overrideGasLimit: input.undeployedGasLimit
287
+ });
288
+ }
282
289
  return fallbackQuote();
283
290
  }
291
+ if (input.priorityFloorGasLimit != null) {
292
+ return quoteL2Gas3({
293
+ ctx,
294
+ route,
295
+ overrideGasLimit: input.priorityFloorGasLimit
296
+ });
297
+ }
284
298
  const modelTx = {
285
299
  to: input.modelTx?.to ?? ctx.sender,
286
300
  from: input.modelTx?.from ?? ctx.sender,
@@ -296,8 +310,7 @@ async function determineNonBaseL2Gas(input) {
296
310
  return fallbackQuote();
297
311
  }
298
312
  return gas;
299
- } catch (err) {
300
- console.warn("Failed to determine non-base deposit L2 gas; defaulting to safe gas limit.", err);
313
+ } catch {
301
314
  return fallbackQuote();
302
315
  }
303
316
  }
@@ -314,45 +327,89 @@ async function determineEthNonBaseL2Gas(input) {
314
327
  route: "eth-nonbase",
315
328
  l1Token: input.ctx.resolvedToken?.l1 ?? FORMAL_ETH_ADDRESS,
316
329
  knownL2Token: input.ctx.resolvedToken?.l2,
317
- modelTx: input.modelTx
330
+ modelTx: input.modelTx,
331
+ priorityFloorGasLimit: input.priorityFloorGasLimit,
332
+ undeployedGasLimit: input.undeployedGasLimit
333
+ });
334
+ }
335
+ var EMPTY_BYTES = "0x";
336
+ var ZERO_RESERVED_WORDS = [0n, 0n, 0n, 0n];
337
+ var L2_CANONICAL_TRANSACTION_TUPLE = "tuple(uint256 txType,uint256 from,uint256 to,uint256 gasLimit,uint256 gasPerPubdataByteLimit,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 paymaster,uint256 nonce,uint256 value,uint256[4] reserved,bytes data,bytes signature,uint256[] factoryDeps,bytes paymasterInput,bytes reservedDynamic)";
338
+ function hexByteLength(hex) {
339
+ return BigInt(Math.max(hex.length - 2, 0) / 2);
340
+ }
341
+ function getPriorityTxEncodedLength(input) {
342
+ const encoded = AbiCoder.defaultAbiCoder().encode(
343
+ [L2_CANONICAL_TRANSACTION_TUPLE],
344
+ [
345
+ [
346
+ 0n,
347
+ BigInt(input.sender),
348
+ BigInt(input.l2Contract),
349
+ 0n,
350
+ input.gasPerPubdata,
351
+ 0n,
352
+ 0n,
353
+ 0n,
354
+ 0n,
355
+ input.l2Value,
356
+ ZERO_RESERVED_WORDS,
357
+ input.l2Calldata,
358
+ EMPTY_BYTES,
359
+ input.factoryDepsHashes ?? [],
360
+ EMPTY_BYTES,
361
+ EMPTY_BYTES
362
+ ]
363
+ ]
364
+ );
365
+ return hexByteLength(encoded);
366
+ }
367
+ function getPriorityTxGasBreakdown(input) {
368
+ return derivePriorityTxGasBreakdown({
369
+ encodedLength: getPriorityTxEncodedLength(input),
370
+ gasPerPubdata: input.gasPerPubdata,
371
+ factoryDepsCount: BigInt(input.factoryDepsHashes?.length ?? 0)
318
372
  });
319
373
  }
320
374
 
321
375
  // src/adapters/ethers/resources/deposits/routes/eth.ts
376
+ var EMPTY_BYTES2 = "0x";
322
377
  function routeEthDirect() {
323
378
  return {
324
379
  async build(p, ctx) {
325
380
  const bh = await ctx.contracts.bridgehub();
326
- const l2TxModel = {
327
- to: p.to ?? ctx.sender,
328
- from: ctx.sender,
329
- data: "0x",
330
- value: 0n
331
- };
381
+ const l2Contract = p.to ?? ctx.sender;
382
+ const l2Value = p.amount;
383
+ const l2Calldata = EMPTY_BYTES2;
384
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
385
+ sender: ctx.sender,
386
+ l2Contract,
387
+ l2Value,
388
+ l2Calldata,
389
+ gasPerPubdata: ctx.gasPerPubdata
390
+ });
391
+ const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
392
+ chainIdL2: ctx.chainIdL2,
393
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
394
+ });
332
395
  const l2GasParams = await quoteL2Gas3({
333
396
  ctx,
334
397
  route: "eth-base",
335
- l2TxForModeling: l2TxModel,
336
- overrideGasLimit: ctx.l2GasLimit,
337
- stateOverrides: {
338
- [ctx.sender]: {
339
- balance: "0xffffffffffffffffffff"
340
- }
341
- }
398
+ overrideGasLimit: quotedL2GasLimit
342
399
  });
343
400
  if (!l2GasParams) {
344
401
  throw new Error("Failed to estimate L2 gas for deposit.");
345
402
  }
346
403
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
347
- const mintValue = baseCost + ctx.operatorTip + p.amount;
404
+ const mintValue = baseCost + ctx.operatorTip + l2Value;
348
405
  const req = buildDirectRequestStruct({
349
406
  chainId: ctx.chainIdL2,
350
407
  mintValue,
351
408
  l2GasLimit: l2GasParams.gasLimit,
352
409
  gasPerPubdata: ctx.gasPerPubdata,
353
410
  refundRecipient: ctx.refundRecipient,
354
- l2Contract: p.to ?? ctx.sender,
355
- l2Value: p.amount
411
+ l2Contract,
412
+ l2Value
356
413
  });
357
414
  const data = bh.interface.encodeFunctionData("requestL2TransactionDirect", [req]);
358
415
  const l1TxCandidate = {
@@ -396,12 +453,57 @@ function routeEthDirect() {
396
453
  }
397
454
  };
398
455
  }
399
-
400
- // src/core/types/primitives.ts
401
- var ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
402
-
403
- // src/adapters/ethers/resources/deposits/routes/erc20-nonbase.ts
404
456
  var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
457
+ var ZERO_L2_TOKEN_ADDRESS2 = "0x0000000000000000000000000000000000000000";
458
+ var ZERO_ASSET_ID = "0x0000000000000000000000000000000000000000000000000000000000000000";
459
+ async function getPriorityGasModel(input) {
460
+ try {
461
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
462
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
463
+ const { chainId: l1ChainId } = await input.ctx.client.l1.getNetwork();
464
+ const isFirstBridge = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID || input.ctx.resolvedToken.originChainId === 0n;
465
+ const erc20MetadataOriginChainId = isFirstBridge ? BigInt(l1ChainId) : input.ctx.resolvedToken.originChainId;
466
+ const erc20Metadata = await l1NativeTokenVault.getERC20Getters(
467
+ input.token,
468
+ erc20MetadataOriginChainId
469
+ );
470
+ const bridgeMintCalldata = AbiCoder.defaultAbiCoder().encode(
471
+ ["address", "address", "address", "uint256", "bytes"],
472
+ [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
473
+ );
474
+ const l2Calldata = isFirstBridge ? new Interface(IL2AssetRouter_default).encodeFunctionData(
475
+ "finalizeDeposit(address,address,address,uint256,bytes)",
476
+ [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
477
+ ) : await (() => {
478
+ return l1AssetRouter.getDepositCalldata(
479
+ input.ctx.sender,
480
+ input.ctx.resolvedToken.assetId,
481
+ bridgeMintCalldata
482
+ );
483
+ })();
484
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
485
+ sender: input.ctx.l1AssetRouter,
486
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
487
+ l2Value: 0n,
488
+ l2Calldata,
489
+ gasPerPubdata: input.ctx.gasPerPubdata
490
+ });
491
+ const model = {
492
+ priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
493
+ chainIdL2: input.ctx.chainIdL2,
494
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
495
+ })
496
+ };
497
+ if (isFirstBridge || input.ctx.resolvedToken.l2.toLowerCase() === ZERO_L2_TOKEN_ADDRESS2) {
498
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
499
+ minBodyGas: priorityFloorBreakdown.minBodyGas
500
+ }) + priorityFloorBreakdown.overhead;
501
+ }
502
+ return model;
503
+ } catch {
504
+ return {};
505
+ }
506
+ }
405
507
  function routeErc20NonBase() {
406
508
  return {
407
509
  async preflight(p, ctx) {
@@ -422,11 +524,29 @@ function routeErc20NonBase() {
422
524
  const l1Signer = ctx.client.getL1Signer();
423
525
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
424
526
  const baseIsEth = ctx.baseIsEth ?? isETH(baseToken);
527
+ const receiver = p.to ?? ctx.sender;
528
+ const secondBridgeCalldata = await wrapAs2(
529
+ "INTERNAL",
530
+ OP_DEPOSITS.nonbase.encodeCalldata,
531
+ () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, receiver)),
532
+ {
533
+ ctx: { where: "encodeSecondBridgeErc20Args" },
534
+ message: "Failed to encode bridging calldata."
535
+ }
536
+ );
537
+ const priorityGasModel = await getPriorityGasModel({
538
+ ctx,
539
+ token: p.token,
540
+ amount: p.amount,
541
+ receiver
542
+ });
425
543
  const l2GasParams = await determineErc20L2Gas({
426
544
  ctx,
427
545
  l1Token: p.token,
546
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
547
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit,
428
548
  modelTx: {
429
- to: p.to ?? ctx.sender,
549
+ to: receiver,
430
550
  from: ctx.sender,
431
551
  data: "0x",
432
552
  value: 0n
@@ -488,15 +608,6 @@ function routeErc20NonBase() {
488
608
  });
489
609
  }
490
610
  }
491
- const secondBridgeCalldata = await wrapAs2(
492
- "INTERNAL",
493
- OP_DEPOSITS.nonbase.encodeCalldata,
494
- () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
495
- {
496
- ctx: { where: "encodeSecondBridgeErc20Args" },
497
- message: "Failed to encode bridging calldata."
498
- }
499
- );
500
611
  const requestStruct = {
501
612
  chainId: ctx.chainIdL2,
502
613
  mintValue,
@@ -554,6 +665,54 @@ function routeErc20NonBase() {
554
665
  };
555
666
  }
556
667
  var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
668
+ var ZERO_L2_TOKEN_ADDRESS3 = "0x0000000000000000000000000000000000000000";
669
+ var ZERO_ASSET_ID2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
670
+ var ntvCodec = createNTVCodec({
671
+ encode: (types, values) => AbiCoder.defaultAbiCoder().encode(types, values),
672
+ keccak256: (data) => keccak256(data)
673
+ });
674
+ async function getPriorityGasModel2(input) {
675
+ try {
676
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
677
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
678
+ const originChainId = input.ctx.resolvedToken.originChainId !== 0n ? input.ctx.resolvedToken.originChainId : BigInt((await input.ctx.client.l1.getNetwork()).chainId);
679
+ const resolvedAssetId = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID2 ? ntvCodec.encodeAssetId(originChainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, ETH_ADDRESS) : input.ctx.resolvedToken.assetId;
680
+ const erc20Metadata = await l1NativeTokenVault.getERC20Getters(
681
+ ETH_ADDRESS,
682
+ originChainId
683
+ );
684
+ const bridgeMintCalldata = AbiCoder.defaultAbiCoder().encode(
685
+ ["address", "address", "address", "uint256", "bytes"],
686
+ [input.ctx.sender, input.receiver, ETH_ADDRESS, input.amount, erc20Metadata]
687
+ );
688
+ const l2Calldata = await l1AssetRouter.getDepositCalldata(
689
+ input.ctx.sender,
690
+ resolvedAssetId,
691
+ bridgeMintCalldata
692
+ );
693
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
694
+ sender: input.ctx.l1AssetRouter,
695
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
696
+ l2Value: 0n,
697
+ l2Calldata,
698
+ gasPerPubdata: input.ctx.gasPerPubdata
699
+ });
700
+ const model = {
701
+ priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
702
+ chainIdL2: input.ctx.chainIdL2,
703
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
704
+ })
705
+ };
706
+ if (input.ctx.resolvedToken.l2.toLowerCase() === ZERO_L2_TOKEN_ADDRESS3) {
707
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
708
+ minBodyGas: priorityFloorBreakdown.minBodyGas
709
+ }) + priorityFloorBreakdown.overhead;
710
+ }
711
+ return model;
712
+ } catch {
713
+ return {};
714
+ }
715
+ }
557
716
  function routeEthNonBase() {
558
717
  return {
559
718
  async preflight(p, ctx) {
@@ -602,15 +761,23 @@ function routeEthNonBase() {
602
761
  async build(p, ctx) {
603
762
  const l1Signer = ctx.client.getL1Signer();
604
763
  const baseToken = ctx.baseTokenL1;
764
+ const receiver = p.to ?? ctx.sender;
765
+ const priorityGasModel = await getPriorityGasModel2({
766
+ ctx,
767
+ amount: p.amount,
768
+ receiver
769
+ });
605
770
  const l2TxModel = {
606
- to: p.to ?? ctx.sender,
771
+ to: receiver,
607
772
  from: ctx.sender,
608
773
  data: "0x",
609
774
  value: 0n
610
775
  };
611
776
  const l2GasParams = await determineEthNonBaseL2Gas({
612
777
  ctx,
613
- modelTx: l2TxModel
778
+ modelTx: l2TxModel,
779
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
780
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit
614
781
  });
615
782
  if (!l2GasParams) throw new Error("Failed to estimate L2 gas parameters.");
616
783
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
@@ -644,12 +811,12 @@ function routeEthNonBase() {
644
811
  const secondBridgeCalldata = await wrapAs3(
645
812
  "INTERNAL",
646
813
  OP_DEPOSITS.ethNonBase.encodeCalldata,
647
- () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
814
+ () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, receiver)),
648
815
  {
649
816
  ctx: {
650
817
  where: "encodeSecondBridgeEthArgs",
651
818
  amount: p.amount.toString(),
652
- to: p.to ?? ctx.sender
819
+ to: receiver
653
820
  }
654
821
  }
655
822
  );
@@ -710,6 +877,7 @@ function routeEthNonBase() {
710
877
  };
711
878
  }
712
879
  var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
880
+ var EMPTY_BYTES3 = "0x";
713
881
  function routeErc20Base() {
714
882
  return {
715
883
  async preflight(p, ctx) {
@@ -740,17 +908,24 @@ function routeErc20Base() {
740
908
  async build(p, ctx) {
741
909
  const l1Signer = ctx.client.getL1Signer();
742
910
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
743
- const l2TxModel = {
744
- to: p.to ?? ctx.sender,
745
- from: ctx.sender,
746
- data: "0x",
747
- value: 0n
748
- };
911
+ const l2Contract = p.to ?? ctx.sender;
912
+ const l2Value = p.amount;
913
+ const l2Calldata = EMPTY_BYTES3;
914
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
915
+ sender: ctx.sender,
916
+ l2Contract,
917
+ l2Value,
918
+ l2Calldata,
919
+ gasPerPubdata: ctx.gasPerPubdata
920
+ });
921
+ const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
922
+ chainIdL2: ctx.chainIdL2,
923
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
924
+ });
749
925
  const l2GasParams = await quoteL2Gas3({
750
926
  ctx,
751
927
  route: "erc20-base",
752
- l2TxForModeling: l2TxModel,
753
- overrideGasLimit: ctx.l2GasLimit
928
+ overrideGasLimit: quotedL2GasLimit
754
929
  });
755
930
  if (!l2GasParams) throw new Error("Failed to estimate L2 gas parameters.");
756
931
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
@@ -789,8 +964,8 @@ function routeErc20Base() {
789
964
  l2GasLimit: l2GasParams.gasLimit,
790
965
  gasPerPubdata: ctx.gasPerPubdata,
791
966
  refundRecipient: ctx.refundRecipient,
792
- l2Contract: p.to ?? ctx.sender,
793
- l2Value: p.amount
967
+ l2Contract,
968
+ l2Value
794
969
  });
795
970
  const bridgehub = await ctx.contracts.bridgehub();
796
971
  const data = bridgehub.interface.encodeFunctionData("requestL2TransactionDirect", [
@@ -839,7 +1014,7 @@ function routeErc20Base() {
839
1014
  }
840
1015
  var { wrapAs: wrapAs5 } = createErrorHandlers("tokens");
841
1016
  var abi = AbiCoder.defaultAbiCoder();
842
- var ntvCodec = createNTVCodec({
1017
+ var ntvCodec2 = createNTVCodec({
843
1018
  encode: (types, values) => abi.encode(types, values),
844
1019
  keccak256: (data) => ethers.keccak256(data)
845
1020
  });
@@ -958,7 +1133,7 @@ function createTokensResource(client) {
958
1133
  return wrapAs5("CONTRACT", "tokens.isChainEthBased", async () => {
959
1134
  const baseAssetId = await getBaseTokenAssetId();
960
1135
  const l1ChainId = await getL1ChainId();
961
- const ethAssetId = ntvCodec.encodeAssetId(
1136
+ const ethAssetId = ntvCodec2.encodeAssetId(
962
1137
  l1ChainId,
963
1138
  L2_NATIVE_TOKEN_VAULT_ADDRESS,
964
1139
  ETH_ADDRESS
@@ -1172,6 +1347,8 @@ function createDepositsResource(client, tokens, contracts) {
1172
1347
  async () => {
1173
1348
  const plan = await prepare(p);
1174
1349
  const stepHashes = {};
1350
+ const { chainId } = await client.l2.getNetwork();
1351
+ const chainIdL2 = BigInt(chainId);
1175
1352
  const managed = new NonceManager(client.signer);
1176
1353
  const from = await managed.getAddress();
1177
1354
  let next;
@@ -1217,8 +1394,14 @@ function createDepositsResource(client, tokens, contracts) {
1217
1394
  }
1218
1395
  if (!p.l1TxOverrides?.gasLimit) {
1219
1396
  try {
1397
+ const preparedGasLimit = step.tx.gasLimit != null ? BigInt(step.tx.gasLimit.toString()) : void 0;
1220
1398
  const est = await client.l1.estimateGas(step.tx);
1221
- step.tx.gasLimit = BigInt(est) * 115n / 100n;
1399
+ step.tx.gasLimit = resolveCreateDepositL1GasLimit({
1400
+ chainIdL2,
1401
+ stepKey: step.key,
1402
+ preparedGasLimit,
1403
+ estimatedGasLimit: BigInt(est)
1404
+ });
1222
1405
  } catch {
1223
1406
  }
1224
1407
  }
@@ -1853,7 +2036,7 @@ var ROUTES2 = {
1853
2036
  };
1854
2037
  function createWithdrawalsResource(client, tokens, contracts) {
1855
2038
  const svc = createFinalizationServices(client);
1856
- const { wrap: wrap6, toResult: toResult3 } = createErrorHandlers("withdrawals");
2039
+ const { wrap: wrap7, toResult: toResult3 } = createErrorHandlers("withdrawals");
1857
2040
  const tokensResource = tokens ?? createTokensResource(client);
1858
2041
  const contractsResource = contracts ?? createContractsResource(client);
1859
2042
  async function buildPlan(p) {
@@ -1874,7 +2057,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1874
2057
  };
1875
2058
  }
1876
2059
  const finalizeCache = /* @__PURE__ */ new Map();
1877
- const quote = (p) => wrap6(
2060
+ const quote = (p) => wrap7(
1878
2061
  OP_WITHDRAWALS.quote,
1879
2062
  async () => {
1880
2063
  const plan = await buildPlan(p);
@@ -1896,7 +2079,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1896
2079
  ctx: { token: p.token, where: "withdrawals.tryQuote" }
1897
2080
  }
1898
2081
  );
1899
- const prepare = (p) => wrap6(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
2082
+ const prepare = (p) => wrap7(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
1900
2083
  message: "Internal error while preparing a withdrawal plan.",
1901
2084
  ctx: { token: p.token, where: "withdrawals.prepare" }
1902
2085
  });
@@ -1904,7 +2087,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1904
2087
  message: "Internal error while preparing a withdrawal plan.",
1905
2088
  ctx: { token: p.token, where: "withdrawals.tryPrepare" }
1906
2089
  });
1907
- const create = (p) => wrap6(
2090
+ const create = (p) => wrap7(
1908
2091
  OP_WITHDRAWALS.create,
1909
2092
  async () => {
1910
2093
  const plan = await prepare(p);
@@ -1975,7 +2158,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1975
2158
  message: "Internal error while creating withdrawal transactions.",
1976
2159
  ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
1977
2160
  });
1978
- const status = (h) => wrap6(
2161
+ const status = (h) => wrap7(
1979
2162
  OP_WITHDRAWALS.status,
1980
2163
  async () => {
1981
2164
  const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2030,7 +2213,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2030
2213
  const wait = (h, opts = {
2031
2214
  for: "l2",
2032
2215
  pollMs: 5500
2033
- }) => wrap6(
2216
+ }) => wrap7(
2034
2217
  OP_WITHDRAWALS.wait,
2035
2218
  async () => {
2036
2219
  const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2095,7 +2278,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2095
2278
  }
2096
2279
  }
2097
2280
  );
2098
- const finalize = (l2TxHash) => wrap6(
2281
+ const finalize = (l2TxHash) => wrap7(
2099
2282
  OP_WITHDRAWALS.finalize.send,
2100
2283
  async () => {
2101
2284
  const pack = await (async () => {
@@ -2206,36 +2389,6 @@ function createWithdrawalsResource(client, tokens, contracts) {
2206
2389
  tryWait
2207
2390
  };
2208
2391
  }
2209
-
2210
- // src/core/resources/interop/attributes/call.ts
2211
- function createCallAttributes(codec) {
2212
- const indirectCall = (messageValue) => codec.encode("indirectCall", [messageValue]);
2213
- const interopCallValue = (bridgedAmount) => codec.encode("interopCallValue", [bridgedAmount]);
2214
- return {
2215
- indirectCall,
2216
- interopCallValue
2217
- };
2218
- }
2219
-
2220
- // src/core/resources/interop/attributes/bundle.ts
2221
- function createBundleAttributes(codec) {
2222
- const executionAddress = (executor) => codec.encode("executionAddress", [executor]);
2223
- const unbundlerAddress = (addr) => codec.encode("unbundlerAddress", [addr]);
2224
- return {
2225
- executionAddress,
2226
- unbundlerAddress
2227
- };
2228
- }
2229
-
2230
- // src/core/resources/interop/attributes/resource.ts
2231
- function createAttributesResource(codec) {
2232
- return {
2233
- call: createCallAttributes(codec),
2234
- bundle: createBundleAttributes(codec)
2235
- };
2236
- }
2237
-
2238
- // src/adapters/ethers/resources/interop/attributes/resource.ts
2239
2392
  function getInteropAttributes(params, ctx) {
2240
2393
  const bundleAttributes = [];
2241
2394
  if (params.execution?.only) {
@@ -2244,6 +2397,7 @@ function getInteropAttributes(params, ctx) {
2244
2397
  if (params.unbundling?.by) {
2245
2398
  bundleAttributes.push(ctx.attributes.bundle.unbundlerAddress(params.unbundling.by));
2246
2399
  }
2400
+ bundleAttributes.push(ctx.attributes.bundle.useFixedFee(params.fee?.useFixed ?? false));
2247
2401
  const callAttributes = params.actions.map((action) => {
2248
2402
  switch (action.type) {
2249
2403
  case "sendNative": {
@@ -2271,185 +2425,6 @@ function createEthersAttributesResource(opts = {}) {
2271
2425
  const encode2 = (fn, args) => iface.encodeFunctionData(fn, args);
2272
2426
  return createAttributesResource({ encode: encode2 });
2273
2427
  }
2274
-
2275
- // src/core/types/flows/interop.ts
2276
- function isInteropExpectedRoot(obj) {
2277
- if (typeof obj !== "object" || obj === null) return false;
2278
- const root = obj;
2279
- return isBigint(root.rootChainId) && isBigint(root.batchNumber) && isHash(root.expectedRoot);
2280
- }
2281
- function isInteropMessageProof(obj) {
2282
- if (typeof obj !== "object" || obj === null) return false;
2283
- const proof = obj;
2284
- return isBigint(proof.chainId) && isBigint(proof.l1BatchNumber) && isBigint(proof.l2MessageIndex) && typeof proof.message === "object" && proof.message !== null && isNumber(proof.message.txNumberInBatch) && isAddress(proof.message.sender) && isHash(proof.message.data) && isHash66Array(proof.proof);
2285
- }
2286
- function isInteropFinalizationInfo(obj) {
2287
- if (typeof obj !== "object" || obj === null) return false;
2288
- const info = obj;
2289
- return isHash66(info.l2SrcTxHash) && isHash66(info.bundleHash) && isBigint(info.dstChainId) && isHash(info.encodedData) && isInteropExpectedRoot(info.expectedRoot) && isInteropMessageProof(info.proof);
2290
- }
2291
-
2292
- // src/core/resources/interop/route.ts
2293
- function sumActionMsgValue(actions) {
2294
- let sum = 0n;
2295
- for (const a of actions) {
2296
- if (a.type === "sendNative") sum += a.amount;
2297
- else if (a.type === "call" && a.value) sum += a.value;
2298
- }
2299
- return sum;
2300
- }
2301
- function sumErc20Amounts(actions) {
2302
- let sum = 0n;
2303
- for (const a of actions) if (a.type === "sendErc20") sum += a.amount;
2304
- return sum;
2305
- }
2306
- function pickInteropRoute(args) {
2307
- const hasErc20 = args.actions.some((a) => a.type === "sendErc20");
2308
- const baseMatches = args.ctx.baseTokenSrc.toLowerCase() === args.ctx.baseTokenDst.toLowerCase();
2309
- if (hasErc20) return "indirect";
2310
- if (!baseMatches) return "indirect";
2311
- return "direct";
2312
- }
2313
-
2314
- // src/core/resources/interop/plan.ts
2315
- function preflightDirect(params, ctx) {
2316
- if (!params.actions?.length) {
2317
- throw new Error('route "direct" requires at least one action.');
2318
- }
2319
- const baseMatch = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
2320
- if (!baseMatch) {
2321
- throw new Error('route "direct" requires matching base tokens between source and destination.');
2322
- }
2323
- for (const action of params.actions) {
2324
- switch (action.type) {
2325
- case "sendNative":
2326
- if (action.amount < 0n) {
2327
- throw new Error("sendNative.amount must be >= 0.");
2328
- }
2329
- break;
2330
- case "call":
2331
- if (action.value != null && action.value < 0n) {
2332
- throw new Error("call.value must be >= 0 when provided.");
2333
- }
2334
- break;
2335
- default:
2336
- throw new Error(
2337
- `route "direct" does not support ${action.type} actions; use the indirect route.`
2338
- );
2339
- }
2340
- }
2341
- }
2342
- function buildDirectBundle(params, ctx, attrs) {
2343
- const totalActionValue = sumActionMsgValue(params.actions);
2344
- const starters = params.actions.map((action, index) => {
2345
- const to = ctx.codec.formatAddress(action.to);
2346
- const callAttributes = attrs.callAttributes[index] ?? [];
2347
- switch (action.type) {
2348
- case "sendNative":
2349
- return [to, "0x", callAttributes];
2350
- case "call":
2351
- return [to, action.data ?? "0x", callAttributes];
2352
- default:
2353
- throw new Error(`buildDirectBundle: unsupported action type "${action.type}".`);
2354
- }
2355
- });
2356
- return {
2357
- dstChain: ctx.codec.formatChain(ctx.dstChainId),
2358
- starters,
2359
- bundleAttributes: attrs.bundleAttributes,
2360
- approvals: [],
2361
- quoteExtras: {
2362
- totalActionValue,
2363
- bridgedTokenTotal: 0n
2364
- }
2365
- };
2366
- }
2367
- function preflightIndirect(params, ctx) {
2368
- if (!params.actions?.length) {
2369
- throw new Error('route "indirect" requires at least one action.');
2370
- }
2371
- const hasErc20 = params.actions.some((a) => a.type === "sendErc20");
2372
- const baseMatches = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
2373
- if (!hasErc20 && baseMatches) {
2374
- throw new Error(
2375
- 'route "indirect" requires ERC-20 actions or mismatched base tokens; use the direct route instead.'
2376
- );
2377
- }
2378
- for (const action of params.actions) {
2379
- switch (action.type) {
2380
- case "sendNative":
2381
- if (action.amount < 0n) {
2382
- throw new Error("sendNative.amount must be >= 0.");
2383
- }
2384
- break;
2385
- case "sendErc20":
2386
- if (action.amount < 0n) {
2387
- throw new Error("sendErc20.amount must be >= 0.");
2388
- }
2389
- break;
2390
- case "call":
2391
- if (action.value != null) {
2392
- if (action.value < 0n) {
2393
- throw new Error("call.value must be >= 0 when provided.");
2394
- }
2395
- if (action.value > 0n && !baseMatches) {
2396
- throw new Error("indirect route does not support call.value when base tokens differ.");
2397
- }
2398
- }
2399
- break;
2400
- default:
2401
- assertNever(action);
2402
- }
2403
- }
2404
- }
2405
- function buildIndirectBundle(params, ctx, attrs, starterData) {
2406
- const totalActionValue = sumActionMsgValue(params.actions);
2407
- const bridgedTokenTotal = sumErc20Amounts(params.actions);
2408
- const approvalMap = /* @__PURE__ */ new Map();
2409
- for (const action of params.actions) {
2410
- if (action.type !== "sendErc20") continue;
2411
- const key = action.token.toLowerCase();
2412
- const existing = approvalMap.get(key);
2413
- if (existing) {
2414
- existing.amount += action.amount;
2415
- } else {
2416
- approvalMap.set(key, {
2417
- token: action.token,
2418
- spender: ctx.l2NativeTokenVault,
2419
- amount: action.amount
2420
- });
2421
- }
2422
- }
2423
- const approvals = Array.from(approvalMap.values());
2424
- const starters = params.actions.map((action, index) => {
2425
- const callAttributes = attrs.callAttributes[index] ?? [];
2426
- if (starterData[index]?.assetRouterPayload) {
2427
- const l2AssetRouter = ctx.codec.formatAddress(ctx.l2AssetRouter);
2428
- return [l2AssetRouter, starterData[index].assetRouterPayload, callAttributes];
2429
- }
2430
- const directTo = ctx.codec.formatAddress(action.to);
2431
- switch (action.type) {
2432
- case "sendNative":
2433
- return [directTo, "0x", callAttributes];
2434
- case "call":
2435
- return [directTo, action.data ?? "0x", callAttributes];
2436
- case "sendErc20":
2437
- throw new Error("buildIndirectBundle: missing assetRouterPayload for sendErc20 action.");
2438
- default:
2439
- return assertNever(action);
2440
- }
2441
- });
2442
- return {
2443
- dstChain: ctx.codec.formatChain(ctx.dstChainId),
2444
- starters,
2445
- bundleAttributes: attrs.bundleAttributes,
2446
- approvals,
2447
- quoteExtras: {
2448
- totalActionValue,
2449
- bridgedTokenTotal
2450
- }
2451
- };
2452
- }
2453
2428
  var PREFIX_EVM_CHAIN = getBytes("0x00010000");
2454
2429
  var PREFIX_EVM_ADDRESS = getBytes("0x000100000014");
2455
2430
  function formatInteropEvmChain(chainId) {
@@ -2490,6 +2465,29 @@ function buildEnsureTokenSteps(erc20Tokens, ctx) {
2490
2465
  }
2491
2466
  }));
2492
2467
  }
2468
+ async function buildApproveSteps(approvals, ctx) {
2469
+ const steps = [];
2470
+ for (const approval of approvals) {
2471
+ const erc20 = new Contract(approval.token, IERC20_default, ctx.client.l2);
2472
+ const currentAllowance = await erc20.allowance(ctx.sender, approval.spender);
2473
+ if (currentAllowance < approval.amount) {
2474
+ steps.push({
2475
+ key: `approve:${approval.token}:${approval.spender}`,
2476
+ kind: "approve",
2477
+ description: `Approve ${approval.spender} to spend ${approval.amount} of ${approval.token}`,
2478
+ tx: {
2479
+ to: approval.token,
2480
+ data: erc20.interface.encodeFunctionData("approve", [
2481
+ approval.spender,
2482
+ approval.amount
2483
+ ]),
2484
+ ...ctx.gasOverrides
2485
+ }
2486
+ });
2487
+ }
2488
+ }
2489
+ return steps;
2490
+ }
2493
2491
  async function resolveErc20AssetIds(erc20Tokens, ctx) {
2494
2492
  const assetIds = /* @__PURE__ */ new Map();
2495
2493
  if (erc20Tokens.length === 0) return assetIds;
@@ -2543,6 +2541,44 @@ async function getStarterData(params, ctx, erc20AssetIds) {
2543
2541
  }
2544
2542
  return starterData;
2545
2543
  }
2544
+ var { wrap: wrap2 } = createErrorHandlers("interop");
2545
+ async function buildFeeInfo(params, ctx, numStarters) {
2546
+ const useFixed = params.fee?.useFixed ?? false;
2547
+ const interopCenter = new Contract(ctx.interopCenter, IInteropCenter_default, ctx.client.l2);
2548
+ if (useFixed) {
2549
+ const zkFeePerCall = await wrap2(
2550
+ OP_INTEROP.svc.fees.zkInteropFee,
2551
+ () => interopCenter.ZK_INTEROP_FEE(),
2552
+ { message: "Failed to fetch ZK interop fee from InteropCenter." }
2553
+ );
2554
+ const zkFeeTotal = zkFeePerCall * BigInt(numStarters);
2555
+ const zkTokenAddress = await wrap2(
2556
+ OP_INTEROP.svc.fees.zkToken,
2557
+ () => interopCenter.zkToken(),
2558
+ { message: "Failed to fetch ZK token address from InteropCenter." }
2559
+ );
2560
+ const approval = {
2561
+ token: zkTokenAddress,
2562
+ spender: ctx.interopCenter,
2563
+ amount: zkFeeTotal
2564
+ };
2565
+ return {
2566
+ approval,
2567
+ fee: { token: zkTokenAddress, amount: zkFeeTotal }
2568
+ };
2569
+ } else {
2570
+ const protocolFeePerCall = await wrap2(
2571
+ OP_INTEROP.svc.fees.protocolFee,
2572
+ () => interopCenter.interopProtocolFee(),
2573
+ { message: "Failed to fetch interop protocol fee from InteropCenter." }
2574
+ );
2575
+ const totalFee = protocolFeePerCall * BigInt(numStarters);
2576
+ return {
2577
+ approval: null,
2578
+ fee: { token: ctx.baseTokens.src, amount: totalFee }
2579
+ };
2580
+ }
2581
+ }
2546
2582
 
2547
2583
  // src/adapters/ethers/resources/interop/routes/indirect.ts
2548
2584
  function routeIndirect() {
@@ -2553,12 +2589,17 @@ function routeIndirect() {
2553
2589
  dstChainId: ctx.dstChainId,
2554
2590
  baseTokens: ctx.baseTokens,
2555
2591
  l2AssetRouter: ctx.l2AssetRouter,
2556
- l2NativeTokenVault: ctx.l2NativeTokenVault});
2592
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
2593
+ codec: interopCodec
2594
+ });
2557
2595
  },
2558
2596
  async build(params, ctx) {
2559
2597
  const steps = [];
2560
2598
  const erc20Tokens = getErc20Tokens(params);
2561
- const erc20AssetIds = await resolveErc20AssetIds(erc20Tokens, ctx);
2599
+ const [erc20AssetIds, feeInfo] = await Promise.all([
2600
+ resolveErc20AssetIds(erc20Tokens, ctx),
2601
+ buildFeeInfo(params, ctx, params.actions.length)
2602
+ ]);
2562
2603
  const attributes = getInteropAttributes(params, ctx);
2563
2604
  const starterData = await getStarterData(params, ctx, erc20AssetIds);
2564
2605
  const bundle = buildIndirectBundle(
@@ -2571,32 +2612,11 @@ function routeIndirect() {
2571
2612
  codec: interopCodec
2572
2613
  },
2573
2614
  attributes,
2574
- starterData
2615
+ starterData,
2616
+ feeInfo
2575
2617
  );
2576
2618
  steps.push(...buildEnsureTokenSteps(erc20Tokens, ctx));
2577
- for (const approval of bundle.approvals) {
2578
- const erc20 = new Contract(approval.token, IERC20_default, ctx.client.l2);
2579
- const currentAllowance = await erc20.allowance(
2580
- ctx.sender,
2581
- ctx.l2NativeTokenVault
2582
- );
2583
- if (currentAllowance < approval.amount) {
2584
- const approveData = erc20.interface.encodeFunctionData("approve", [
2585
- ctx.l2NativeTokenVault,
2586
- approval.amount
2587
- ]);
2588
- steps.push({
2589
- key: `approve:${approval.token}:${ctx.l2NativeTokenVault}`,
2590
- kind: "approve",
2591
- description: `Approve ${ctx.l2NativeTokenVault} to spend ${approval.amount} of ${approval.token}`,
2592
- tx: {
2593
- to: approval.token,
2594
- data: approveData,
2595
- ...ctx.gasOverrides
2596
- }
2597
- });
2598
- }
2599
- }
2619
+ steps.push(...await buildApproveSteps(bundle.approvals, ctx));
2600
2620
  const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
2601
2621
  bundle.dstChain,
2602
2622
  bundle.starters,
@@ -2609,14 +2629,15 @@ function routeIndirect() {
2609
2629
  tx: {
2610
2630
  to: ctx.interopCenter,
2611
2631
  data,
2612
- value: bundle.quoteExtras.totalActionValue,
2632
+ value: bundle.quoteExtras.totalActionValue + feeInfo.fee.amount,
2613
2633
  ...ctx.gasOverrides
2614
2634
  }
2615
2635
  });
2616
2636
  return {
2617
2637
  steps,
2618
2638
  approvals: bundle.approvals,
2619
- quoteExtras: bundle.quoteExtras
2639
+ quoteExtras: bundle.quoteExtras,
2640
+ interopFee: feeInfo.fee
2620
2641
  };
2621
2642
  }
2622
2643
  };
@@ -2631,12 +2652,14 @@ function routeDirect() {
2631
2652
  dstChainId: ctx.dstChainId,
2632
2653
  baseTokens: ctx.baseTokens,
2633
2654
  l2AssetRouter: ctx.l2AssetRouter,
2634
- l2NativeTokenVault: ctx.l2NativeTokenVault});
2655
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
2656
+ codec: interopCodec
2657
+ });
2635
2658
  },
2636
- // eslint-disable-next-line @typescript-eslint/require-await
2637
2659
  async build(params, ctx) {
2638
2660
  const steps = [];
2639
2661
  const attrs = getInteropAttributes(params, ctx);
2662
+ const feeInfo = await buildFeeInfo(params, ctx, params.actions.length);
2640
2663
  const built = buildDirectBundle(
2641
2664
  params,
2642
2665
  {
@@ -2646,8 +2669,10 @@ function routeDirect() {
2646
2669
  l2NativeTokenVault: ctx.l2NativeTokenVault,
2647
2670
  codec: interopCodec
2648
2671
  },
2649
- attrs
2672
+ attrs,
2673
+ feeInfo
2650
2674
  );
2675
+ steps.push(...await buildApproveSteps(built.approvals, ctx));
2651
2676
  const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
2652
2677
  built.dstChain,
2653
2678
  built.starters,
@@ -2657,43 +2682,28 @@ function routeDirect() {
2657
2682
  key: "sendBundle",
2658
2683
  kind: "interop.center",
2659
2684
  description: `Send interop bundle (direct route; ${params.actions.length} actions)`,
2660
- // In direct route, msg.value equals the total forwarded value across
2661
- // all calls (sendNative.amount + call.value).
2685
+ // msg.value = forwarded action value + protocol fee.
2662
2686
  tx: {
2663
2687
  to: ctx.interopCenter,
2664
2688
  data,
2665
- value: built.quoteExtras.totalActionValue,
2689
+ value: built.quoteExtras.totalActionValue + feeInfo.fee.amount,
2666
2690
  ...ctx.gasOverrides
2667
2691
  }
2668
2692
  });
2669
2693
  return {
2670
2694
  steps,
2671
2695
  approvals: built.approvals,
2672
- quoteExtras: built.quoteExtras
2696
+ quoteExtras: built.quoteExtras,
2697
+ interopFee: feeInfo.fee
2673
2698
  };
2674
2699
  }
2675
2700
  };
2676
2701
  }
2677
- var MIN_INTEROP_PROTOCOL = 31;
2678
2702
  async function assertInteropProtocolVersion(client, srcChainId, dstChainId) {
2679
2703
  const [srcProtocolVersion, dstProtocolVersion] = await Promise.all([
2680
2704
  client.getProtocolVersion(srcChainId),
2681
2705
  client.getProtocolVersion(dstChainId)
2682
2706
  ]);
2683
- const assertProtocolVersion = (chainId, protocolVersion) => {
2684
- if (protocolVersion[1] < MIN_INTEROP_PROTOCOL) {
2685
- throw createError("VALIDATION", {
2686
- resource: "interop",
2687
- operation: OP_INTEROP.context.protocolVersion,
2688
- message: `Interop requires protocol version 31.0+. Found: ${protocolVersion[1]}.${protocolVersion[2]} for chain: ${chainId}.`,
2689
- context: {
2690
- chainId,
2691
- requiredMinor: MIN_INTEROP_PROTOCOL,
2692
- semver: protocolVersion
2693
- }
2694
- });
2695
- }
2696
- };
2697
2707
  assertProtocolVersion(srcChainId, srcProtocolVersion);
2698
2708
  assertProtocolVersion(dstChainId, dstProtocolVersion);
2699
2709
  }
@@ -2749,7 +2759,7 @@ function getTopics() {
2749
2759
  };
2750
2760
  return { topics, centerIface };
2751
2761
  }
2752
- var { wrap: wrap2 } = createErrorHandlers("interop");
2762
+ var { wrap: wrap3 } = createErrorHandlers("interop");
2753
2763
  var DEFAULT_BLOCKS_RANGE_SIZE = 1e4;
2754
2764
  var DEFAULT_MAX_BLOCKS_BACK = 2e4;
2755
2765
  var SAFE_BLOCKS_RANGE_SIZE = 1e3;
@@ -2762,9 +2772,16 @@ function parseMaxBlockRangeLimit(error) {
2762
2772
  return Number.isInteger(limit) && limit > 0 ? limit : null;
2763
2773
  }
2764
2774
  async function getTxReceipt(provider, txHash) {
2765
- const receipt = await wrap2(
2775
+ const receipt = await wrap3(
2766
2776
  OP_INTEROP.svc.status.sourceReceipt,
2767
- () => provider.getTransactionReceipt(txHash),
2777
+ async () => {
2778
+ try {
2779
+ return await provider.getTransactionReceipt(txHash);
2780
+ } catch (error) {
2781
+ if (isReceiptNotFound(error)) return null;
2782
+ throw error;
2783
+ }
2784
+ },
2768
2785
  {
2769
2786
  ctx: { where: "l2.getTransactionReceipt", l2SrcTxHash: txHash },
2770
2787
  message: "Failed to fetch source L2 receipt for interop tx."
@@ -2783,7 +2800,7 @@ async function getTxReceipt(provider, txHash) {
2783
2800
  async function getLogs(provider, address, topics, opts) {
2784
2801
  const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
2785
2802
  const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
2786
- return await wrap2(
2803
+ return await wrap3(
2787
2804
  OP_INTEROP.svc.status.dstLogs,
2788
2805
  async () => {
2789
2806
  const currentBlock = await provider.getBlockNumber();
@@ -2830,7 +2847,7 @@ async function getLogs(provider, address, topics, opts) {
2830
2847
  );
2831
2848
  }
2832
2849
  async function getInteropRoot(provider, rootChainId, batchNumber) {
2833
- return await wrap2(
2850
+ return await wrap3(
2834
2851
  OP_INTEROP.svc.status.getRoot,
2835
2852
  async () => {
2836
2853
  const rootStorage = new Contract(
@@ -2848,7 +2865,7 @@ async function getInteropRoot(provider, rootChainId, batchNumber) {
2848
2865
  }
2849
2866
 
2850
2867
  // src/adapters/ethers/resources/interop/services/finalization/bundle.ts
2851
- var { wrap: wrap3 } = createErrorHandlers("interop");
2868
+ var { wrap: wrap4 } = createErrorHandlers("interop");
2852
2869
  async function getBundleStatus(client, dstProvider, topics, bundleHash, opts) {
2853
2870
  const { interopHandler } = await client.ensureAddresses();
2854
2871
  const bundleLogs = await getLogs(dstProvider, interopHandler, [null, bundleHash], opts);
@@ -2880,7 +2897,7 @@ async function executeBundle(client, dstProvider, info, opts) {
2880
2897
  context: { bundleHash }
2881
2898
  });
2882
2899
  }
2883
- const signer = await wrap3(OP_INTEROP.exec.sendStep, () => client.signerFor(dstProvider), {
2900
+ const signer = await wrap4(OP_INTEROP.exec.sendStep, () => client.signerFor(dstProvider), {
2884
2901
  message: "Failed to resolve destination signer."
2885
2902
  });
2886
2903
  const { interopHandler } = await client.ensureAddresses();
@@ -2929,144 +2946,6 @@ async function executeBundle(client, dstProvider, info, opts) {
2929
2946
  );
2930
2947
  }
2931
2948
  }
2932
-
2933
- // src/core/resources/interop/finalization.ts
2934
- var DEFAULT_POLL_MS = 1e3;
2935
- var DEFAULT_TIMEOUT_MS = 3e5;
2936
- function resolveIdsFromWaitable(input) {
2937
- if (typeof input === "string") {
2938
- return { l2SrcTxHash: input };
2939
- }
2940
- return {
2941
- l2SrcTxHash: input.l2SrcTxHash,
2942
- bundleHash: input.bundleHash,
2943
- dstExecTxHash: input.dstExecTxHash
2944
- };
2945
- }
2946
- function parseBundleSentFromReceipt(input) {
2947
- const { receipt, interopCenter, interopBundleSentTopic, decodeInteropBundleSent: decodeInteropBundleSent2 } = input;
2948
- const bundleSentLog = receipt.logs.find(
2949
- (log) => log.address.toLowerCase() === interopCenter.toLowerCase() && log.topics[0].toLowerCase() === interopBundleSentTopic.toLowerCase()
2950
- );
2951
- if (!bundleSentLog) {
2952
- throw createError("STATE", {
2953
- resource: "interop",
2954
- operation: OP_INTEROP.svc.status.parseSentLog,
2955
- message: "Failed to locate InteropBundleSent event in source receipt.",
2956
- context: { receipt, interopCenter }
2957
- });
2958
- }
2959
- const decoded = decodeInteropBundleSent2({
2960
- data: bundleSentLog.data,
2961
- topics: bundleSentLog.topics
2962
- });
2963
- return { bundleHash: decoded.bundleHash, dstChainId: decoded.destinationChainId };
2964
- }
2965
- function parseBundleReceiptInfo(params) {
2966
- const {
2967
- rawReceipt,
2968
- interopCenter,
2969
- interopBundleSentTopic,
2970
- decodeInteropBundleSent: decodeInteropBundleSent2,
2971
- decodeL1MessageData: decodeL1MessageData2,
2972
- l2SrcTxHash
2973
- } = params;
2974
- let l2ToL1LogIndex = -1;
2975
- let l1MessageData = null;
2976
- let found;
2977
- for (const log of rawReceipt.logs) {
2978
- if (isL1MessageSentLog(log)) {
2979
- l2ToL1LogIndex += 1;
2980
- try {
2981
- l1MessageData = decodeL1MessageData2(log);
2982
- } catch (e) {
2983
- throw createError("STATE", {
2984
- resource: "interop",
2985
- operation: OP_INTEROP.svc.status.parseSentLog,
2986
- message: "Failed to decode L1MessageSent log data for interop bundle.",
2987
- context: { l2SrcTxHash, l2ToL1LogIndex },
2988
- cause: e
2989
- });
2990
- }
2991
- continue;
2992
- }
2993
- if (log.address.toLowerCase() !== interopCenter.toLowerCase() || log.topics[0].toLowerCase() !== interopBundleSentTopic.toLowerCase())
2994
- continue;
2995
- const decoded = decodeInteropBundleSent2({
2996
- data: log.data,
2997
- topics: log.topics
2998
- });
2999
- found = {
3000
- bundleHash: decoded.bundleHash,
3001
- dstChainId: decoded.destinationChainId,
3002
- sourceChainId: decoded.sourceChainId
3003
- };
3004
- break;
3005
- }
3006
- if (!found) {
3007
- throw createError("STATE", {
3008
- resource: "interop",
3009
- operation: OP_INTEROP.svc.status.parseSentLog,
3010
- message: "Failed to locate InteropBundleSent event in source receipt.",
3011
- context: { l2SrcTxHash, interopCenter }
3012
- });
3013
- }
3014
- if (!l1MessageData) {
3015
- throw createError("STATE", {
3016
- resource: "interop",
3017
- operation: OP_INTEROP.svc.status.parseSentLog,
3018
- message: "Failed to locate L1MessageSent log data for interop bundle.",
3019
- context: { l2SrcTxHash, interopCenter }
3020
- });
3021
- }
3022
- return {
3023
- bundleHash: found.bundleHash,
3024
- dstChainId: found.dstChainId,
3025
- sourceChainId: found.sourceChainId,
3026
- l1MessageData,
3027
- l2ToL1LogIndex,
3028
- txNumberInBatch: Number(rawReceipt.transactionIndex),
3029
- rawReceipt
3030
- };
3031
- }
3032
- function getBundleEncodedData(messageData) {
3033
- const prefix = `0x${messageData.slice(2, 4)}`;
3034
- if (prefix !== BUNDLE_IDENTIFIER) {
3035
- throw createError("STATE", {
3036
- resource: "interop",
3037
- operation: OP_INTEROP.wait,
3038
- message: "Unexpected bundle prefix in L1MessageSent data.",
3039
- context: { prefix, expected: BUNDLE_IDENTIFIER }
3040
- });
3041
- }
3042
- return `0x${messageData.slice(4)}`;
3043
- }
3044
- function buildFinalizationInfo(ids, bundleInfo, proof, messageData) {
3045
- const expectedRoot = {
3046
- rootChainId: bundleInfo.sourceChainId,
3047
- batchNumber: proof.batchNumber,
3048
- expectedRoot: proof.root
3049
- };
3050
- const messageProof = {
3051
- chainId: bundleInfo.sourceChainId,
3052
- l1BatchNumber: proof.batchNumber,
3053
- l2MessageIndex: proof.id,
3054
- message: {
3055
- txNumberInBatch: bundleInfo.txNumberInBatch,
3056
- sender: L2_INTEROP_CENTER_ADDRESS,
3057
- data: messageData
3058
- },
3059
- proof: proof.proof
3060
- };
3061
- return {
3062
- l2SrcTxHash: ids.l2SrcTxHash,
3063
- bundleHash: bundleInfo.bundleHash,
3064
- dstChainId: bundleInfo.dstChainId,
3065
- expectedRoot,
3066
- proof: messageProof,
3067
- encodedData: getBundleEncodedData(messageData)
3068
- };
3069
- }
3070
2949
  function decodeInteropBundleSent(centerIface, log) {
3071
2950
  const decoded = centerIface.decodeEventLog(
3072
2951
  "InteropBundleSent",
@@ -3085,7 +2964,7 @@ function decodeL1MessageData(log) {
3085
2964
  }
3086
2965
 
3087
2966
  // src/adapters/ethers/resources/interop/services/finalization/polling.ts
3088
- var { wrap: wrap4 } = createErrorHandlers("interop");
2967
+ var { wrap: wrap5 } = createErrorHandlers("interop");
3089
2968
  function isProofNotReadyError(error) {
3090
2969
  return isZKsyncError(error, {
3091
2970
  operation: "zksrpc.getL2ToL1LogProof",
@@ -3122,30 +3001,26 @@ async function waitForProof(client, l2SrcTxHash, logIndex, blockNumber, pollMs,
3122
3001
  });
3123
3002
  }
3124
3003
  try {
3125
- return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex);
3004
+ return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex, "messageRoot" /* MessageRoot */);
3126
3005
  } catch (error) {
3127
3006
  if (!isProofNotReadyError(error)) throw error;
3128
3007
  }
3129
3008
  await sleep(pollMs);
3130
3009
  }
3131
3010
  }
3132
- async function waitForRoot(provider, expectedRoot, pollMs, deadline) {
3011
+ async function waitForRoot(provider, chainId, batchNumber, pollMs, deadline) {
3133
3012
  while (true) {
3134
3013
  if (Date.now() > deadline) {
3135
3014
  throw createError("TIMEOUT", {
3136
3015
  resource: "interop",
3137
3016
  operation: OP_INTEROP.svc.wait.timeout,
3138
3017
  message: "Timed out waiting for interop root to become available.",
3139
- context: { expectedRoot }
3018
+ context: { chainId, batchNumber }
3140
3019
  });
3141
3020
  }
3142
3021
  let interopRoot = null;
3143
3022
  try {
3144
- const root = await getInteropRoot(
3145
- provider,
3146
- expectedRoot.rootChainId,
3147
- expectedRoot.batchNumber
3148
- );
3023
+ const root = await getInteropRoot(provider, chainId, batchNumber);
3149
3024
  if (root !== ZERO_HASH) {
3150
3025
  interopRoot = root;
3151
3026
  }
@@ -3154,18 +3029,7 @@ async function waitForRoot(provider, expectedRoot, pollMs, deadline) {
3154
3029
  interopRoot = null;
3155
3030
  }
3156
3031
  if (interopRoot) {
3157
- if (interopRoot.toLowerCase() === expectedRoot.expectedRoot.toLowerCase()) {
3158
- return;
3159
- }
3160
- throw createError("STATE", {
3161
- resource: "interop",
3162
- operation: OP_INTEROP.wait,
3163
- message: "Interop root mismatch.",
3164
- context: {
3165
- expected: expectedRoot.expectedRoot,
3166
- got: interopRoot
3167
- }
3168
- });
3032
+ return interopRoot;
3169
3033
  }
3170
3034
  await sleep(pollMs);
3171
3035
  }
@@ -3180,7 +3044,7 @@ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
3180
3044
  context: { txHash }
3181
3045
  });
3182
3046
  }
3183
- const receipt = await wrap4(
3047
+ const receipt = await wrap5(
3184
3048
  OP_INTEROP.svc.status.sourceReceipt,
3185
3049
  () => client.zks.getReceiptWithL2ToL1(txHash),
3186
3050
  {
@@ -3194,7 +3058,7 @@ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
3194
3058
  await sleep(pollMs);
3195
3059
  }
3196
3060
  }
3197
- async function waitForFinalization(client, dstProvider, input, opts) {
3061
+ async function waitForFinalization(client, dstProvider, gwProvider, input, opts) {
3198
3062
  const { topics, centerIface } = getTopics();
3199
3063
  const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
3200
3064
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -3243,7 +3107,16 @@ async function waitForFinalization(client, dstProvider, input, opts) {
3243
3107
  proof,
3244
3108
  bundleInfo.l1MessageData
3245
3109
  );
3246
- await waitForRoot(dstProvider, finalizationInfo.expectedRoot, pollMs, deadline);
3110
+ if (proof.gatewayBlockNumber == null) {
3111
+ throw createError("STATE", {
3112
+ resource: "interop",
3113
+ operation: OP_INTEROP.svc.wait.timeout,
3114
+ message: "Proof missing gatewayBlockNumber required for interop finalization.",
3115
+ context: { l2SrcTxHash: ids.l2SrcTxHash }
3116
+ });
3117
+ }
3118
+ const { chainId: gwChainId } = await gwProvider.getNetwork();
3119
+ await waitForRoot(dstProvider, gwChainId, proof.gatewayBlockNumber, pollMs, deadline);
3247
3120
  return finalizationInfo;
3248
3121
  }
3249
3122
 
@@ -3289,8 +3162,8 @@ function createInteropFinalizationServices(client) {
3289
3162
  status(dstProvider, input, opts) {
3290
3163
  return getStatus(client, dstProvider, input, opts);
3291
3164
  },
3292
- wait(dstProvider, input, opts) {
3293
- return waitForFinalization(client, dstProvider, input, opts);
3165
+ wait(dstProvider, gwProvider, input, opts) {
3166
+ return waitForFinalization(client, dstProvider, gwProvider, input, opts);
3294
3167
  },
3295
3168
  async finalize(dstProvider, info, opts) {
3296
3169
  const execResult = await executeBundle(client, dstProvider, info, opts);
@@ -3302,24 +3175,56 @@ function createInteropFinalizationServices(client) {
3302
3175
  }
3303
3176
  };
3304
3177
  }
3305
- function resolveDstProvider(dstChain) {
3306
- return typeof dstChain === "string" ? new JsonRpcProvider(dstChain) : dstChain;
3178
+ function resolveChainRef(ref) {
3179
+ return typeof ref === "string" ? new JsonRpcProvider(ref) : ref;
3307
3180
  }
3308
- function resolveWaitableInput(waitableInput) {
3309
- const input = waitableInput;
3310
- return {
3311
- dstProvider: resolveDstProvider(waitableInput.dstChain),
3312
- waitable: input.waitable ? input.waitable : waitableInput
3313
- };
3181
+
3182
+ // src/adapters/ethers/resources/interop/services/gas.ts
3183
+ async function quoteStepsL2Fee(steps, ctx) {
3184
+ if (steps.length === 0) return 0n;
3185
+ const estimator = ethersToGasEstimator(ctx.client.l2);
3186
+ let maxFeePerGas;
3187
+ try {
3188
+ const fees = await estimator.estimateFeesPerGas();
3189
+ maxFeePerGas = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
3190
+ } catch {
3191
+ return void 0;
3192
+ }
3193
+ let total = 0n;
3194
+ for (const step of steps) {
3195
+ try {
3196
+ const coreTx = toCoreTx({ ...step.tx, from: ctx.sender });
3197
+ const est = await estimator.estimateGas(coreTx);
3198
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
3199
+ total += buffered * maxFeePerGas;
3200
+ } catch {
3201
+ return void 0;
3202
+ }
3203
+ }
3204
+ return total;
3314
3205
  }
3315
3206
 
3316
3207
  // src/adapters/ethers/resources/interop/index.ts
3317
- var { wrap: wrap5, toResult: toResult2 } = createErrorHandlers("interop");
3208
+ var { wrap: wrap6, toResult: toResult2 } = createErrorHandlers("interop");
3318
3209
  var ROUTES3 = {
3319
3210
  direct: routeDirect(),
3320
3211
  indirect: routeIndirect()
3321
3212
  };
3322
- function createInteropResource(client, tokens, contracts, attributes) {
3213
+ function createInteropResource(client, config, tokens, contracts, attributes) {
3214
+ let gwProviderCache;
3215
+ function requireConfig() {
3216
+ if (!config)
3217
+ throw createError("STATE", {
3218
+ resource: "interop",
3219
+ operation: "interop.init",
3220
+ message: "Interop is not configured. Pass gwChain in createEthersSdk options."
3221
+ });
3222
+ return config;
3223
+ }
3224
+ function getGwProvider() {
3225
+ if (!gwProviderCache) gwProviderCache = resolveChainRef(requireConfig().gwChain);
3226
+ return gwProviderCache;
3227
+ }
3323
3228
  const svc = createInteropFinalizationServices(client);
3324
3229
  const tokensResource = tokens ?? createTokensResource(client);
3325
3230
  const contractsResource = contracts ?? createContractsResource(client);
@@ -3343,11 +3248,11 @@ function createInteropResource(client, tokens, contracts, attributes) {
3343
3248
  baseTokenDst: ctx.baseTokens.dst
3344
3249
  }
3345
3250
  });
3346
- await wrap5(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
3251
+ await wrap6(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
3347
3252
  message: "Interop preflight failed.",
3348
3253
  ctx: { where: `routes.${route}.preflight` }
3349
3254
  });
3350
- const { steps, approvals, quoteExtras } = await wrap5(
3255
+ const { steps, approvals, quoteExtras, interopFee } = await wrap6(
3351
3256
  OP_INTEROP.routes[route].build,
3352
3257
  () => ROUTES3[route].build(params, ctx),
3353
3258
  {
@@ -3355,11 +3260,14 @@ function createInteropResource(client, tokens, contracts, attributes) {
3355
3260
  ctx: { where: `routes.${route}.build` }
3356
3261
  }
3357
3262
  );
3263
+ const l2Fee = await quoteStepsL2Fee(steps, ctx).catch(() => void 0);
3358
3264
  const summary = {
3359
3265
  route,
3360
3266
  approvalsNeeded: approvals,
3361
3267
  totalActionValue: quoteExtras.totalActionValue,
3362
- bridgedTokenTotal: quoteExtras.bridgedTokenTotal
3268
+ bridgedTokenTotal: quoteExtras.bridgedTokenTotal,
3269
+ interopFee,
3270
+ l2Fee
3363
3271
  };
3364
3272
  return { plan: { route, summary, steps }, ctx };
3365
3273
  }
@@ -3367,20 +3275,23 @@ function createInteropResource(client, tokens, contracts, attributes) {
3367
3275
  const { plan } = await buildPlanWithCtx(dstProvider, params);
3368
3276
  return plan;
3369
3277
  }
3370
- const quote = (params) => wrap5(OP_INTEROP.quote, async () => {
3371
- const plan = await buildPlan(resolveDstProvider(params.dstChain), params);
3278
+ const quote = (dstChain, params) => wrap6(OP_INTEROP.quote, async () => {
3279
+ const plan = await buildPlan(resolveChainRef(dstChain), params);
3372
3280
  return plan.summary;
3373
3281
  });
3374
- const tryQuote = (params) => toResult2(OP_INTEROP.tryQuote, () => quote(params));
3375
- const prepare = (params) => wrap5(OP_INTEROP.prepare, () => buildPlan(resolveDstProvider(params.dstChain), params), {
3282
+ const tryQuote = (dstChain, params) => toResult2(OP_INTEROP.tryQuote, () => quote(dstChain, params));
3283
+ const prepare = (dstChain, params) => wrap6(OP_INTEROP.prepare, () => buildPlan(resolveChainRef(dstChain), params), {
3376
3284
  message: "Internal error while preparing an interop plan.",
3377
3285
  ctx: { where: "interop.prepare" }
3378
3286
  });
3379
- const tryPrepare = (params) => toResult2(OP_INTEROP.tryPrepare, () => prepare(params));
3380
- const create = (params) => wrap5(
3287
+ const tryPrepare = (dstChain, params) => toResult2(
3288
+ OP_INTEROP.tryPrepare,
3289
+ () => prepare(dstChain, params)
3290
+ );
3291
+ const create = (dstChain, params) => wrap6(
3381
3292
  OP_INTEROP.create,
3382
3293
  async () => {
3383
- const { plan, ctx } = await buildPlanWithCtx(resolveDstProvider(params.dstChain), params);
3294
+ const { plan, ctx } = await buildPlanWithCtx(resolveChainRef(dstChain), params);
3384
3295
  const signer = ctx.client.signerFor(ctx.client.l2);
3385
3296
  const srcProvider = ctx.client.l2;
3386
3297
  const from = await signer.getAddress();
@@ -3442,7 +3353,6 @@ function createInteropResource(client, tokens, contracts, attributes) {
3442
3353
  const last = Object.values(stepHashes).pop();
3443
3354
  return {
3444
3355
  kind: "interop",
3445
- dstChain: params.dstChain,
3446
3356
  stepHashes,
3447
3357
  plan,
3448
3358
  l2SrcTxHash: last ?? "0x"
@@ -3453,46 +3363,27 @@ function createInteropResource(client, tokens, contracts, attributes) {
3453
3363
  ctx: { where: "interop.create" }
3454
3364
  }
3455
3365
  );
3456
- const tryCreate = (params) => toResult2(OP_INTEROP.tryCreate, () => create(params));
3457
- const status = (h, opts) => {
3458
- const { dstProvider, waitable } = resolveWaitableInput(h);
3459
- return wrap5(OP_INTEROP.status, () => svc.status(dstProvider, waitable, opts), {
3460
- message: "Internal error while checking interop status.",
3461
- ctx: { where: "interop.status" }
3462
- });
3463
- };
3464
- const wait = (h, opts) => {
3465
- const { dstProvider, waitable } = resolveWaitableInput(h);
3466
- return wrap5(
3467
- OP_INTEROP.wait,
3468
- async () => {
3469
- const info = await svc.wait(dstProvider, waitable, opts);
3470
- return { ...info, dstChain: h.dstChain };
3471
- },
3472
- {
3473
- message: "Internal error while waiting for interop finalization.",
3474
- ctx: { where: "interop.wait" }
3475
- }
3476
- );
3477
- };
3478
- const tryWait = (h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(h, opts));
3479
- const finalize = (h, opts) => wrap5(
3366
+ const tryCreate = (dstChain, params) => toResult2(
3367
+ OP_INTEROP.tryCreate,
3368
+ () => create(dstChain, params)
3369
+ );
3370
+ const status = (dstChain, h, opts) => wrap6(OP_INTEROP.status, () => svc.status(resolveChainRef(dstChain), h, opts), {
3371
+ message: "Internal error while checking interop status.",
3372
+ ctx: { where: "interop.status" }
3373
+ });
3374
+ const wait = (dstChain, h, opts) => wrap6(OP_INTEROP.wait, () => svc.wait(resolveChainRef(dstChain), getGwProvider(), h, opts), {
3375
+ message: "Internal error while waiting for interop finalization.",
3376
+ ctx: { where: "interop.wait" }
3377
+ });
3378
+ const tryWait = (dstChain, h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(dstChain, h, opts));
3379
+ const finalize = (dstChain, h, opts) => wrap6(
3480
3380
  OP_INTEROP.finalize,
3481
3381
  async () => {
3382
+ const dstProvider = resolveChainRef(dstChain);
3482
3383
  if (isInteropFinalizationInfo(h)) {
3483
- if (h.dstChain == null) {
3484
- throw createError("STATE", {
3485
- resource: "interop",
3486
- operation: OP_INTEROP.finalize,
3487
- message: "Missing dstChain in interop finalization info.",
3488
- context: { input: h }
3489
- });
3490
- }
3491
- const dstProvider2 = resolveDstProvider(h.dstChain);
3492
- return svc.finalize(dstProvider2, h, opts);
3384
+ return svc.finalize(dstProvider, h, opts);
3493
3385
  }
3494
- const { dstProvider, waitable } = resolveWaitableInput(h);
3495
- const info = await svc.wait(dstProvider, waitable);
3386
+ const info = await svc.wait(dstProvider, getGwProvider(), h);
3496
3387
  return svc.finalize(dstProvider, info, opts);
3497
3388
  },
3498
3389
  {
@@ -3500,7 +3391,7 @@ function createInteropResource(client, tokens, contracts, attributes) {
3500
3391
  ctx: { where: "interop.finalize" }
3501
3392
  }
3502
3393
  );
3503
- const tryFinalize = (h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(h, opts));
3394
+ const tryFinalize = (dstChain, h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(dstChain, h, opts));
3504
3395
  return {
3505
3396
  quote,
3506
3397
  tryQuote,
@@ -3517,10 +3408,10 @@ function createInteropResource(client, tokens, contracts, attributes) {
3517
3408
  }
3518
3409
 
3519
3410
  // src/adapters/ethers/sdk.ts
3520
- function createEthersSdk(client) {
3411
+ function createEthersSdk(client, options) {
3521
3412
  const tokens = createTokensResource(client);
3522
3413
  const contracts = createContractsResource(client);
3523
- const interop = createInteropResource(client);
3414
+ const interop = createInteropResource(client, options?.interop, tokens, contracts);
3524
3415
  return {
3525
3416
  deposits: createDepositsResource(client, tokens, contracts),
3526
3417
  withdrawals: createWithdrawalsResource(client, tokens, contracts),