@matterlabs/zksync-js 0.0.13 → 0.0.14

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 (66) 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.js +7 -6
  4. package/dist/adapters/ethers/index.cjs +501 -242
  5. package/dist/adapters/ethers/index.cjs.map +1 -1
  6. package/dist/adapters/ethers/index.js +10 -9
  7. package/dist/adapters/ethers/resources/deposits/routes/priority.d.ts +12 -0
  8. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +4 -0
  9. package/dist/adapters/ethers/resources/interop/index.d.ts +14 -14
  10. package/dist/adapters/ethers/resources/interop/resolvers.d.ts +3 -8
  11. package/dist/adapters/ethers/resources/interop/routes/types.d.ts +2 -1
  12. package/dist/adapters/ethers/resources/interop/services/erc20.d.ts +10 -0
  13. package/dist/adapters/ethers/resources/interop/services/fee.d.ts +12 -0
  14. package/dist/adapters/ethers/resources/interop/services/finalization/index.d.ts +1 -1
  15. package/dist/adapters/ethers/resources/interop/services/finalization/polling.d.ts +1 -1
  16. package/dist/adapters/ethers/resources/interop/types.d.ts +6 -14
  17. package/dist/adapters/ethers/sdk.cjs +912 -252
  18. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  19. package/dist/adapters/ethers/sdk.d.ts +6 -1
  20. package/dist/adapters/ethers/sdk.js +8 -7
  21. package/dist/adapters/viem/client.cjs +8 -4
  22. package/dist/adapters/viem/client.cjs.map +1 -1
  23. package/dist/adapters/viem/client.js +7 -6
  24. package/dist/adapters/viem/index.cjs +315 -73
  25. package/dist/adapters/viem/index.cjs.map +1 -1
  26. package/dist/adapters/viem/index.js +10 -9
  27. package/dist/adapters/viem/resources/deposits/routes/priority.d.ts +13 -0
  28. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +4 -0
  29. package/dist/adapters/viem/sdk.cjs +307 -69
  30. package/dist/adapters/viem/sdk.cjs.map +1 -1
  31. package/dist/adapters/viem/sdk.js +7 -7
  32. package/dist/{chunk-E3KP7XCG.js → chunk-3HHUZXSV.js} +1 -1
  33. package/dist/{chunk-UDBRUBEK.js → chunk-5RRJDPAJ.js} +2 -2
  34. package/dist/{chunk-EDWBCPO3.js → chunk-75IOOODG.js} +253 -53
  35. package/dist/{chunk-R5WRFPK2.js → chunk-7CAVFIMW.js} +5 -4
  36. package/dist/chunk-BWKWWLY4.js +9 -0
  37. package/dist/{chunk-4S4XDA4N.js → chunk-DYJKK5FW.js} +17 -15
  38. package/dist/{chunk-5L6EYUJB.js → chunk-EOBXYHTZ.js} +35 -7
  39. package/dist/{chunk-53MC5BR2.js → chunk-HP3EWKJL.js} +1 -1
  40. package/dist/{chunk-HI64OOAR.js → chunk-J47RI3G7.js} +1 -1
  41. package/dist/{chunk-RI73VJSH.js → chunk-JY62QO3W.js} +44 -21
  42. package/dist/{chunk-QQ2OR434.js → chunk-MT4X5FEO.js} +18 -2
  43. package/dist/{chunk-2RIARDXZ.js → chunk-OTXPSNNC.js} +5 -4
  44. package/dist/{chunk-5R7L5NM5.js → chunk-XDRCN4FC.js} +2 -2
  45. package/dist/{chunk-JHO2UQ5F.js → chunk-XKRNLFET.js} +394 -200
  46. package/dist/core/constants.cjs +17 -1
  47. package/dist/core/constants.cjs.map +1 -1
  48. package/dist/core/constants.d.ts +9 -1
  49. package/dist/core/constants.js +1 -1
  50. package/dist/core/index.cjs +52 -24
  51. package/dist/core/index.cjs.map +1 -1
  52. package/dist/core/index.js +6 -5
  53. package/dist/core/internal/abis/IERC7786Attributes.d.ts +21 -11
  54. package/dist/core/internal/abis/IInteropCenter.d.ts +4 -0
  55. package/dist/core/resources/deposits/priority.d.ts +37 -0
  56. package/dist/core/resources/interop/attributes/bundle.d.ts +1 -0
  57. package/dist/core/resources/interop/attributes/resource.d.ts +1 -0
  58. package/dist/core/resources/interop/plan.d.ts +11 -3
  59. package/dist/core/rpc/types.d.ts +1 -0
  60. package/dist/core/rpc/zks.d.ts +5 -1
  61. package/dist/core/types/errors.d.ts +5 -0
  62. package/dist/core/types/flows/interop.d.ts +11 -18
  63. package/dist/index.cjs +69 -25
  64. package/dist/index.cjs.map +1 -1
  65. package/dist/index.js +6 -5
  66. package/package.json +1 -1
@@ -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-5RRJDPAJ.js';
2
+ import { createNTVCodec, toGasOverrides, buildFeeBreakdown, derivePriorityTxGasBreakdown, quoteL2Gas, quoteL2BaseCost, quoteL1Gas, derivePriorityBodyGasEstimateCap, quoteL2Gas2 } from './chunk-EOBXYHTZ.js';
3
+ import { findL1MessageSentLog, messengerLogIndex, isL1MessageSentLog, 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, isBigint, isHash, IInteropCenter_default, sleep, isETH, normalizeAddrEq, isNumber, isAddress, isHash66Array, IInteropRootStorage_default, IL2AssetRouter_default, L2NativeTokenVault_default, assertNever } from './chunk-JY62QO3W.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, L2_ASSET_ROUTER_ADDRESS, FORMAL_ETH_ADDRESS } from './chunk-MT4X5FEO.js';
6
+ import { Interface, keccak256, AbiCoder, ethers, getBytes, Contract, NonceManager, JsonRpcProvider, isError, getAddress, concat, hexlify, toBeArray, toBeHex } 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,86 @@ 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 ?? priorityFloorBreakdown.derivedL2GasLimit;
332
392
  const l2GasParams = await quoteL2Gas3({
333
393
  ctx,
334
394
  route: "eth-base",
335
- l2TxForModeling: l2TxModel,
336
- overrideGasLimit: ctx.l2GasLimit,
337
- stateOverrides: {
338
- [ctx.sender]: {
339
- balance: "0xffffffffffffffffffff"
340
- }
341
- }
395
+ overrideGasLimit: quotedL2GasLimit
342
396
  });
343
397
  if (!l2GasParams) {
344
398
  throw new Error("Failed to estimate L2 gas for deposit.");
345
399
  }
346
400
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
347
- const mintValue = baseCost + ctx.operatorTip + p.amount;
401
+ const mintValue = baseCost + ctx.operatorTip + l2Value;
348
402
  const req = buildDirectRequestStruct({
349
403
  chainId: ctx.chainIdL2,
350
404
  mintValue,
351
405
  l2GasLimit: l2GasParams.gasLimit,
352
406
  gasPerPubdata: ctx.gasPerPubdata,
353
407
  refundRecipient: ctx.refundRecipient,
354
- l2Contract: p.to ?? ctx.sender,
355
- l2Value: p.amount
408
+ l2Contract,
409
+ l2Value
356
410
  });
357
411
  const data = bh.interface.encodeFunctionData("requestL2TransactionDirect", [req]);
358
412
  const l1TxCandidate = {
@@ -402,6 +456,53 @@ var ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000
402
456
 
403
457
  // src/adapters/ethers/resources/deposits/routes/erc20-nonbase.ts
404
458
  var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
459
+ var ZERO_L2_TOKEN_ADDRESS2 = "0x0000000000000000000000000000000000000000";
460
+ var ZERO_ASSET_ID = "0x0000000000000000000000000000000000000000000000000000000000000000";
461
+ async function getPriorityGasModel(input) {
462
+ try {
463
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
464
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
465
+ const { chainId: l1ChainId } = await input.ctx.client.l1.getNetwork();
466
+ const isFirstBridge = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID || input.ctx.resolvedToken.originChainId === 0n;
467
+ const erc20MetadataOriginChainId = isFirstBridge ? BigInt(l1ChainId) : input.ctx.resolvedToken.originChainId;
468
+ const erc20Metadata = await l1NativeTokenVault.getERC20Getters(
469
+ input.token,
470
+ erc20MetadataOriginChainId
471
+ );
472
+ const bridgeMintCalldata = AbiCoder.defaultAbiCoder().encode(
473
+ ["address", "address", "address", "uint256", "bytes"],
474
+ [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
475
+ );
476
+ const l2Calldata = isFirstBridge ? new Interface(IL2AssetRouter_default).encodeFunctionData(
477
+ "finalizeDeposit(address,address,address,uint256,bytes)",
478
+ [input.ctx.sender, input.receiver, input.token, input.amount, erc20Metadata]
479
+ ) : await (() => {
480
+ return l1AssetRouter.getDepositCalldata(
481
+ input.ctx.sender,
482
+ input.ctx.resolvedToken.assetId,
483
+ bridgeMintCalldata
484
+ );
485
+ })();
486
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
487
+ sender: input.ctx.l1AssetRouter,
488
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
489
+ l2Value: 0n,
490
+ l2Calldata,
491
+ gasPerPubdata: input.ctx.gasPerPubdata
492
+ });
493
+ const model = {
494
+ priorityFloorGasLimit: priorityFloorBreakdown.derivedL2GasLimit
495
+ };
496
+ if (isFirstBridge || input.ctx.resolvedToken.l2.toLowerCase() === ZERO_L2_TOKEN_ADDRESS2) {
497
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
498
+ minBodyGas: priorityFloorBreakdown.minBodyGas
499
+ }) + priorityFloorBreakdown.overhead;
500
+ }
501
+ return model;
502
+ } catch {
503
+ return {};
504
+ }
505
+ }
405
506
  function routeErc20NonBase() {
406
507
  return {
407
508
  async preflight(p, ctx) {
@@ -422,11 +523,29 @@ function routeErc20NonBase() {
422
523
  const l1Signer = ctx.client.getL1Signer();
423
524
  const baseToken = ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2);
424
525
  const baseIsEth = ctx.baseIsEth ?? isETH(baseToken);
526
+ const receiver = p.to ?? ctx.sender;
527
+ const secondBridgeCalldata = await wrapAs2(
528
+ "INTERNAL",
529
+ OP_DEPOSITS.nonbase.encodeCalldata,
530
+ () => Promise.resolve(encodeSecondBridgeErc20Args(p.token, p.amount, receiver)),
531
+ {
532
+ ctx: { where: "encodeSecondBridgeErc20Args" },
533
+ message: "Failed to encode bridging calldata."
534
+ }
535
+ );
536
+ const priorityGasModel = await getPriorityGasModel({
537
+ ctx,
538
+ token: p.token,
539
+ amount: p.amount,
540
+ receiver
541
+ });
425
542
  const l2GasParams = await determineErc20L2Gas({
426
543
  ctx,
427
544
  l1Token: p.token,
545
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
546
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit,
428
547
  modelTx: {
429
- to: p.to ?? ctx.sender,
548
+ to: receiver,
430
549
  from: ctx.sender,
431
550
  data: "0x",
432
551
  value: 0n
@@ -488,15 +607,6 @@ function routeErc20NonBase() {
488
607
  });
489
608
  }
490
609
  }
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
610
  const requestStruct = {
501
611
  chainId: ctx.chainIdL2,
502
612
  mintValue,
@@ -554,6 +664,51 @@ function routeErc20NonBase() {
554
664
  };
555
665
  }
556
666
  var { wrapAs: wrapAs3 } = createErrorHandlers("deposits");
667
+ var ZERO_L2_TOKEN_ADDRESS3 = "0x0000000000000000000000000000000000000000";
668
+ var ZERO_ASSET_ID2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
669
+ var ntvCodec = createNTVCodec({
670
+ encode: (types, values) => AbiCoder.defaultAbiCoder().encode(types, values),
671
+ keccak256: (data) => keccak256(data)
672
+ });
673
+ async function getPriorityGasModel2(input) {
674
+ try {
675
+ const l1AssetRouter = await input.ctx.contracts.l1AssetRouter();
676
+ const l1NativeTokenVault = await input.ctx.contracts.l1NativeTokenVault();
677
+ const originChainId = input.ctx.resolvedToken.originChainId !== 0n ? input.ctx.resolvedToken.originChainId : BigInt((await input.ctx.client.l1.getNetwork()).chainId);
678
+ const resolvedAssetId = input.ctx.resolvedToken.assetId.toLowerCase() === ZERO_ASSET_ID2 ? ntvCodec.encodeAssetId(originChainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, ETH_ADDRESS) : input.ctx.resolvedToken.assetId;
679
+ const erc20Metadata = await l1NativeTokenVault.getERC20Getters(
680
+ ETH_ADDRESS,
681
+ originChainId
682
+ );
683
+ const bridgeMintCalldata = AbiCoder.defaultAbiCoder().encode(
684
+ ["address", "address", "address", "uint256", "bytes"],
685
+ [input.ctx.sender, input.receiver, ETH_ADDRESS, input.amount, erc20Metadata]
686
+ );
687
+ const l2Calldata = await l1AssetRouter.getDepositCalldata(
688
+ input.ctx.sender,
689
+ resolvedAssetId,
690
+ bridgeMintCalldata
691
+ );
692
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
693
+ sender: input.ctx.l1AssetRouter,
694
+ l2Contract: L2_ASSET_ROUTER_ADDRESS,
695
+ l2Value: 0n,
696
+ l2Calldata,
697
+ gasPerPubdata: input.ctx.gasPerPubdata
698
+ });
699
+ const model = {
700
+ priorityFloorGasLimit: priorityFloorBreakdown.derivedL2GasLimit
701
+ };
702
+ if (input.ctx.resolvedToken.l2.toLowerCase() === ZERO_L2_TOKEN_ADDRESS3) {
703
+ model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
704
+ minBodyGas: priorityFloorBreakdown.minBodyGas
705
+ }) + priorityFloorBreakdown.overhead;
706
+ }
707
+ return model;
708
+ } catch {
709
+ return {};
710
+ }
711
+ }
557
712
  function routeEthNonBase() {
558
713
  return {
559
714
  async preflight(p, ctx) {
@@ -602,15 +757,23 @@ function routeEthNonBase() {
602
757
  async build(p, ctx) {
603
758
  const l1Signer = ctx.client.getL1Signer();
604
759
  const baseToken = ctx.baseTokenL1;
760
+ const receiver = p.to ?? ctx.sender;
761
+ const priorityGasModel = await getPriorityGasModel2({
762
+ ctx,
763
+ amount: p.amount,
764
+ receiver
765
+ });
605
766
  const l2TxModel = {
606
- to: p.to ?? ctx.sender,
767
+ to: receiver,
607
768
  from: ctx.sender,
608
769
  data: "0x",
609
770
  value: 0n
610
771
  };
611
772
  const l2GasParams = await determineEthNonBaseL2Gas({
612
773
  ctx,
613
- modelTx: l2TxModel
774
+ modelTx: l2TxModel,
775
+ priorityFloorGasLimit: priorityGasModel.priorityFloorGasLimit,
776
+ undeployedGasLimit: priorityGasModel.undeployedGasLimit
614
777
  });
615
778
  if (!l2GasParams) throw new Error("Failed to estimate L2 gas parameters.");
616
779
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
@@ -644,12 +807,12 @@ function routeEthNonBase() {
644
807
  const secondBridgeCalldata = await wrapAs3(
645
808
  "INTERNAL",
646
809
  OP_DEPOSITS.ethNonBase.encodeCalldata,
647
- () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, p.to ?? ctx.sender)),
810
+ () => Promise.resolve(encodeSecondBridgeEthArgs(p.amount, receiver)),
648
811
  {
649
812
  ctx: {
650
813
  where: "encodeSecondBridgeEthArgs",
651
814
  amount: p.amount.toString(),
652
- to: p.to ?? ctx.sender
815
+ to: receiver
653
816
  }
654
817
  }
655
818
  );
@@ -710,6 +873,7 @@ function routeEthNonBase() {
710
873
  };
711
874
  }
712
875
  var { wrapAs: wrapAs4 } = createErrorHandlers("deposits");
876
+ var EMPTY_BYTES3 = "0x";
713
877
  function routeErc20Base() {
714
878
  return {
715
879
  async preflight(p, ctx) {
@@ -740,17 +904,21 @@ function routeErc20Base() {
740
904
  async build(p, ctx) {
741
905
  const l1Signer = ctx.client.getL1Signer();
742
906
  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
- };
907
+ const l2Contract = p.to ?? ctx.sender;
908
+ const l2Value = p.amount;
909
+ const l2Calldata = EMPTY_BYTES3;
910
+ const priorityFloorBreakdown = getPriorityTxGasBreakdown({
911
+ sender: ctx.sender,
912
+ l2Contract,
913
+ l2Value,
914
+ l2Calldata,
915
+ gasPerPubdata: ctx.gasPerPubdata
916
+ });
917
+ const quotedL2GasLimit = ctx.l2GasLimit ?? priorityFloorBreakdown.derivedL2GasLimit;
749
918
  const l2GasParams = await quoteL2Gas3({
750
919
  ctx,
751
920
  route: "erc20-base",
752
- l2TxForModeling: l2TxModel,
753
- overrideGasLimit: ctx.l2GasLimit
921
+ overrideGasLimit: quotedL2GasLimit
754
922
  });
755
923
  if (!l2GasParams) throw new Error("Failed to estimate L2 gas parameters.");
756
924
  const baseCost = await quoteL2BaseCost2({ ctx, l2GasLimit: l2GasParams.gasLimit });
@@ -789,8 +957,8 @@ function routeErc20Base() {
789
957
  l2GasLimit: l2GasParams.gasLimit,
790
958
  gasPerPubdata: ctx.gasPerPubdata,
791
959
  refundRecipient: ctx.refundRecipient,
792
- l2Contract: p.to ?? ctx.sender,
793
- l2Value: p.amount
960
+ l2Contract,
961
+ l2Value
794
962
  });
795
963
  const bridgehub = await ctx.contracts.bridgehub();
796
964
  const data = bridgehub.interface.encodeFunctionData("requestL2TransactionDirect", [
@@ -839,7 +1007,7 @@ function routeErc20Base() {
839
1007
  }
840
1008
  var { wrapAs: wrapAs5 } = createErrorHandlers("tokens");
841
1009
  var abi = AbiCoder.defaultAbiCoder();
842
- var ntvCodec = createNTVCodec({
1010
+ var ntvCodec2 = createNTVCodec({
843
1011
  encode: (types, values) => abi.encode(types, values),
844
1012
  keccak256: (data) => ethers.keccak256(data)
845
1013
  });
@@ -958,7 +1126,7 @@ function createTokensResource(client) {
958
1126
  return wrapAs5("CONTRACT", "tokens.isChainEthBased", async () => {
959
1127
  const baseAssetId = await getBaseTokenAssetId();
960
1128
  const l1ChainId = await getL1ChainId();
961
- const ethAssetId = ntvCodec.encodeAssetId(
1129
+ const ethAssetId = ntvCodec2.encodeAssetId(
962
1130
  l1ChainId,
963
1131
  L2_NATIVE_TOKEN_VAULT_ADDRESS,
964
1132
  ETH_ADDRESS
@@ -1853,7 +2021,7 @@ var ROUTES2 = {
1853
2021
  };
1854
2022
  function createWithdrawalsResource(client, tokens, contracts) {
1855
2023
  const svc = createFinalizationServices(client);
1856
- const { wrap: wrap6, toResult: toResult3 } = createErrorHandlers("withdrawals");
2024
+ const { wrap: wrap7, toResult: toResult3 } = createErrorHandlers("withdrawals");
1857
2025
  const tokensResource = tokens ?? createTokensResource(client);
1858
2026
  const contractsResource = contracts ?? createContractsResource(client);
1859
2027
  async function buildPlan(p) {
@@ -1874,7 +2042,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1874
2042
  };
1875
2043
  }
1876
2044
  const finalizeCache = /* @__PURE__ */ new Map();
1877
- const quote = (p) => wrap6(
2045
+ const quote = (p) => wrap7(
1878
2046
  OP_WITHDRAWALS.quote,
1879
2047
  async () => {
1880
2048
  const plan = await buildPlan(p);
@@ -1896,7 +2064,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1896
2064
  ctx: { token: p.token, where: "withdrawals.tryQuote" }
1897
2065
  }
1898
2066
  );
1899
- const prepare = (p) => wrap6(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
2067
+ const prepare = (p) => wrap7(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
1900
2068
  message: "Internal error while preparing a withdrawal plan.",
1901
2069
  ctx: { token: p.token, where: "withdrawals.prepare" }
1902
2070
  });
@@ -1904,7 +2072,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1904
2072
  message: "Internal error while preparing a withdrawal plan.",
1905
2073
  ctx: { token: p.token, where: "withdrawals.tryPrepare" }
1906
2074
  });
1907
- const create = (p) => wrap6(
2075
+ const create = (p) => wrap7(
1908
2076
  OP_WITHDRAWALS.create,
1909
2077
  async () => {
1910
2078
  const plan = await prepare(p);
@@ -1975,7 +2143,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
1975
2143
  message: "Internal error while creating withdrawal transactions.",
1976
2144
  ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
1977
2145
  });
1978
- const status = (h) => wrap6(
2146
+ const status = (h) => wrap7(
1979
2147
  OP_WITHDRAWALS.status,
1980
2148
  async () => {
1981
2149
  const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2030,7 +2198,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2030
2198
  const wait = (h, opts = {
2031
2199
  for: "l2",
2032
2200
  pollMs: 5500
2033
- }) => wrap6(
2201
+ }) => wrap7(
2034
2202
  OP_WITHDRAWALS.wait,
2035
2203
  async () => {
2036
2204
  const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
@@ -2095,7 +2263,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
2095
2263
  }
2096
2264
  }
2097
2265
  );
2098
- const finalize = (l2TxHash) => wrap6(
2266
+ const finalize = (l2TxHash) => wrap7(
2099
2267
  OP_WITHDRAWALS.finalize.send,
2100
2268
  async () => {
2101
2269
  const pack = await (async () => {
@@ -2221,9 +2389,11 @@ function createCallAttributes(codec) {
2221
2389
  function createBundleAttributes(codec) {
2222
2390
  const executionAddress = (executor) => codec.encode("executionAddress", [executor]);
2223
2391
  const unbundlerAddress = (addr) => codec.encode("unbundlerAddress", [addr]);
2392
+ const useFixedFee = (enabled) => codec.encode("useFixedFee", [enabled]);
2224
2393
  return {
2225
2394
  executionAddress,
2226
- unbundlerAddress
2395
+ unbundlerAddress,
2396
+ useFixedFee
2227
2397
  };
2228
2398
  }
2229
2399
 
@@ -2244,6 +2414,7 @@ function getInteropAttributes(params, ctx) {
2244
2414
  if (params.unbundling?.by) {
2245
2415
  bundleAttributes.push(ctx.attributes.bundle.unbundlerAddress(params.unbundling.by));
2246
2416
  }
2417
+ bundleAttributes.push(ctx.attributes.bundle.useFixedFee(params.fee?.useFixed ?? false));
2247
2418
  const callAttributes = params.actions.map((action) => {
2248
2419
  switch (action.type) {
2249
2420
  case "sendNative": {
@@ -2273,11 +2444,6 @@ function createEthersAttributesResource(opts = {}) {
2273
2444
  }
2274
2445
 
2275
2446
  // 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
2447
  function isInteropMessageProof(obj) {
2282
2448
  if (typeof obj !== "object" || obj === null) return false;
2283
2449
  const proof = obj;
@@ -2286,7 +2452,7 @@ function isInteropMessageProof(obj) {
2286
2452
  function isInteropFinalizationInfo(obj) {
2287
2453
  if (typeof obj !== "object" || obj === null) return false;
2288
2454
  const info = obj;
2289
- return isHash66(info.l2SrcTxHash) && isHash66(info.bundleHash) && isBigint(info.dstChainId) && isHash(info.encodedData) && isInteropExpectedRoot(info.expectedRoot) && isInteropMessageProof(info.proof);
2455
+ return isHash66(info.l2SrcTxHash) && isHash66(info.bundleHash) && isBigint(info.dstChainId) && isHash(info.encodedData) && isInteropMessageProof(info.proof);
2290
2456
  }
2291
2457
 
2292
2458
  // src/core/resources/interop/route.ts
@@ -2339,7 +2505,7 @@ function preflightDirect(params, ctx) {
2339
2505
  }
2340
2506
  }
2341
2507
  }
2342
- function buildDirectBundle(params, ctx, attrs) {
2508
+ function buildDirectBundle(params, ctx, attrs, interopFeeInfo) {
2343
2509
  const totalActionValue = sumActionMsgValue(params.actions);
2344
2510
  const starters = params.actions.map((action, index) => {
2345
2511
  const to = ctx.codec.formatAddress(action.to);
@@ -2357,7 +2523,8 @@ function buildDirectBundle(params, ctx, attrs) {
2357
2523
  dstChain: ctx.codec.formatChain(ctx.dstChainId),
2358
2524
  starters,
2359
2525
  bundleAttributes: attrs.bundleAttributes,
2360
- approvals: [],
2526
+ approvals: interopFeeInfo.approval ? [interopFeeInfo.approval] : [],
2527
+ interopFee: interopFeeInfo.fee,
2361
2528
  quoteExtras: {
2362
2529
  totalActionValue,
2363
2530
  bridgedTokenTotal: 0n
@@ -2402,7 +2569,7 @@ function preflightIndirect(params, ctx) {
2402
2569
  }
2403
2570
  }
2404
2571
  }
2405
- function buildIndirectBundle(params, ctx, attrs, starterData) {
2572
+ function buildIndirectBundle(params, ctx, attrs, starterData, interopFeeInfo) {
2406
2573
  const totalActionValue = sumActionMsgValue(params.actions);
2407
2574
  const bridgedTokenTotal = sumErc20Amounts(params.actions);
2408
2575
  const approvalMap = /* @__PURE__ */ new Map();
@@ -2421,6 +2588,7 @@ function buildIndirectBundle(params, ctx, attrs, starterData) {
2421
2588
  }
2422
2589
  }
2423
2590
  const approvals = Array.from(approvalMap.values());
2591
+ if (interopFeeInfo.approval) approvals.push(interopFeeInfo.approval);
2424
2592
  const starters = params.actions.map((action, index) => {
2425
2593
  const callAttributes = attrs.callAttributes[index] ?? [];
2426
2594
  if (starterData[index]?.assetRouterPayload) {
@@ -2444,6 +2612,7 @@ function buildIndirectBundle(params, ctx, attrs, starterData) {
2444
2612
  starters,
2445
2613
  bundleAttributes: attrs.bundleAttributes,
2446
2614
  approvals,
2615
+ interopFee: interopFeeInfo.fee,
2447
2616
  quoteExtras: {
2448
2617
  totalActionValue,
2449
2618
  bridgedTokenTotal
@@ -2490,6 +2659,29 @@ function buildEnsureTokenSteps(erc20Tokens, ctx) {
2490
2659
  }
2491
2660
  }));
2492
2661
  }
2662
+ async function buildApproveSteps(approvals, ctx) {
2663
+ const steps = [];
2664
+ for (const approval of approvals) {
2665
+ const erc20 = new Contract(approval.token, IERC20_default, ctx.client.l2);
2666
+ const currentAllowance = await erc20.allowance(ctx.sender, approval.spender);
2667
+ if (currentAllowance < approval.amount) {
2668
+ steps.push({
2669
+ key: `approve:${approval.token}:${approval.spender}`,
2670
+ kind: "approve",
2671
+ description: `Approve ${approval.spender} to spend ${approval.amount} of ${approval.token}`,
2672
+ tx: {
2673
+ to: approval.token,
2674
+ data: erc20.interface.encodeFunctionData("approve", [
2675
+ approval.spender,
2676
+ approval.amount
2677
+ ]),
2678
+ ...ctx.gasOverrides
2679
+ }
2680
+ });
2681
+ }
2682
+ }
2683
+ return steps;
2684
+ }
2493
2685
  async function resolveErc20AssetIds(erc20Tokens, ctx) {
2494
2686
  const assetIds = /* @__PURE__ */ new Map();
2495
2687
  if (erc20Tokens.length === 0) return assetIds;
@@ -2543,6 +2735,44 @@ async function getStarterData(params, ctx, erc20AssetIds) {
2543
2735
  }
2544
2736
  return starterData;
2545
2737
  }
2738
+ var { wrap: wrap2 } = createErrorHandlers("interop");
2739
+ async function buildFeeInfo(params, ctx, numStarters) {
2740
+ const useFixed = params.fee?.useFixed ?? false;
2741
+ const interopCenter = new Contract(ctx.interopCenter, IInteropCenter_default, ctx.client.l2);
2742
+ if (useFixed) {
2743
+ const zkFeePerCall = await wrap2(
2744
+ OP_INTEROP.svc.fees.zkInteropFee,
2745
+ () => interopCenter.ZK_INTEROP_FEE(),
2746
+ { message: "Failed to fetch ZK interop fee from InteropCenter." }
2747
+ );
2748
+ const zkFeeTotal = zkFeePerCall * BigInt(numStarters);
2749
+ const zkTokenAddress = await wrap2(
2750
+ OP_INTEROP.svc.fees.zkToken,
2751
+ () => interopCenter.zkToken(),
2752
+ { message: "Failed to fetch ZK token address from InteropCenter." }
2753
+ );
2754
+ const approval = {
2755
+ token: zkTokenAddress,
2756
+ spender: ctx.interopCenter,
2757
+ amount: zkFeeTotal
2758
+ };
2759
+ return {
2760
+ approval,
2761
+ fee: { token: zkTokenAddress, amount: zkFeeTotal }
2762
+ };
2763
+ } else {
2764
+ const protocolFeePerCall = await wrap2(
2765
+ OP_INTEROP.svc.fees.protocolFee,
2766
+ () => interopCenter.interopProtocolFee(),
2767
+ { message: "Failed to fetch interop protocol fee from InteropCenter." }
2768
+ );
2769
+ const totalFee = protocolFeePerCall * BigInt(numStarters);
2770
+ return {
2771
+ approval: null,
2772
+ fee: { token: ctx.baseTokens.src, amount: totalFee }
2773
+ };
2774
+ }
2775
+ }
2546
2776
 
2547
2777
  // src/adapters/ethers/resources/interop/routes/indirect.ts
2548
2778
  function routeIndirect() {
@@ -2558,7 +2788,10 @@ function routeIndirect() {
2558
2788
  async build(params, ctx) {
2559
2789
  const steps = [];
2560
2790
  const erc20Tokens = getErc20Tokens(params);
2561
- const erc20AssetIds = await resolveErc20AssetIds(erc20Tokens, ctx);
2791
+ const [erc20AssetIds, feeInfo] = await Promise.all([
2792
+ resolveErc20AssetIds(erc20Tokens, ctx),
2793
+ buildFeeInfo(params, ctx, params.actions.length)
2794
+ ]);
2562
2795
  const attributes = getInteropAttributes(params, ctx);
2563
2796
  const starterData = await getStarterData(params, ctx, erc20AssetIds);
2564
2797
  const bundle = buildIndirectBundle(
@@ -2571,32 +2804,11 @@ function routeIndirect() {
2571
2804
  codec: interopCodec
2572
2805
  },
2573
2806
  attributes,
2574
- starterData
2807
+ starterData,
2808
+ feeInfo
2575
2809
  );
2576
2810
  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
- }
2811
+ steps.push(...await buildApproveSteps(bundle.approvals, ctx));
2600
2812
  const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
2601
2813
  bundle.dstChain,
2602
2814
  bundle.starters,
@@ -2609,14 +2821,15 @@ function routeIndirect() {
2609
2821
  tx: {
2610
2822
  to: ctx.interopCenter,
2611
2823
  data,
2612
- value: bundle.quoteExtras.totalActionValue,
2824
+ value: bundle.quoteExtras.totalActionValue + feeInfo.fee.amount,
2613
2825
  ...ctx.gasOverrides
2614
2826
  }
2615
2827
  });
2616
2828
  return {
2617
2829
  steps,
2618
2830
  approvals: bundle.approvals,
2619
- quoteExtras: bundle.quoteExtras
2831
+ quoteExtras: bundle.quoteExtras,
2832
+ interopFee: feeInfo.fee
2620
2833
  };
2621
2834
  }
2622
2835
  };
@@ -2633,10 +2846,10 @@ function routeDirect() {
2633
2846
  l2AssetRouter: ctx.l2AssetRouter,
2634
2847
  l2NativeTokenVault: ctx.l2NativeTokenVault});
2635
2848
  },
2636
- // eslint-disable-next-line @typescript-eslint/require-await
2637
2849
  async build(params, ctx) {
2638
2850
  const steps = [];
2639
2851
  const attrs = getInteropAttributes(params, ctx);
2852
+ const feeInfo = await buildFeeInfo(params, ctx, params.actions.length);
2640
2853
  const built = buildDirectBundle(
2641
2854
  params,
2642
2855
  {
@@ -2646,8 +2859,10 @@ function routeDirect() {
2646
2859
  l2NativeTokenVault: ctx.l2NativeTokenVault,
2647
2860
  codec: interopCodec
2648
2861
  },
2649
- attrs
2862
+ attrs,
2863
+ feeInfo
2650
2864
  );
2865
+ steps.push(...await buildApproveSteps(built.approvals, ctx));
2651
2866
  const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
2652
2867
  built.dstChain,
2653
2868
  built.starters,
@@ -2657,19 +2872,19 @@ function routeDirect() {
2657
2872
  key: "sendBundle",
2658
2873
  kind: "interop.center",
2659
2874
  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).
2875
+ // msg.value = forwarded action value + protocol fee.
2662
2876
  tx: {
2663
2877
  to: ctx.interopCenter,
2664
2878
  data,
2665
- value: built.quoteExtras.totalActionValue,
2879
+ value: built.quoteExtras.totalActionValue + feeInfo.fee.amount,
2666
2880
  ...ctx.gasOverrides
2667
2881
  }
2668
2882
  });
2669
2883
  return {
2670
2884
  steps,
2671
2885
  approvals: built.approvals,
2672
- quoteExtras: built.quoteExtras
2886
+ quoteExtras: built.quoteExtras,
2887
+ interopFee: feeInfo.fee
2673
2888
  };
2674
2889
  }
2675
2890
  };
@@ -2749,7 +2964,7 @@ function getTopics() {
2749
2964
  };
2750
2965
  return { topics, centerIface };
2751
2966
  }
2752
- var { wrap: wrap2 } = createErrorHandlers("interop");
2967
+ var { wrap: wrap3 } = createErrorHandlers("interop");
2753
2968
  var DEFAULT_BLOCKS_RANGE_SIZE = 1e4;
2754
2969
  var DEFAULT_MAX_BLOCKS_BACK = 2e4;
2755
2970
  var SAFE_BLOCKS_RANGE_SIZE = 1e3;
@@ -2762,7 +2977,7 @@ function parseMaxBlockRangeLimit(error) {
2762
2977
  return Number.isInteger(limit) && limit > 0 ? limit : null;
2763
2978
  }
2764
2979
  async function getTxReceipt(provider, txHash) {
2765
- const receipt = await wrap2(
2980
+ const receipt = await wrap3(
2766
2981
  OP_INTEROP.svc.status.sourceReceipt,
2767
2982
  () => provider.getTransactionReceipt(txHash),
2768
2983
  {
@@ -2783,7 +2998,7 @@ async function getTxReceipt(provider, txHash) {
2783
2998
  async function getLogs(provider, address, topics, opts) {
2784
2999
  const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
2785
3000
  const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
2786
- return await wrap2(
3001
+ return await wrap3(
2787
3002
  OP_INTEROP.svc.status.dstLogs,
2788
3003
  async () => {
2789
3004
  const currentBlock = await provider.getBlockNumber();
@@ -2830,7 +3045,7 @@ async function getLogs(provider, address, topics, opts) {
2830
3045
  );
2831
3046
  }
2832
3047
  async function getInteropRoot(provider, rootChainId, batchNumber) {
2833
- return await wrap2(
3048
+ return await wrap3(
2834
3049
  OP_INTEROP.svc.status.getRoot,
2835
3050
  async () => {
2836
3051
  const rootStorage = new Contract(
@@ -2848,7 +3063,7 @@ async function getInteropRoot(provider, rootChainId, batchNumber) {
2848
3063
  }
2849
3064
 
2850
3065
  // src/adapters/ethers/resources/interop/services/finalization/bundle.ts
2851
- var { wrap: wrap3 } = createErrorHandlers("interop");
3066
+ var { wrap: wrap4 } = createErrorHandlers("interop");
2852
3067
  async function getBundleStatus(client, dstProvider, topics, bundleHash, opts) {
2853
3068
  const { interopHandler } = await client.ensureAddresses();
2854
3069
  const bundleLogs = await getLogs(dstProvider, interopHandler, [null, bundleHash], opts);
@@ -2880,7 +3095,7 @@ async function executeBundle(client, dstProvider, info, opts) {
2880
3095
  context: { bundleHash }
2881
3096
  });
2882
3097
  }
2883
- const signer = await wrap3(OP_INTEROP.exec.sendStep, () => client.signerFor(dstProvider), {
3098
+ const signer = await wrap4(OP_INTEROP.exec.sendStep, () => client.signerFor(dstProvider), {
2884
3099
  message: "Failed to resolve destination signer."
2885
3100
  });
2886
3101
  const { interopHandler } = await client.ensureAddresses();
@@ -3042,11 +3257,6 @@ function getBundleEncodedData(messageData) {
3042
3257
  return `0x${messageData.slice(4)}`;
3043
3258
  }
3044
3259
  function buildFinalizationInfo(ids, bundleInfo, proof, messageData) {
3045
- const expectedRoot = {
3046
- rootChainId: bundleInfo.sourceChainId,
3047
- batchNumber: proof.batchNumber,
3048
- expectedRoot: proof.root
3049
- };
3050
3260
  const messageProof = {
3051
3261
  chainId: bundleInfo.sourceChainId,
3052
3262
  l1BatchNumber: proof.batchNumber,
@@ -3062,7 +3272,6 @@ function buildFinalizationInfo(ids, bundleInfo, proof, messageData) {
3062
3272
  l2SrcTxHash: ids.l2SrcTxHash,
3063
3273
  bundleHash: bundleInfo.bundleHash,
3064
3274
  dstChainId: bundleInfo.dstChainId,
3065
- expectedRoot,
3066
3275
  proof: messageProof,
3067
3276
  encodedData: getBundleEncodedData(messageData)
3068
3277
  };
@@ -3085,7 +3294,7 @@ function decodeL1MessageData(log) {
3085
3294
  }
3086
3295
 
3087
3296
  // src/adapters/ethers/resources/interop/services/finalization/polling.ts
3088
- var { wrap: wrap4 } = createErrorHandlers("interop");
3297
+ var { wrap: wrap5 } = createErrorHandlers("interop");
3089
3298
  function isProofNotReadyError(error) {
3090
3299
  return isZKsyncError(error, {
3091
3300
  operation: "zksrpc.getL2ToL1LogProof",
@@ -3122,30 +3331,26 @@ async function waitForProof(client, l2SrcTxHash, logIndex, blockNumber, pollMs,
3122
3331
  });
3123
3332
  }
3124
3333
  try {
3125
- return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex);
3334
+ return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex, "messageRoot" /* MessageRoot */);
3126
3335
  } catch (error) {
3127
3336
  if (!isProofNotReadyError(error)) throw error;
3128
3337
  }
3129
3338
  await sleep(pollMs);
3130
3339
  }
3131
3340
  }
3132
- async function waitForRoot(provider, expectedRoot, pollMs, deadline) {
3341
+ async function waitForRoot(provider, chainId, batchNumber, pollMs, deadline) {
3133
3342
  while (true) {
3134
3343
  if (Date.now() > deadline) {
3135
3344
  throw createError("TIMEOUT", {
3136
3345
  resource: "interop",
3137
3346
  operation: OP_INTEROP.svc.wait.timeout,
3138
3347
  message: "Timed out waiting for interop root to become available.",
3139
- context: { expectedRoot }
3348
+ context: { chainId, batchNumber }
3140
3349
  });
3141
3350
  }
3142
3351
  let interopRoot = null;
3143
3352
  try {
3144
- const root = await getInteropRoot(
3145
- provider,
3146
- expectedRoot.rootChainId,
3147
- expectedRoot.batchNumber
3148
- );
3353
+ const root = await getInteropRoot(provider, chainId, batchNumber);
3149
3354
  if (root !== ZERO_HASH) {
3150
3355
  interopRoot = root;
3151
3356
  }
@@ -3154,18 +3359,7 @@ async function waitForRoot(provider, expectedRoot, pollMs, deadline) {
3154
3359
  interopRoot = null;
3155
3360
  }
3156
3361
  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
- });
3362
+ return interopRoot;
3169
3363
  }
3170
3364
  await sleep(pollMs);
3171
3365
  }
@@ -3180,7 +3374,7 @@ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
3180
3374
  context: { txHash }
3181
3375
  });
3182
3376
  }
3183
- const receipt = await wrap4(
3377
+ const receipt = await wrap5(
3184
3378
  OP_INTEROP.svc.status.sourceReceipt,
3185
3379
  () => client.zks.getReceiptWithL2ToL1(txHash),
3186
3380
  {
@@ -3194,7 +3388,7 @@ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
3194
3388
  await sleep(pollMs);
3195
3389
  }
3196
3390
  }
3197
- async function waitForFinalization(client, dstProvider, input, opts) {
3391
+ async function waitForFinalization(client, dstProvider, gwProvider, input, opts) {
3198
3392
  const { topics, centerIface } = getTopics();
3199
3393
  const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
3200
3394
  const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -3243,7 +3437,16 @@ async function waitForFinalization(client, dstProvider, input, opts) {
3243
3437
  proof,
3244
3438
  bundleInfo.l1MessageData
3245
3439
  );
3246
- await waitForRoot(dstProvider, finalizationInfo.expectedRoot, pollMs, deadline);
3440
+ if (proof.gatewayBlockNumber == null) {
3441
+ throw createError("STATE", {
3442
+ resource: "interop",
3443
+ operation: OP_INTEROP.svc.wait.timeout,
3444
+ message: "Proof missing gatewayBlockNumber required for interop finalization.",
3445
+ context: { l2SrcTxHash: ids.l2SrcTxHash }
3446
+ });
3447
+ }
3448
+ const { chainId: gwChainId } = await gwProvider.getNetwork();
3449
+ await waitForRoot(dstProvider, gwChainId, proof.gatewayBlockNumber, pollMs, deadline);
3247
3450
  return finalizationInfo;
3248
3451
  }
3249
3452
 
@@ -3289,8 +3492,8 @@ function createInteropFinalizationServices(client) {
3289
3492
  status(dstProvider, input, opts) {
3290
3493
  return getStatus(client, dstProvider, input, opts);
3291
3494
  },
3292
- wait(dstProvider, input, opts) {
3293
- return waitForFinalization(client, dstProvider, input, opts);
3495
+ wait(dstProvider, gwProvider, input, opts) {
3496
+ return waitForFinalization(client, dstProvider, gwProvider, input, opts);
3294
3497
  },
3295
3498
  async finalize(dstProvider, info, opts) {
3296
3499
  const execResult = await executeBundle(client, dstProvider, info, opts);
@@ -3302,24 +3505,31 @@ function createInteropFinalizationServices(client) {
3302
3505
  }
3303
3506
  };
3304
3507
  }
3305
- function resolveDstProvider(dstChain) {
3306
- return typeof dstChain === "string" ? new JsonRpcProvider(dstChain) : dstChain;
3307
- }
3308
- function resolveWaitableInput(waitableInput) {
3309
- const input = waitableInput;
3310
- return {
3311
- dstProvider: resolveDstProvider(waitableInput.dstChain),
3312
- waitable: input.waitable ? input.waitable : waitableInput
3313
- };
3508
+ function resolveChainRef(ref) {
3509
+ return typeof ref === "string" ? new JsonRpcProvider(ref) : ref;
3314
3510
  }
3315
3511
 
3316
3512
  // src/adapters/ethers/resources/interop/index.ts
3317
- var { wrap: wrap5, toResult: toResult2 } = createErrorHandlers("interop");
3513
+ var { wrap: wrap6, toResult: toResult2 } = createErrorHandlers("interop");
3318
3514
  var ROUTES3 = {
3319
3515
  direct: routeDirect(),
3320
3516
  indirect: routeIndirect()
3321
3517
  };
3322
- function createInteropResource(client, tokens, contracts, attributes) {
3518
+ function createInteropResource(client, config, tokens, contracts, attributes) {
3519
+ let gwProviderCache;
3520
+ function requireConfig() {
3521
+ if (!config)
3522
+ throw createError("STATE", {
3523
+ resource: "interop",
3524
+ operation: "interop.init",
3525
+ message: "Interop is not configured. Pass gwChain in createEthersSdk options."
3526
+ });
3527
+ return config;
3528
+ }
3529
+ function getGwProvider() {
3530
+ if (!gwProviderCache) gwProviderCache = resolveChainRef(requireConfig().gwChain);
3531
+ return gwProviderCache;
3532
+ }
3323
3533
  const svc = createInteropFinalizationServices(client);
3324
3534
  const tokensResource = tokens ?? createTokensResource(client);
3325
3535
  const contractsResource = contracts ?? createContractsResource(client);
@@ -3343,11 +3553,11 @@ function createInteropResource(client, tokens, contracts, attributes) {
3343
3553
  baseTokenDst: ctx.baseTokens.dst
3344
3554
  }
3345
3555
  });
3346
- await wrap5(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
3556
+ await wrap6(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
3347
3557
  message: "Interop preflight failed.",
3348
3558
  ctx: { where: `routes.${route}.preflight` }
3349
3559
  });
3350
- const { steps, approvals, quoteExtras } = await wrap5(
3560
+ const { steps, approvals, quoteExtras, interopFee } = await wrap6(
3351
3561
  OP_INTEROP.routes[route].build,
3352
3562
  () => ROUTES3[route].build(params, ctx),
3353
3563
  {
@@ -3359,7 +3569,8 @@ function createInteropResource(client, tokens, contracts, attributes) {
3359
3569
  route,
3360
3570
  approvalsNeeded: approvals,
3361
3571
  totalActionValue: quoteExtras.totalActionValue,
3362
- bridgedTokenTotal: quoteExtras.bridgedTokenTotal
3572
+ bridgedTokenTotal: quoteExtras.bridgedTokenTotal,
3573
+ interopFee
3363
3574
  };
3364
3575
  return { plan: { route, summary, steps }, ctx };
3365
3576
  }
@@ -3367,20 +3578,23 @@ function createInteropResource(client, tokens, contracts, attributes) {
3367
3578
  const { plan } = await buildPlanWithCtx(dstProvider, params);
3368
3579
  return plan;
3369
3580
  }
3370
- const quote = (params) => wrap5(OP_INTEROP.quote, async () => {
3371
- const plan = await buildPlan(resolveDstProvider(params.dstChain), params);
3581
+ const quote = (dstChain, params) => wrap6(OP_INTEROP.quote, async () => {
3582
+ const plan = await buildPlan(resolveChainRef(dstChain), params);
3372
3583
  return plan.summary;
3373
3584
  });
3374
- const tryQuote = (params) => toResult2(OP_INTEROP.tryQuote, () => quote(params));
3375
- const prepare = (params) => wrap5(OP_INTEROP.prepare, () => buildPlan(resolveDstProvider(params.dstChain), params), {
3585
+ const tryQuote = (dstChain, params) => toResult2(OP_INTEROP.tryQuote, () => quote(dstChain, params));
3586
+ const prepare = (dstChain, params) => wrap6(OP_INTEROP.prepare, () => buildPlan(resolveChainRef(dstChain), params), {
3376
3587
  message: "Internal error while preparing an interop plan.",
3377
3588
  ctx: { where: "interop.prepare" }
3378
3589
  });
3379
- const tryPrepare = (params) => toResult2(OP_INTEROP.tryPrepare, () => prepare(params));
3380
- const create = (params) => wrap5(
3590
+ const tryPrepare = (dstChain, params) => toResult2(
3591
+ OP_INTEROP.tryPrepare,
3592
+ () => prepare(dstChain, params)
3593
+ );
3594
+ const create = (dstChain, params) => wrap6(
3381
3595
  OP_INTEROP.create,
3382
3596
  async () => {
3383
- const { plan, ctx } = await buildPlanWithCtx(resolveDstProvider(params.dstChain), params);
3597
+ const { plan, ctx } = await buildPlanWithCtx(resolveChainRef(dstChain), params);
3384
3598
  const signer = ctx.client.signerFor(ctx.client.l2);
3385
3599
  const srcProvider = ctx.client.l2;
3386
3600
  const from = await signer.getAddress();
@@ -3442,7 +3656,6 @@ function createInteropResource(client, tokens, contracts, attributes) {
3442
3656
  const last = Object.values(stepHashes).pop();
3443
3657
  return {
3444
3658
  kind: "interop",
3445
- dstChain: params.dstChain,
3446
3659
  stepHashes,
3447
3660
  plan,
3448
3661
  l2SrcTxHash: last ?? "0x"
@@ -3453,46 +3666,27 @@ function createInteropResource(client, tokens, contracts, attributes) {
3453
3666
  ctx: { where: "interop.create" }
3454
3667
  }
3455
3668
  );
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(
3669
+ const tryCreate = (dstChain, params) => toResult2(
3670
+ OP_INTEROP.tryCreate,
3671
+ () => create(dstChain, params)
3672
+ );
3673
+ const status = (dstChain, h, opts) => wrap6(OP_INTEROP.status, () => svc.status(resolveChainRef(dstChain), h, opts), {
3674
+ message: "Internal error while checking interop status.",
3675
+ ctx: { where: "interop.status" }
3676
+ });
3677
+ const wait = (dstChain, h, opts) => wrap6(OP_INTEROP.wait, () => svc.wait(resolveChainRef(dstChain), getGwProvider(), h, opts), {
3678
+ message: "Internal error while waiting for interop finalization.",
3679
+ ctx: { where: "interop.wait" }
3680
+ });
3681
+ const tryWait = (dstChain, h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(dstChain, h, opts));
3682
+ const finalize = (dstChain, h, opts) => wrap6(
3480
3683
  OP_INTEROP.finalize,
3481
3684
  async () => {
3685
+ const dstProvider = resolveChainRef(dstChain);
3482
3686
  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);
3687
+ return svc.finalize(dstProvider, h, opts);
3493
3688
  }
3494
- const { dstProvider, waitable } = resolveWaitableInput(h);
3495
- const info = await svc.wait(dstProvider, waitable);
3689
+ const info = await svc.wait(dstProvider, getGwProvider(), h);
3496
3690
  return svc.finalize(dstProvider, info, opts);
3497
3691
  },
3498
3692
  {
@@ -3500,7 +3694,7 @@ function createInteropResource(client, tokens, contracts, attributes) {
3500
3694
  ctx: { where: "interop.finalize" }
3501
3695
  }
3502
3696
  );
3503
- const tryFinalize = (h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(h, opts));
3697
+ const tryFinalize = (dstChain, h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(dstChain, h, opts));
3504
3698
  return {
3505
3699
  quote,
3506
3700
  tryQuote,
@@ -3517,10 +3711,10 @@ function createInteropResource(client, tokens, contracts, attributes) {
3517
3711
  }
3518
3712
 
3519
3713
  // src/adapters/ethers/sdk.ts
3520
- function createEthersSdk(client) {
3714
+ function createEthersSdk(client, options) {
3521
3715
  const tokens = createTokensResource(client);
3522
3716
  const contracts = createContractsResource(client);
3523
- const interop = createInteropResource(client);
3717
+ const interop = createInteropResource(client, options?.interop, tokens, contracts);
3524
3718
  return {
3525
3719
  deposits: createDepositsResource(client, tokens, contracts),
3526
3720
  withdrawals: createWithdrawalsResource(client, tokens, contracts),