@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 { createNTVCodec, toGasOverrides, buildFeeBreakdown, quoteL2Gas, quoteL1Gas, quoteL2Gas2 } from './chunk-5L6EYUJB.js';
2
- import { findL1MessageSentLog, messengerLogIndex, pickWithdrawRoute } from './chunk-E3KP7XCG.js';
3
- import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-5R7L5NM5.js';
4
- import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, IBridgehub_default, isETH, normalizeAddrEq, L2NativeTokenVault_default, IL2AssetRouter_default, IBaseToken_default } 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, SAFE_L1_BRIDGE_GAS, FORMAL_ETH_ADDRESS } from './chunk-QQ2OR434.js';
6
- import { keccak256, encodeAbiParameters, concat, decodeEventLog, decodeAbiParameters, encodeFunctionData, zeroAddress } from 'viem';
1
+ 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, quoteL1Gas, derivePriorityBodyGasEstimateCap, quoteL2Gas2, assertProtocolVersion } from './chunk-K2UVKMLN.js';
2
+ import { findL1MessageSentLog, messengerLogIndex, pickWithdrawRoute } from './chunk-3HHUZXSV.js';
3
+ import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-YIWXIP2M.js';
4
+ import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, OP_INTEROP, IInteropHandler_default, IInteropCenter_default, sleep, IERC7786Attributes_default, IBridgehub_default, isETH, normalizeAddrEq, L2NativeTokenVault_default, IL2AssetRouter_default, IBaseToken_default, IInteropRootStorage_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 { keccak256, encodeAbiParameters, toBytes, concat, decodeEventLog, decodeAbiParameters, createWalletClient, custom, encodeFunctionData, createPublicClient, http, encodeEventTopics, numberToHex, zeroAddress, getAddress, toHex } from 'viem';
7
7
 
8
8
  // src/adapters/viem/resources/deposits/context.ts
9
9
  async function commonCtx(p, client, tokens, contracts) {
@@ -221,8 +221,22 @@ async function determineNonBaseL2Gas(input) {
221
221
  try {
222
222
  const l2TokenAddress = input.knownL2Token ?? (ctx.tokens ? await ctx.tokens.toL2Address(l1Token) : await (await ctx.contracts.l2NativeTokenVault()).read.l2TokenAddress([l1Token]));
223
223
  if (l2TokenAddress === zeroAddress) {
224
+ if (input.undeployedGasLimit != null) {
225
+ return quoteL2Gas3({
226
+ ctx,
227
+ route,
228
+ overrideGasLimit: input.undeployedGasLimit
229
+ });
230
+ }
224
231
  return fallbackQuote();
225
232
  }
233
+ if (input.priorityFloorGasLimit != null) {
234
+ return quoteL2Gas3({
235
+ ctx,
236
+ route,
237
+ overrideGasLimit: input.priorityFloorGasLimit
238
+ });
239
+ }
226
240
  const modelTx = {
227
241
  to: input.modelTx?.to ?? ctx.sender,
228
242
  from: input.modelTx?.from ?? ctx.sender,
@@ -238,8 +252,7 @@ async function determineNonBaseL2Gas(input) {
238
252
  return fallbackQuote();
239
253
  }
240
254
  return gas;
241
- } catch (err) {
242
- console.warn("Failed to determine non-base deposit L2 gas; defaulting to safe gas limit.", err);
255
+ } catch {
243
256
  return fallbackQuote();
244
257
  }
245
258
  }
@@ -256,7 +269,9 @@ async function determineEthNonBaseL2Gas(input) {
256
269
  route: "eth-nonbase",
257
270
  l1Token: input.ctx.resolvedToken?.l1 ?? FORMAL_ETH_ADDRESS,
258
271
  knownL2Token: input.ctx.resolvedToken?.l2,
259
- modelTx: input.modelTx
272
+ modelTx: input.modelTx,
273
+ priorityFloorGasLimit: input.priorityFloorGasLimit,
274
+ undeployedGasLimit: input.undeployedGasLimit
260
275
  });
261
276
  }
262
277
 
@@ -281,35 +296,95 @@ async function quoteL2BaseCost(input) {
281
296
  { ctx: { chainIdL2: ctx.chainIdL2 } }
282
297
  );
283
298
  }
299
+ var EMPTY_BYTES = "0x";
300
+ var ZERO_RESERVED_WORDS = [0n, 0n, 0n, 0n];
301
+ var L2_CANONICAL_TRANSACTION_PARAMETER = {
302
+ type: "tuple",
303
+ components: [
304
+ { name: "txType", type: "uint256" },
305
+ { name: "from", type: "uint256" },
306
+ { name: "to", type: "uint256" },
307
+ { name: "gasLimit", type: "uint256" },
308
+ { name: "gasPerPubdataByteLimit", type: "uint256" },
309
+ { name: "maxFeePerGas", type: "uint256" },
310
+ { name: "maxPriorityFeePerGas", type: "uint256" },
311
+ { name: "paymaster", type: "uint256" },
312
+ { name: "nonce", type: "uint256" },
313
+ { name: "value", type: "uint256" },
314
+ { name: "reserved", type: "uint256[4]" },
315
+ { name: "data", type: "bytes" },
316
+ { name: "signature", type: "bytes" },
317
+ { name: "factoryDeps", type: "uint256[]" },
318
+ { name: "paymasterInput", type: "bytes" },
319
+ { name: "reservedDynamic", type: "bytes" }
320
+ ]
321
+ };
322
+ function hexByteLength(hex) {
323
+ return BigInt(Math.max(hex.length - 2, 0) / 2);
324
+ }
325
+ function getPriorityTxEncodedLength(input) {
326
+ const encoded = encodeAbiParameters(
327
+ [L2_CANONICAL_TRANSACTION_PARAMETER],
328
+ [
329
+ {
330
+ txType: 0n,
331
+ from: BigInt(input.sender),
332
+ to: BigInt(input.l2Contract),
333
+ gasLimit: 0n,
334
+ gasPerPubdataByteLimit: input.gasPerPubdata,
335
+ maxFeePerGas: 0n,
336
+ maxPriorityFeePerGas: 0n,
337
+ paymaster: 0n,
338
+ nonce: 0n,
339
+ value: input.l2Value,
340
+ reserved: ZERO_RESERVED_WORDS,
341
+ data: input.l2Calldata,
342
+ signature: EMPTY_BYTES,
343
+ factoryDeps: input.factoryDepsHashes ?? [],
344
+ paymasterInput: EMPTY_BYTES,
345
+ reservedDynamic: EMPTY_BYTES
346
+ }
347
+ ]
348
+ );
349
+ return hexByteLength(encoded);
350
+ }
351
+ function getPriorityTxGasBreakdown(input) {
352
+ return derivePriorityTxGasBreakdown({
353
+ encodedLength: getPriorityTxEncodedLength(input),
354
+ gasPerPubdata: input.gasPerPubdata,
355
+ factoryDepsCount: BigInt(input.factoryDepsHashes?.length ?? 0)
356
+ });
357
+ }
284
358
 
285
359
  // src/adapters/viem/resources/deposits/routes/eth.ts
286
360
  var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
361
+ var EMPTY_BYTES2 = "0x";
287
362
  function routeEthDirect() {
288
363
  return {
289
364
  async build(p, ctx) {
290
- const l2TxModel = {
291
- to: p.to ?? ctx.sender,
292
- from: ctx.sender,
293
- data: "0x",
294
- value: p.amount
295
- };
365
+ const l2Contract = p.to ?? ctx.sender;
366
+ const l2Value = p.amount;
367
+ const l2Calldata = EMPTY_BYTES2;
368
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
369
+ sender: ctx.sender,
370
+ l2Contract,
371
+ l2Value,
372
+ l2Calldata,
373
+ gasPerPubdata: ctx.gasPerPubdata
374
+ });
375
+ const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
376
+ chainIdL2: ctx.chainIdL2,
377
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
378
+ });
296
379
  const l2GasParams = await quoteL2Gas3({
297
380
  ctx,
298
381
  route: "eth-base",
299
- l2TxForModeling: l2TxModel,
300
- overrideGasLimit: ctx.l2GasLimit,
301
- stateOverrides: {
302
- [ctx.sender]: {
303
- balance: "0xffffffffffffffffffff"
304
- }
305
- }
382
+ overrideGasLimit: quotedL2GasLimit
306
383
  });
307
384
  if (!l2GasParams) {
308
385
  throw new Error("Failed to estimate L2 gas for deposit.");
309
386
  }
310
387
  const baseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2GasParams.gasLimit });
311
- const l2Contract = p.to ?? ctx.sender;
312
- const l2Value = p.amount;
313
388
  const mintValue = baseCost + ctx.operatorTip + l2Value;
314
389
  const req = buildDirectRequestStruct({
315
390
  chainId: ctx.chainIdL2,
@@ -353,12 +428,21 @@ function routeEthDirect() {
353
428
  tx: l1TxCandidate,
354
429
  overrides: ctx.gasOverrides
355
430
  });
431
+ let bridgeTx = { ...sim.request };
432
+ if (l1Gas) {
433
+ bridgeTx = {
434
+ ...bridgeTx,
435
+ gas: l1Gas.gasLimit,
436
+ maxFeePerGas: l1Gas.maxFeePerGas,
437
+ maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
438
+ };
439
+ }
356
440
  const steps = [
357
441
  {
358
442
  key: "bridgehub:direct",
359
443
  kind: "bridgehub:direct",
360
444
  description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
361
- tx: { ...sim.request, ...l1Gas }
445
+ tx: bridgeTx
362
446
  }
363
447
  ];
364
448
  const fees = buildFeeBreakdown({
@@ -378,6 +462,62 @@ function routeEthDirect() {
378
462
  };
379
463
  }
380
464
  var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
465
+ var ZERO_ASSET_ID = "0x0000000000000000000000000000000000000000000000000000000000000000";
466
+ async function getPriorityGasModel(input) {
467
+ try {
468
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
469
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
470
+ const l1ChainId = BigInt(await input.ctx.client.l1.getChainId());
471
+ const isFirstBridge = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID || input.ctx.resolvedToken.originChainId === 0n;
472
+ const erc20MetadataOriginChainId = isFirstBridge ? l1ChainId : input.ctx.resolvedToken.originChainId;
473
+ const erc20Metadata = await l1NativeTokenVault.read.getERC20Getters([
474
+ input.token,
475
+ erc20MetadataOriginChainId
476
+ ]);
477
+ const bridgeMintCalldata = encodeAbiParameters(
478
+ [
479
+ { type: "address", name: "originalCaller" },
480
+ { type: "address", name: "receiver" },
481
+ { type: "address", name: "originToken" },
482
+ { type: "uint256", name: "amount" },
483
+ { type: "bytes", name: "erc20Metadata" }
484
+ ],
485
+ [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
486
+ );
487
+ const l2Calldata = isFirstBridge ? encodeFunctionData({
488
+ abi: IL2AssetRouter_default,
489
+ functionName: "finalizeDeposit",
490
+ args: [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
491
+ }) : await (async () => {
492
+ return await l1AssetRouter.read.getDepositCalldata([
493
+ input.ctx.sender,
494
+ input.ctx.resolvedToken.assetId,
495
+ bridgeMintCalldata
496
+ ]);
497
+ })();
498
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
499
+ sender: input.ctx.l1AssetRouter,
500
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
501
+ l2Value: 0n,
502
+ l2Calldata,
503
+ gasPerPubdata: input.ctx.gasPerPubdata
504
+ });
505
+ const model = {
506
+ priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
507
+ chainIdL2: input.ctx.chainIdL2,
508
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
509
+ })
510
+ };
511
+ if (isFirstBridge || input.ctx.resolvedToken.l2.toLowerCase() === zeroAddress) {
512
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
513
+ minBodyGas: priorityFloorBreakdown.minBodyGas
514
+ }) + priorityFloorBreakdown.overhead;
515
+ }
516
+ return model;
517
+ } catch {
518
+ return {};
519
+ }
520
+ }
381
521
  function routeErc20NonBase() {
382
522
  return {
383
523
  // TODO: do we even need these validations?
@@ -408,11 +548,33 @@ function routeErc20NonBase() {
408
548
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
409
549
  const baseIsEth = ctx.baseIsEth ?? isETH(baseToken);
410
550
  const assetRouter = ctx.l1AssetRouter;
551
+ const receiver = p.to ?? ctx.sender;
552
+ const secondBridgeCalldata = await wrapAs3(
553
+ "INTERNAL",
554
+ OP_DEPOSITS.nonbase.encodeCalldata,
555
+ () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, receiver)),
556
+ {
557
+ ctx: {
558
+ where: "encodeSecondBridgeErc20Args",
559
+ token: p.token,
560
+ amount: p.amount.toString()
561
+ },
562
+ message: "Failed to encode bridging calldata."
563
+ }
564
+ );
565
+ const priorityGasModel = await getPriorityGasModel({
566
+ ctx,
567
+ token: p.token,
568
+ amount: p.amount,
569
+ receiver
570
+ });
411
571
  const l2Gas = await determineErc20L2Gas({
412
572
  ctx,
413
573
  l1Token: p.token,
574
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
575
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit,
414
576
  modelTx: {
415
- to: p.to ?? ctx.sender,
577
+ to: receiver,
416
578
  from: ctx.sender,
417
579
  data: "0x",
418
580
  value: 0n
@@ -501,19 +663,6 @@ function routeErc20NonBase() {
501
663
  });
502
664
  }
503
665
  }
504
- const secondBridgeCalldata = await wrapAs3(
505
- "INTERNAL",
506
- OP_DEPOSITS.nonbase.encodeCalldata,
507
- () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, p.to ?? ctx.sender)),
508
- {
509
- ctx: {
510
- where: "encodeSecondBridgeErc20Args",
511
- token: p.token,
512
- amount: p.amount.toString()
513
- },
514
- message: "Failed to encode bridging calldata."
515
- }
516
- );
517
666
  const requestStruct = {
518
667
  chainId: ctx.chainIdL2,
519
668
  mintValue,
@@ -605,6 +754,62 @@ function routeErc20NonBase() {
605
754
  };
606
755
  }
607
756
  var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
757
+ var ZERO_ASSET_ID2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
758
+ var ntvCodec = createNTVCodec({
759
+ encode: (types, values) => encodeAbiParameters(
760
+ types.map((type, index) => ({ type, name: `arg${index}` })),
761
+ values
762
+ ),
763
+ keccak256
764
+ });
765
+ async function getPriorityGasModel2(input) {
766
+ try {
767
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
768
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
769
+ const originChainId = input.ctx.resolvedToken.originChainId !== 0n ? input.ctx.resolvedToken.originChainId : BigInt(await input.ctx.client.l1.getChainId());
770
+ const resolvedAssetId = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID2 ? ntvCodec.encodeAssetId(originChainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, ETH_ADDRESS) : input.ctx.resolvedToken.assetId;
771
+ const erc20Metadata = await l1NativeTokenVault.read.getERC20Getters([
772
+ ETH_ADDRESS,
773
+ originChainId
774
+ ]);
775
+ const bridgeMintCalldata = encodeAbiParameters(
776
+ [
777
+ { type: "address", name: "originalCaller" },
778
+ { type: "address", name: "receiver" },
779
+ { type: "address", name: "originToken" },
780
+ { type: "uint256", name: "amount" },
781
+ { type: "bytes", name: "erc20Metadata" }
782
+ ],
783
+ [input.ctx.sender, input.receiver, ETH_ADDRESS, input.amount, erc20Metadata]
784
+ );
785
+ const l2Calldata = await l1AssetRouter.read.getDepositCalldata([
786
+ input.ctx.sender,
787
+ resolvedAssetId,
788
+ bridgeMintCalldata
789
+ ]);
790
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
791
+ sender: input.ctx.l1AssetRouter,
792
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
793
+ l2Value: 0n,
794
+ l2Calldata,
795
+ gasPerPubdata: input.ctx.gasPerPubdata
796
+ });
797
+ const model = {
798
+ priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
799
+ chainIdL2: input.ctx.chainIdL2,
800
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
801
+ })
802
+ };
803
+ if (input.ctx.resolvedToken.l2.toLowerCase() === zeroAddress) {
804
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
805
+ minBodyGas: priorityFloorBreakdown.minBodyGas
806
+ }) + priorityFloorBreakdown.overhead;
807
+ }
808
+ return model;
809
+ } catch {
810
+ return {};
811
+ }
812
+ }
608
813
  function routeEthNonBase() {
609
814
  return {
610
815
  // TODO: do we even need these validations?
@@ -651,15 +856,23 @@ function routeEthNonBase() {
651
856
  },
652
857
  async build(p, ctx) {
653
858
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
859
+ const receiver = p.to ?? ctx.sender;
860
+ const priorityGasModel = await getPriorityGasModel2({
861
+ ctx,
862
+ amount: p.amount,
863
+ receiver
864
+ });
654
865
  const l2TxModel = {
655
- to: p.to ?? ctx.sender,
866
+ to: receiver,
656
867
  from: ctx.sender,
657
868
  data: "0x",
658
869
  value: 0n
659
870
  };
660
871
  const l2Gas = await determineEthNonBaseL2Gas({
661
872
  ctx,
662
- modelTx: l2TxModel
873
+ modelTx: l2TxModel,
874
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
875
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit
663
876
  });
664
877
  if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
665
878
  const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
@@ -708,12 +921,12 @@ function routeEthNonBase() {
708
921
  const secondBridgeCalldata = await wrapAs4(
709
922
  "INTERNAL",
710
923
  OP_DEPOSITS.ethNonBase.encodeCalldata,
711
- () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
924
+ () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, receiver)),
712
925
  {
713
926
  ctx: {
714
927
  where: "encodeSecondBridgeEthArgs",
715
928
  amount: p.amount.toString(),
716
- to: p.to ?? ctx.sender
929
+ to: receiver
717
930
  },
718
931
  message: "Failed to encode ETH bridging calldata."
719
932
  }
@@ -814,6 +1027,7 @@ function routeEthNonBase() {
814
1027
  };
815
1028
  }
816
1029
  var { wrapAs: wrapAs5 } = createErrorHandlers("deposits");
1030
+ var EMPTY_BYTES3 = "0x";
817
1031
  function routeErc20Base() {
818
1032
  return {
819
1033
  async preflight(p, ctx) {
@@ -841,17 +1055,24 @@ function routeErc20Base() {
841
1055
  },
842
1056
  async build(p, ctx) {
843
1057
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
844
- const l2TxModel = {
845
- to: p.to ?? ctx.sender,
846
- from: ctx.sender,
847
- data: "0x",
848
- value: 0n
849
- };
1058
+ const l2Contract = p.to ?? ctx.sender;
1059
+ const l2Value = p.amount;
1060
+ const l2Calldata = EMPTY_BYTES3;
1061
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
1062
+ sender: ctx.sender,
1063
+ l2Contract,
1064
+ l2Value,
1065
+ l2Calldata,
1066
+ gasPerPubdata: ctx.gasPerPubdata
1067
+ });
1068
+ const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
1069
+ chainIdL2: ctx.chainIdL2,
1070
+ gasLimit: priorityFloorBreakdown.derivedL2GasLimit
1071
+ });
850
1072
  const l2Gas = await quoteL2Gas3({
851
1073
  ctx,
852
1074
  route: "erc20-base",
853
- l2TxForModeling: l2TxModel,
854
- overrideGasLimit: ctx.l2GasLimit
1075
+ overrideGasLimit: quotedL2GasLimit
855
1076
  });
856
1077
  if (!l2Gas) throw new Error("Failed to estimate L2 gas parameters.");
857
1078
  const l2BaseCost = await quoteL2BaseCost({ ctx, l2GasLimit: l2Gas.gasLimit });
@@ -903,8 +1124,8 @@ function routeErc20Base() {
903
1124
  l2GasLimit: l2Gas.gasLimit,
904
1125
  gasPerPubdata: ctx.gasPerPubdata,
905
1126
  refundRecipient: ctx.refundRecipient,
906
- l2Contract: p.to ?? ctx.sender,
907
- l2Value: p.amount
1127
+ l2Contract,
1128
+ l2Value
908
1129
  });
909
1130
  let bridgeTx;
910
1131
  let calldata;
@@ -1066,7 +1287,7 @@ async function waitForL2ExecutionFromL1Tx(l1, l2, l1TxHash) {
1066
1287
  return { l2Receipt, l2TxHash };
1067
1288
  }
1068
1289
  var { wrapAs: wrapAs6 } = createErrorHandlers("tokens");
1069
- var ntvCodec = createNTVCodec({
1290
+ var ntvCodec2 = createNTVCodec({
1070
1291
  encode: (types, values) => encodeAbiParameters(
1071
1292
  types.map((t, i) => ({ type: t, name: `arg${i}` })),
1072
1293
  values
@@ -1180,7 +1401,7 @@ function createTokensResource(client) {
1180
1401
  return wrapAs6("CONTRACT", "tokens.isChainEthBased", async () => {
1181
1402
  const baseAssetId = await getBaseTokenAssetId();
1182
1403
  const l1ChainId = await getL1ChainId();
1183
- const ethAssetId = ntvCodec.encodeAssetId(
1404
+ const ethAssetId = ntvCodec2.encodeAssetId(
1184
1405
  l1ChainId,
1185
1406
  L2_NATIVE_TOKEN_VAULT_ADDRESS,
1186
1407
  ETH_ADDRESS
@@ -1379,6 +1600,7 @@ function createDepositsResource(client, tokens, contracts) {
1379
1600
  async () => {
1380
1601
  const plan = await prepare(p);
1381
1602
  const stepHashes = {};
1603
+ const chainIdL2 = BigInt(await client.l2.getChainId());
1382
1604
  const from = client.account.address;
1383
1605
  let next;
1384
1606
  if (typeof p.l1TxOverrides?.nonce === "number") {
@@ -1430,6 +1652,7 @@ function createDepositsResource(client, tokens, contracts) {
1430
1652
  }
1431
1653
  if (!p.l1TxOverrides?.gasLimit) {
1432
1654
  try {
1655
+ const preparedGasLimit = step.tx.gas;
1433
1656
  const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
1434
1657
  maxFeePerGas: step.tx.maxFeePerGas,
1435
1658
  maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas
@@ -1448,7 +1671,12 @@ function createDepositsResource(client, tokens, contracts) {
1448
1671
  const gas = await client.l1.estimateContractGas({
1449
1672
  ...params
1450
1673
  });
1451
- step.tx.gas = gas * 115n / 100n;
1674
+ step.tx.gas = resolveCreateDepositL1GasLimit({
1675
+ chainIdL2,
1676
+ stepKey: step.key,
1677
+ preparedGasLimit,
1678
+ estimatedGasLimit: gas
1679
+ });
1452
1680
  } catch {
1453
1681
  }
1454
1682
  }
@@ -2209,7 +2437,7 @@ var ROUTES2 = {
2209
2437
  };
2210
2438
  function createWithdrawalsResource(client, tokens, contracts) {
2211
2439
  const svc = createFinalizationServices(client);
2212
- const { wrap: wrap2, toResult: toResult2 } = createErrorHandlers("withdrawals");
2440
+ const { wrap: wrap7, toResult: toResult3 } = createErrorHandlers("withdrawals");
2213
2441
  const tokensResource = tokens ?? createTokensResource(client);
2214
2442
  const contractsResource = contracts ?? createContractsResource(client);
2215
2443
  async function buildPlan(p) {
@@ -2230,23 +2458,23 @@ function createWithdrawalsResource(client, tokens, contracts) {
2230
2458
  };
2231
2459
  }
2232
2460
  const finalizeCache = /* @__PURE__ */ new Map();
2233
- const quote = (p) => wrap2(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
2461
+ const quote = (p) => wrap7(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
2234
2462
  message: "Internal error while preparing a withdrawal quote.",
2235
2463
  ctx: { token: p.token, where: "withdrawals.quote" }
2236
2464
  });
2237
- const tryQuote = (p) => toResult2(OP_WITHDRAWALS.tryQuote, () => quote(p), {
2465
+ const tryQuote = (p) => toResult3(OP_WITHDRAWALS.tryQuote, () => quote(p), {
2238
2466
  message: "Internal error while preparing a withdrawal quote.",
2239
2467
  ctx: { token: p.token, where: "withdrawals.tryQuote" }
2240
2468
  });
2241
- const prepare = (p) => wrap2(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
2469
+ const prepare = (p) => wrap7(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
2242
2470
  message: "Internal error while preparing a withdrawal plan.",
2243
2471
  ctx: { token: p.token, where: "withdrawals.prepare" }
2244
2472
  });
2245
- const tryPrepare = (p) => toResult2(OP_WITHDRAWALS.tryPrepare, () => prepare(p), {
2473
+ const tryPrepare = (p) => toResult3(OP_WITHDRAWALS.tryPrepare, () => prepare(p), {
2246
2474
  message: "Internal error while preparing a withdrawal plan.",
2247
2475
  ctx: { token: p.token, where: "withdrawals.tryPrepare" }
2248
2476
  });
2249
- const create = (p) => wrap2(
2477
+ const create = (p) => wrap7(
2250
2478
  OP_WITHDRAWALS.create,
2251
2479
  async () => {
2252
2480
  const plan = await prepare(p);
@@ -2350,11 +2578,11 @@ function createWithdrawalsResource(client, tokens, contracts) {
2350
2578
  ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.create" }
2351
2579
  }
2352
2580
  );
2353
- const tryCreate = (p) => toResult2(OP_WITHDRAWALS.tryCreate, () => create(p), {
2581
+ const tryCreate = (p) => toResult3(OP_WITHDRAWALS.tryCreate, () => create(p), {
2354
2582
  message: "Internal error while creating withdrawal transactions.",
2355
2583
  ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
2356
2584
  });
2357
- const status = (h) => wrap2(
2585
+ const status = (h) => wrap7(
2358
2586
  OP_WITHDRAWALS.status,
2359
2587
  async () => {
2360
2588
  const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2409,7 +2637,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2409
2637
  const wait = (h, opts = {
2410
2638
  for: "l2",
2411
2639
  pollMs: 5500
2412
- }) => wrap2(
2640
+ }) => wrap7(
2413
2641
  OP_WITHDRAWALS.wait,
2414
2642
  async () => {
2415
2643
  const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2478,7 +2706,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2478
2706
  }
2479
2707
  }
2480
2708
  );
2481
- const finalize = (l2TxHash) => wrap2(
2709
+ const finalize = (l2TxHash) => wrap7(
2482
2710
  OP_WITHDRAWALS.finalize.send,
2483
2711
  async () => {
2484
2712
  const pack = await (async () => {
@@ -2550,11 +2778,11 @@ function createWithdrawalsResource(client, tokens, contracts) {
2550
2778
  ctx: { l2TxHash, where: "withdrawals.finalize" }
2551
2779
  }
2552
2780
  );
2553
- const tryFinalize = (l2TxHash) => toResult2("withdrawals.tryFinalize", () => finalize(l2TxHash), {
2781
+ const tryFinalize = (l2TxHash) => toResult3("withdrawals.tryFinalize", () => finalize(l2TxHash), {
2554
2782
  message: "Internal error while attempting to tryFinalize withdrawal.",
2555
2783
  ctx: { l2TxHash, where: "withdrawals.tryFinalize" }
2556
2784
  });
2557
- const tryWait = (h, opts) => toResult2(
2785
+ const tryWait = (h, opts) => toResult3(
2558
2786
  OP_WITHDRAWALS.tryWait,
2559
2787
  async () => {
2560
2788
  const v = await wait(h, opts);
@@ -2589,17 +2817,1105 @@ function createWithdrawalsResource(client, tokens, contracts) {
2589
2817
  tryWait
2590
2818
  };
2591
2819
  }
2820
+ function getInteropAttributes(params, ctx) {
2821
+ const bundleAttributes = [];
2822
+ if (params.execution?.only) {
2823
+ bundleAttributes.push(ctx.attributes.bundle.executionAddress(params.execution.only));
2824
+ }
2825
+ if (params.unbundling?.by) {
2826
+ bundleAttributes.push(ctx.attributes.bundle.unbundlerAddress(params.unbundling.by));
2827
+ }
2828
+ bundleAttributes.push(ctx.attributes.bundle.useFixedFee(params.fee?.useFixed ?? false));
2829
+ const callAttributes = params.actions.map((action) => {
2830
+ switch (action.type) {
2831
+ case "sendNative": {
2832
+ const baseMatches = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
2833
+ if (baseMatches) {
2834
+ return [ctx.attributes.call.interopCallValue(action.amount)];
2835
+ }
2836
+ return [ctx.attributes.call.indirectCall(action.amount)];
2837
+ }
2838
+ case "call":
2839
+ if (action.value && action.value > 0n) {
2840
+ return [ctx.attributes.call.interopCallValue(action.value)];
2841
+ }
2842
+ return [];
2843
+ case "sendErc20":
2844
+ return [ctx.attributes.call.indirectCall(0n)];
2845
+ default:
2846
+ assertNever(action);
2847
+ }
2848
+ });
2849
+ return { bundleAttributes, callAttributes };
2850
+ }
2851
+ function createViemAttributesResource() {
2852
+ const encode = (fn, args) => encodeFunctionData({
2853
+ abi: IERC7786Attributes_default,
2854
+ functionName: fn,
2855
+ args
2856
+ });
2857
+ return createAttributesResource({ encode });
2858
+ }
2859
+ var PREFIX_EVM_CHAIN = toBytes("0x00010000");
2860
+ var PREFIX_EVM_ADDRESS = toBytes("0x000100000014");
2861
+ function formatInteropEvmChain(chainId) {
2862
+ const chainRef = toBytes(toHex(chainId));
2863
+ const chainRefLength = toBytes(toHex(chainRef.length, { size: 1 }));
2864
+ const payload = concat([PREFIX_EVM_CHAIN, chainRefLength, chainRef, new Uint8Array([0])]);
2865
+ return toHex(payload);
2866
+ }
2867
+ function formatInteropEvmAddress(address) {
2868
+ const normalized = getAddress(address);
2869
+ const addrBytes = toBytes(normalized);
2870
+ const payload = concat([PREFIX_EVM_ADDRESS, addrBytes]);
2871
+ return toHex(payload);
2872
+ }
2873
+ var interopCodec = {
2874
+ formatChain: formatInteropEvmChain,
2875
+ formatAddress: formatInteropEvmAddress
2876
+ };
2877
+ function getErc20Tokens(params) {
2878
+ const erc20Tokens = /* @__PURE__ */ new Map();
2879
+ for (const action of params.actions) {
2880
+ if (action.type !== "sendErc20") continue;
2881
+ erc20Tokens.set(action.token.toLowerCase(), action.token);
2882
+ }
2883
+ return Array.from(erc20Tokens.values());
2884
+ }
2885
+ function buildEnsureTokenSteps(erc20Tokens, ctx) {
2886
+ if (erc20Tokens.length === 0) return [];
2887
+ return erc20Tokens.map((token) => ({
2888
+ key: `ensure-token:${token.toLowerCase()}`,
2889
+ kind: "interop.ntv.ensure-token",
2890
+ description: `Ensure ${token} is registered in the native token vault`,
2891
+ tx: {
2892
+ to: ctx.l2NativeTokenVault,
2893
+ data: encodeFunctionData({
2894
+ abi: L2NativeTokenVault_default,
2895
+ functionName: "ensureTokenIsRegistered",
2896
+ args: [token]
2897
+ }),
2898
+ ...ctx.gasOverrides
2899
+ }
2900
+ }));
2901
+ }
2902
+ async function buildApproveSteps(approvals, ctx) {
2903
+ const steps = [];
2904
+ for (const approval of approvals) {
2905
+ const currentAllowance = await ctx.client.l2.readContract({
2906
+ address: approval.token,
2907
+ abi: IERC20_default,
2908
+ functionName: "allowance",
2909
+ args: [ctx.sender, approval.spender]
2910
+ });
2911
+ if (currentAllowance < approval.amount) {
2912
+ steps.push({
2913
+ key: `approve:${approval.token}:${approval.spender}`,
2914
+ kind: "approve",
2915
+ description: `Approve ${approval.spender} to spend ${approval.amount} of ${approval.token}`,
2916
+ tx: {
2917
+ to: approval.token,
2918
+ data: encodeFunctionData({
2919
+ abi: IERC20_default,
2920
+ functionName: "approve",
2921
+ args: [approval.spender, approval.amount]
2922
+ }),
2923
+ ...ctx.gasOverrides
2924
+ }
2925
+ });
2926
+ }
2927
+ }
2928
+ return steps;
2929
+ }
2930
+ async function resolveErc20AssetIds(erc20Tokens, ctx) {
2931
+ const assetIds = /* @__PURE__ */ new Map();
2932
+ if (erc20Tokens.length === 0) return assetIds;
2933
+ for (const token of erc20Tokens) {
2934
+ const { result: assetId } = await ctx.client.l2.simulateContract({
2935
+ address: ctx.l2NativeTokenVault,
2936
+ abi: L2NativeTokenVault_default,
2937
+ functionName: "ensureTokenIsRegistered",
2938
+ args: [token],
2939
+ account: ctx.sender
2940
+ });
2941
+ assetIds.set(token.toLowerCase(), assetId);
2942
+ }
2943
+ return assetIds;
2944
+ }
2945
+
2946
+ // src/adapters/viem/resources/interop/services/starter-data.ts
2947
+ async function getStarterData(params, ctx, erc20AssetIds) {
2948
+ const starterData = [];
2949
+ for (const action of params.actions) {
2950
+ switch (action.type) {
2951
+ case "sendErc20": {
2952
+ const assetId = erc20AssetIds.get(action.token.toLowerCase());
2953
+ if (!assetId) {
2954
+ throw new Error(`Missing precomputed assetId for token ${action.token}.`);
2955
+ }
2956
+ const transferData = encodeNativeTokenVaultTransferData(
2957
+ action.amount,
2958
+ action.to,
2959
+ action.token
2960
+ );
2961
+ const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
2962
+ starterData.push({ assetRouterPayload });
2963
+ break;
2964
+ }
2965
+ case "sendNative":
2966
+ if (!ctx.baseTokens.matches) {
2967
+ const assetId = await ctx.tokens.baseTokenAssetId();
2968
+ const transferData = encodeNativeTokenVaultTransferData(
2969
+ action.amount,
2970
+ action.to,
2971
+ ctx.baseTokens.src
2972
+ );
2973
+ const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
2974
+ starterData.push({ assetRouterPayload });
2975
+ } else {
2976
+ starterData.push({});
2977
+ }
2978
+ break;
2979
+ case "call":
2980
+ starterData.push({});
2981
+ break;
2982
+ default:
2983
+ assertNever(action);
2984
+ }
2985
+ }
2986
+ return starterData;
2987
+ }
2988
+
2989
+ // src/adapters/viem/resources/interop/services/fee.ts
2990
+ var { wrap: wrap2 } = createErrorHandlers("interop");
2991
+ async function buildFeeInfo(params, ctx, numStarters) {
2992
+ const useFixed = params.fee?.useFixed ?? false;
2993
+ if (useFixed) {
2994
+ const zkFeePerCall = await wrap2(
2995
+ OP_INTEROP.svc.fees.zkInteropFee,
2996
+ () => ctx.client.l2.readContract({
2997
+ address: ctx.interopCenter,
2998
+ abi: IInteropCenter_default,
2999
+ functionName: "ZK_INTEROP_FEE"
3000
+ }),
3001
+ { message: "Failed to fetch ZK interop fee from InteropCenter." }
3002
+ );
3003
+ const zkFeeTotal = zkFeePerCall * BigInt(numStarters);
3004
+ const zkTokenAddress = await wrap2(
3005
+ OP_INTEROP.svc.fees.zkToken,
3006
+ () => ctx.client.l2.readContract({
3007
+ address: ctx.interopCenter,
3008
+ abi: IInteropCenter_default,
3009
+ functionName: "zkToken"
3010
+ }),
3011
+ { message: "Failed to fetch ZK token address from InteropCenter." }
3012
+ );
3013
+ const approval = {
3014
+ token: zkTokenAddress,
3015
+ spender: ctx.interopCenter,
3016
+ amount: zkFeeTotal
3017
+ };
3018
+ return {
3019
+ approval,
3020
+ fee: { token: zkTokenAddress, amount: zkFeeTotal }
3021
+ };
3022
+ } else {
3023
+ const protocolFeePerCall = await wrap2(
3024
+ OP_INTEROP.svc.fees.protocolFee,
3025
+ () => ctx.client.l2.readContract({
3026
+ address: ctx.interopCenter,
3027
+ abi: IInteropCenter_default,
3028
+ functionName: "interopProtocolFee"
3029
+ }),
3030
+ { message: "Failed to fetch interop protocol fee from InteropCenter." }
3031
+ );
3032
+ const totalFee = protocolFeePerCall * BigInt(numStarters);
3033
+ return {
3034
+ approval: null,
3035
+ fee: { token: ctx.baseTokens.src, amount: totalFee }
3036
+ };
3037
+ }
3038
+ }
3039
+
3040
+ // src/adapters/viem/resources/interop/routes/indirect.ts
3041
+ function routeIndirect() {
3042
+ return {
3043
+ // eslint-disable-next-line @typescript-eslint/require-await
3044
+ async preflight(params, ctx) {
3045
+ preflightIndirect(params, {
3046
+ dstChainId: ctx.dstChainId,
3047
+ baseTokens: ctx.baseTokens,
3048
+ l2AssetRouter: ctx.l2AssetRouter,
3049
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
3050
+ codec: interopCodec
3051
+ });
3052
+ },
3053
+ async build(params, ctx) {
3054
+ const steps = [];
3055
+ const erc20Tokens = getErc20Tokens(params);
3056
+ const [erc20AssetIds, feeInfo] = await Promise.all([
3057
+ resolveErc20AssetIds(erc20Tokens, ctx),
3058
+ buildFeeInfo(params, ctx, params.actions.length)
3059
+ ]);
3060
+ const attributes = getInteropAttributes(params, ctx);
3061
+ const starterData = await getStarterData(params, ctx, erc20AssetIds);
3062
+ const bundle = buildIndirectBundle(
3063
+ params,
3064
+ {
3065
+ dstChainId: ctx.dstChainId,
3066
+ baseTokens: ctx.baseTokens,
3067
+ l2AssetRouter: ctx.l2AssetRouter,
3068
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
3069
+ codec: interopCodec
3070
+ },
3071
+ attributes,
3072
+ starterData,
3073
+ feeInfo
3074
+ );
3075
+ steps.push(...buildEnsureTokenSteps(erc20Tokens, ctx));
3076
+ steps.push(...await buildApproveSteps(bundle.approvals, ctx));
3077
+ const data = encodeFunctionData({
3078
+ abi: IInteropCenter_default,
3079
+ functionName: "sendBundle",
3080
+ args: [bundle.dstChain, bundle.starters, bundle.bundleAttributes]
3081
+ });
3082
+ steps.push({
3083
+ key: "sendBundle",
3084
+ kind: "interop.center",
3085
+ description: "Send interop bundle (indirect route)",
3086
+ tx: {
3087
+ to: ctx.interopCenter,
3088
+ data,
3089
+ value: bundle.quoteExtras.totalActionValue + feeInfo.fee.amount,
3090
+ ...ctx.gasOverrides
3091
+ }
3092
+ });
3093
+ return {
3094
+ steps,
3095
+ approvals: bundle.approvals,
3096
+ quoteExtras: bundle.quoteExtras,
3097
+ interopFee: feeInfo.fee
3098
+ };
3099
+ }
3100
+ };
3101
+ }
3102
+ function routeDirect() {
3103
+ return {
3104
+ // eslint-disable-next-line @typescript-eslint/require-await
3105
+ async preflight(params, ctx) {
3106
+ preflightDirect(params, {
3107
+ dstChainId: ctx.dstChainId,
3108
+ baseTokens: ctx.baseTokens,
3109
+ l2AssetRouter: ctx.l2AssetRouter,
3110
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
3111
+ codec: interopCodec
3112
+ });
3113
+ },
3114
+ async build(params, ctx) {
3115
+ const steps = [];
3116
+ const attrs = getInteropAttributes(params, ctx);
3117
+ const feeInfo = await buildFeeInfo(params, ctx, params.actions.length);
3118
+ const built = buildDirectBundle(
3119
+ params,
3120
+ {
3121
+ dstChainId: ctx.dstChainId,
3122
+ baseTokens: ctx.baseTokens,
3123
+ l2AssetRouter: ctx.l2AssetRouter,
3124
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
3125
+ codec: interopCodec
3126
+ },
3127
+ attrs,
3128
+ feeInfo
3129
+ );
3130
+ steps.push(...await buildApproveSteps(built.approvals, ctx));
3131
+ const data = encodeFunctionData({
3132
+ abi: IInteropCenter_default,
3133
+ functionName: "sendBundle",
3134
+ args: [built.dstChain, built.starters, built.bundleAttributes]
3135
+ });
3136
+ steps.push({
3137
+ key: "sendBundle",
3138
+ kind: "interop.center",
3139
+ description: `Send interop bundle (direct route; ${params.actions.length} actions)`,
3140
+ tx: {
3141
+ to: ctx.interopCenter,
3142
+ data,
3143
+ value: built.quoteExtras.totalActionValue + feeInfo.fee.amount,
3144
+ ...ctx.gasOverrides
3145
+ }
3146
+ });
3147
+ return {
3148
+ steps,
3149
+ approvals: built.approvals,
3150
+ quoteExtras: built.quoteExtras,
3151
+ interopFee: feeInfo.fee
3152
+ };
3153
+ }
3154
+ };
3155
+ }
3156
+
3157
+ // src/adapters/viem/resources/interop/context.ts
3158
+ async function assertInteropProtocolVersion(client, srcChainId, dstChainId) {
3159
+ const [srcProtocolVersion, dstProtocolVersion] = await Promise.all([
3160
+ client.getProtocolVersion(srcChainId),
3161
+ client.getProtocolVersion(dstChainId)
3162
+ ]);
3163
+ assertProtocolVersion(srcChainId, srcProtocolVersion);
3164
+ assertProtocolVersion(dstChainId, dstProtocolVersion);
3165
+ }
3166
+ async function commonCtx3(dstPublicClient, params, client, tokens, contracts, attributes) {
3167
+ const sender = client.account.address;
3168
+ const chainId = BigInt(await client.l2.getChainId());
3169
+ const dstChainId = BigInt(await dstPublicClient.getChainId());
3170
+ const {
3171
+ bridgehub,
3172
+ l2AssetRouter,
3173
+ l2NativeTokenVault,
3174
+ interopCenter,
3175
+ interopHandler,
3176
+ l2MessageVerification
3177
+ } = await contracts.addresses();
3178
+ await assertInteropProtocolVersion(client, chainId, dstChainId);
3179
+ const [srcBaseToken, dstBaseToken] = await Promise.all([
3180
+ client.baseToken(chainId),
3181
+ client.baseToken(dstChainId)
3182
+ ]);
3183
+ const baseMatches = srcBaseToken.toLowerCase() === dstBaseToken.toLowerCase();
3184
+ return {
3185
+ client,
3186
+ tokens,
3187
+ contracts,
3188
+ sender,
3189
+ chainIdL2: chainId,
3190
+ chainId,
3191
+ bridgehub,
3192
+ dstChainId,
3193
+ dstPublicClient,
3194
+ interopCenter,
3195
+ interopHandler,
3196
+ l2MessageVerification,
3197
+ l2AssetRouter,
3198
+ l2NativeTokenVault,
3199
+ baseTokens: { src: srcBaseToken, dst: dstBaseToken, matches: baseMatches },
3200
+ attributes,
3201
+ gasOverrides: params.txOverrides ? toGasOverrides(params.txOverrides) : void 0
3202
+ };
3203
+ }
3204
+ function getTopics() {
3205
+ const topics = {
3206
+ interopBundleSent: encodeEventTopics({
3207
+ abi: IInteropCenter_default,
3208
+ eventName: "InteropBundleSent"
3209
+ })[0],
3210
+ bundleVerified: encodeEventTopics({
3211
+ abi: IInteropHandler_default,
3212
+ eventName: "BundleVerified"
3213
+ })[0],
3214
+ bundleExecuted: encodeEventTopics({
3215
+ abi: IInteropHandler_default,
3216
+ eventName: "BundleExecuted"
3217
+ })[0],
3218
+ bundleUnbundled: encodeEventTopics({
3219
+ abi: IInteropHandler_default,
3220
+ eventName: "BundleUnbundled"
3221
+ })[0]
3222
+ };
3223
+ return { topics };
3224
+ }
3225
+ var { wrap: wrap3 } = createErrorHandlers("interop");
3226
+ var DEFAULT_BLOCKS_RANGE_SIZE = 1e4;
3227
+ var DEFAULT_MAX_BLOCKS_BACK = 2e4;
3228
+ var SAFE_BLOCKS_RANGE_SIZE = 1e3;
3229
+ function parseMaxBlockRangeLimit(error) {
3230
+ const msg = typeof error === "object" && error !== null && "message" in error ? String(error.message) : String(error);
3231
+ const match = /query exceeds max block range\s+(\d+)/i.exec(msg);
3232
+ if (!match) return null;
3233
+ const limit = Number.parseInt(match[1], 10);
3234
+ return Number.isInteger(limit) && limit > 0 ? limit : null;
3235
+ }
3236
+ async function getTxReceipt(provider, txHash) {
3237
+ const receipt = await wrap3(
3238
+ OP_INTEROP.svc.status.sourceReceipt,
3239
+ async () => {
3240
+ try {
3241
+ return await provider.getTransactionReceipt({ hash: txHash });
3242
+ } catch (error) {
3243
+ if (isReceiptNotFound(error)) return null;
3244
+ throw error;
3245
+ }
3246
+ },
3247
+ {
3248
+ ctx: { where: "l2.getTransactionReceipt", l2SrcTxHash: txHash },
3249
+ message: "Failed to fetch source L2 receipt for interop tx."
3250
+ }
3251
+ );
3252
+ if (!receipt) return null;
3253
+ return {
3254
+ logs: receipt.logs.map((log) => ({
3255
+ address: log.address,
3256
+ topics: log.topics,
3257
+ data: log.data,
3258
+ transactionHash: log.transactionHash
3259
+ }))
3260
+ };
3261
+ }
3262
+ async function getLogs(provider, address, topics, opts) {
3263
+ const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
3264
+ const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
3265
+ return await wrap3(
3266
+ OP_INTEROP.svc.status.dstLogs,
3267
+ async () => {
3268
+ const currentBlock = await provider.getBlockNumber();
3269
+ const minBlock = BigInt(Math.max(0, Number(currentBlock) - maxBlocksBack));
3270
+ let toBlock = currentBlock;
3271
+ let chunkSize = initialChunkSize;
3272
+ while (toBlock >= minBlock) {
3273
+ const fromBlock = toBlock - BigInt(chunkSize) + 1n > minBlock ? toBlock - BigInt(chunkSize) + 1n : minBlock;
3274
+ try {
3275
+ const rawLogs = await provider.request({
3276
+ method: "eth_getLogs",
3277
+ params: [
3278
+ {
3279
+ address,
3280
+ topics,
3281
+ fromBlock: numberToHex(fromBlock),
3282
+ toBlock: numberToHex(toBlock)
3283
+ }
3284
+ ]
3285
+ });
3286
+ if (rawLogs.length > 0) {
3287
+ return rawLogs.map((log) => ({
3288
+ address: log.address,
3289
+ topics: log.topics,
3290
+ data: log.data,
3291
+ transactionHash: log.transactionHash
3292
+ }));
3293
+ }
3294
+ toBlock = fromBlock - 1n;
3295
+ } catch (error) {
3296
+ const serverLimit = parseMaxBlockRangeLimit(error);
3297
+ if (serverLimit == null) {
3298
+ if (chunkSize > SAFE_BLOCKS_RANGE_SIZE) {
3299
+ chunkSize = SAFE_BLOCKS_RANGE_SIZE;
3300
+ } else {
3301
+ throw error;
3302
+ }
3303
+ } else {
3304
+ chunkSize = Math.min(chunkSize, serverLimit);
3305
+ }
3306
+ }
3307
+ }
3308
+ return [];
3309
+ },
3310
+ {
3311
+ ctx: { address, maxBlocksBack, logChunkSize: initialChunkSize },
3312
+ message: "Failed to query destination bundle lifecycle logs."
3313
+ }
3314
+ );
3315
+ }
3316
+ async function getInteropRoot(provider, rootChainId, batchNumber) {
3317
+ return await wrap3(
3318
+ OP_INTEROP.svc.status.getRoot,
3319
+ async () => {
3320
+ return await provider.readContract({
3321
+ address: L2_INTEROP_ROOT_STORAGE_ADDRESS,
3322
+ abi: IInteropRootStorage_default,
3323
+ functionName: "interopRoots",
3324
+ args: [rootChainId, batchNumber]
3325
+ });
3326
+ },
3327
+ {
3328
+ ctx: { rootChainId, batchNumber },
3329
+ message: "Failed to get interop root from the destination chain."
3330
+ }
3331
+ );
3332
+ }
3333
+
3334
+ // src/adapters/viem/resources/interop/services/finalization/bundle.ts
3335
+ var { wrap: wrap4 } = createErrorHandlers("interop");
3336
+ async function getBundleStatus(client, dstProvider, topics, bundleHash, opts) {
3337
+ const { interopHandler } = await client.ensureAddresses();
3338
+ const bundleLogs = await getLogs(dstProvider, interopHandler, [null, bundleHash], opts);
3339
+ const findLastByTopic = (eventTopic) => bundleLogs.findLast((log) => log.topics[0].toLowerCase() === eventTopic.toLowerCase());
3340
+ const lifecycleChecks = [
3341
+ { phase: "UNBUNDLED", topic: topics.bundleUnbundled, includeTxHash: true },
3342
+ { phase: "EXECUTED", topic: topics.bundleExecuted, includeTxHash: true },
3343
+ { phase: "VERIFIED", topic: topics.bundleVerified }
3344
+ ];
3345
+ for (const check of lifecycleChecks) {
3346
+ const match = findLastByTopic(check.topic);
3347
+ if (!match) continue;
3348
+ if (check.includeTxHash) {
3349
+ return { phase: check.phase, dstExecTxHash: match.transactionHash };
3350
+ }
3351
+ return { phase: check.phase };
3352
+ }
3353
+ return { phase: "SENT" };
3354
+ }
3355
+ async function executeBundle(client, dstProvider, info, opts) {
3356
+ const { topics } = getTopics();
3357
+ const { bundleHash, encodedData, proof } = info;
3358
+ const dstStatus = await getBundleStatus(client, dstProvider, topics, bundleHash, opts);
3359
+ if (["EXECUTED", "UNBUNDLED"].includes(dstStatus.phase)) {
3360
+ throw createError("STATE", {
3361
+ resource: "interop",
3362
+ operation: OP_INTEROP.finalize,
3363
+ message: `Interop bundle has already been ${dstStatus.phase.toLowerCase()}.`,
3364
+ context: { bundleHash }
3365
+ });
3366
+ }
3367
+ const dstWallet = await wrap4(
3368
+ OP_INTEROP.exec.sendStep,
3369
+ () => createWalletClient({
3370
+ account: client.account,
3371
+ transport: custom(dstProvider.transport),
3372
+ chain: dstProvider.chain
3373
+ }),
3374
+ { message: "Failed to create destination wallet client." }
3375
+ );
3376
+ const { interopHandler } = await client.ensureAddresses();
3377
+ try {
3378
+ const hash = await dstWallet.writeContract({
3379
+ address: interopHandler,
3380
+ abi: IInteropHandler_default,
3381
+ functionName: "executeBundle",
3382
+ args: [encodedData, proof],
3383
+ account: client.account,
3384
+ chain: dstProvider.chain ?? null
3385
+ });
3386
+ return {
3387
+ hash,
3388
+ wait: async () => {
3389
+ try {
3390
+ const receipt = await dstProvider.waitForTransactionReceipt({ hash });
3391
+ if (receipt.status === "reverted") {
3392
+ throw createError("EXECUTION", {
3393
+ resource: "interop",
3394
+ operation: OP_INTEROP.exec.waitStep,
3395
+ message: "Interop bundle execution reverted on destination.",
3396
+ context: { txHash: hash }
3397
+ });
3398
+ }
3399
+ return receipt;
3400
+ } catch (e) {
3401
+ if (isZKsyncError(e)) throw e;
3402
+ throw toZKsyncError(
3403
+ "EXECUTION",
3404
+ {
3405
+ resource: "interop",
3406
+ operation: OP_INTEROP.exec.waitStep,
3407
+ message: "Failed while waiting for executeBundle transaction on destination.",
3408
+ context: { txHash: hash }
3409
+ },
3410
+ e
3411
+ );
3412
+ }
3413
+ }
3414
+ };
3415
+ } catch (e) {
3416
+ throw toZKsyncError(
3417
+ "EXECUTION",
3418
+ {
3419
+ resource: "interop",
3420
+ operation: OP_INTEROP.exec.sendStep,
3421
+ message: "Failed to send executeBundle transaction on destination chain."
3422
+ },
3423
+ e
3424
+ );
3425
+ }
3426
+ }
3427
+ function decodeInteropBundleSent(log) {
3428
+ const { args } = decodeEventLog({
3429
+ abi: IInteropCenter_default,
3430
+ eventName: "InteropBundleSent",
3431
+ data: log.data,
3432
+ topics: log.topics
3433
+ });
3434
+ return {
3435
+ bundleHash: args.interopBundleHash,
3436
+ sourceChainId: args.interopBundle.sourceChainId,
3437
+ destinationChainId: args.interopBundle.destinationChainId
3438
+ };
3439
+ }
3440
+ function decodeL1MessageData(log) {
3441
+ const [decoded] = decodeAbiParameters([{ type: "bytes" }], log.data);
3442
+ return decoded;
3443
+ }
3444
+
3445
+ // src/adapters/viem/resources/interop/services/finalization/polling.ts
3446
+ var { wrap: wrap5 } = createErrorHandlers("interop");
3447
+ function isProofNotReadyError(error) {
3448
+ return isZKsyncError(error, {
3449
+ operation: "zksrpc.getL2ToL1LogProof",
3450
+ messageIncludes: "proof not yet available"
3451
+ });
3452
+ }
3453
+ function shouldRetryRootFetch(error) {
3454
+ if (!isZKsyncError(error)) return false;
3455
+ return error.envelope.operation === OP_INTEROP.svc.status.getRoot;
3456
+ }
3457
+ async function waitForProof(client, l2SrcTxHash, logIndex, blockNumber, pollMs, deadline) {
3458
+ while (true) {
3459
+ if (Date.now() > deadline) {
3460
+ throw createError("TIMEOUT", {
3461
+ resource: "interop",
3462
+ operation: OP_INTEROP.svc.wait.timeout,
3463
+ message: "Timed out waiting for block to be finalized.",
3464
+ context: { l2SrcTxHash, logIndex, blockNumber }
3465
+ });
3466
+ }
3467
+ const finalizedBlock = await client.l2.getBlock({ blockTag: "finalized" });
3468
+ if (finalizedBlock && finalizedBlock.number !== null && finalizedBlock.number >= blockNumber) {
3469
+ break;
3470
+ }
3471
+ await sleep(pollMs);
3472
+ }
3473
+ while (true) {
3474
+ if (Date.now() > deadline) {
3475
+ throw createError("TIMEOUT", {
3476
+ resource: "interop",
3477
+ operation: OP_INTEROP.svc.wait.timeout,
3478
+ message: "Timed out waiting for L2->L1 log proof to become available.",
3479
+ context: { l2SrcTxHash, logIndex }
3480
+ });
3481
+ }
3482
+ try {
3483
+ return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex, "messageRoot" /* MessageRoot */);
3484
+ } catch (error) {
3485
+ if (!isProofNotReadyError(error)) throw error;
3486
+ }
3487
+ await sleep(pollMs);
3488
+ }
3489
+ }
3490
+ async function waitForRoot(provider, chainId, batchNumber, pollMs, deadline) {
3491
+ while (true) {
3492
+ if (Date.now() > deadline) {
3493
+ throw createError("TIMEOUT", {
3494
+ resource: "interop",
3495
+ operation: OP_INTEROP.svc.wait.timeout,
3496
+ message: "Timed out waiting for interop root to become available.",
3497
+ context: { chainId, batchNumber }
3498
+ });
3499
+ }
3500
+ let interopRoot = null;
3501
+ try {
3502
+ const root = await getInteropRoot(provider, chainId, batchNumber);
3503
+ if (root !== ZERO_HASH) {
3504
+ interopRoot = root;
3505
+ }
3506
+ } catch (error) {
3507
+ if (!shouldRetryRootFetch(error)) throw error;
3508
+ interopRoot = null;
3509
+ }
3510
+ if (interopRoot) {
3511
+ return interopRoot;
3512
+ }
3513
+ await sleep(pollMs);
3514
+ }
3515
+ }
3516
+ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
3517
+ while (true) {
3518
+ if (Date.now() > deadline) {
3519
+ throw createError("TIMEOUT", {
3520
+ resource: "interop",
3521
+ operation: OP_INTEROP.svc.wait.timeout,
3522
+ message: "Timed out waiting for source receipt to be available.",
3523
+ context: { txHash }
3524
+ });
3525
+ }
3526
+ const receipt = await wrap5(
3527
+ OP_INTEROP.svc.status.sourceReceipt,
3528
+ () => client.zks.getReceiptWithL2ToL1(txHash),
3529
+ {
3530
+ ctx: { where: "zks.getReceiptWithL2ToL1", txHash },
3531
+ message: "Failed to fetch source L2 receipt (with L2->L1 logs) for interop tx."
3532
+ }
3533
+ );
3534
+ if (receipt) {
3535
+ return receipt;
3536
+ }
3537
+ await sleep(pollMs);
3538
+ }
3539
+ }
3540
+ async function waitForFinalization(client, dstProvider, gwProvider, input, opts) {
3541
+ const { topics } = getTopics();
3542
+ const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
3543
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
3544
+ const deadline = Date.now() + timeoutMs;
3545
+ const ids = resolveIdsFromWaitable(input);
3546
+ if (!ids.l2SrcTxHash) {
3547
+ throw createError("STATE", {
3548
+ resource: "interop",
3549
+ operation: OP_INTEROP.svc.status.sourceReceipt,
3550
+ message: "Cannot wait for interop finalization: missing l2SrcTxHash.",
3551
+ context: { input }
3552
+ });
3553
+ }
3554
+ const { interopCenter } = await client.ensureAddresses();
3555
+ let bundleInfo = null;
3556
+ while (!bundleInfo) {
3557
+ if (Date.now() > deadline) {
3558
+ throw createError("TIMEOUT", {
3559
+ resource: "interop",
3560
+ operation: OP_INTEROP.svc.wait.timeout,
3561
+ message: "Timed out waiting for source receipt to be available.",
3562
+ context: { l2SrcTxHash: ids.l2SrcTxHash }
3563
+ });
3564
+ }
3565
+ const txReceipt = await waitForTxReceipt(client, ids.l2SrcTxHash, pollMs, deadline);
3566
+ bundleInfo = parseBundleReceiptInfo({
3567
+ rawReceipt: txReceipt,
3568
+ interopCenter,
3569
+ interopBundleSentTopic: topics.interopBundleSent,
3570
+ decodeInteropBundleSent: (log) => decodeInteropBundleSent(log),
3571
+ decodeL1MessageData,
3572
+ l2SrcTxHash: ids.l2SrcTxHash
3573
+ });
3574
+ }
3575
+ const proof = await waitForProof(
3576
+ client,
3577
+ ids.l2SrcTxHash,
3578
+ bundleInfo.l2ToL1LogIndex,
3579
+ BigInt(bundleInfo.rawReceipt.blockNumber),
3580
+ pollMs,
3581
+ deadline
3582
+ );
3583
+ const finalizationInfo = buildFinalizationInfo(
3584
+ { l2SrcTxHash: ids.l2SrcTxHash, bundleHash: ids.bundleHash },
3585
+ bundleInfo,
3586
+ proof,
3587
+ bundleInfo.l1MessageData
3588
+ );
3589
+ if (proof.gatewayBlockNumber == null) {
3590
+ throw createError("STATE", {
3591
+ resource: "interop",
3592
+ operation: OP_INTEROP.svc.wait.timeout,
3593
+ message: "Proof missing gatewayBlockNumber required for interop finalization.",
3594
+ context: { l2SrcTxHash: ids.l2SrcTxHash }
3595
+ });
3596
+ }
3597
+ const gwChainId = BigInt(await gwProvider.getChainId());
3598
+ await waitForRoot(dstProvider, gwChainId, proof.gatewayBlockNumber, pollMs, deadline);
3599
+ return finalizationInfo;
3600
+ }
3601
+
3602
+ // src/adapters/viem/resources/interop/services/finalization/status.ts
3603
+ async function getStatus(client, dstProvider, input, opts) {
3604
+ const { topics } = getTopics();
3605
+ const baseIds = resolveIdsFromWaitable(input);
3606
+ const enrichedIds = await (async () => {
3607
+ if (baseIds.bundleHash) return baseIds;
3608
+ if (!baseIds.l2SrcTxHash) return baseIds;
3609
+ const { interopCenter } = await client.ensureAddresses();
3610
+ const receipt = await getTxReceipt(client.l2, baseIds.l2SrcTxHash);
3611
+ if (!receipt) return baseIds;
3612
+ const { bundleHash } = parseBundleSentFromReceipt({
3613
+ receipt: { logs: receipt.logs },
3614
+ interopCenter,
3615
+ interopBundleSentTopic: topics.interopBundleSent,
3616
+ decodeInteropBundleSent: (log) => decodeInteropBundleSent(log)
3617
+ });
3618
+ return { ...baseIds, bundleHash };
3619
+ })();
3620
+ if (!enrichedIds.bundleHash) {
3621
+ const phase = enrichedIds.l2SrcTxHash ? "SENT" : "UNKNOWN";
3622
+ return {
3623
+ phase,
3624
+ l2SrcTxHash: enrichedIds.l2SrcTxHash,
3625
+ bundleHash: enrichedIds.bundleHash,
3626
+ dstExecTxHash: enrichedIds.dstExecTxHash
3627
+ };
3628
+ }
3629
+ const dstInfo = await getBundleStatus(client, dstProvider, topics, enrichedIds.bundleHash, opts);
3630
+ return {
3631
+ phase: dstInfo.phase,
3632
+ l2SrcTxHash: enrichedIds.l2SrcTxHash,
3633
+ bundleHash: enrichedIds.bundleHash,
3634
+ dstExecTxHash: dstInfo.dstExecTxHash ?? enrichedIds.dstExecTxHash
3635
+ };
3636
+ }
3637
+
3638
+ // src/adapters/viem/resources/interop/services/finalization/index.ts
3639
+ function createInteropFinalizationServices(client) {
3640
+ return {
3641
+ status(dstProvider, input, opts) {
3642
+ return getStatus(client, dstProvider, input, opts);
3643
+ },
3644
+ wait(dstProvider, gwProvider, input, opts) {
3645
+ return waitForFinalization(client, dstProvider, gwProvider, input, opts);
3646
+ },
3647
+ async finalize(dstProvider, info, opts) {
3648
+ const execResult = await executeBundle(client, dstProvider, info, opts);
3649
+ await execResult.wait();
3650
+ return {
3651
+ bundleHash: info.bundleHash,
3652
+ dstExecTxHash: execResult.hash
3653
+ };
3654
+ }
3655
+ };
3656
+ }
3657
+ function resolveChainRef(ref) {
3658
+ if (typeof ref === "string") {
3659
+ return createPublicClient({ transport: http(ref) });
3660
+ }
3661
+ return ref;
3662
+ }
3663
+
3664
+ // src/adapters/viem/resources/interop/services/gas.ts
3665
+ async function quoteStepsL2Fee(steps, ctx) {
3666
+ if (steps.length === 0) return 0n;
3667
+ const estimator = viemToGasEstimator(ctx.client.l2);
3668
+ let maxFeePerGas;
3669
+ try {
3670
+ const fees = await estimator.estimateFeesPerGas();
3671
+ maxFeePerGas = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
3672
+ } catch {
3673
+ return void 0;
3674
+ }
3675
+ let total = 0n;
3676
+ for (const step of steps) {
3677
+ try {
3678
+ const coreTx = {
3679
+ to: step.tx.to,
3680
+ from: ctx.sender,
3681
+ data: step.tx.data,
3682
+ value: step.tx.value,
3683
+ gasLimit: step.tx.gas ?? step.tx.gasLimit,
3684
+ maxFeePerGas: step.tx.maxFeePerGas,
3685
+ maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas
3686
+ };
3687
+ const est = await estimator.estimateGas(coreTx);
3688
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
3689
+ total += buffered * maxFeePerGas;
3690
+ } catch {
3691
+ return void 0;
3692
+ }
3693
+ }
3694
+ return total;
3695
+ }
3696
+
3697
+ // src/adapters/viem/resources/interop/index.ts
3698
+ var { wrap: wrap6, toResult: toResult2 } = createErrorHandlers("interop");
3699
+ var ROUTES3 = {
3700
+ direct: routeDirect(),
3701
+ indirect: routeIndirect()
3702
+ };
3703
+ function createInteropResource(client, config, tokens, contracts, attributes) {
3704
+ let gwProviderCache;
3705
+ function requireConfig() {
3706
+ if (!config)
3707
+ throw createError("STATE", {
3708
+ resource: "interop",
3709
+ operation: "interop.init",
3710
+ message: "Interop is not configured. Pass gwChain in createViemSdk options."
3711
+ });
3712
+ return config;
3713
+ }
3714
+ function getGwProvider() {
3715
+ if (!gwProviderCache) gwProviderCache = resolveChainRef(requireConfig().gwChain);
3716
+ return gwProviderCache;
3717
+ }
3718
+ const svc = createInteropFinalizationServices(client);
3719
+ const tokensResource = tokens ?? createTokensResource(client);
3720
+ const contractsResource = contracts ?? createContractsResource(client);
3721
+ const attributesResource = attributes ?? createViemAttributesResource();
3722
+ async function buildPlanWithCtx(dstPublicClient, params) {
3723
+ const ctx = await commonCtx3(
3724
+ dstPublicClient,
3725
+ params,
3726
+ client,
3727
+ tokensResource,
3728
+ contractsResource,
3729
+ attributesResource
3730
+ );
3731
+ const route = pickInteropRoute({
3732
+ actions: params.actions,
3733
+ ctx: {
3734
+ sender: ctx.sender,
3735
+ srcChainId: ctx.chainId,
3736
+ dstChainId: ctx.dstChainId,
3737
+ baseTokenSrc: ctx.baseTokens.src,
3738
+ baseTokenDst: ctx.baseTokens.dst
3739
+ }
3740
+ });
3741
+ await wrap6(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
3742
+ message: "Interop preflight failed.",
3743
+ ctx: { where: `routes.${route}.preflight` }
3744
+ });
3745
+ const { steps, approvals, quoteExtras, interopFee } = await wrap6(
3746
+ OP_INTEROP.routes[route].build,
3747
+ () => ROUTES3[route].build(params, ctx),
3748
+ {
3749
+ message: "Failed to build interop route plan.",
3750
+ ctx: { where: `routes.${route}.build` }
3751
+ }
3752
+ );
3753
+ const l2Fee = await quoteStepsL2Fee(steps, ctx).catch(() => void 0);
3754
+ const summary = {
3755
+ route,
3756
+ approvalsNeeded: approvals,
3757
+ totalActionValue: quoteExtras.totalActionValue,
3758
+ bridgedTokenTotal: quoteExtras.bridgedTokenTotal,
3759
+ interopFee,
3760
+ l2Fee
3761
+ };
3762
+ return { plan: { route, summary, steps }, ctx };
3763
+ }
3764
+ async function buildPlan(dstPublicClient, params) {
3765
+ const { plan } = await buildPlanWithCtx(dstPublicClient, params);
3766
+ return plan;
3767
+ }
3768
+ const quote = (dstChain, params) => wrap6(OP_INTEROP.quote, async () => {
3769
+ const plan = await buildPlan(resolveChainRef(dstChain), params);
3770
+ return plan.summary;
3771
+ });
3772
+ const tryQuote = (dstChain, params) => toResult2(OP_INTEROP.tryQuote, () => quote(dstChain, params));
3773
+ const prepare = (dstChain, params) => wrap6(OP_INTEROP.prepare, () => buildPlan(resolveChainRef(dstChain), params), {
3774
+ message: "Internal error while preparing an interop plan.",
3775
+ ctx: { where: "interop.prepare" }
3776
+ });
3777
+ const tryPrepare = (dstChain, params) => toResult2(
3778
+ OP_INTEROP.tryPrepare,
3779
+ () => prepare(dstChain, params)
3780
+ );
3781
+ const create = (dstChain, params) => wrap6(
3782
+ OP_INTEROP.create,
3783
+ async () => {
3784
+ const { plan } = await buildPlanWithCtx(resolveChainRef(dstChain), params);
3785
+ const l2Wallet = client.getL2Wallet();
3786
+ const from = client.account.address;
3787
+ let next;
3788
+ if (typeof params.txOverrides?.nonce === "number") {
3789
+ next = params.txOverrides.nonce;
3790
+ } else {
3791
+ const blockTag = params.txOverrides?.nonce ?? "pending";
3792
+ next = await client.l2.getTransactionCount({ address: from, blockTag });
3793
+ }
3794
+ const stepHashes = {};
3795
+ for (const step of plan.steps) {
3796
+ let gasLimit = step.tx.gas ?? step.tx.gasLimit;
3797
+ if (!gasLimit) {
3798
+ try {
3799
+ const est = await client.l2.estimateGas({
3800
+ account: from,
3801
+ to: step.tx.to,
3802
+ data: step.tx.data,
3803
+ value: step.tx.value
3804
+ });
3805
+ gasLimit = est * 115n / 100n;
3806
+ } catch {
3807
+ }
3808
+ }
3809
+ let hash;
3810
+ try {
3811
+ hash = await l2Wallet.sendTransaction({
3812
+ to: step.tx.to,
3813
+ data: step.tx.data,
3814
+ value: step.tx.value,
3815
+ gas: gasLimit,
3816
+ maxFeePerGas: step.tx.maxFeePerGas,
3817
+ maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas,
3818
+ nonce: next++,
3819
+ account: client.account,
3820
+ chain: null
3821
+ });
3822
+ stepHashes[step.key] = hash;
3823
+ const rcpt = await client.l2.waitForTransactionReceipt({ hash });
3824
+ if (rcpt.status === "reverted") {
3825
+ throw createError("EXECUTION", {
3826
+ resource: "interop",
3827
+ operation: "interop.create.sendTransaction",
3828
+ message: "Interop transaction reverted on source L2.",
3829
+ context: { step: step.key, txHash: hash }
3830
+ });
3831
+ }
3832
+ } catch (e) {
3833
+ if (isZKsyncError(e)) throw e;
3834
+ throw toZKsyncError(
3835
+ "EXECUTION",
3836
+ {
3837
+ resource: "interop",
3838
+ operation: "interop.create.sendTransaction",
3839
+ message: "Failed to send or confirm an interop transaction step.",
3840
+ context: {
3841
+ step: step.key,
3842
+ txHash: hash,
3843
+ nonce: next - 1
3844
+ }
3845
+ },
3846
+ e
3847
+ );
3848
+ }
3849
+ }
3850
+ const last = Object.values(stepHashes).pop();
3851
+ return {
3852
+ kind: "interop",
3853
+ stepHashes,
3854
+ plan,
3855
+ l2SrcTxHash: last ?? "0x"
3856
+ };
3857
+ },
3858
+ {
3859
+ message: "Internal error while creating interop bundle.",
3860
+ ctx: { where: "interop.create" }
3861
+ }
3862
+ );
3863
+ const tryCreate = (dstChain, params) => toResult2(
3864
+ OP_INTEROP.tryCreate,
3865
+ () => create(dstChain, params)
3866
+ );
3867
+ const status = (dstChain, h, opts) => wrap6(OP_INTEROP.status, () => svc.status(resolveChainRef(dstChain), h, opts), {
3868
+ message: "Internal error while checking interop status.",
3869
+ ctx: { where: "interop.status" }
3870
+ });
3871
+ const wait = (dstChain, h, opts) => wrap6(OP_INTEROP.wait, () => svc.wait(resolveChainRef(dstChain), getGwProvider(), h, opts), {
3872
+ message: "Internal error while waiting for interop finalization.",
3873
+ ctx: { where: "interop.wait" }
3874
+ });
3875
+ const tryWait = (dstChain, h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(dstChain, h, opts));
3876
+ const finalize = (dstChain, h, opts) => wrap6(
3877
+ OP_INTEROP.finalize,
3878
+ async () => {
3879
+ const dstProvider = resolveChainRef(dstChain);
3880
+ if (isInteropFinalizationInfo(h)) {
3881
+ return svc.finalize(dstProvider, h, opts);
3882
+ }
3883
+ const info = await svc.wait(dstProvider, getGwProvider(), h);
3884
+ return svc.finalize(dstProvider, info, opts);
3885
+ },
3886
+ {
3887
+ message: "Failed to finalize/execute interop bundle on destination.",
3888
+ ctx: { where: "interop.finalize" }
3889
+ }
3890
+ );
3891
+ const tryFinalize = (dstChain, h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(dstChain, h, opts));
3892
+ return {
3893
+ quote,
3894
+ tryQuote,
3895
+ prepare,
3896
+ tryPrepare,
3897
+ create,
3898
+ tryCreate,
3899
+ status,
3900
+ wait,
3901
+ tryWait,
3902
+ finalize,
3903
+ tryFinalize
3904
+ };
3905
+ }
2592
3906
 
2593
3907
  // src/adapters/viem/sdk.ts
2594
- function createViemSdk(client) {
3908
+ function createViemSdk(client, options) {
2595
3909
  const tokens = createTokensResource(client);
2596
3910
  const contracts = createContractsResource(client);
3911
+ const interop = createInteropResource(client, options?.interop, tokens, contracts);
2597
3912
  return {
2598
3913
  deposits: createDepositsResource(client, tokens, contracts),
2599
3914
  withdrawals: createWithdrawalsResource(client, tokens, contracts),
2600
3915
  tokens,
2601
- contracts
3916
+ contracts,
3917
+ interop
2602
3918
  };
2603
3919
  }
2604
3920
 
2605
- export { buildDirectRequestStruct, createContractsResource, createDepositsResource, createFinalizationServices, createTokensResource, createViemSdk, createWithdrawalsResource, encodeNativeTokenVaultTransferData, encodeSecondBridgeArgs, encodeSecondBridgeDataV1, encodeSecondBridgeErc20Args, encodeSecondBridgeEthArgs, getL2TransactionHashFromLogs };
3921
+ export { buildDirectRequestStruct, createContractsResource, createDepositsResource, createFinalizationServices, createInteropFinalizationServices, createInteropResource, createTokensResource, createViemSdk, createWithdrawalsResource, encodeNativeTokenVaultTransferData, encodeSecondBridgeArgs, encodeSecondBridgeDataV1, encodeSecondBridgeErc20Args, encodeSecondBridgeEthArgs, getL2TransactionHashFromLogs };