@matterlabs/zksync-js 0.0.7 → 0.0.9

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 (105) hide show
  1. package/dist/adapters/ethers/client.cjs +1705 -259
  2. package/dist/adapters/ethers/client.cjs.map +1 -1
  3. package/dist/adapters/ethers/client.d.ts +11 -0
  4. package/dist/adapters/ethers/client.js +6 -6
  5. package/dist/adapters/ethers/index.cjs +5254 -2335
  6. package/dist/adapters/ethers/index.cjs.map +1 -1
  7. package/dist/adapters/ethers/index.d.ts +4 -0
  8. package/dist/adapters/ethers/index.js +9 -9
  9. package/dist/adapters/ethers/resources/contracts/types.d.ts +15 -0
  10. package/dist/adapters/ethers/resources/deposits/context.d.ts +3 -3
  11. package/dist/adapters/ethers/resources/deposits/services/gas.d.ts +2 -2
  12. package/dist/adapters/ethers/resources/deposits/services/verification.d.ts +1 -1
  13. package/dist/adapters/ethers/resources/interop/address.d.ts +18 -0
  14. package/dist/adapters/ethers/resources/interop/attributes/resource.d.ts +9 -0
  15. package/dist/adapters/ethers/resources/interop/context.d.ts +36 -0
  16. package/dist/adapters/ethers/resources/interop/index.d.ts +63 -0
  17. package/dist/adapters/ethers/resources/interop/resolvers.d.ts +9 -0
  18. package/dist/adapters/ethers/resources/interop/routes/direct.d.ts +2 -0
  19. package/dist/adapters/ethers/resources/interop/routes/indirect.d.ts +2 -0
  20. package/dist/adapters/ethers/resources/interop/routes/types.d.ts +13 -0
  21. package/dist/adapters/ethers/resources/interop/services/erc20.d.ts +15 -0
  22. package/dist/adapters/ethers/resources/interop/services/finalization/bundle.d.ts +15 -0
  23. package/dist/adapters/ethers/resources/interop/services/finalization/data-fetchers.d.ts +17 -0
  24. package/dist/adapters/ethers/resources/interop/services/finalization/decoders.d.ts +12 -0
  25. package/dist/adapters/ethers/resources/interop/services/finalization/index.d.ts +13 -0
  26. package/dist/adapters/ethers/resources/interop/services/finalization/polling.d.ts +7 -0
  27. package/dist/adapters/ethers/resources/interop/services/finalization/status.d.ts +5 -0
  28. package/dist/adapters/ethers/resources/interop/services/finalization/topics.d.ts +6 -0
  29. package/dist/adapters/ethers/resources/interop/services/starter-data.d.ts +6 -0
  30. package/dist/adapters/ethers/resources/interop/types.d.ts +16 -0
  31. package/dist/adapters/ethers/resources/withdrawals/context.d.ts +2 -2
  32. package/dist/adapters/ethers/sdk.cjs +3384 -931
  33. package/dist/adapters/ethers/sdk.cjs.map +1 -1
  34. package/dist/adapters/ethers/sdk.d.ts +10 -8
  35. package/dist/adapters/ethers/sdk.js +7 -7
  36. package/dist/adapters/viem/client.cjs +760 -64
  37. package/dist/adapters/viem/client.cjs.map +1 -1
  38. package/dist/adapters/viem/client.js +6 -4
  39. package/dist/adapters/viem/index.cjs +404 -276
  40. package/dist/adapters/viem/index.cjs.map +1 -1
  41. package/dist/adapters/viem/index.d.ts +1 -0
  42. package/dist/adapters/viem/index.js +9 -8
  43. package/dist/adapters/viem/resources/deposits/context.d.ts +3 -3
  44. package/dist/adapters/viem/resources/deposits/services/gas.d.ts +2 -2
  45. package/dist/adapters/viem/resources/deposits/services/verification.d.ts +1 -1
  46. package/dist/adapters/viem/resources/withdrawals/context.d.ts +2 -2
  47. package/dist/adapters/viem/sdk.cjs +84 -92
  48. package/dist/adapters/viem/sdk.cjs.map +1 -1
  49. package/dist/adapters/viem/sdk.js +6 -6
  50. package/dist/{chunk-KRIRXY74.js → chunk-5AG6B7UX.js} +38 -63
  51. package/dist/{chunk-NNFWIAVG.js → chunk-AIFHAPJC.js} +9 -3
  52. package/dist/{chunk-NCR42O6O.js → chunk-C3AGOEHR.js} +11 -1
  53. package/dist/{chunk-GIXLOHLK.js → chunk-FBKBF7YM.js} +1534 -12
  54. package/dist/{chunk-VRL6Y4YJ.js → chunk-IYEDEUXG.js} +1 -1
  55. package/dist/chunk-JNWHQJU3.js +209 -0
  56. package/dist/{chunk-7VP6742W.js → chunk-KLNFDFLA.js} +43 -32
  57. package/dist/{chunk-GNERKUWO.js → chunk-QDJOEVGJ.js} +2 -2
  58. package/dist/{chunk-EWLA4NUE.js → chunk-RRKVUW3G.js} +1377 -78
  59. package/dist/{chunk-KAMEGD6I.js → chunk-SRPKTXIF.js} +1 -1
  60. package/dist/{chunk-WY36Z6YB.js → chunk-UIXU35ZU.js} +57 -19
  61. package/dist/{chunk-P5PIWVEO.js → chunk-ZVHFVUDE.js} +14 -26
  62. package/dist/core/abi.d.ts +5 -0
  63. package/dist/core/constants.cjs +10 -0
  64. package/dist/core/constants.cjs.map +1 -1
  65. package/dist/core/constants.d.ts +10 -0
  66. package/dist/core/constants.js +1 -1
  67. package/dist/core/index.cjs +1676 -186
  68. package/dist/core/index.cjs.map +1 -1
  69. package/dist/core/index.d.ts +2 -1
  70. package/dist/core/index.js +5 -5
  71. package/dist/core/internal/abis/IERC7786Attributes.d.ts +42 -0
  72. package/dist/core/internal/abis/IInteropCenter.d.ts +211 -0
  73. package/dist/core/internal/abis/IInteropHandler.d.ts +166 -0
  74. package/dist/core/internal/abis/InteropCenter.d.ts +578 -0
  75. package/dist/core/internal/abis/InteropRootStorage.d.ts +20 -0
  76. package/dist/core/internal/abis/L2MessageVerification.d.ts +277 -0
  77. package/dist/core/resources/interop/attributes/bundle.d.ts +6 -0
  78. package/dist/core/resources/interop/attributes/call.d.ts +6 -0
  79. package/dist/core/resources/interop/attributes/index.d.ts +4 -0
  80. package/dist/core/resources/interop/attributes/resource.d.ts +12 -0
  81. package/dist/core/resources/interop/attributes/types.d.ts +4 -0
  82. package/dist/core/resources/interop/events.d.ts +7 -0
  83. package/dist/core/resources/interop/finalization.d.ts +60 -0
  84. package/dist/core/resources/interop/plan.d.ts +39 -0
  85. package/dist/core/resources/interop/route.d.ts +15 -0
  86. package/dist/core/rpc/types.d.ts +9 -0
  87. package/dist/core/types/errors.d.ts +56 -5
  88. package/dist/core/types/fees.d.ts +9 -0
  89. package/dist/core/types/flows/base.d.ts +1 -1
  90. package/dist/core/types/flows/interop.d.ts +207 -0
  91. package/dist/core/types/flows/withdrawals.d.ts +0 -8
  92. package/dist/core/types/primitives.d.ts +1 -0
  93. package/dist/core/types/transactions.d.ts +10 -0
  94. package/dist/core/utils/addr.d.ts +2 -2
  95. package/dist/core/utils/events.d.ts +12 -0
  96. package/dist/core/utils/hash.d.ts +5 -0
  97. package/dist/core/utils/index.d.ts +5 -0
  98. package/dist/core/utils/number.d.ts +2 -0
  99. package/dist/index.cjs +1686 -186
  100. package/dist/index.cjs.map +1 -1
  101. package/dist/index.d.ts +2 -1
  102. package/dist/index.js +5 -5
  103. package/package.json +6 -2
  104. package/dist/chunk-NGXRO2ZX.js +0 -142
  105. package/dist/core/resources/withdrawals/events.d.ts +0 -9
@@ -5,6 +5,19 @@ var sha3 = require('@noble/hashes/sha3');
5
5
  var utils = require('@noble/hashes/utils');
6
6
 
7
7
  // src/adapters/ethers/resources/deposits/services/verification.ts
8
+
9
+ // src/core/utils/hash.ts
10
+ var RegExpHex = /^0x[0-9a-fA-F]*$/;
11
+ var isHash = (x, length) => {
12
+ if (!x || typeof x !== "string") return false;
13
+ return (length === void 0 || x.length === length) && RegExpHex.test(x);
14
+ };
15
+ var isHashArray = (x, length) => {
16
+ if (!Array.isArray(x)) return false;
17
+ return x.every((item) => isHash(item, length));
18
+ };
19
+ var isHash66 = (x) => isHash(x, 66);
20
+ var isHash66Array = (x) => isHashArray(x, 66);
8
21
  var k256hex = (s) => `0x${utils.bytesToHex(sha3.keccak_256(utils.utf8ToBytes(s)))}`.toLowerCase();
9
22
  var FORMAL_ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
10
23
  var ETH_ADDRESS = "0x0000000000000000000000000000000000000001";
@@ -12,6 +25,9 @@ var L2_ASSET_ROUTER_ADDRESS = "0x0000000000000000000000000000000000010003";
12
25
  var L2_NATIVE_TOKEN_VAULT_ADDRESS = "0x0000000000000000000000000000000000010004";
13
26
  var L1_MESSENGER_ADDRESS = "0x0000000000000000000000000000000000008008";
14
27
  var L2_BASE_TOKEN_ADDRESS = "0x000000000000000000000000000000000000800A";
28
+ var L2_INTEROP_CENTER_ADDRESS = "0x000000000000000000000000000000000001000d";
29
+ var L2_INTEROP_ROOT_STORAGE_ADDRESS = "0x0000000000000000000000000000000000010008";
30
+ var BUNDLE_IDENTIFIER = "0x01";
15
31
  var TOPIC_L1_MESSAGE_SENT_NEW = k256hex("L1MessageSent(uint256,bytes32,bytes)");
16
32
  var TOPIC_L1_MESSAGE_SENT_LEG = k256hex("L1MessageSent(address,bytes32,bytes)");
17
33
  var TOPIC_CANONICAL_ASSIGNED = "0x779f441679936c5441b671969f37400b8c3ed0071cb47444431bf985754560df";
@@ -23,8 +39,14 @@ var DEFAULT_PUBDATA_BYTES = 155n;
23
39
  var DEFAULT_ABI_BYTES = 400n;
24
40
  var SAFE_L1_BRIDGE_GAS = 700000n;
25
41
 
42
+ // src/core/utils/number.ts
43
+ var isNumber = (x) => typeof x === "number" && Number.isFinite(x);
44
+ var isBigint = (x) => typeof x === "bigint";
45
+
26
46
  // src/core/utils/addr.ts
27
- var isHash66 = (x) => !!x && x.startsWith("0x") && x.length === 66;
47
+ function isAddress(x) {
48
+ return isHash(x, 42);
49
+ }
28
50
  function isAddressEq(a, b) {
29
51
  return a.toLowerCase() === b.toLowerCase();
30
52
  }
@@ -43,6 +65,14 @@ function normalizeAddrEq(a, b) {
43
65
  var hexEq = (a, b) => a.toLowerCase() === b.toLowerCase();
44
66
  var normalizeL1Token = (token) => isAddressEq(token, FORMAL_ETH_ADDRESS) ? ETH_ADDRESS : token;
45
67
 
68
+ // src/core/utils/index.ts
69
+ function sleep(ms) {
70
+ return new Promise((resolve) => setTimeout(resolve, ms));
71
+ }
72
+ function assertNever(x) {
73
+ throw new Error("Unexpected action type: " + String(x));
74
+ }
75
+
46
76
  // src/core/errors/formatter.ts
47
77
  function elideMiddle(s, max = 96) {
48
78
  if (s.length <= max) return s;
@@ -53,7 +83,7 @@ function shortJSON(v, max = 240) {
53
83
  try {
54
84
  const s = JSON.stringify(
55
85
  v,
56
- (_k, val) => typeof val === "bigint" ? `${val.toString()}n` : val
86
+ (_k, val) => isBigint(val) ? `${val.toString()}n` : val
57
87
  );
58
88
  return s.length > max ? elideMiddle(s, max) : s;
59
89
  } catch {
@@ -73,7 +103,7 @@ function formatContextLine(ctx) {
73
103
  if (txHash !== void 0)
74
104
  parts.push(`txHash=${typeof txHash === "string" ? txHash : shortJSON(txHash, 96)}`);
75
105
  if (nonce !== void 0) {
76
- const nonceStr = typeof nonce === "string" || typeof nonce === "number" || typeof nonce === "bigint" ? String(nonce) : shortJSON(nonce, 48);
106
+ const nonceStr = typeof nonce === "string" || isNumber(nonce) || isBigint(nonce) ? String(nonce) : shortJSON(nonce, 48);
77
107
  parts.push(`nonce=${nonceStr}`);
78
108
  }
79
109
  return parts.length ? ` ${kv("Context", parts.join(" \u2022 "))}` : void 0;
@@ -103,17 +133,17 @@ function formatCause(c) {
103
133
  const head = [];
104
134
  if (obj.name !== void 0) {
105
135
  const nameVal = obj.name;
106
- const nameStr = typeof nameVal === "string" || typeof nameVal === "number" || typeof nameVal === "bigint" || typeof nameVal === "boolean" ? String(nameVal) : shortJSON(nameVal, 120);
136
+ const nameStr = typeof nameVal === "string" || isNumber(nameVal) || isBigint(nameVal) || typeof nameVal === "boolean" ? String(nameVal) : shortJSON(nameVal, 120);
107
137
  head.push(`name=${nameStr}`);
108
138
  }
109
139
  if (obj.code !== void 0) {
110
140
  const codeVal = obj.code;
111
- const codeStr = typeof codeVal === "string" || typeof codeVal === "number" || typeof codeVal === "bigint" || typeof codeVal === "boolean" ? String(codeVal) : shortJSON(codeVal, 120);
141
+ const codeStr = typeof codeVal === "string" || isNumber(codeVal) || isBigint(codeVal) || typeof codeVal === "boolean" ? String(codeVal) : shortJSON(codeVal, 120);
112
142
  head.push(`code=${codeStr}`);
113
143
  }
114
144
  if (head.length) out.push(` ${kv("Cause", head.join(" "))}`);
115
145
  if (obj.message) {
116
- const messageStr = typeof obj.message === "string" || typeof obj.message === "number" || typeof obj.message === "bigint" || typeof obj.message === "boolean" ? String(obj.message) : shortJSON(obj.message, 600);
146
+ const messageStr = typeof obj.message === "string" || isNumber(obj.message) || isBigint(obj.message) || typeof obj.message === "boolean" ? String(obj.message) : shortJSON(obj.message, 600);
117
147
  out.push(` message=${elideMiddle(messageStr, 600)}`);
118
148
  }
119
149
  if (obj.data) {
@@ -188,12 +218,18 @@ if (kInspect) {
188
218
  enumerable: false
189
219
  });
190
220
  }
191
- function isZKsyncError(e) {
221
+ function isZKsyncError(e, opts) {
192
222
  if (!e || typeof e !== "object") return false;
193
223
  const maybe = e;
194
224
  if (!("envelope" in maybe)) return false;
195
225
  const envelope = maybe.envelope;
196
- return typeof envelope?.type === "string" && typeof envelope?.message === "string";
226
+ if (typeof envelope?.type !== "string" || typeof envelope?.message !== "string") return false;
227
+ if (opts?.type && envelope.type !== opts.type) return false;
228
+ if (opts?.resource && envelope.resource !== opts.resource) return false;
229
+ if (opts?.operation && envelope.operation !== opts.operation) return false;
230
+ if (opts?.messageIncludes && !envelope.message.toLowerCase().includes(opts.messageIncludes.toLowerCase()))
231
+ return false;
232
+ return true;
197
233
  }
198
234
  function isReceiptNotFound(e) {
199
235
  const chain = [];
@@ -308,11 +344,9 @@ var OP_WITHDRAWALS = {
308
344
  rawReceipt: "withdrawals.finalize.fetchParams:rawReceipt",
309
345
  messengerIndex: "withdrawals.finalize.fetchParams:messengerIndex",
310
346
  proof: "withdrawals.finalize.fetchParams:proof",
311
- network: "withdrawals.finalize.fetchParams:network",
312
- ensureAddresses: "withdrawals.finalize.fetchParams:ensureAddresses"
347
+ network: "withdrawals.finalize.fetchParams:network"
313
348
  },
314
349
  readiness: {
315
- ensureAddresses: "withdrawals.finalize.readiness:ensureAddresses",
316
350
  isFinalized: "withdrawals.finalize.readiness:isWithdrawalFinalized",
317
351
  simulate: "withdrawals.finalize.readiness:simulate"
318
352
  },
@@ -322,6 +356,54 @@ var OP_WITHDRAWALS = {
322
356
  estimate: "withdrawals.finalize.estimateFinalizationFees"
323
357
  }
324
358
  };
359
+ var OP_INTEROP = {
360
+ // high-level flow ops (match resource methods)
361
+ quote: "interop.quote",
362
+ tryQuote: "interop.tryQuote",
363
+ prepare: "interop.prepare",
364
+ tryPrepare: "interop.tryPrepare",
365
+ create: "interop.create",
366
+ tryCreate: "interop.tryCreate",
367
+ status: "interop.status",
368
+ wait: "interop.wait",
369
+ tryWait: "interop.tryWait",
370
+ finalize: "interop.finalize",
371
+ tryFinalize: "interop.tryFinalize",
372
+ context: {
373
+ chainTypeManager: "interop.chainTypeManager",
374
+ protocolVersion: "interop.protocolVersion"
375
+ },
376
+ // route-specific ops (keep names aligned with files)
377
+ routes: {
378
+ direct: {
379
+ preflight: "interop.routes.direct:preflight",
380
+ build: "interop.routes.direct:build"
381
+ },
382
+ indirect: {
383
+ preflight: "interop.routes.indirect:preflight",
384
+ build: "interop.routes.indirect:build"
385
+ }
386
+ },
387
+ // execution path (nonce, gas, send, wait) – mirrors deposits’ style
388
+ exec: {
389
+ sendStep: "interop.exec:sendStep",
390
+ waitStep: "interop.exec:waitStep"
391
+ },
392
+ // status service (logs & derivation)
393
+ svc: {
394
+ status: {
395
+ sourceReceipt: "interop.svc.status:sourceReceipt",
396
+ parseSentLog: "interop.svc.status:parseSentLog",
397
+ dstLogs: "interop.svc.status:dstLogs",
398
+ derive: "interop.svc.status:derive",
399
+ getRoot: "interop.svc.status:getRoot"
400
+ },
401
+ wait: {
402
+ poll: "interop.svc.wait:poll",
403
+ timeout: "interop.svc.wait:timeout"
404
+ }
405
+ }
406
+ };
325
407
 
326
408
  // src/core/errors/factory.ts
327
409
  function createError(type, input) {
@@ -358,7 +440,7 @@ var I_BRIDGEHUB = new ethers.Interface([
358
440
  "event NewPriorityRequest(uint256 indexed chainId, address indexed sender, bytes32 txHash, uint256 txId, bytes data)"
359
441
  ]);
360
442
  var TOPIC_BRIDGEHUB_NPR = I_BRIDGEHUB.getEvent("NewPriorityRequest").topicHash;
361
- function extractL2TxHashFromL1Logs(logs) {
443
+ function getL2TransactionHashFromLogs(logs) {
362
444
  for (const lg of logs) {
363
445
  if ((lg.topics?.[0] ?? "").toLowerCase() === TOPIC_BRIDGEHUB_NPR.toLowerCase()) {
364
446
  try {
@@ -385,7 +467,7 @@ function extractL2TxHashFromL1Logs(logs) {
385
467
  async function waitForL2ExecutionFromL1Tx(l1, l2, l1TxHash) {
386
468
  const l1Receipt = await l1.waitForTransaction(l1TxHash);
387
469
  if (!l1Receipt) throw new Error("No L1 receipt found");
388
- const l2TxHash = extractL2TxHashFromL1Logs(l1Receipt.logs);
470
+ const l2TxHash = getL2TransactionHashFromLogs(l1Receipt.logs);
389
471
  if (!l2TxHash) {
390
472
  throw createError("VERIFICATION", {
391
473
  message: "Failed to extract L2 transaction hash from L1 logs",
@@ -3182,6 +3264,63 @@ var IERC20ABI = [
3182
3264
  ];
3183
3265
  var IERC20_default = IERC20ABI;
3184
3266
 
3267
+ // src/core/internal/abis/IERC7786Attributes.ts
3268
+ var IERC7786AttributesABI = [
3269
+ {
3270
+ inputs: [
3271
+ {
3272
+ internalType: "bytes",
3273
+ name: "_executionAddress",
3274
+ type: "bytes"
3275
+ }
3276
+ ],
3277
+ name: "executionAddress",
3278
+ outputs: [],
3279
+ stateMutability: "pure",
3280
+ type: "function"
3281
+ },
3282
+ {
3283
+ inputs: [
3284
+ {
3285
+ internalType: "uint256",
3286
+ name: "_indirectCallMessageValue",
3287
+ type: "uint256"
3288
+ }
3289
+ ],
3290
+ name: "indirectCall",
3291
+ outputs: [],
3292
+ stateMutability: "pure",
3293
+ type: "function"
3294
+ },
3295
+ {
3296
+ inputs: [
3297
+ {
3298
+ internalType: "uint256",
3299
+ name: "_interopCallValue",
3300
+ type: "uint256"
3301
+ }
3302
+ ],
3303
+ name: "interopCallValue",
3304
+ outputs: [],
3305
+ stateMutability: "pure",
3306
+ type: "function"
3307
+ },
3308
+ {
3309
+ inputs: [
3310
+ {
3311
+ internalType: "bytes",
3312
+ name: "_unbundlerAddress",
3313
+ type: "bytes"
3314
+ }
3315
+ ],
3316
+ name: "unbundlerAddress",
3317
+ outputs: [],
3318
+ stateMutability: "pure",
3319
+ type: "function"
3320
+ }
3321
+ ];
3322
+ var IERC7786Attributes_default = IERC7786AttributesABI;
3323
+
3185
3324
  // src/core/internal/abis/Mailbox.ts
3186
3325
  var MailboxABI = [
3187
3326
  {
@@ -3558,206 +3697,1213 @@ var MailboxABI = [
3558
3697
  ];
3559
3698
  var Mailbox_default = MailboxABI;
3560
3699
 
3561
- // src/adapters/ethers/resources/deposits/context.ts
3562
- async function commonCtx(p, client, tokens, contracts) {
3563
- const { bridgehub, l1AssetRouter } = await contracts.addresses();
3564
- const { chainId } = await client.l2.getNetwork();
3565
- const sender = await client.signer.getAddress();
3566
- const gasPerPubdata = p.gasPerPubdata ?? 800n;
3567
- const operatorTip = p.operatorTip ?? 0n;
3568
- const refundRecipient = p.refundRecipient ?? sender;
3569
- const resolvedToken = await tokens.resolve(p.token, { chain: "l1" });
3570
- const baseTokenAssetId = resolvedToken.baseTokenAssetId;
3571
- const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
3572
- const baseIsEth = resolvedToken.isChainEthBased;
3573
- const route = (() => {
3574
- if (resolvedToken.kind === "eth") {
3575
- return baseIsEth ? "eth-base" : "eth-nonbase";
3576
- }
3577
- if (resolvedToken.kind === "base") {
3578
- return baseIsEth ? "eth-base" : "erc20-base";
3579
- }
3580
- return "erc20-nonbase";
3581
- })();
3582
- return {
3583
- client,
3584
- tokens,
3585
- contracts,
3586
- resolvedToken,
3587
- baseTokenAssetId,
3588
- baseTokenL1,
3589
- baseIsEth,
3590
- l1AssetRouter,
3591
- route,
3592
- bridgehub,
3593
- chainIdL2: BigInt(chainId),
3594
- sender,
3595
- gasOverrides: p.l1TxOverrides,
3596
- l2GasLimit: p.l2GasLimit,
3597
- gasPerPubdata,
3598
- operatorTip,
3599
- refundRecipient
3600
- };
3601
- }
3602
- function encodeNativeTokenVaultTransferData(amount, receiver, token) {
3603
- return new ethers.AbiCoder().encode(["uint256", "address", "address"], [amount, receiver, token]);
3604
- }
3605
- function encodeSecondBridgeArgs(token, amount, l2Receiver) {
3606
- return ethers.AbiCoder.defaultAbiCoder().encode(
3607
- ["address", "uint256", "address"],
3608
- [token, amount, l2Receiver]
3609
- );
3610
- }
3611
- function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
3612
- return encodeSecondBridgeArgs(token, amount, l2Receiver);
3613
- }
3614
- function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
3615
- return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
3616
- }
3617
- function buildDirectRequestStruct(args) {
3618
- return {
3619
- chainId: args.chainId,
3620
- l2Contract: args.l2Contract,
3621
- mintValue: args.mintValue,
3622
- l2Value: args.l2Value,
3623
- l2Calldata: "0x",
3624
- l2GasLimit: args.l2GasLimit,
3625
- l2GasPerPubdataByteLimit: args.gasPerPubdata,
3626
- factoryDeps: [],
3627
- refundRecipient: args.refundRecipient
3628
- };
3629
- }
3630
-
3631
- // src/core/resources/deposits/gas.ts
3632
- function makeGasQuote(p) {
3633
- const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
3634
- return {
3635
- gasLimit: p.gasLimit,
3636
- maxFeePerGas: p.maxFeePerGas,
3637
- maxPriorityFeePerGas,
3638
- gasPerPubdata: p.gasPerPubdata,
3639
- maxCost: p.gasLimit * p.maxFeePerGas
3640
- };
3641
- }
3642
- async function fetchFees(estimator) {
3643
- try {
3644
- const fees = await estimator.estimateFeesPerGas();
3645
- if (fees.maxFeePerGas != null) {
3646
- return {
3647
- maxFeePerGas: fees.maxFeePerGas,
3648
- maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
3649
- };
3650
- }
3651
- if (fees.gasPrice != null) {
3652
- return {
3653
- maxFeePerGas: fees.gasPrice,
3654
- maxPriorityFeePerGas: 0n
3655
- };
3656
- }
3657
- } catch {
3658
- }
3659
- try {
3660
- const gp = await estimator.getGasPrice();
3661
- return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
3662
- } catch {
3663
- return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
3664
- }
3665
- }
3666
- async function quoteL1Gas(input) {
3667
- const { estimator, tx, overrides, fallbackGasLimit } = input;
3668
- let market;
3669
- const getMarket = async () => {
3670
- if (market) return market;
3671
- market = await fetchFees(estimator);
3672
- return market;
3673
- };
3674
- const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
3675
- const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
3676
- const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
3677
- if (explicitGasLimit != null) {
3678
- return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
3679
- }
3680
- try {
3681
- const est = await estimator.estimateGas(tx);
3682
- const buffered = BigInt(est) * (100n + BUFFER) / 100n;
3683
- return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
3684
- } catch (err) {
3685
- if (fallbackGasLimit != null) {
3686
- return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
3687
- }
3688
- console.warn("L1 gas estimation failed", err);
3689
- return void 0;
3690
- }
3691
- }
3692
- async function quoteL2Gas(input) {
3693
- const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
3694
- const market = await fetchFees(estimator);
3695
- const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
3696
- const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
3697
- const explicit = overrideGasLimit ?? txGasLimit;
3698
- if (explicit != null) {
3699
- return makeGasQuote({
3700
- gasLimit: explicit,
3701
- maxFeePerGas,
3702
- gasPerPubdata
3703
- });
3704
- }
3705
- if (!tx) {
3706
- return makeGasQuote({
3707
- gasLimit: l2GasLimit ?? 0n,
3708
- maxFeePerGas,
3709
- gasPerPubdata
3710
- });
3711
- }
3712
- try {
3713
- const execEstimate = await estimator.estimateGas(tx, stateOverrides);
3714
- const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
3715
- const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
3716
- const pp = gasPerPubdata ?? 800n;
3717
- const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
3718
- const pubdataOverhead = pubdataBytes * pp;
3719
- let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
3720
- total = total * (100n + BUFFER) / 100n;
3721
- return makeGasQuote({
3722
- gasLimit: total,
3723
- maxFeePerGas,
3724
- gasPerPubdata: pp
3725
- });
3726
- } catch (err) {
3727
- console.warn("L2 gas estimation failed", err);
3728
- return makeGasQuote({
3729
- gasLimit: l2GasLimit ?? 0n,
3730
- maxFeePerGas,
3731
- gasPerPubdata
3732
- });
3733
- }
3734
- }
3735
- async function quoteL2BaseCost(input) {
3736
- const { estimator, encode: encode2, bridgehub, chainIdL2, l2GasLimit, gasPerPubdata } = input;
3737
- const market = await fetchFees(estimator);
3738
- const l1GasPrice = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
3739
- if (l1GasPrice === 0n) {
3740
- throw new Error("Could not fetch L1 gas price for Bridgehub base cost calculation.");
3741
- }
3742
- const data = encode2(IBridgehub_default, "l2TransactionBaseCost", [
3743
- chainIdL2,
3744
- l1GasPrice,
3745
- l2GasLimit,
3746
- gasPerPubdata
3747
- ]);
3748
- const raw = await estimator.call({
3749
- to: bridgehub,
3750
- data
3751
- });
3752
- return BigInt(raw);
3753
- }
3754
-
3755
- // src/adapters/ethers/estimator.ts
3756
- function toCoreTx(tx) {
3757
- return {
3758
- to: tx.to,
3759
- from: tx.from,
3760
- data: tx.data,
3700
+ // src/core/internal/abis/InteropCenter.ts
3701
+ var InteropCenterABI = [
3702
+ {
3703
+ inputs: [
3704
+ {
3705
+ internalType: "bytes4",
3706
+ name: "selector",
3707
+ type: "bytes4"
3708
+ }
3709
+ ],
3710
+ name: "AttributeAlreadySet",
3711
+ type: "error"
3712
+ },
3713
+ {
3714
+ inputs: [
3715
+ {
3716
+ internalType: "bytes4",
3717
+ name: "selector",
3718
+ type: "bytes4"
3719
+ },
3720
+ {
3721
+ internalType: "uint256",
3722
+ name: "restriction",
3723
+ type: "uint256"
3724
+ }
3725
+ ],
3726
+ name: "AttributeViolatesRestriction",
3727
+ type: "error"
3728
+ },
3729
+ {
3730
+ inputs: [
3731
+ {
3732
+ internalType: "uint256",
3733
+ name: "expected",
3734
+ type: "uint256"
3735
+ },
3736
+ {
3737
+ internalType: "uint256",
3738
+ name: "actual",
3739
+ type: "uint256"
3740
+ }
3741
+ ],
3742
+ name: "IndirectCallValueMismatch",
3743
+ type: "error"
3744
+ },
3745
+ {
3746
+ inputs: [
3747
+ {
3748
+ internalType: "bytes",
3749
+ name: "interoperableAddress",
3750
+ type: "bytes"
3751
+ }
3752
+ ],
3753
+ name: "InteroperableAddressChainReferenceNotEmpty",
3754
+ type: "error"
3755
+ },
3756
+ {
3757
+ inputs: [
3758
+ {
3759
+ internalType: "bytes",
3760
+ name: "interoperableAddress",
3761
+ type: "bytes"
3762
+ }
3763
+ ],
3764
+ name: "InteroperableAddressNotEmpty",
3765
+ type: "error"
3766
+ },
3767
+ {
3768
+ inputs: [
3769
+ {
3770
+ internalType: "bytes",
3771
+ name: "",
3772
+ type: "bytes"
3773
+ }
3774
+ ],
3775
+ name: "InteroperableAddressParsingError",
3776
+ type: "error"
3777
+ },
3778
+ {
3779
+ inputs: [
3780
+ {
3781
+ internalType: "uint256",
3782
+ name: "expectedMsgValue",
3783
+ type: "uint256"
3784
+ },
3785
+ {
3786
+ internalType: "uint256",
3787
+ name: "providedMsgValue",
3788
+ type: "uint256"
3789
+ }
3790
+ ],
3791
+ name: "MsgValueMismatch",
3792
+ type: "error"
3793
+ },
3794
+ {
3795
+ inputs: [],
3796
+ name: "NotInGatewayMode",
3797
+ type: "error"
3798
+ },
3799
+ {
3800
+ inputs: [
3801
+ {
3802
+ internalType: "uint256",
3803
+ name: "sourceChainId",
3804
+ type: "uint256"
3805
+ },
3806
+ {
3807
+ internalType: "uint256",
3808
+ name: "destinationChainId",
3809
+ type: "uint256"
3810
+ }
3811
+ ],
3812
+ name: "NotL2ToL2",
3813
+ type: "error"
3814
+ },
3815
+ {
3816
+ inputs: [],
3817
+ name: "SlotOccupied",
3818
+ type: "error"
3819
+ },
3820
+ {
3821
+ inputs: [
3822
+ {
3823
+ internalType: "address",
3824
+ name: "caller",
3825
+ type: "address"
3826
+ }
3827
+ ],
3828
+ name: "Unauthorized",
3829
+ type: "error"
3830
+ },
3831
+ {
3832
+ inputs: [
3833
+ {
3834
+ internalType: "bytes4",
3835
+ name: "selector",
3836
+ type: "bytes4"
3837
+ }
3838
+ ],
3839
+ name: "UnsupportedAttribute",
3840
+ type: "error"
3841
+ },
3842
+ {
3843
+ anonymous: false,
3844
+ inputs: [
3845
+ {
3846
+ indexed: false,
3847
+ internalType: "uint8",
3848
+ name: "version",
3849
+ type: "uint8"
3850
+ }
3851
+ ],
3852
+ name: "Initialized",
3853
+ type: "event"
3854
+ },
3855
+ {
3856
+ anonymous: false,
3857
+ inputs: [
3858
+ {
3859
+ indexed: false,
3860
+ internalType: "bytes32",
3861
+ name: "l2l1MsgHash",
3862
+ type: "bytes32"
3863
+ },
3864
+ {
3865
+ indexed: false,
3866
+ internalType: "bytes32",
3867
+ name: "interopBundleHash",
3868
+ type: "bytes32"
3869
+ },
3870
+ {
3871
+ components: [
3872
+ {
3873
+ internalType: "bytes1",
3874
+ name: "version",
3875
+ type: "bytes1"
3876
+ },
3877
+ {
3878
+ internalType: "uint256",
3879
+ name: "sourceChainId",
3880
+ type: "uint256"
3881
+ },
3882
+ {
3883
+ internalType: "uint256",
3884
+ name: "destinationChainId",
3885
+ type: "uint256"
3886
+ },
3887
+ {
3888
+ internalType: "bytes32",
3889
+ name: "interopBundleSalt",
3890
+ type: "bytes32"
3891
+ },
3892
+ {
3893
+ components: [
3894
+ {
3895
+ internalType: "bytes1",
3896
+ name: "version",
3897
+ type: "bytes1"
3898
+ },
3899
+ {
3900
+ internalType: "bool",
3901
+ name: "shadowAccount",
3902
+ type: "bool"
3903
+ },
3904
+ {
3905
+ internalType: "address",
3906
+ name: "to",
3907
+ type: "address"
3908
+ },
3909
+ {
3910
+ internalType: "address",
3911
+ name: "from",
3912
+ type: "address"
3913
+ },
3914
+ {
3915
+ internalType: "uint256",
3916
+ name: "value",
3917
+ type: "uint256"
3918
+ },
3919
+ {
3920
+ internalType: "bytes",
3921
+ name: "data",
3922
+ type: "bytes"
3923
+ }
3924
+ ],
3925
+ internalType: "struct InteropCall[]",
3926
+ name: "calls",
3927
+ type: "tuple[]"
3928
+ },
3929
+ {
3930
+ components: [
3931
+ {
3932
+ internalType: "bytes",
3933
+ name: "executionAddress",
3934
+ type: "bytes"
3935
+ },
3936
+ {
3937
+ internalType: "bytes",
3938
+ name: "unbundlerAddress",
3939
+ type: "bytes"
3940
+ }
3941
+ ],
3942
+ internalType: "struct BundleAttributes",
3943
+ name: "bundleAttributes",
3944
+ type: "tuple"
3945
+ }
3946
+ ],
3947
+ indexed: false,
3948
+ internalType: "struct InteropBundle",
3949
+ name: "interopBundle",
3950
+ type: "tuple"
3951
+ }
3952
+ ],
3953
+ name: "InteropBundleSent",
3954
+ type: "event"
3955
+ },
3956
+ {
3957
+ anonymous: false,
3958
+ inputs: [
3959
+ {
3960
+ indexed: true,
3961
+ internalType: "bytes32",
3962
+ name: "sendId",
3963
+ type: "bytes32"
3964
+ },
3965
+ {
3966
+ indexed: false,
3967
+ internalType: "bytes",
3968
+ name: "sender",
3969
+ type: "bytes"
3970
+ },
3971
+ {
3972
+ indexed: false,
3973
+ internalType: "bytes",
3974
+ name: "recipient",
3975
+ type: "bytes"
3976
+ },
3977
+ {
3978
+ indexed: false,
3979
+ internalType: "bytes",
3980
+ name: "payload",
3981
+ type: "bytes"
3982
+ },
3983
+ {
3984
+ indexed: false,
3985
+ internalType: "uint256",
3986
+ name: "value",
3987
+ type: "uint256"
3988
+ },
3989
+ {
3990
+ indexed: false,
3991
+ internalType: "bytes[]",
3992
+ name: "attributes",
3993
+ type: "bytes[]"
3994
+ }
3995
+ ],
3996
+ name: "MessageSent",
3997
+ type: "event"
3998
+ },
3999
+ {
4000
+ anonymous: false,
4001
+ inputs: [
4002
+ {
4003
+ indexed: true,
4004
+ internalType: "address",
4005
+ name: "oldAssetRouter",
4006
+ type: "address"
4007
+ },
4008
+ {
4009
+ indexed: true,
4010
+ internalType: "address",
4011
+ name: "newAssetRouter",
4012
+ type: "address"
4013
+ }
4014
+ ],
4015
+ name: "NewAssetRouter",
4016
+ type: "event"
4017
+ },
4018
+ {
4019
+ anonymous: false,
4020
+ inputs: [
4021
+ {
4022
+ indexed: true,
4023
+ internalType: "address",
4024
+ name: "oldAssetTracker",
4025
+ type: "address"
4026
+ },
4027
+ {
4028
+ indexed: true,
4029
+ internalType: "address",
4030
+ name: "newAssetTracker",
4031
+ type: "address"
4032
+ }
4033
+ ],
4034
+ name: "NewAssetTracker",
4035
+ type: "event"
4036
+ },
4037
+ {
4038
+ anonymous: false,
4039
+ inputs: [
4040
+ {
4041
+ indexed: true,
4042
+ internalType: "address",
4043
+ name: "previousOwner",
4044
+ type: "address"
4045
+ },
4046
+ {
4047
+ indexed: true,
4048
+ internalType: "address",
4049
+ name: "newOwner",
4050
+ type: "address"
4051
+ }
4052
+ ],
4053
+ name: "OwnershipTransferStarted",
4054
+ type: "event"
4055
+ },
4056
+ {
4057
+ anonymous: false,
4058
+ inputs: [
4059
+ {
4060
+ indexed: true,
4061
+ internalType: "address",
4062
+ name: "previousOwner",
4063
+ type: "address"
4064
+ },
4065
+ {
4066
+ indexed: true,
4067
+ internalType: "address",
4068
+ name: "newOwner",
4069
+ type: "address"
4070
+ }
4071
+ ],
4072
+ name: "OwnershipTransferred",
4073
+ type: "event"
4074
+ },
4075
+ {
4076
+ anonymous: false,
4077
+ inputs: [
4078
+ {
4079
+ indexed: false,
4080
+ internalType: "address",
4081
+ name: "account",
4082
+ type: "address"
4083
+ }
4084
+ ],
4085
+ name: "Paused",
4086
+ type: "event"
4087
+ },
4088
+ {
4089
+ anonymous: false,
4090
+ inputs: [
4091
+ {
4092
+ indexed: false,
4093
+ internalType: "address",
4094
+ name: "account",
4095
+ type: "address"
4096
+ }
4097
+ ],
4098
+ name: "Unpaused",
4099
+ type: "event"
4100
+ },
4101
+ {
4102
+ inputs: [],
4103
+ name: "L1_CHAIN_ID",
4104
+ outputs: [
4105
+ {
4106
+ internalType: "uint256",
4107
+ name: "",
4108
+ type: "uint256"
4109
+ }
4110
+ ],
4111
+ stateMutability: "view",
4112
+ type: "function"
4113
+ },
4114
+ {
4115
+ inputs: [],
4116
+ name: "acceptOwnership",
4117
+ outputs: [],
4118
+ stateMutability: "nonpayable",
4119
+ type: "function"
4120
+ },
4121
+ {
4122
+ inputs: [
4123
+ {
4124
+ internalType: "uint256",
4125
+ name: "_chainId",
4126
+ type: "uint256"
4127
+ },
4128
+ {
4129
+ internalType: "bytes32",
4130
+ name: "_canonicalTxHash",
4131
+ type: "bytes32"
4132
+ },
4133
+ {
4134
+ internalType: "uint64",
4135
+ name: "_expirationTimestamp",
4136
+ type: "uint64"
4137
+ },
4138
+ {
4139
+ components: [
4140
+ {
4141
+ internalType: "bytes1",
4142
+ name: "version",
4143
+ type: "bytes1"
4144
+ },
4145
+ {
4146
+ internalType: "address",
4147
+ name: "originToken",
4148
+ type: "address"
4149
+ },
4150
+ {
4151
+ internalType: "bytes32",
4152
+ name: "baseTokenAssetId",
4153
+ type: "bytes32"
4154
+ },
4155
+ {
4156
+ internalType: "uint256",
4157
+ name: "baseTokenAmount",
4158
+ type: "uint256"
4159
+ },
4160
+ {
4161
+ internalType: "bytes32",
4162
+ name: "assetId",
4163
+ type: "bytes32"
4164
+ },
4165
+ {
4166
+ internalType: "uint256",
4167
+ name: "amount",
4168
+ type: "uint256"
4169
+ },
4170
+ {
4171
+ internalType: "uint256",
4172
+ name: "tokenOriginChainId",
4173
+ type: "uint256"
4174
+ }
4175
+ ],
4176
+ internalType: "struct BalanceChange",
4177
+ name: "_balanceChange",
4178
+ type: "tuple"
4179
+ }
4180
+ ],
4181
+ name: "forwardTransactionOnGatewayWithBalanceChange",
4182
+ outputs: [],
4183
+ stateMutability: "nonpayable",
4184
+ type: "function"
4185
+ },
4186
+ {
4187
+ inputs: [
4188
+ {
4189
+ internalType: "uint256",
4190
+ name: "_l1ChainId",
4191
+ type: "uint256"
4192
+ },
4193
+ {
4194
+ internalType: "address",
4195
+ name: "_owner",
4196
+ type: "address"
4197
+ }
4198
+ ],
4199
+ name: "initL2",
4200
+ outputs: [],
4201
+ stateMutability: "nonpayable",
4202
+ type: "function"
4203
+ },
4204
+ {
4205
+ inputs: [
4206
+ {
4207
+ internalType: "address",
4208
+ name: "sender",
4209
+ type: "address"
4210
+ }
4211
+ ],
4212
+ name: "interopBundleNonce",
4213
+ outputs: [
4214
+ {
4215
+ internalType: "uint256",
4216
+ name: "numberOfBundlesSent",
4217
+ type: "uint256"
4218
+ }
4219
+ ],
4220
+ stateMutability: "view",
4221
+ type: "function"
4222
+ },
4223
+ {
4224
+ inputs: [],
4225
+ name: "owner",
4226
+ outputs: [
4227
+ {
4228
+ internalType: "address",
4229
+ name: "",
4230
+ type: "address"
4231
+ }
4232
+ ],
4233
+ stateMutability: "view",
4234
+ type: "function"
4235
+ },
4236
+ {
4237
+ inputs: [
4238
+ {
4239
+ internalType: "bytes[]",
4240
+ name: "_attributes",
4241
+ type: "bytes[]"
4242
+ },
4243
+ {
4244
+ internalType: "enum IInteropCenter.AttributeParsingRestrictions",
4245
+ name: "_restriction",
4246
+ type: "uint8"
4247
+ }
4248
+ ],
4249
+ name: "parseAttributes",
4250
+ outputs: [
4251
+ {
4252
+ components: [
4253
+ {
4254
+ internalType: "uint256",
4255
+ name: "interopCallValue",
4256
+ type: "uint256"
4257
+ },
4258
+ {
4259
+ internalType: "bool",
4260
+ name: "indirectCall",
4261
+ type: "bool"
4262
+ },
4263
+ {
4264
+ internalType: "uint256",
4265
+ name: "indirectCallMessageValue",
4266
+ type: "uint256"
4267
+ }
4268
+ ],
4269
+ internalType: "struct CallAttributes",
4270
+ name: "callAttributes",
4271
+ type: "tuple"
4272
+ },
4273
+ {
4274
+ components: [
4275
+ {
4276
+ internalType: "bytes",
4277
+ name: "executionAddress",
4278
+ type: "bytes"
4279
+ },
4280
+ {
4281
+ internalType: "bytes",
4282
+ name: "unbundlerAddress",
4283
+ type: "bytes"
4284
+ }
4285
+ ],
4286
+ internalType: "struct BundleAttributes",
4287
+ name: "bundleAttributes",
4288
+ type: "tuple"
4289
+ }
4290
+ ],
4291
+ stateMutability: "pure",
4292
+ type: "function"
4293
+ },
4294
+ {
4295
+ inputs: [],
4296
+ name: "pause",
4297
+ outputs: [],
4298
+ stateMutability: "nonpayable",
4299
+ type: "function"
4300
+ },
4301
+ {
4302
+ inputs: [],
4303
+ name: "paused",
4304
+ outputs: [
4305
+ {
4306
+ internalType: "bool",
4307
+ name: "",
4308
+ type: "bool"
4309
+ }
4310
+ ],
4311
+ stateMutability: "view",
4312
+ type: "function"
4313
+ },
4314
+ {
4315
+ inputs: [],
4316
+ name: "pendingOwner",
4317
+ outputs: [
4318
+ {
4319
+ internalType: "address",
4320
+ name: "",
4321
+ type: "address"
4322
+ }
4323
+ ],
4324
+ stateMutability: "view",
4325
+ type: "function"
4326
+ },
4327
+ {
4328
+ inputs: [],
4329
+ name: "renounceOwnership",
4330
+ outputs: [],
4331
+ stateMutability: "nonpayable",
4332
+ type: "function"
4333
+ },
4334
+ {
4335
+ inputs: [
4336
+ {
4337
+ internalType: "bytes",
4338
+ name: "_destinationChainId",
4339
+ type: "bytes"
4340
+ },
4341
+ {
4342
+ components: [
4343
+ {
4344
+ internalType: "bytes",
4345
+ name: "to",
4346
+ type: "bytes"
4347
+ },
4348
+ {
4349
+ internalType: "bytes",
4350
+ name: "data",
4351
+ type: "bytes"
4352
+ },
4353
+ {
4354
+ internalType: "bytes[]",
4355
+ name: "callAttributes",
4356
+ type: "bytes[]"
4357
+ }
4358
+ ],
4359
+ internalType: "struct InteropCallStarter[]",
4360
+ name: "_callStarters",
4361
+ type: "tuple[]"
4362
+ },
4363
+ {
4364
+ internalType: "bytes[]",
4365
+ name: "_bundleAttributes",
4366
+ type: "bytes[]"
4367
+ }
4368
+ ],
4369
+ name: "sendBundle",
4370
+ outputs: [
4371
+ {
4372
+ internalType: "bytes32",
4373
+ name: "bundleHash",
4374
+ type: "bytes32"
4375
+ }
4376
+ ],
4377
+ stateMutability: "payable",
4378
+ type: "function"
4379
+ },
4380
+ {
4381
+ inputs: [
4382
+ {
4383
+ internalType: "bytes",
4384
+ name: "recipient",
4385
+ type: "bytes"
4386
+ },
4387
+ {
4388
+ internalType: "bytes",
4389
+ name: "payload",
4390
+ type: "bytes"
4391
+ },
4392
+ {
4393
+ internalType: "bytes[]",
4394
+ name: "attributes",
4395
+ type: "bytes[]"
4396
+ }
4397
+ ],
4398
+ name: "sendMessage",
4399
+ outputs: [
4400
+ {
4401
+ internalType: "bytes32",
4402
+ name: "sendId",
4403
+ type: "bytes32"
4404
+ }
4405
+ ],
4406
+ stateMutability: "payable",
4407
+ type: "function"
4408
+ },
4409
+ {
4410
+ inputs: [
4411
+ {
4412
+ internalType: "bytes4",
4413
+ name: "_attributeSelector",
4414
+ type: "bytes4"
4415
+ }
4416
+ ],
4417
+ name: "supportsAttribute",
4418
+ outputs: [
4419
+ {
4420
+ internalType: "bool",
4421
+ name: "",
4422
+ type: "bool"
4423
+ }
4424
+ ],
4425
+ stateMutability: "pure",
4426
+ type: "function"
4427
+ },
4428
+ {
4429
+ inputs: [
4430
+ {
4431
+ internalType: "address",
4432
+ name: "newOwner",
4433
+ type: "address"
4434
+ }
4435
+ ],
4436
+ name: "transferOwnership",
4437
+ outputs: [],
4438
+ stateMutability: "nonpayable",
4439
+ type: "function"
4440
+ },
4441
+ {
4442
+ inputs: [],
4443
+ name: "unpause",
4444
+ outputs: [],
4445
+ stateMutability: "nonpayable",
4446
+ type: "function"
4447
+ }
4448
+ ];
4449
+ var InteropCenter_default = InteropCenterABI;
4450
+
4451
+ // src/core/internal/abis/IInteropHandler.ts
4452
+ var IInteropHandlerABI = [
4453
+ {
4454
+ anonymous: false,
4455
+ inputs: [
4456
+ {
4457
+ indexed: true,
4458
+ internalType: "bytes32",
4459
+ name: "bundleHash",
4460
+ type: "bytes32"
4461
+ }
4462
+ ],
4463
+ name: "BundleExecuted",
4464
+ type: "event"
4465
+ },
4466
+ {
4467
+ anonymous: false,
4468
+ inputs: [
4469
+ {
4470
+ indexed: true,
4471
+ internalType: "bytes32",
4472
+ name: "bundleHash",
4473
+ type: "bytes32"
4474
+ }
4475
+ ],
4476
+ name: "BundleUnbundled",
4477
+ type: "event"
4478
+ },
4479
+ {
4480
+ anonymous: false,
4481
+ inputs: [
4482
+ {
4483
+ indexed: true,
4484
+ internalType: "bytes32",
4485
+ name: "bundleHash",
4486
+ type: "bytes32"
4487
+ }
4488
+ ],
4489
+ name: "BundleVerified",
4490
+ type: "event"
4491
+ },
4492
+ {
4493
+ anonymous: false,
4494
+ inputs: [
4495
+ {
4496
+ indexed: true,
4497
+ internalType: "bytes32",
4498
+ name: "bundleHash",
4499
+ type: "bytes32"
4500
+ },
4501
+ {
4502
+ indexed: true,
4503
+ internalType: "uint256",
4504
+ name: "callIndex",
4505
+ type: "uint256"
4506
+ },
4507
+ {
4508
+ indexed: false,
4509
+ internalType: "enum CallStatus",
4510
+ name: "status",
4511
+ type: "uint8"
4512
+ }
4513
+ ],
4514
+ name: "CallProcessed",
4515
+ type: "event"
4516
+ },
4517
+ {
4518
+ inputs: [
4519
+ {
4520
+ internalType: "bytes",
4521
+ name: "_bundle",
4522
+ type: "bytes"
4523
+ },
4524
+ {
4525
+ components: [
4526
+ {
4527
+ internalType: "uint256",
4528
+ name: "chainId",
4529
+ type: "uint256"
4530
+ },
4531
+ {
4532
+ internalType: "uint256",
4533
+ name: "l1BatchNumber",
4534
+ type: "uint256"
4535
+ },
4536
+ {
4537
+ internalType: "uint256",
4538
+ name: "l2MessageIndex",
4539
+ type: "uint256"
4540
+ },
4541
+ {
4542
+ components: [
4543
+ {
4544
+ internalType: "uint16",
4545
+ name: "txNumberInBatch",
4546
+ type: "uint16"
4547
+ },
4548
+ {
4549
+ internalType: "address",
4550
+ name: "sender",
4551
+ type: "address"
4552
+ },
4553
+ {
4554
+ internalType: "bytes",
4555
+ name: "data",
4556
+ type: "bytes"
4557
+ }
4558
+ ],
4559
+ internalType: "struct L2Message",
4560
+ name: "message",
4561
+ type: "tuple"
4562
+ },
4563
+ {
4564
+ internalType: "bytes32[]",
4565
+ name: "proof",
4566
+ type: "bytes32[]"
4567
+ }
4568
+ ],
4569
+ internalType: "struct MessageInclusionProof",
4570
+ name: "_proof",
4571
+ type: "tuple"
4572
+ }
4573
+ ],
4574
+ name: "executeBundle",
4575
+ outputs: [],
4576
+ stateMutability: "nonpayable",
4577
+ type: "function"
4578
+ },
4579
+ {
4580
+ inputs: [
4581
+ {
4582
+ internalType: "uint256",
4583
+ name: "_sourceChainId",
4584
+ type: "uint256"
4585
+ },
4586
+ {
4587
+ internalType: "bytes",
4588
+ name: "_bundle",
4589
+ type: "bytes"
4590
+ },
4591
+ {
4592
+ internalType: "enum CallStatus[]",
4593
+ name: "_callStatus",
4594
+ type: "uint8[]"
4595
+ }
4596
+ ],
4597
+ name: "unbundleBundle",
4598
+ outputs: [],
4599
+ stateMutability: "nonpayable",
4600
+ type: "function"
4601
+ },
4602
+ {
4603
+ inputs: [
4604
+ {
4605
+ internalType: "bytes",
4606
+ name: "_bundle",
4607
+ type: "bytes"
4608
+ },
4609
+ {
4610
+ components: [
4611
+ {
4612
+ internalType: "uint256",
4613
+ name: "chainId",
4614
+ type: "uint256"
4615
+ },
4616
+ {
4617
+ internalType: "uint256",
4618
+ name: "l1BatchNumber",
4619
+ type: "uint256"
4620
+ },
4621
+ {
4622
+ internalType: "uint256",
4623
+ name: "l2MessageIndex",
4624
+ type: "uint256"
4625
+ },
4626
+ {
4627
+ components: [
4628
+ {
4629
+ internalType: "uint16",
4630
+ name: "txNumberInBatch",
4631
+ type: "uint16"
4632
+ },
4633
+ {
4634
+ internalType: "address",
4635
+ name: "sender",
4636
+ type: "address"
4637
+ },
4638
+ {
4639
+ internalType: "bytes",
4640
+ name: "data",
4641
+ type: "bytes"
4642
+ }
4643
+ ],
4644
+ internalType: "struct L2Message",
4645
+ name: "message",
4646
+ type: "tuple"
4647
+ },
4648
+ {
4649
+ internalType: "bytes32[]",
4650
+ name: "proof",
4651
+ type: "bytes32[]"
4652
+ }
4653
+ ],
4654
+ internalType: "struct MessageInclusionProof",
4655
+ name: "_proof",
4656
+ type: "tuple"
4657
+ }
4658
+ ],
4659
+ name: "verifyBundle",
4660
+ outputs: [],
4661
+ stateMutability: "nonpayable",
4662
+ type: "function"
4663
+ }
4664
+ ];
4665
+ var IInteropHandler_default = IInteropHandlerABI;
4666
+
4667
+ // src/core/internal/abis/InteropRootStorage.ts
4668
+ var InteropRootStorageABI = [
4669
+ {
4670
+ inputs: [
4671
+ {
4672
+ internalType: "uint256",
4673
+ name: "chainId",
4674
+ type: "uint256"
4675
+ },
4676
+ {
4677
+ internalType: "uint256",
4678
+ name: "batchNumber",
4679
+ type: "uint256"
4680
+ }
4681
+ ],
4682
+ name: "interopRoots",
4683
+ outputs: [
4684
+ {
4685
+ internalType: "bytes32",
4686
+ name: "",
4687
+ type: "bytes32"
4688
+ }
4689
+ ],
4690
+ stateMutability: "view",
4691
+ type: "function"
4692
+ }
4693
+ ];
4694
+ var InteropRootStorage_default = InteropRootStorageABI;
4695
+
4696
+ // src/core/types/fees.ts
4697
+ function toGasOverrides(overrides) {
4698
+ const { nonce: _, ...gas } = overrides;
4699
+ return gas;
4700
+ }
4701
+
4702
+ // src/adapters/ethers/resources/deposits/context.ts
4703
+ async function commonCtx(p, client, tokens, contracts) {
4704
+ const { bridgehub, l1AssetRouter } = await contracts.addresses();
4705
+ const { chainId } = await client.l2.getNetwork();
4706
+ const sender = await client.signer.getAddress();
4707
+ const gasPerPubdata = p.gasPerPubdata ?? 800n;
4708
+ const operatorTip = p.operatorTip ?? 0n;
4709
+ const refundRecipient = p.refundRecipient ?? sender;
4710
+ const resolvedToken = await tokens.resolve(p.token, { chain: "l1" });
4711
+ const baseTokenAssetId = resolvedToken.baseTokenAssetId;
4712
+ const baseTokenL1 = await tokens.l1TokenFromAssetId(baseTokenAssetId);
4713
+ const baseIsEth = resolvedToken.isChainEthBased;
4714
+ const route = (() => {
4715
+ if (resolvedToken.kind === "eth") {
4716
+ return baseIsEth ? "eth-base" : "eth-nonbase";
4717
+ }
4718
+ if (resolvedToken.kind === "base") {
4719
+ return baseIsEth ? "eth-base" : "erc20-base";
4720
+ }
4721
+ return "erc20-nonbase";
4722
+ })();
4723
+ return {
4724
+ client,
4725
+ tokens,
4726
+ contracts,
4727
+ resolvedToken,
4728
+ baseTokenAssetId,
4729
+ baseTokenL1,
4730
+ baseIsEth,
4731
+ l1AssetRouter,
4732
+ route,
4733
+ bridgehub,
4734
+ chainIdL2: BigInt(chainId),
4735
+ sender,
4736
+ gasOverrides: p.l1TxOverrides ? toGasOverrides(p.l1TxOverrides) : void 0,
4737
+ l2GasLimit: p.l2GasLimit,
4738
+ gasPerPubdata,
4739
+ operatorTip,
4740
+ refundRecipient
4741
+ };
4742
+ }
4743
+ function encodeNativeTokenVaultTransferData(amount, receiver, token) {
4744
+ return new ethers.AbiCoder().encode(["uint256", "address", "address"], [amount, receiver, token]);
4745
+ }
4746
+ function encodeSecondBridgeDataV1(assetId, transferData) {
4747
+ const abi2 = new ethers.AbiCoder();
4748
+ const data = abi2.encode(["bytes32", "bytes"], [assetId, transferData]);
4749
+ return ethers.ethers.concat(["0x01", data]);
4750
+ }
4751
+ function encodeSecondBridgeArgs(token, amount, l2Receiver) {
4752
+ return ethers.AbiCoder.defaultAbiCoder().encode(
4753
+ ["address", "uint256", "address"],
4754
+ [token, amount, l2Receiver]
4755
+ );
4756
+ }
4757
+ function encodeSecondBridgeErc20Args(token, amount, l2Receiver) {
4758
+ return encodeSecondBridgeArgs(token, amount, l2Receiver);
4759
+ }
4760
+ function encodeSecondBridgeEthArgs(amount, l2Receiver, ethToken = ETH_ADDRESS) {
4761
+ return encodeSecondBridgeArgs(ethToken, amount, l2Receiver);
4762
+ }
4763
+ function buildDirectRequestStruct(args) {
4764
+ return {
4765
+ chainId: args.chainId,
4766
+ l2Contract: args.l2Contract,
4767
+ mintValue: args.mintValue,
4768
+ l2Value: args.l2Value,
4769
+ l2Calldata: "0x",
4770
+ l2GasLimit: args.l2GasLimit,
4771
+ l2GasPerPubdataByteLimit: args.gasPerPubdata,
4772
+ factoryDeps: [],
4773
+ refundRecipient: args.refundRecipient
4774
+ };
4775
+ }
4776
+
4777
+ // src/core/resources/deposits/gas.ts
4778
+ function makeGasQuote(p) {
4779
+ const maxPriorityFeePerGas = p.maxPriorityFeePerGas ?? 0n;
4780
+ return {
4781
+ gasLimit: p.gasLimit,
4782
+ maxFeePerGas: p.maxFeePerGas,
4783
+ maxPriorityFeePerGas,
4784
+ gasPerPubdata: p.gasPerPubdata,
4785
+ maxCost: p.gasLimit * p.maxFeePerGas
4786
+ };
4787
+ }
4788
+ async function fetchFees(estimator) {
4789
+ try {
4790
+ const fees = await estimator.estimateFeesPerGas();
4791
+ if (fees.maxFeePerGas != null) {
4792
+ return {
4793
+ maxFeePerGas: fees.maxFeePerGas,
4794
+ maxPriorityFeePerGas: fees.maxPriorityFeePerGas ?? 0n
4795
+ };
4796
+ }
4797
+ if (fees.gasPrice != null) {
4798
+ return {
4799
+ maxFeePerGas: fees.gasPrice,
4800
+ maxPriorityFeePerGas: 0n
4801
+ };
4802
+ }
4803
+ } catch {
4804
+ }
4805
+ try {
4806
+ const gp = await estimator.getGasPrice();
4807
+ return { maxFeePerGas: gp, maxPriorityFeePerGas: 0n };
4808
+ } catch {
4809
+ return { maxFeePerGas: 0n, maxPriorityFeePerGas: 0n };
4810
+ }
4811
+ }
4812
+ async function quoteL1Gas(input) {
4813
+ const { estimator, tx, overrides, fallbackGasLimit } = input;
4814
+ let market;
4815
+ const getMarket = async () => {
4816
+ if (market) return market;
4817
+ market = await fetchFees(estimator);
4818
+ return market;
4819
+ };
4820
+ const maxFeePerGas = overrides?.maxFeePerGas ?? (tx.maxFeePerGas != null ? BigInt(tx.maxFeePerGas) : (await getMarket()).maxFeePerGas);
4821
+ const maxPriorityFeePerGas = overrides?.maxPriorityFeePerGas ?? (tx.maxPriorityFeePerGas != null ? BigInt(tx.maxPriorityFeePerGas) : (await getMarket()).maxPriorityFeePerGas);
4822
+ const explicitGasLimit = overrides?.gasLimit ?? (tx.gasLimit != null ? BigInt(tx.gasLimit) : void 0);
4823
+ if (explicitGasLimit != null) {
4824
+ return makeGasQuote({ gasLimit: explicitGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4825
+ }
4826
+ try {
4827
+ const est = await estimator.estimateGas(tx);
4828
+ const buffered = BigInt(est) * (100n + BUFFER) / 100n;
4829
+ return makeGasQuote({ gasLimit: buffered, maxFeePerGas, maxPriorityFeePerGas });
4830
+ } catch (err) {
4831
+ if (fallbackGasLimit != null) {
4832
+ return makeGasQuote({ gasLimit: fallbackGasLimit, maxFeePerGas, maxPriorityFeePerGas });
4833
+ }
4834
+ console.warn("L1 gas estimation failed", err);
4835
+ return void 0;
4836
+ }
4837
+ }
4838
+ async function quoteL2Gas(input) {
4839
+ const { estimator, route, tx, gasPerPubdata, l2GasLimit, overrideGasLimit, stateOverrides } = input;
4840
+ const market = await fetchFees(estimator);
4841
+ const maxFeePerGas = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
4842
+ const txGasLimit = tx?.gasLimit != null ? BigInt(tx.gasLimit) : void 0;
4843
+ const explicit = overrideGasLimit ?? txGasLimit;
4844
+ if (explicit != null) {
4845
+ return makeGasQuote({
4846
+ gasLimit: explicit,
4847
+ maxFeePerGas,
4848
+ gasPerPubdata
4849
+ });
4850
+ }
4851
+ if (!tx) {
4852
+ return makeGasQuote({
4853
+ gasLimit: l2GasLimit ?? 0n,
4854
+ maxFeePerGas,
4855
+ gasPerPubdata
4856
+ });
4857
+ }
4858
+ try {
4859
+ const execEstimate = await estimator.estimateGas(tx, stateOverrides);
4860
+ const memoryBytes = route === "erc20-nonbase" ? 500n : DEFAULT_ABI_BYTES;
4861
+ const pubdataBytes = route === "erc20-nonbase" ? 200n : DEFAULT_PUBDATA_BYTES;
4862
+ const pp = gasPerPubdata ?? 800n;
4863
+ const memoryOverhead = memoryBytes * TX_MEMORY_OVERHEAD_GAS;
4864
+ const pubdataOverhead = pubdataBytes * pp;
4865
+ let total = BigInt(execEstimate) + TX_OVERHEAD_GAS + memoryOverhead + pubdataOverhead;
4866
+ total = total * (100n + BUFFER) / 100n;
4867
+ return makeGasQuote({
4868
+ gasLimit: total,
4869
+ maxFeePerGas,
4870
+ gasPerPubdata: pp
4871
+ });
4872
+ } catch (err) {
4873
+ console.warn("L2 gas estimation failed", err);
4874
+ return makeGasQuote({
4875
+ gasLimit: l2GasLimit ?? 0n,
4876
+ maxFeePerGas,
4877
+ gasPerPubdata
4878
+ });
4879
+ }
4880
+ }
4881
+ async function quoteL2BaseCost(input) {
4882
+ const { estimator, encode: encode2, bridgehub, chainIdL2, l2GasLimit, gasPerPubdata } = input;
4883
+ const market = await fetchFees(estimator);
4884
+ const l1GasPrice = market.maxFeePerGas || market.maxPriorityFeePerGas || 0n;
4885
+ if (l1GasPrice === 0n) {
4886
+ throw new Error("Could not fetch L1 gas price for Bridgehub base cost calculation.");
4887
+ }
4888
+ const data = encode2(IBridgehub_default, "l2TransactionBaseCost", [
4889
+ chainIdL2,
4890
+ l1GasPrice,
4891
+ l2GasLimit,
4892
+ gasPerPubdata
4893
+ ]);
4894
+ const raw = await estimator.call({
4895
+ to: bridgehub,
4896
+ data
4897
+ });
4898
+ return BigInt(raw);
4899
+ }
4900
+
4901
+ // src/adapters/ethers/estimator.ts
4902
+ function toCoreTx(tx) {
4903
+ return {
4904
+ to: tx.to,
4905
+ from: tx.from,
4906
+ data: tx.data,
3761
4907
  value: tx.value ? BigInt(tx.value) : void 0,
3762
4908
  gasLimit: tx.gasLimit ? BigInt(tx.gasLimit) : void 0,
3763
4909
  maxFeePerGas: tx.maxFeePerGas ? BigInt(tx.maxFeePerGas) : void 0,
@@ -3842,15 +4988,15 @@ function createErrorOps(decodeRevert2) {
3842
4988
  throw toZKsyncError2(kind, { resource, operation, context: opts?.ctx ?? {}, message }, e);
3843
4989
  }
3844
4990
  }
3845
- function wrap2(operation, fn, opts) {
4991
+ function wrap6(operation, fn, opts) {
3846
4992
  return run("INTERNAL", operation, fn, opts);
3847
4993
  }
3848
4994
  function wrapAs9(kind, operation, fn, opts) {
3849
4995
  return run(kind, operation, fn, opts);
3850
4996
  }
3851
- async function toResult2(operation, fn, opts) {
4997
+ async function toResult3(operation, fn, opts) {
3852
4998
  try {
3853
- const value = await wrap2(operation, fn, opts);
4999
+ const value = await wrap6(operation, fn, opts);
3854
5000
  return { ok: true, value };
3855
5001
  } catch (e) {
3856
5002
  const shaped = isZKsyncError(e) ? e : toZKsyncError2(
@@ -3866,7 +5012,7 @@ function createErrorOps(decodeRevert2) {
3866
5012
  return { ok: false, error: shaped };
3867
5013
  }
3868
5014
  }
3869
- return { wrap: wrap2, wrapAs: wrapAs9, toResult: toResult2 };
5015
+ return { wrap: wrap6, wrapAs: wrapAs9, toResult: toResult3 };
3870
5016
  }
3871
5017
  return { toZKsyncError: toZKsyncError2, createErrorHandlers: createErrorHandlers2 };
3872
5018
  }
@@ -4197,6 +5343,11 @@ function routeEthDirect() {
4197
5343
  }
4198
5344
  };
4199
5345
  }
5346
+
5347
+ // src/core/types/primitives.ts
5348
+ var ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
5349
+
5350
+ // src/adapters/ethers/resources/deposits/routes/erc20-nonbase.ts
4200
5351
  var { wrapAs: wrapAs2 } = createErrorHandlers("deposits");
4201
5352
  function routeErc20NonBase() {
4202
5353
  return {
@@ -4899,6 +6050,18 @@ function createContractsResource(client) {
4899
6050
  const { l2BaseTokenSystem: l2BaseTokenSystem2 } = await instances();
4900
6051
  return l2BaseTokenSystem2;
4901
6052
  }
6053
+ async function interopCenter() {
6054
+ const { interopCenter: interopCenter2 } = await instances();
6055
+ return interopCenter2;
6056
+ }
6057
+ async function interopHandler() {
6058
+ const { interopHandler: interopHandler2 } = await instances();
6059
+ return interopHandler2;
6060
+ }
6061
+ async function l2MessageVerification() {
6062
+ const { l2MessageVerification: l2MessageVerification2 } = await instances();
6063
+ return l2MessageVerification2;
6064
+ }
4902
6065
  return {
4903
6066
  addresses,
4904
6067
  instances,
@@ -4908,7 +6071,10 @@ function createContractsResource(client) {
4908
6071
  l1Nullifier,
4909
6072
  l2AssetRouter,
4910
6073
  l2NativeTokenVault,
4911
- l2BaseTokenSystem
6074
+ l2BaseTokenSystem,
6075
+ interopCenter,
6076
+ interopHandler,
6077
+ l2MessageVerification
4912
6078
  };
4913
6079
  }
4914
6080
 
@@ -4973,7 +6139,13 @@ function createDepositsResource(client, tokens, contracts) {
4973
6139
  const stepHashes = {};
4974
6140
  const managed = new ethers.NonceManager(client.signer);
4975
6141
  const from = await managed.getAddress();
4976
- let next = await client.l1.getTransactionCount(from, "latest");
6142
+ let next;
6143
+ if (typeof p.l1TxOverrides?.nonce === "number") {
6144
+ next = p.l1TxOverrides.nonce;
6145
+ } else {
6146
+ const blockTag = p.l1TxOverrides?.nonce ?? "latest";
6147
+ next = await client.l1.getTransactionCount(from, blockTag);
6148
+ }
4977
6149
  for (const step of plan.steps) {
4978
6150
  if (step.kind === "approve") {
4979
6151
  try {
@@ -5081,14 +6253,14 @@ function createDepositsResource(client, tokens, contracts) {
5081
6253
  if (!l1Rcpt) return { phase: "L1_PENDING", l1TxHash };
5082
6254
  let l2TxHash;
5083
6255
  try {
5084
- l2TxHash = extractL2TxHashFromL1Logs(l1Rcpt.logs) ?? void 0;
6256
+ l2TxHash = getL2TransactionHashFromLogs(l1Rcpt.logs) ?? void 0;
5085
6257
  } catch (e) {
5086
6258
  throw toZKsyncError(
5087
6259
  "INTERNAL",
5088
6260
  {
5089
6261
  resource: "deposits",
5090
- operation: "deposits.status.extractL2TxHashFromL1Logs",
5091
- context: { where: "extractL2TxHashFromL1Logs", l1TxHash },
6262
+ operation: "deposits.status.getL2TransactionHashFromLogs",
6263
+ context: { where: "getL2TransactionHashFromLogs", l1TxHash },
5092
6264
  message: "Failed to derive L2 transaction hash from L1 logs."
5093
6265
  },
5094
6266
  e
@@ -5238,7 +6410,7 @@ async function commonCtx2(p, client, tokens, contracts) {
5238
6410
  l2NativeTokenVault,
5239
6411
  l2BaseTokenSystem,
5240
6412
  baseIsEth,
5241
- gasOverrides: p.l2TxOverrides
6413
+ gasOverrides: p.l2TxOverrides ? toGasOverrides(p.l2TxOverrides) : void 0
5242
6414
  };
5243
6415
  }
5244
6416
 
@@ -5314,820 +6486,2099 @@ async function quoteL2Gas4(input) {
5314
6486
  });
5315
6487
  }
5316
6488
 
5317
- // src/adapters/ethers/resources/withdrawals/services/fees.ts
5318
- function buildFeeBreakdown2(p) {
5319
- const l2Total = p.l2Gas?.maxCost ?? 0n;
5320
- const l2 = {
5321
- total: l2Total,
5322
- gasLimit: p.l2Gas?.gasLimit ?? 0n,
5323
- maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
5324
- maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
5325
- };
5326
- return {
5327
- token: p.feeToken,
5328
- maxTotal: l2Total,
5329
- l2
5330
- };
6489
+ // src/adapters/ethers/resources/withdrawals/services/fees.ts
6490
+ function buildFeeBreakdown2(p) {
6491
+ const l2Total = p.l2Gas?.maxCost ?? 0n;
6492
+ const l2 = {
6493
+ total: l2Total,
6494
+ gasLimit: p.l2Gas?.gasLimit ?? 0n,
6495
+ maxFeePerGas: p.l2Gas?.maxFeePerGas ?? 0n,
6496
+ maxPriorityFeePerGas: p.l2Gas?.maxPriorityFeePerGas
6497
+ };
6498
+ return {
6499
+ token: p.feeToken,
6500
+ maxTotal: l2Total,
6501
+ l2
6502
+ };
6503
+ }
6504
+
6505
+ // src/adapters/ethers/resources/withdrawals/routes/eth.ts
6506
+ var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
6507
+ function routeEthBase() {
6508
+ return {
6509
+ async build(p, ctx) {
6510
+ const steps = [];
6511
+ const base = await ctx.contracts.l2BaseTokenSystem();
6512
+ const data = await wrapAs6(
6513
+ "INTERNAL",
6514
+ OP_WITHDRAWALS.eth.encodeWithdraw,
6515
+ () => Promise.resolve(base.interface.encodeFunctionData("withdraw", [p.to ?? ctx.sender])),
6516
+ {
6517
+ ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
6518
+ message: "Failed to encode ETH withdraw calldata."
6519
+ }
6520
+ );
6521
+ const tx = {
6522
+ to: L2_BASE_TOKEN_ADDRESS,
6523
+ data,
6524
+ from: ctx.sender,
6525
+ value: p.amount
6526
+ };
6527
+ const gas = await quoteL2Gas4({ ctx, tx });
6528
+ if (gas) {
6529
+ tx.gasLimit = gas.gasLimit;
6530
+ tx.maxFeePerGas = gas.maxFeePerGas;
6531
+ tx.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
6532
+ }
6533
+ const fees = buildFeeBreakdown2({
6534
+ feeToken: L2_BASE_TOKEN_ADDRESS,
6535
+ l2Gas: gas
6536
+ });
6537
+ steps.push({
6538
+ key: "l2-base-token:withdraw",
6539
+ kind: "l2-base-token:withdraw",
6540
+ description: "Withdraw ETH via L2 Base Token System",
6541
+ tx
6542
+ });
6543
+ return { steps, approvals: [], fees };
6544
+ }
6545
+ };
6546
+ }
6547
+ var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
6548
+ var SIG = {
6549
+ withdraw: "withdraw(bytes32,bytes)"
6550
+ };
6551
+ function routeErc20NonBase2() {
6552
+ return {
6553
+ async build(p, ctx) {
6554
+ const steps = [];
6555
+ const approvals = [];
6556
+ const erc20 = new ethers.Contract(p.token, IERC20_default, ctx.client.getL2Signer());
6557
+ const current = await wrapAs7(
6558
+ "CONTRACT",
6559
+ OP_WITHDRAWALS.erc20.allowance,
6560
+ () => erc20.allowance(ctx.sender, ctx.l2NativeTokenVault),
6561
+ {
6562
+ ctx: {
6563
+ where: "erc20.allowance",
6564
+ chain: "L2",
6565
+ token: p.token,
6566
+ spender: ctx.l2NativeTokenVault
6567
+ },
6568
+ message: "Failed to read L2 ERC-20 allowance."
6569
+ }
6570
+ );
6571
+ if (current < p.amount) {
6572
+ approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
6573
+ const data = erc20.interface.encodeFunctionData("approve", [
6574
+ ctx.l2NativeTokenVault,
6575
+ p.amount
6576
+ ]);
6577
+ const approveTx = {
6578
+ to: p.token,
6579
+ data,
6580
+ from: ctx.sender
6581
+ };
6582
+ const approveGas = await quoteL2Gas4({ ctx, tx: approveTx });
6583
+ if (approveGas) {
6584
+ approveTx.gasLimit = approveGas.gasLimit;
6585
+ approveTx.maxFeePerGas = approveGas.maxFeePerGas;
6586
+ approveTx.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
6587
+ }
6588
+ steps.push({
6589
+ key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
6590
+ kind: "approve:l2",
6591
+ description: `Approve ${p.amount} to NativeTokenVault`,
6592
+ tx: approveTx
6593
+ });
6594
+ }
6595
+ const assetId = await wrapAs7(
6596
+ "CONTRACT",
6597
+ OP_WITHDRAWALS.erc20.ensureRegistered,
6598
+ async () => {
6599
+ const ntv = await ctx.contracts.l2NativeTokenVault();
6600
+ const ensured = await ntv.getFunction("ensureTokenIsRegistered").staticCall(p.token);
6601
+ return ensured;
6602
+ },
6603
+ {
6604
+ ctx: { where: "L2NativeTokenVault.ensureTokenIsRegistered", token: p.token },
6605
+ message: "Failed to ensure token is registered in L2NativeTokenVault."
6606
+ }
6607
+ );
6608
+ const assetData = await wrapAs7(
6609
+ "INTERNAL",
6610
+ OP_WITHDRAWALS.erc20.encodeAssetData,
6611
+ () => Promise.resolve(
6612
+ encodeNativeTokenVaultTransferData(p.amount, p.to ?? ctx.sender, p.token)
6613
+ ),
6614
+ {
6615
+ ctx: { where: "AbiCoder.encode", token: p.token, to: p.to ?? ctx.sender },
6616
+ message: "Failed to encode burn/withdraw asset data."
6617
+ }
6618
+ );
6619
+ const l2ar = await ctx.contracts.l2AssetRouter();
6620
+ const dataWithdraw = await wrapAs7(
6621
+ "INTERNAL",
6622
+ OP_WITHDRAWALS.erc20.encodeWithdraw,
6623
+ () => Promise.resolve(l2ar.interface.encodeFunctionData(SIG.withdraw, [assetId, assetData])),
6624
+ {
6625
+ ctx: { where: "L2AssetRouter.withdraw", assetId },
6626
+ message: "Failed to encode withdraw calldata."
6627
+ }
6628
+ );
6629
+ const withdrawTx = {
6630
+ to: ctx.l2AssetRouter,
6631
+ data: dataWithdraw,
6632
+ from: ctx.sender
6633
+ };
6634
+ const withdrawGas = current >= p.amount ? await quoteL2Gas4({ ctx, tx: withdrawTx }) : void 0;
6635
+ if (withdrawGas) {
6636
+ withdrawTx.gasLimit = withdrawGas.gasLimit;
6637
+ withdrawTx.maxFeePerGas = withdrawGas.maxFeePerGas;
6638
+ withdrawTx.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
6639
+ }
6640
+ steps.push({
6641
+ key: "l2-asset-router:withdraw",
6642
+ kind: "l2-asset-router:withdraw",
6643
+ description: "Burn on L2 & send L2\u2192L1 message",
6644
+ tx: withdrawTx
6645
+ });
6646
+ const fees = buildFeeBreakdown2({
6647
+ feeToken: ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2),
6648
+ l2Gas: withdrawGas
6649
+ });
6650
+ return { steps, approvals, fees };
6651
+ }
6652
+ };
6653
+ }
6654
+
6655
+ // src/core/utils/events.ts
6656
+ function extractPreferAddress(opts) {
6657
+ const preferAddr = typeof opts?.prefer === "object" ? opts.prefer.address : opts?.prefer === "assetRouter" ? L2_ASSET_ROUTER_ADDRESS : L1_MESSENGER_ADDRESS;
6658
+ return (preferAddr || L1_MESSENGER_ADDRESS).toLowerCase();
6659
+ }
6660
+ function findL1MessageSentLog(receipt, opts) {
6661
+ const index = opts?.index ?? 0;
6662
+ const preferAddr = extractPreferAddress(opts);
6663
+ const matches = receipt.logs.filter((lg) => {
6664
+ const t0 = (lg.topics?.[0] ?? "").toLowerCase();
6665
+ return t0 === TOPIC_L1_MESSAGE_SENT_NEW || t0 === TOPIC_L1_MESSAGE_SENT_LEG;
6666
+ });
6667
+ if (!matches.length) {
6668
+ throw new Error("No L1MessageSent event found in L2 receipt logs.");
6669
+ }
6670
+ const preferred = matches.find((lg) => (lg.address ?? "").toLowerCase() === preferAddr);
6671
+ const chosen = preferred ?? matches[index] ?? matches[0];
6672
+ if (!chosen) {
6673
+ throw new Error("No suitable L1MessageSent event found.");
6674
+ }
6675
+ return chosen;
6676
+ }
6677
+ function isL1MessageSentLog(log, opts) {
6678
+ const topic = log.topics[0].toLowerCase();
6679
+ const preferAddr = extractPreferAddress(opts);
6680
+ return log.address.toLowerCase() === preferAddr && (topic === TOPIC_L1_MESSAGE_SENT_LEG.toLowerCase() || topic === TOPIC_L1_MESSAGE_SENT_NEW.toLowerCase());
6681
+ }
6682
+
6683
+ // src/core/resources/withdrawals/logs.ts
6684
+ function messengerLogIndex(raw, opts) {
6685
+ const index = opts?.index;
6686
+ const messenger = (opts?.messenger).toLowerCase();
6687
+ const arr = Array.isArray(raw?.l2ToL1Logs) ? raw.l2ToL1Logs : [];
6688
+ const hits = arr.map((lg, i) => ({ i, lg })).filter(({ lg }) => (lg?.sender ?? "").toLowerCase() === messenger);
6689
+ if (!hits.length) {
6690
+ throw new Error("No L2->L1 messenger logs found in receipt.");
6691
+ }
6692
+ return (hits[index] ?? hits[0]).i;
5331
6693
  }
5332
6694
 
5333
- // src/adapters/ethers/resources/withdrawals/routes/eth.ts
5334
- var { wrapAs: wrapAs6 } = createErrorHandlers("withdrawals");
5335
- function routeEthBase() {
6695
+ // src/adapters/ethers/resources/withdrawals/services/finalization.ts
6696
+ var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
6697
+ var IL1NullifierMini = [
6698
+ "function isWithdrawalFinalized(uint256,uint256,uint256) view returns (bool)"
6699
+ ];
6700
+ function createFinalizationServices(client) {
6701
+ const { l1, l2, signer } = client;
5336
6702
  return {
5337
- async build(p, ctx) {
5338
- const steps = [];
5339
- const base = await ctx.contracts.l2BaseTokenSystem();
5340
- const data = await wrapAs6(
6703
+ async fetchFinalizeDepositParams(l2TxHash) {
6704
+ const parsed = await wrapAs8(
6705
+ "RPC",
6706
+ OP_WITHDRAWALS.finalize.fetchParams.receipt,
6707
+ () => client.zks.getReceiptWithL2ToL1(l2TxHash),
6708
+ {
6709
+ ctx: { where: "getReceiptWithL2ToL1", l2TxHash },
6710
+ message: "Failed to fetch L2 receipt (with L2\u2192L1 logs)."
6711
+ }
6712
+ );
6713
+ if (!parsed) {
6714
+ throw createError("STATE", {
6715
+ resource: "withdrawals",
6716
+ operation: OP_WITHDRAWALS.finalize.fetchParams.receipt,
6717
+ message: "L2 receipt not found.",
6718
+ context: { l2TxHash }
6719
+ });
6720
+ }
6721
+ const ev = await wrapAs8(
5341
6722
  "INTERNAL",
5342
- OP_WITHDRAWALS.eth.encodeWithdraw,
5343
- () => Promise.resolve(base.interface.encodeFunctionData("withdraw", [p.to ?? ctx.sender])),
6723
+ OP_WITHDRAWALS.finalize.fetchParams.findMessage,
6724
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
6725
+ () => Promise.resolve(findL1MessageSentLog(parsed, { index: 0 })),
5344
6726
  {
5345
- ctx: { where: "L2BaseToken.withdraw", to: p.to ?? ctx.sender },
5346
- message: "Failed to encode ETH withdraw calldata."
6727
+ ctx: { l2TxHash, index: 0 },
6728
+ message: "Failed to locate L1MessageSent event in L2 receipt."
5347
6729
  }
5348
6730
  );
5349
- const tx = {
5350
- to: L2_BASE_TOKEN_ADDRESS,
5351
- data,
5352
- from: ctx.sender,
5353
- value: p.amount
6731
+ const message = await wrapAs8(
6732
+ "INTERNAL",
6733
+ OP_WITHDRAWALS.finalize.fetchParams.decodeMessage,
6734
+ () => Promise.resolve(ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ev.data)[0]),
6735
+ {
6736
+ ctx: { where: "decode L1MessageSent", data: ev.data },
6737
+ message: "Failed to decode withdrawal message."
6738
+ }
6739
+ );
6740
+ const raw = await wrapAs8(
6741
+ "RPC",
6742
+ OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
6743
+ () => client.zks.getReceiptWithL2ToL1(l2TxHash),
6744
+ {
6745
+ ctx: { where: "getReceiptWithL2ToL1 (raw)", l2TxHash },
6746
+ message: "Failed to fetch raw L2 receipt."
6747
+ }
6748
+ );
6749
+ if (!raw) {
6750
+ throw createError("STATE", {
6751
+ resource: "withdrawals",
6752
+ operation: OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
6753
+ message: "Raw L2 receipt not found.",
6754
+ context: { l2TxHash }
6755
+ });
6756
+ }
6757
+ const idx = await wrapAs8(
6758
+ "INTERNAL",
6759
+ OP_WITHDRAWALS.finalize.fetchParams.messengerIndex,
6760
+ () => Promise.resolve(messengerLogIndex(raw, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
6761
+ {
6762
+ ctx: { where: "derive messenger log index", l2TxHash, receipt: raw },
6763
+ message: "Failed to derive messenger log index."
6764
+ }
6765
+ );
6766
+ const proof = await wrapAs8(
6767
+ "RPC",
6768
+ OP_WITHDRAWALS.finalize.fetchParams.proof,
6769
+ () => client.zks.getL2ToL1LogProof(l2TxHash, idx),
6770
+ {
6771
+ ctx: { where: "get L2\u2192L1 log proof", l2TxHash, messengerLogIndex: idx },
6772
+ message: "Failed to fetch L2\u2192L1 log proof."
6773
+ }
6774
+ );
6775
+ const { chainId } = await wrapAs8(
6776
+ "RPC",
6777
+ OP_WITHDRAWALS.finalize.fetchParams.network,
6778
+ () => l2.getNetwork(),
6779
+ {
6780
+ ctx: { where: "l2.getNetwork" },
6781
+ message: "Failed to read L2 network."
6782
+ }
6783
+ );
6784
+ const txIndex = Number(parsed.transactionIndex ?? 0);
6785
+ const params = {
6786
+ chainId: BigInt(chainId),
6787
+ l2BatchNumber: proof.batchNumber,
6788
+ l2MessageIndex: proof.id,
6789
+ l2Sender: L2_ASSET_ROUTER_ADDRESS,
6790
+ l2TxNumberInBatch: txIndex,
6791
+ message,
6792
+ merkleProof: proof.proof
5354
6793
  };
5355
- const gas = await quoteL2Gas4({ ctx, tx });
5356
- if (gas) {
5357
- tx.gasLimit = gas.gasLimit;
5358
- tx.maxFeePerGas = gas.maxFeePerGas;
5359
- tx.maxPriorityFeePerGas = gas.maxPriorityFeePerGas;
6794
+ const { l1Nullifier } = await client.ensureAddresses();
6795
+ return { params, nullifier: l1Nullifier };
6796
+ },
6797
+ async simulateFinalizeReadiness(params) {
6798
+ const { l1Nullifier } = await client.ensureAddresses();
6799
+ const done = await (async () => {
6800
+ try {
6801
+ const cMini = new ethers.Contract(l1Nullifier, IL1NullifierMini, l1);
6802
+ const isFinalized = await wrapAs8(
6803
+ "RPC",
6804
+ OP_WITHDRAWALS.finalize.readiness.isFinalized,
6805
+ () => cMini.isWithdrawalFinalized(
6806
+ params.chainId,
6807
+ params.l2BatchNumber,
6808
+ params.l2MessageIndex
6809
+ ),
6810
+ {
6811
+ ctx: { where: "isWithdrawalFinalized", params },
6812
+ message: "Failed to read finalization status."
6813
+ }
6814
+ );
6815
+ return Boolean(isFinalized);
6816
+ } catch {
6817
+ return false;
6818
+ }
6819
+ })();
6820
+ if (done) return { kind: "FINALIZED" };
6821
+ const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, l1);
6822
+ try {
6823
+ await c.finalizeDeposit.staticCall(params);
6824
+ return { kind: "READY" };
6825
+ } catch (e) {
6826
+ return classifyReadinessFromRevert(e);
6827
+ }
6828
+ },
6829
+ async isWithdrawalFinalized(key) {
6830
+ const { l1Nullifier } = await client.ensureAddresses();
6831
+ const c = new ethers.Contract(l1Nullifier, IL1NullifierMini, l1);
6832
+ return await wrapAs8(
6833
+ "RPC",
6834
+ OP_WITHDRAWALS.finalize.isFinalized,
6835
+ () => c.isWithdrawalFinalized(key.chainIdL2, key.l2BatchNumber, key.l2MessageIndex),
6836
+ {
6837
+ ctx: { where: "isWithdrawalFinalized", key },
6838
+ message: "Failed to read finalization status."
6839
+ }
6840
+ );
6841
+ },
6842
+ async estimateFinalization(params) {
6843
+ const { l1Nullifier } = await client.ensureAddresses();
6844
+ const signer2 = client.getL1Signer();
6845
+ const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, signer2);
6846
+ const gasLimit = await wrapAs8(
6847
+ "RPC",
6848
+ OP_WITHDRAWALS.finalize.estimate,
6849
+ () => c.finalizeDeposit.estimateGas(params),
6850
+ {
6851
+ ctx: {
6852
+ where: "estimateGas(finalizeDeposit)",
6853
+ chainIdL2: params.chainId,
6854
+ l2BatchNumber: params.l2BatchNumber,
6855
+ l2MessageIndex: params.l2MessageIndex,
6856
+ l1Nullifier
6857
+ },
6858
+ message: "Failed to estimate gas for finalizeDeposit."
6859
+ }
6860
+ );
6861
+ const feeData = await wrapAs8("RPC", OP_WITHDRAWALS.finalize.estimate, () => l1.getFeeData(), {
6862
+ ctx: { where: "l1.getFeeData" },
6863
+ message: "Failed to estimate fee data for finalizeDeposit."
6864
+ });
6865
+ const maxFeePerGas = feeData.maxFeePerGas ?? feeData.gasPrice ?? // legacy-style gas price if present
6866
+ (() => {
6867
+ throw createError("RPC", {
6868
+ resource: "withdrawals",
6869
+ operation: OP_WITHDRAWALS.finalize.estimate,
6870
+ message: "Provider did not return gas price or EIP-1559 fields.",
6871
+ context: { feeData }
6872
+ });
6873
+ })();
6874
+ const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
6875
+ return {
6876
+ gasLimit,
6877
+ maxFeePerGas,
6878
+ maxPriorityFeePerGas
6879
+ };
6880
+ },
6881
+ async finalizeDeposit(params) {
6882
+ const { l1Nullifier } = await client.ensureAddresses();
6883
+ const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, signer);
6884
+ try {
6885
+ const receipt = await c.finalizeDeposit(params);
6886
+ const hash = receipt.hash;
6887
+ return {
6888
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
6889
+ hash,
6890
+ wait: async () => {
6891
+ try {
6892
+ return await receipt.wait();
6893
+ } catch (e) {
6894
+ throw toZKsyncError(
6895
+ "EXECUTION",
6896
+ {
6897
+ resource: "withdrawals",
6898
+ operation: OP_WITHDRAWALS.finalize.wait,
6899
+ message: "Failed while waiting for finalizeDeposit transaction.",
6900
+ context: { txHash: hash }
6901
+ },
6902
+ e
6903
+ );
6904
+ }
6905
+ }
6906
+ };
6907
+ } catch (e) {
6908
+ throw toZKsyncError(
6909
+ "EXECUTION",
6910
+ {
6911
+ resource: "withdrawals",
6912
+ operation: OP_WITHDRAWALS.finalize.send,
6913
+ message: "Failed to send finalizeDeposit transaction.",
6914
+ context: {
6915
+ chainIdL2: params.chainId,
6916
+ l2BatchNumber: params.l2BatchNumber,
6917
+ l2MessageIndex: params.l2MessageIndex,
6918
+ l1Nullifier
6919
+ }
6920
+ },
6921
+ e
6922
+ );
5360
6923
  }
5361
- const fees = buildFeeBreakdown2({
5362
- feeToken: L2_BASE_TOKEN_ADDRESS,
5363
- l2Gas: gas
5364
- });
5365
- steps.push({
5366
- key: "l2-base-token:withdraw",
5367
- kind: "l2-base-token:withdraw",
5368
- description: "Withdraw ETH via L2 Base Token System",
5369
- tx
5370
- });
5371
- return { steps, approvals: [], fees };
5372
6924
  }
5373
6925
  };
5374
6926
  }
5375
- var { wrapAs: wrapAs7 } = createErrorHandlers("withdrawals");
5376
- var SIG = {
5377
- withdraw: "withdraw(bytes32,bytes)"
6927
+
6928
+ // src/adapters/ethers/resources/withdrawals/index.ts
6929
+ var ROUTES2 = {
6930
+ base: routeEthBase(),
6931
+ // BaseTokenSystem.withdraw, chain base = ETH
6932
+ "erc20-nonbase": routeErc20NonBase2()
6933
+ // AssetRouter.withdraw for non-base ERC-20s
5378
6934
  };
5379
- function routeErc20NonBase2() {
5380
- return {
5381
- async build(p, ctx) {
5382
- const steps = [];
5383
- const approvals = [];
5384
- const erc20 = new ethers.Contract(p.token, IERC20_default, ctx.client.getL2Signer());
5385
- const current = await wrapAs7(
5386
- "CONTRACT",
5387
- OP_WITHDRAWALS.erc20.allowance,
5388
- () => erc20.allowance(ctx.sender, ctx.l2NativeTokenVault),
5389
- {
5390
- ctx: {
5391
- where: "erc20.allowance",
5392
- chain: "L2",
5393
- token: p.token,
5394
- spender: ctx.l2NativeTokenVault
6935
+ function createWithdrawalsResource(client, tokens, contracts) {
6936
+ const svc = createFinalizationServices(client);
6937
+ const { wrap: wrap6, toResult: toResult3 } = createErrorHandlers("withdrawals");
6938
+ const tokensResource = tokens ?? createTokensResource(client);
6939
+ const contractsResource = contracts ?? createContractsResource(client);
6940
+ async function buildPlan(p) {
6941
+ const ctx = await commonCtx2(p, client, tokensResource, contractsResource);
6942
+ await ROUTES2[ctx.route].preflight?.(p, ctx);
6943
+ const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
6944
+ return {
6945
+ route: ctx.route,
6946
+ summary: {
6947
+ route: ctx.route,
6948
+ approvalsNeeded: approvals,
6949
+ amounts: {
6950
+ transfer: { token: p.token, amount: p.amount }
6951
+ },
6952
+ fees
6953
+ },
6954
+ steps
6955
+ };
6956
+ }
6957
+ const finalizeCache = /* @__PURE__ */ new Map();
6958
+ const quote = (p) => wrap6(
6959
+ OP_WITHDRAWALS.quote,
6960
+ async () => {
6961
+ const plan = await buildPlan(p);
6962
+ return plan.summary;
6963
+ },
6964
+ {
6965
+ message: "Internal error while preparing a withdrawal quote.",
6966
+ ctx: { token: p.token, where: "withdrawals.quote" }
6967
+ }
6968
+ );
6969
+ const tryQuote = (p) => toResult3(
6970
+ OP_WITHDRAWALS.tryQuote,
6971
+ async () => {
6972
+ const plan = await buildPlan(p);
6973
+ return plan.summary;
6974
+ },
6975
+ {
6976
+ message: "Internal error while preparing a withdrawal quote.",
6977
+ ctx: { token: p.token, where: "withdrawals.tryQuote" }
6978
+ }
6979
+ );
6980
+ const prepare = (p) => wrap6(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
6981
+ message: "Internal error while preparing a withdrawal plan.",
6982
+ ctx: { token: p.token, where: "withdrawals.prepare" }
6983
+ });
6984
+ const tryPrepare = (p) => toResult3(OP_WITHDRAWALS.tryPrepare, () => buildPlan(p), {
6985
+ message: "Internal error while preparing a withdrawal plan.",
6986
+ ctx: { token: p.token, where: "withdrawals.tryPrepare" }
6987
+ });
6988
+ const create = (p) => wrap6(
6989
+ OP_WITHDRAWALS.create,
6990
+ async () => {
6991
+ const plan = await prepare(p);
6992
+ const stepHashes = {};
6993
+ const managed = new ethers.NonceManager(client.getL2Signer());
6994
+ const from = await managed.getAddress();
6995
+ let next;
6996
+ if (typeof p.l2TxOverrides?.nonce === "number") {
6997
+ next = p.l2TxOverrides.nonce;
6998
+ } else {
6999
+ const blockTag = p.l2TxOverrides?.nonce ?? "pending";
7000
+ next = await client.l2.getTransactionCount(from, blockTag);
7001
+ }
7002
+ for (const step of plan.steps) {
7003
+ step.tx.nonce = next++;
7004
+ if (p.l2TxOverrides) {
7005
+ const overrides = p.l2TxOverrides;
7006
+ if (overrides.gasLimit != null) step.tx.gasLimit = overrides.gasLimit;
7007
+ if (overrides.maxFeePerGas != null) step.tx.maxFeePerGas = overrides.maxFeePerGas;
7008
+ if (overrides.maxPriorityFeePerGas != null) {
7009
+ step.tx.maxPriorityFeePerGas = overrides.maxPriorityFeePerGas;
7010
+ }
7011
+ }
7012
+ if (!step.tx.gasLimit) {
7013
+ try {
7014
+ const est = await client.l2.estimateGas(step.tx);
7015
+ step.tx.gasLimit = BigInt(est) * 115n / 100n;
7016
+ } catch {
7017
+ }
7018
+ }
7019
+ let hash;
7020
+ try {
7021
+ const sent = await managed.sendTransaction(step.tx);
7022
+ hash = sent.hash;
7023
+ stepHashes[step.key] = hash;
7024
+ const rcpt = await sent.wait();
7025
+ if (rcpt?.status === 0) {
7026
+ throw createError("EXECUTION", {
7027
+ resource: "withdrawals",
7028
+ operation: "withdrawals.create.sendTransaction",
7029
+ message: "Withdrawal transaction reverted on L2 during a step.",
7030
+ context: { step: step.key, txHash: hash, nonce: Number(step.tx.nonce ?? -1) }
7031
+ });
7032
+ }
7033
+ } catch (e) {
7034
+ throw toZKsyncError(
7035
+ "EXECUTION",
7036
+ {
7037
+ resource: "withdrawals",
7038
+ operation: "withdrawals.create.sendTransaction",
7039
+ message: "Failed to send or confirm a withdrawal transaction step.",
7040
+ context: { step: step.key, txHash: hash, nonce: Number(step.tx.nonce ?? -1) }
7041
+ },
7042
+ e
7043
+ );
7044
+ }
7045
+ }
7046
+ const keys = Object.keys(stepHashes);
7047
+ const l2TxHash = stepHashes[keys[keys.length - 1]];
7048
+ return { kind: "withdrawal", l2TxHash, stepHashes, plan };
7049
+ },
7050
+ {
7051
+ message: "Internal error while creating withdrawal transactions.",
7052
+ ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.create" }
7053
+ }
7054
+ );
7055
+ const tryCreate = (p) => toResult3(OP_WITHDRAWALS.tryCreate, () => create(p), {
7056
+ message: "Internal error while creating withdrawal transactions.",
7057
+ ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
7058
+ });
7059
+ const status = (h) => wrap6(
7060
+ OP_WITHDRAWALS.status,
7061
+ async () => {
7062
+ const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
7063
+ if (!l2TxHash || l2TxHash === "0x") {
7064
+ return { phase: "UNKNOWN", l2TxHash: "0x" };
7065
+ }
7066
+ let l2Rcpt;
7067
+ try {
7068
+ l2Rcpt = await client.l2.getTransactionReceipt(l2TxHash);
7069
+ } catch (e) {
7070
+ if (isReceiptNotFound(e)) {
7071
+ return { phase: "L2_PENDING", l2TxHash };
7072
+ }
7073
+ throw toZKsyncError(
7074
+ "RPC",
7075
+ {
7076
+ resource: "withdrawals",
7077
+ operation: "withdrawals.status.getTransactionReceipt",
7078
+ message: "Failed to fetch L2 transaction receipt.",
7079
+ context: { l2TxHash, where: "l2.getTransactionReceipt" }
5395
7080
  },
5396
- message: "Failed to read L2 ERC-20 allowance."
7081
+ e
7082
+ );
7083
+ }
7084
+ if (!l2Rcpt) return { phase: "L2_PENDING", l2TxHash };
7085
+ let pack;
7086
+ try {
7087
+ pack = await svc.fetchFinalizeDepositParams(l2TxHash);
7088
+ } catch {
7089
+ return { phase: "PENDING", l2TxHash };
7090
+ }
7091
+ const key = {
7092
+ chainIdL2: pack.params.chainId,
7093
+ l2BatchNumber: pack.params.l2BatchNumber,
7094
+ l2MessageIndex: pack.params.l2MessageIndex
7095
+ };
7096
+ try {
7097
+ const done = await svc.isWithdrawalFinalized(key);
7098
+ if (done) return { phase: "FINALIZED", l2TxHash, key };
7099
+ } catch {
7100
+ }
7101
+ const readiness = await svc.simulateFinalizeReadiness(pack.params);
7102
+ if (readiness.kind === "FINALIZED") return { phase: "FINALIZED", l2TxHash, key };
7103
+ if (readiness.kind === "READY") return { phase: "READY_TO_FINALIZE", l2TxHash, key };
7104
+ return { phase: "PENDING", l2TxHash, key };
7105
+ },
7106
+ {
7107
+ message: "Internal error while checking withdrawal status.",
7108
+ ctx: { where: "withdrawals.status", l2TxHash: typeof h === "string" ? h : h.l2TxHash }
7109
+ }
7110
+ );
7111
+ const wait = (h, opts = {
7112
+ for: "l2",
7113
+ pollMs: 5500
7114
+ }) => wrap6(
7115
+ OP_WITHDRAWALS.wait,
7116
+ async () => {
7117
+ const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
7118
+ if (!l2Hash || l2Hash === "0x") return null;
7119
+ if (opts.for === "l2") {
7120
+ let rcpt;
7121
+ try {
7122
+ rcpt = await client.l2.waitForTransaction(l2Hash);
7123
+ } catch (e) {
7124
+ throw toZKsyncError(
7125
+ "RPC",
7126
+ {
7127
+ resource: "withdrawals",
7128
+ operation: "withdrawals.wait.l2.waitForTransaction",
7129
+ message: "Failed while waiting for L2 transaction.",
7130
+ context: { l2TxHash: l2Hash }
7131
+ },
7132
+ e
7133
+ );
5397
7134
  }
5398
- );
5399
- if (current < p.amount) {
5400
- approvals.push({ token: p.token, spender: ctx.l2NativeTokenVault, amount: p.amount });
5401
- const data = erc20.interface.encodeFunctionData("approve", [
5402
- ctx.l2NativeTokenVault,
5403
- p.amount
5404
- ]);
5405
- const approveTx = {
5406
- to: p.token,
5407
- data,
5408
- from: ctx.sender
5409
- };
5410
- const approveGas = await quoteL2Gas4({ ctx, tx: approveTx });
5411
- if (approveGas) {
5412
- approveTx.gasLimit = approveGas.gasLimit;
5413
- approveTx.maxFeePerGas = approveGas.maxFeePerGas;
5414
- approveTx.maxPriorityFeePerGas = approveGas.maxPriorityFeePerGas;
7135
+ if (!rcpt) return null;
7136
+ try {
7137
+ const raw = await client.zks.getReceiptWithL2ToL1(l2Hash);
7138
+ rcpt.l2ToL1Logs = raw?.l2ToL1Logs ?? [];
7139
+ } catch {
7140
+ rcpt.l2ToL1Logs = rcpt.l2ToL1Logs ?? [];
7141
+ }
7142
+ return rcpt;
7143
+ }
7144
+ const poll = Math.max(1e3, opts.pollMs ?? 2500);
7145
+ const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : void 0;
7146
+ while (true) {
7147
+ const s = await status(l2Hash);
7148
+ if (opts.for === "ready") {
7149
+ if (s.phase === "READY_TO_FINALIZE" || s.phase === "FINALIZED") return null;
7150
+ } else {
7151
+ if (s.phase === "FINALIZED") {
7152
+ const l1Hash = finalizeCache.get(l2Hash);
7153
+ if (l1Hash) {
7154
+ try {
7155
+ const l1Rcpt = await client.l1.getTransactionReceipt(l1Hash);
7156
+ if (l1Rcpt) {
7157
+ finalizeCache.delete(l2Hash);
7158
+ return l1Rcpt;
7159
+ }
7160
+ } catch {
7161
+ }
7162
+ }
7163
+ return null;
7164
+ }
5415
7165
  }
5416
- steps.push({
5417
- key: `approve:l2:${p.token}:${ctx.l2NativeTokenVault}`,
5418
- kind: "approve:l2",
5419
- description: `Approve ${p.amount} to NativeTokenVault`,
5420
- tx: approveTx
5421
- });
7166
+ if (deadline && Date.now() > deadline) return null;
7167
+ await new Promise((r) => setTimeout(r, poll));
5422
7168
  }
5423
- const resolved = ctx.resolvedToken ?? (ctx.tokens ? await ctx.tokens.resolve(p.token, { chain: "l2" }) : void 0);
5424
- const assetId = resolved?.assetId ?? await wrapAs7(
5425
- "CONTRACT",
5426
- OP_WITHDRAWALS.erc20.ensureRegistered,
5427
- async () => {
5428
- const ntv = await ctx.contracts.l2NativeTokenVault();
5429
- const ensured = await ntv.getFunction("ensureTokenIsRegistered").staticCall(p.token);
5430
- return ensured;
5431
- },
5432
- {
5433
- ctx: { where: "L2NativeTokenVault.ensureTokenIsRegistered", token: p.token },
5434
- message: "Failed to ensure token is registered in L2NativeTokenVault."
7169
+ },
7170
+ {
7171
+ message: "Internal error while waiting for withdrawal.",
7172
+ ctx: {
7173
+ where: "withdrawals.wait",
7174
+ l2TxHash: typeof h === "string" ? h : h.l2TxHash,
7175
+ for: opts.for
7176
+ }
7177
+ }
7178
+ );
7179
+ const finalize = (l2TxHash) => wrap6(
7180
+ OP_WITHDRAWALS.finalize.send,
7181
+ async () => {
7182
+ const pack = await (async () => {
7183
+ try {
7184
+ return await svc.fetchFinalizeDepositParams(l2TxHash);
7185
+ } catch (e) {
7186
+ throw createError("STATE", {
7187
+ resource: "withdrawals",
7188
+ operation: OP_WITHDRAWALS.finalize.fetchParams.receipt,
7189
+ message: "Withdrawal not ready: finalize params unavailable.",
7190
+ context: { l2TxHash },
7191
+ cause: e
7192
+ });
5435
7193
  }
5436
- );
5437
- const assetData = await wrapAs7(
5438
- "INTERNAL",
5439
- OP_WITHDRAWALS.erc20.encodeAssetData,
5440
- () => Promise.resolve(
5441
- encodeNativeTokenVaultTransferData(p.amount, p.to ?? ctx.sender, p.token)
5442
- ),
5443
- {
5444
- ctx: { where: "AbiCoder.encode", token: p.token, to: p.to ?? ctx.sender },
5445
- message: "Failed to encode burn/withdraw asset data."
7194
+ })();
7195
+ const { params } = pack;
7196
+ const key = {
7197
+ chainIdL2: params.chainId,
7198
+ l2BatchNumber: params.l2BatchNumber,
7199
+ l2MessageIndex: params.l2MessageIndex
7200
+ };
7201
+ try {
7202
+ const done = await svc.isWithdrawalFinalized(key);
7203
+ if (done) {
7204
+ const statusNow = await status(l2TxHash);
7205
+ return { status: statusNow };
5446
7206
  }
5447
- );
5448
- const l2ar = await ctx.contracts.l2AssetRouter();
5449
- const dataWithdraw = await wrapAs7(
5450
- "INTERNAL",
5451
- OP_WITHDRAWALS.erc20.encodeWithdraw,
5452
- () => Promise.resolve(l2ar.interface.encodeFunctionData(SIG.withdraw, [assetId, assetData])),
5453
- {
5454
- ctx: { where: "L2AssetRouter.withdraw", assetId },
5455
- message: "Failed to encode withdraw calldata."
7207
+ } catch {
7208
+ }
7209
+ const readiness = await svc.simulateFinalizeReadiness(params);
7210
+ if (readiness.kind === "FINALIZED") {
7211
+ const statusNow = await status(l2TxHash);
7212
+ return { status: statusNow };
7213
+ }
7214
+ if (readiness.kind === "NOT_READY") {
7215
+ throw createError("STATE", {
7216
+ resource: "withdrawals",
7217
+ operation: OP_WITHDRAWALS.finalize.readiness.simulate,
7218
+ message: "Withdrawal not ready to finalize.",
7219
+ context: readiness
7220
+ });
7221
+ }
7222
+ try {
7223
+ const tx = await svc.finalizeDeposit(params);
7224
+ finalizeCache.set(l2TxHash, tx.hash);
7225
+ const rcpt = await tx.wait();
7226
+ const statusNow = await status(l2TxHash);
7227
+ return { status: statusNow, receipt: rcpt };
7228
+ } catch (e) {
7229
+ const statusNow = await status(l2TxHash);
7230
+ if (statusNow.phase === "FINALIZED") return { status: statusNow };
7231
+ try {
7232
+ const again = await svc.simulateFinalizeReadiness(params);
7233
+ if (again.kind === "NOT_READY") {
7234
+ throw createError("STATE", {
7235
+ resource: "withdrawals",
7236
+ operation: OP_WITHDRAWALS.finalize.readiness.simulate,
7237
+ message: "Withdrawal not ready to finalize.",
7238
+ context: again
7239
+ });
7240
+ }
7241
+ } catch {
5456
7242
  }
5457
- );
5458
- const withdrawTx = {
5459
- to: ctx.l2AssetRouter,
5460
- data: dataWithdraw,
5461
- from: ctx.sender
5462
- };
5463
- const withdrawGas = await quoteL2Gas4({ ctx, tx: withdrawTx });
5464
- if (withdrawGas) {
5465
- withdrawTx.gasLimit = withdrawGas.gasLimit;
5466
- withdrawTx.maxFeePerGas = withdrawGas.maxFeePerGas;
5467
- withdrawTx.maxPriorityFeePerGas = withdrawGas.maxPriorityFeePerGas;
7243
+ throw e;
5468
7244
  }
5469
- steps.push({
5470
- key: "l2-asset-router:withdraw",
5471
- kind: "l2-asset-router:withdraw",
5472
- description: "Burn on L2 & send L2\u2192L1 message",
5473
- tx: withdrawTx
5474
- });
5475
- const fees = buildFeeBreakdown2({
5476
- feeToken: ctx.baseTokenL1 ?? await ctx.client.baseToken(ctx.chainIdL2),
5477
- l2Gas: withdrawGas
7245
+ },
7246
+ {
7247
+ message: "Internal error while attempting to finalize withdrawal.",
7248
+ ctx: { l2TxHash, where: "withdrawals.finalize" }
7249
+ }
7250
+ );
7251
+ const tryFinalize = (l2TxHash) => toResult3("withdrawals.tryFinalize", () => finalize(l2TxHash), {
7252
+ message: "Internal error while attempting to tryFinalize withdrawal.",
7253
+ ctx: { l2TxHash, where: "withdrawals.tryFinalize" }
7254
+ });
7255
+ const tryWait = (h, opts) => toResult3(
7256
+ OP_WITHDRAWALS.tryWait,
7257
+ async () => {
7258
+ const v = await wait(h, opts);
7259
+ if (v) return v;
7260
+ throw createError("STATE", {
7261
+ resource: "withdrawals",
7262
+ operation: "withdrawals.tryWait",
7263
+ message: opts.for === "l2" ? "No L2 receipt yet; the withdrawal has not executed on L2." : "No L1 receipt yet; the withdrawal has not been included on L1.",
7264
+ context: {
7265
+ for: opts.for,
7266
+ l2TxHash: typeof h === "string" ? h : "l2TxHash" in h ? h.l2TxHash : void 0,
7267
+ where: "withdrawals.tryWait"
7268
+ }
5478
7269
  });
5479
- return { steps, approvals, fees };
7270
+ },
7271
+ {
7272
+ message: "Internal error while waiting for withdrawal.",
7273
+ ctx: { input: h, for: opts?.for, where: "withdrawals.tryWait" }
5480
7274
  }
7275
+ );
7276
+ return {
7277
+ quote,
7278
+ tryQuote,
7279
+ prepare,
7280
+ tryPrepare,
7281
+ create,
7282
+ tryCreate,
7283
+ status,
7284
+ wait,
7285
+ finalize,
7286
+ tryFinalize,
7287
+ tryWait
5481
7288
  };
5482
7289
  }
5483
7290
 
5484
- // src/core/resources/withdrawals/events.ts
5485
- function findL1MessageSentLog(receipt, opts) {
5486
- const index = opts?.index;
5487
- const preferAddr = typeof opts?.prefer === "object" ? opts?.prefer.address : opts?.prefer === "assetRouter" ? L2_ASSET_ROUTER_ADDRESS : L1_MESSENGER_ADDRESS;
5488
- const prefer = (preferAddr ?? L1_MESSENGER_ADDRESS).toLowerCase();
5489
- const matches = receipt.logs.filter((lg) => {
5490
- const t0 = (lg.topics?.[0] ?? "").toLowerCase();
5491
- return t0 === TOPIC_L1_MESSAGE_SENT_NEW || t0 === TOPIC_L1_MESSAGE_SENT_LEG;
5492
- });
5493
- if (!matches.length) {
5494
- throw new Error("No L1MessageSent event found in L2 receipt logs.");
5495
- }
5496
- const preferred = matches.find((lg) => (lg.address ?? "").toLowerCase() === prefer);
5497
- const chosen = preferred ?? matches[index] ?? matches[0];
5498
- if (!chosen) {
5499
- throw new Error("No suitable L1MessageSent event found.");
5500
- }
5501
- return chosen;
7291
+ // src/core/resources/interop/attributes/call.ts
7292
+ function createCallAttributes(codec) {
7293
+ const indirectCall = (messageValue) => codec.encode("indirectCall", [messageValue]);
7294
+ const interopCallValue = (bridgedAmount) => codec.encode("interopCallValue", [bridgedAmount]);
7295
+ return {
7296
+ indirectCall,
7297
+ interopCallValue
7298
+ };
5502
7299
  }
5503
7300
 
5504
- // src/core/resources/withdrawals/logs.ts
5505
- function messengerLogIndex(raw, opts) {
5506
- const index = opts?.index;
5507
- const messenger = (opts?.messenger).toLowerCase();
5508
- const arr = Array.isArray(raw?.l2ToL1Logs) ? raw.l2ToL1Logs : [];
5509
- const hits = arr.map((lg, i) => ({ i, lg })).filter(({ lg }) => (lg?.sender ?? "").toLowerCase() === messenger);
5510
- if (!hits.length) {
5511
- throw new Error("No L2->L1 messenger logs found in receipt.");
5512
- }
5513
- return (hits[index] ?? hits[0]).i;
7301
+ // src/core/resources/interop/attributes/bundle.ts
7302
+ function createBundleAttributes(codec) {
7303
+ const executionAddress = (executor) => codec.encode("executionAddress", [executor]);
7304
+ const unbundlerAddress = (addr) => codec.encode("unbundlerAddress", [addr]);
7305
+ return {
7306
+ executionAddress,
7307
+ unbundlerAddress
7308
+ };
5514
7309
  }
5515
7310
 
5516
- // src/adapters/ethers/resources/withdrawals/services/finalization.ts
5517
- var { wrapAs: wrapAs8 } = createErrorHandlers("withdrawals");
5518
- var IL1NullifierMini = [
5519
- "function isWithdrawalFinalized(uint256,uint256,uint256) view returns (bool)"
5520
- ];
5521
- function createFinalizationServices(client) {
5522
- const { l1, l2, signer } = client;
7311
+ // src/core/resources/interop/attributes/resource.ts
7312
+ function createAttributesResource(codec) {
5523
7313
  return {
5524
- async fetchFinalizeDepositParams(l2TxHash) {
5525
- const parsed = await wrapAs8(
5526
- "RPC",
5527
- OP_WITHDRAWALS.finalize.fetchParams.receipt,
5528
- () => client.zks.getReceiptWithL2ToL1(l2TxHash),
5529
- {
5530
- ctx: { where: "getReceiptWithL2ToL1", l2TxHash },
5531
- message: "Failed to fetch L2 receipt (with L2\u2192L1 logs)."
7314
+ call: createCallAttributes(codec),
7315
+ bundle: createBundleAttributes(codec)
7316
+ };
7317
+ }
7318
+
7319
+ // src/adapters/ethers/resources/interop/attributes/resource.ts
7320
+ function getInteropAttributes(params, ctx) {
7321
+ const bundleAttributes = [];
7322
+ if (params.execution?.only) {
7323
+ bundleAttributes.push(ctx.attributes.bundle.executionAddress(params.execution.only));
7324
+ }
7325
+ if (params.unbundling?.by) {
7326
+ bundleAttributes.push(ctx.attributes.bundle.unbundlerAddress(params.unbundling.by));
7327
+ }
7328
+ const callAttributes = params.actions.map((action) => {
7329
+ switch (action.type) {
7330
+ case "sendNative": {
7331
+ const baseMatches = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
7332
+ if (baseMatches) {
7333
+ return [ctx.attributes.call.interopCallValue(action.amount)];
5532
7334
  }
5533
- );
5534
- if (!parsed) {
5535
- throw createError("STATE", {
5536
- resource: "withdrawals",
5537
- operation: OP_WITHDRAWALS.finalize.fetchParams.receipt,
5538
- message: "L2 receipt not found.",
5539
- context: { l2TxHash }
5540
- });
7335
+ return [ctx.attributes.call.indirectCall(action.amount)];
5541
7336
  }
5542
- const ev = await wrapAs8(
5543
- "INTERNAL",
5544
- OP_WITHDRAWALS.finalize.fetchParams.findMessage,
5545
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
5546
- () => Promise.resolve(findL1MessageSentLog(parsed, { index: 0 })),
5547
- {
5548
- ctx: { l2TxHash, index: 0 },
5549
- message: "Failed to locate L1MessageSent event in L2 receipt."
7337
+ case "call":
7338
+ if (action.value && action.value > 0n) {
7339
+ return [ctx.attributes.call.interopCallValue(action.value)];
7340
+ }
7341
+ return [];
7342
+ case "sendErc20":
7343
+ return [ctx.attributes.call.indirectCall(0n)];
7344
+ default:
7345
+ assertNever(action);
7346
+ }
7347
+ });
7348
+ return { bundleAttributes, callAttributes };
7349
+ }
7350
+ function createEthersAttributesResource(opts = {}) {
7351
+ const iface = opts.iface ?? new ethers.Interface(IERC7786Attributes_default);
7352
+ const encode2 = (fn, args) => iface.encodeFunctionData(fn, args);
7353
+ return createAttributesResource({ encode: encode2 });
7354
+ }
7355
+
7356
+ // src/core/types/flows/interop.ts
7357
+ function isInteropExpectedRoot(obj) {
7358
+ if (typeof obj !== "object" || obj === null) return false;
7359
+ const root = obj;
7360
+ return isBigint(root.rootChainId) && isBigint(root.batchNumber) && isHash(root.expectedRoot);
7361
+ }
7362
+ function isInteropMessageProof(obj) {
7363
+ if (typeof obj !== "object" || obj === null) return false;
7364
+ const proof = obj;
7365
+ return isBigint(proof.chainId) && isBigint(proof.l1BatchNumber) && isBigint(proof.l2MessageIndex) && typeof proof.message === "object" && proof.message !== null && isNumber(proof.message.txNumberInBatch) && isAddress(proof.message.sender) && isHash(proof.message.data) && isHash66Array(proof.proof);
7366
+ }
7367
+ function isInteropFinalizationInfo(obj) {
7368
+ if (typeof obj !== "object" || obj === null) return false;
7369
+ const info = obj;
7370
+ return isHash66(info.l2SrcTxHash) && isHash66(info.bundleHash) && isBigint(info.dstChainId) && isHash(info.encodedData) && isInteropExpectedRoot(info.expectedRoot) && isInteropMessageProof(info.proof);
7371
+ }
7372
+
7373
+ // src/core/resources/interop/route.ts
7374
+ function sumActionMsgValue(actions) {
7375
+ let sum = 0n;
7376
+ for (const a of actions) {
7377
+ if (a.type === "sendNative") sum += a.amount;
7378
+ else if (a.type === "call" && a.value) sum += a.value;
7379
+ }
7380
+ return sum;
7381
+ }
7382
+ function sumErc20Amounts(actions) {
7383
+ let sum = 0n;
7384
+ for (const a of actions) if (a.type === "sendErc20") sum += a.amount;
7385
+ return sum;
7386
+ }
7387
+ function pickInteropRoute(args) {
7388
+ const hasErc20 = args.actions.some((a) => a.type === "sendErc20");
7389
+ const baseMatches = args.ctx.baseTokenSrc.toLowerCase() === args.ctx.baseTokenDst.toLowerCase();
7390
+ if (hasErc20) return "indirect";
7391
+ if (!baseMatches) return "indirect";
7392
+ return "direct";
7393
+ }
7394
+
7395
+ // src/core/resources/interop/plan.ts
7396
+ function preflightDirect(params, ctx) {
7397
+ if (!params.actions?.length) {
7398
+ throw new Error('route "direct" requires at least one action.');
7399
+ }
7400
+ const baseMatch = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
7401
+ if (!baseMatch) {
7402
+ throw new Error('route "direct" requires matching base tokens between source and destination.');
7403
+ }
7404
+ for (const action of params.actions) {
7405
+ switch (action.type) {
7406
+ case "sendNative":
7407
+ if (action.amount < 0n) {
7408
+ throw new Error("sendNative.amount must be >= 0.");
5550
7409
  }
5551
- );
5552
- const message = await wrapAs8(
5553
- "INTERNAL",
5554
- OP_WITHDRAWALS.finalize.fetchParams.decodeMessage,
5555
- () => Promise.resolve(ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ev.data)[0]),
5556
- {
5557
- ctx: { where: "decode L1MessageSent", data: ev.data },
5558
- message: "Failed to decode withdrawal message."
7410
+ break;
7411
+ case "call":
7412
+ if (action.value != null && action.value < 0n) {
7413
+ throw new Error("call.value must be >= 0 when provided.");
5559
7414
  }
5560
- );
5561
- const raw = await wrapAs8(
5562
- "RPC",
5563
- OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
5564
- () => client.zks.getReceiptWithL2ToL1(l2TxHash),
5565
- {
5566
- ctx: { where: "getReceiptWithL2ToL1 (raw)", l2TxHash },
5567
- message: "Failed to fetch raw L2 receipt."
7415
+ break;
7416
+ default:
7417
+ throw new Error(
7418
+ `route "direct" does not support ${action.type} actions; use the indirect route.`
7419
+ );
7420
+ }
7421
+ }
7422
+ }
7423
+ function buildDirectBundle(params, ctx, attrs) {
7424
+ const totalActionValue = sumActionMsgValue(params.actions);
7425
+ const starters = params.actions.map((action, index) => {
7426
+ const to = ctx.codec.formatAddress(action.to);
7427
+ const callAttributes = attrs.callAttributes[index] ?? [];
7428
+ switch (action.type) {
7429
+ case "sendNative":
7430
+ return [to, "0x", callAttributes];
7431
+ case "call":
7432
+ return [to, action.data ?? "0x", callAttributes];
7433
+ default:
7434
+ throw new Error(`buildDirectBundle: unsupported action type "${action.type}".`);
7435
+ }
7436
+ });
7437
+ return {
7438
+ dstChain: ctx.codec.formatChain(ctx.dstChainId),
7439
+ starters,
7440
+ bundleAttributes: attrs.bundleAttributes,
7441
+ approvals: [],
7442
+ quoteExtras: {
7443
+ totalActionValue,
7444
+ bridgedTokenTotal: 0n
7445
+ }
7446
+ };
7447
+ }
7448
+ function preflightIndirect(params, ctx) {
7449
+ if (!params.actions?.length) {
7450
+ throw new Error('route "indirect" requires at least one action.');
7451
+ }
7452
+ const hasErc20 = params.actions.some((a) => a.type === "sendErc20");
7453
+ const baseMatches = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
7454
+ if (!hasErc20 && baseMatches) {
7455
+ throw new Error(
7456
+ 'route "indirect" requires ERC-20 actions or mismatched base tokens; use the direct route instead.'
7457
+ );
7458
+ }
7459
+ for (const action of params.actions) {
7460
+ switch (action.type) {
7461
+ case "sendNative":
7462
+ if (action.amount < 0n) {
7463
+ throw new Error("sendNative.amount must be >= 0.");
5568
7464
  }
5569
- );
5570
- if (!raw) {
5571
- throw createError("STATE", {
5572
- resource: "withdrawals",
5573
- operation: OP_WITHDRAWALS.finalize.fetchParams.rawReceipt,
5574
- message: "Raw L2 receipt not found.",
5575
- context: { l2TxHash }
5576
- });
5577
- }
5578
- const idx = await wrapAs8(
5579
- "INTERNAL",
5580
- OP_WITHDRAWALS.finalize.fetchParams.messengerIndex,
5581
- () => Promise.resolve(messengerLogIndex(raw, { index: 0, messenger: L1_MESSENGER_ADDRESS })),
5582
- {
5583
- ctx: { where: "derive messenger log index", l2TxHash, receipt: raw },
5584
- message: "Failed to derive messenger log index."
7465
+ break;
7466
+ case "sendErc20":
7467
+ if (action.amount < 0n) {
7468
+ throw new Error("sendErc20.amount must be >= 0.");
5585
7469
  }
5586
- );
5587
- const proof = await wrapAs8(
5588
- "RPC",
5589
- OP_WITHDRAWALS.finalize.fetchParams.proof,
5590
- () => client.zks.getL2ToL1LogProof(l2TxHash, idx),
5591
- {
5592
- ctx: { where: "get L2\u2192L1 log proof", l2TxHash, messengerLogIndex: idx },
5593
- message: "Failed to fetch L2\u2192L1 log proof."
7470
+ break;
7471
+ case "call":
7472
+ if (action.value != null) {
7473
+ if (action.value < 0n) {
7474
+ throw new Error("call.value must be >= 0 when provided.");
7475
+ }
7476
+ if (action.value > 0n && !baseMatches) {
7477
+ throw new Error("indirect route does not support call.value when base tokens differ.");
7478
+ }
5594
7479
  }
5595
- );
5596
- const { chainId } = await wrapAs8(
5597
- "RPC",
5598
- OP_WITHDRAWALS.finalize.fetchParams.network,
5599
- () => l2.getNetwork(),
5600
- {
5601
- ctx: { where: "l2.getNetwork" },
5602
- message: "Failed to read L2 network."
7480
+ break;
7481
+ default:
7482
+ assertNever(action);
7483
+ }
7484
+ }
7485
+ }
7486
+ function buildIndirectBundle(params, ctx, attrs, starterData) {
7487
+ const totalActionValue = sumActionMsgValue(params.actions);
7488
+ const bridgedTokenTotal = sumErc20Amounts(params.actions);
7489
+ const approvalMap = /* @__PURE__ */ new Map();
7490
+ for (const action of params.actions) {
7491
+ if (action.type !== "sendErc20") continue;
7492
+ const key = action.token.toLowerCase();
7493
+ const existing = approvalMap.get(key);
7494
+ if (existing) {
7495
+ existing.amount += action.amount;
7496
+ } else {
7497
+ approvalMap.set(key, {
7498
+ token: action.token,
7499
+ spender: ctx.l2NativeTokenVault,
7500
+ amount: action.amount
7501
+ });
7502
+ }
7503
+ }
7504
+ const approvals = Array.from(approvalMap.values());
7505
+ const starters = params.actions.map((action, index) => {
7506
+ const callAttributes = attrs.callAttributes[index] ?? [];
7507
+ if (starterData[index]?.assetRouterPayload) {
7508
+ const l2AssetRouter = ctx.codec.formatAddress(ctx.l2AssetRouter);
7509
+ return [l2AssetRouter, starterData[index].assetRouterPayload, callAttributes];
7510
+ }
7511
+ const directTo = ctx.codec.formatAddress(action.to);
7512
+ switch (action.type) {
7513
+ case "sendNative":
7514
+ return [directTo, "0x", callAttributes];
7515
+ case "call":
7516
+ return [directTo, action.data ?? "0x", callAttributes];
7517
+ case "sendErc20":
7518
+ throw new Error("buildIndirectBundle: missing assetRouterPayload for sendErc20 action.");
7519
+ default:
7520
+ return assertNever(action);
7521
+ }
7522
+ });
7523
+ return {
7524
+ dstChain: ctx.codec.formatChain(ctx.dstChainId),
7525
+ starters,
7526
+ bundleAttributes: attrs.bundleAttributes,
7527
+ approvals,
7528
+ quoteExtras: {
7529
+ totalActionValue,
7530
+ bridgedTokenTotal
7531
+ }
7532
+ };
7533
+ }
7534
+ var PREFIX_EVM_CHAIN = ethers.getBytes("0x00010000");
7535
+ var PREFIX_EVM_ADDRESS = ethers.getBytes("0x000100000014");
7536
+ function formatInteropEvmChain(chainId) {
7537
+ const chainRef = ethers.toBeArray(chainId);
7538
+ const chainRefLength = ethers.getBytes(ethers.toBeHex(chainRef.length, 1));
7539
+ const payload = ethers.concat([PREFIX_EVM_CHAIN, chainRefLength, chainRef, new Uint8Array([0])]);
7540
+ return ethers.hexlify(payload);
7541
+ }
7542
+ function formatInteropEvmAddress(address) {
7543
+ const normalized = ethers.getAddress(address);
7544
+ const addrBytes = ethers.getBytes(normalized);
7545
+ const payload = ethers.concat([PREFIX_EVM_ADDRESS, addrBytes]);
7546
+ return ethers.hexlify(payload);
7547
+ }
7548
+ var interopCodec = {
7549
+ formatChain: formatInteropEvmChain,
7550
+ formatAddress: formatInteropEvmAddress
7551
+ };
7552
+ function getErc20Tokens(params) {
7553
+ const erc20Tokens = /* @__PURE__ */ new Map();
7554
+ for (const action of params.actions) {
7555
+ if (action.type !== "sendErc20") continue;
7556
+ erc20Tokens.set(action.token.toLowerCase(), action.token);
7557
+ }
7558
+ return Array.from(erc20Tokens.values());
7559
+ }
7560
+ function buildEnsureTokenSteps(erc20Tokens, ctx) {
7561
+ if (erc20Tokens.length === 0) return [];
7562
+ const ntv = new ethers.Contract(ctx.l2NativeTokenVault, L2NativeTokenVault_default, ctx.client.l2);
7563
+ return erc20Tokens.map((token) => ({
7564
+ key: `ensure-token:${token.toLowerCase()}`,
7565
+ kind: "interop.ntv.ensure-token",
7566
+ description: `Ensure ${token} is registered in the native token vault`,
7567
+ tx: {
7568
+ to: ctx.l2NativeTokenVault,
7569
+ data: ntv.interface.encodeFunctionData("ensureTokenIsRegistered", [token]),
7570
+ ...ctx.gasOverrides
7571
+ }
7572
+ }));
7573
+ }
7574
+ async function resolveErc20AssetIds(erc20Tokens, ctx) {
7575
+ const assetIds = /* @__PURE__ */ new Map();
7576
+ if (erc20Tokens.length === 0) return assetIds;
7577
+ const ntv = new ethers.Contract(ctx.l2NativeTokenVault, L2NativeTokenVault_default, ctx.client.getL2Signer());
7578
+ for (const token of erc20Tokens) {
7579
+ const assetId = await ntv.getFunction("ensureTokenIsRegistered").staticCall(token);
7580
+ assetIds.set(token.toLowerCase(), assetId);
7581
+ }
7582
+ return assetIds;
7583
+ }
7584
+
7585
+ // src/adapters/ethers/resources/interop/services/starter-data.ts
7586
+ async function getStarterData(params, ctx, erc20AssetIds) {
7587
+ const starterData = [];
7588
+ for (const action of params.actions) {
7589
+ switch (action.type) {
7590
+ case "sendErc20": {
7591
+ const assetId = erc20AssetIds.get(action.token.toLowerCase());
7592
+ if (!assetId) {
7593
+ throw new Error(`Missing precomputed assetId for token ${action.token}.`);
5603
7594
  }
5604
- );
5605
- const txIndex = Number(parsed.transactionIndex ?? 0);
5606
- const params = {
5607
- chainId: BigInt(chainId),
5608
- l2BatchNumber: proof.batchNumber,
5609
- l2MessageIndex: proof.id,
5610
- l2Sender: L2_ASSET_ROUTER_ADDRESS,
5611
- l2TxNumberInBatch: txIndex,
5612
- message,
5613
- merkleProof: proof.proof
5614
- };
5615
- const { l1Nullifier } = await wrapAs8(
5616
- "INTERNAL",
5617
- OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
5618
- () => client.ensureAddresses(),
5619
- {
5620
- ctx: { where: "ensureAddresses" },
5621
- message: "Failed to ensure L1 Nullifier address."
7595
+ const transferData = encodeNativeTokenVaultTransferData(
7596
+ action.amount,
7597
+ action.to,
7598
+ action.token
7599
+ );
7600
+ const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
7601
+ starterData.push({ assetRouterPayload });
7602
+ break;
7603
+ }
7604
+ case "sendNative":
7605
+ if (!ctx.baseTokens.matches) {
7606
+ const assetId = await ctx.tokens.baseTokenAssetId();
7607
+ const transferData = encodeNativeTokenVaultTransferData(
7608
+ action.amount,
7609
+ action.to,
7610
+ ctx.baseTokens.src
7611
+ );
7612
+ const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
7613
+ starterData.push({ assetRouterPayload });
7614
+ } else {
7615
+ starterData.push({});
5622
7616
  }
5623
- );
5624
- return { params, nullifier: l1Nullifier };
7617
+ break;
7618
+ case "call":
7619
+ starterData.push({});
7620
+ break;
7621
+ default:
7622
+ assertNever(action);
7623
+ }
7624
+ }
7625
+ return starterData;
7626
+ }
7627
+
7628
+ // src/adapters/ethers/resources/interop/routes/indirect.ts
7629
+ function routeIndirect() {
7630
+ return {
7631
+ // eslint-disable-next-line @typescript-eslint/require-await
7632
+ async preflight(params, ctx) {
7633
+ preflightIndirect(params, {
7634
+ dstChainId: ctx.dstChainId,
7635
+ baseTokens: ctx.baseTokens,
7636
+ l2AssetRouter: ctx.l2AssetRouter,
7637
+ l2NativeTokenVault: ctx.l2NativeTokenVault});
5625
7638
  },
5626
- async simulateFinalizeReadiness(params) {
5627
- const { l1Nullifier } = await wrapAs8(
5628
- "INTERNAL",
5629
- OP_WITHDRAWALS.finalize.readiness.ensureAddresses,
5630
- () => client.ensureAddresses(),
7639
+ async build(params, ctx) {
7640
+ const steps = [];
7641
+ const erc20Tokens = getErc20Tokens(params);
7642
+ const erc20AssetIds = await resolveErc20AssetIds(erc20Tokens, ctx);
7643
+ const attributes = getInteropAttributes(params, ctx);
7644
+ const starterData = await getStarterData(params, ctx, erc20AssetIds);
7645
+ const bundle = buildIndirectBundle(
7646
+ params,
5631
7647
  {
5632
- ctx: { where: "ensureAddresses" },
5633
- message: "Failed to ensure L1 Nullifier address."
5634
- }
7648
+ dstChainId: ctx.dstChainId,
7649
+ baseTokens: ctx.baseTokens,
7650
+ l2AssetRouter: ctx.l2AssetRouter,
7651
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
7652
+ codec: interopCodec
7653
+ },
7654
+ attributes,
7655
+ starterData
5635
7656
  );
5636
- const done = await (async () => {
5637
- try {
5638
- const cMini = new ethers.Contract(l1Nullifier, IL1NullifierMini, l1);
5639
- const isFinalized = await wrapAs8(
5640
- "RPC",
5641
- OP_WITHDRAWALS.finalize.readiness.isFinalized,
5642
- () => cMini.isWithdrawalFinalized(
5643
- params.chainId,
5644
- params.l2BatchNumber,
5645
- params.l2MessageIndex
5646
- ),
5647
- {
5648
- ctx: { where: "isWithdrawalFinalized", params },
5649
- message: "Failed to read finalization status."
7657
+ steps.push(...buildEnsureTokenSteps(erc20Tokens, ctx));
7658
+ for (const approval of bundle.approvals) {
7659
+ const erc20 = new ethers.Contract(approval.token, IERC20_default, ctx.client.l2);
7660
+ const currentAllowance = await erc20.allowance(
7661
+ ctx.sender,
7662
+ ctx.l2NativeTokenVault
7663
+ );
7664
+ if (currentAllowance < approval.amount) {
7665
+ const approveData = erc20.interface.encodeFunctionData("approve", [
7666
+ ctx.l2NativeTokenVault,
7667
+ approval.amount
7668
+ ]);
7669
+ steps.push({
7670
+ key: `approve:${approval.token}:${ctx.l2NativeTokenVault}`,
7671
+ kind: "approve",
7672
+ description: `Approve ${ctx.l2NativeTokenVault} to spend ${approval.amount} of ${approval.token}`,
7673
+ tx: {
7674
+ to: approval.token,
7675
+ data: approveData,
7676
+ ...ctx.gasOverrides
5650
7677
  }
5651
- );
5652
- return Boolean(isFinalized);
5653
- } catch {
5654
- return false;
7678
+ });
5655
7679
  }
5656
- })();
5657
- if (done) return { kind: "FINALIZED" };
5658
- const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, l1);
5659
- try {
5660
- await c.finalizeDeposit.staticCall(params);
5661
- return { kind: "READY" };
5662
- } catch (e) {
5663
- return classifyReadinessFromRevert(e);
5664
7680
  }
7681
+ const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
7682
+ bundle.dstChain,
7683
+ bundle.starters,
7684
+ bundle.bundleAttributes
7685
+ ]);
7686
+ steps.push({
7687
+ key: "sendBundle",
7688
+ kind: "interop.center",
7689
+ description: "Send interop bundle (indirect route)",
7690
+ tx: {
7691
+ to: ctx.interopCenter,
7692
+ data,
7693
+ value: bundle.quoteExtras.totalActionValue,
7694
+ ...ctx.gasOverrides
7695
+ }
7696
+ });
7697
+ return {
7698
+ steps,
7699
+ approvals: bundle.approvals,
7700
+ quoteExtras: bundle.quoteExtras
7701
+ };
7702
+ }
7703
+ };
7704
+ }
7705
+
7706
+ // src/adapters/ethers/resources/interop/routes/direct.ts
7707
+ function routeDirect() {
7708
+ return {
7709
+ // eslint-disable-next-line @typescript-eslint/require-await
7710
+ async preflight(params, ctx) {
7711
+ preflightDirect(params, {
7712
+ dstChainId: ctx.dstChainId,
7713
+ baseTokens: ctx.baseTokens,
7714
+ l2AssetRouter: ctx.l2AssetRouter,
7715
+ l2NativeTokenVault: ctx.l2NativeTokenVault});
5665
7716
  },
5666
- async isWithdrawalFinalized(key) {
5667
- const { l1Nullifier } = await wrapAs8(
5668
- "INTERNAL",
5669
- OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
5670
- () => client.ensureAddresses(),
7717
+ // eslint-disable-next-line @typescript-eslint/require-await
7718
+ async build(params, ctx) {
7719
+ const steps = [];
7720
+ const attrs = getInteropAttributes(params, ctx);
7721
+ const built = buildDirectBundle(
7722
+ params,
5671
7723
  {
5672
- ctx: { where: "ensureAddresses" },
5673
- message: "Failed to ensure L1 Nullifier address."
5674
- }
7724
+ dstChainId: ctx.dstChainId,
7725
+ baseTokens: ctx.baseTokens,
7726
+ l2AssetRouter: ctx.l2AssetRouter,
7727
+ l2NativeTokenVault: ctx.l2NativeTokenVault,
7728
+ codec: interopCodec
7729
+ },
7730
+ attrs
5675
7731
  );
5676
- const c = new ethers.Contract(l1Nullifier, IL1NullifierMini, l1);
5677
- return await wrapAs8(
5678
- "RPC",
5679
- OP_WITHDRAWALS.finalize.isFinalized,
5680
- () => c.isWithdrawalFinalized(key.chainIdL2, key.l2BatchNumber, key.l2MessageIndex),
5681
- {
5682
- ctx: { where: "isWithdrawalFinalized", key },
5683
- message: "Failed to read finalization status."
7732
+ const data = ctx.ifaces.interopCenter.encodeFunctionData("sendBundle", [
7733
+ built.dstChain,
7734
+ built.starters,
7735
+ built.bundleAttributes
7736
+ ]);
7737
+ steps.push({
7738
+ key: "sendBundle",
7739
+ kind: "interop.center",
7740
+ description: `Send interop bundle (direct route; ${params.actions.length} actions)`,
7741
+ // In direct route, msg.value equals the total forwarded value across
7742
+ // all calls (sendNative.amount + call.value).
7743
+ tx: {
7744
+ to: ctx.interopCenter,
7745
+ data,
7746
+ value: built.quoteExtras.totalActionValue,
7747
+ ...ctx.gasOverrides
5684
7748
  }
5685
- );
5686
- },
5687
- async estimateFinalization(params) {
5688
- const { l1Nullifier } = await wrapAs8(
5689
- "INTERNAL",
5690
- OP_WITHDRAWALS.finalize.estimate,
5691
- () => client.ensureAddresses(),
5692
- {
5693
- ctx: { where: "ensureAddresses" },
5694
- message: "Failed to ensure L1 Nullifier address."
7749
+ });
7750
+ return {
7751
+ steps,
7752
+ approvals: built.approvals,
7753
+ quoteExtras: built.quoteExtras
7754
+ };
7755
+ }
7756
+ };
7757
+ }
7758
+ var MIN_INTEROP_PROTOCOL = 31;
7759
+ async function assertInteropProtocolVersion(client, srcChainId, dstChainId) {
7760
+ const [srcProtocolVersion, dstProtocolVersion] = await Promise.all([
7761
+ client.getProtocolVersion(srcChainId),
7762
+ client.getProtocolVersion(dstChainId)
7763
+ ]);
7764
+ const assertProtocolVersion = (chainId, protocolVersion) => {
7765
+ if (protocolVersion[1] < MIN_INTEROP_PROTOCOL) {
7766
+ throw createError("VALIDATION", {
7767
+ resource: "interop",
7768
+ operation: OP_INTEROP.context.protocolVersion,
7769
+ message: `Interop requires protocol version 31.0+. Found: ${protocolVersion[1]}.${protocolVersion[2]} for chain: ${chainId}.`,
7770
+ context: {
7771
+ chainId,
7772
+ requiredMinor: MIN_INTEROP_PROTOCOL,
7773
+ semver: protocolVersion
5695
7774
  }
5696
- );
5697
- const signer2 = client.getL1Signer();
5698
- const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, signer2);
5699
- const gasLimit = await wrapAs8(
5700
- "RPC",
5701
- OP_WITHDRAWALS.finalize.estimate,
5702
- () => c.finalizeDeposit.estimateGas(params),
5703
- {
5704
- ctx: {
5705
- where: "estimateGas(finalizeDeposit)",
5706
- chainIdL2: params.chainId,
5707
- l2BatchNumber: params.l2BatchNumber,
5708
- l2MessageIndex: params.l2MessageIndex,
5709
- l1Nullifier
5710
- },
5711
- message: "Failed to estimate gas for finalizeDeposit."
7775
+ });
7776
+ }
7777
+ };
7778
+ assertProtocolVersion(srcChainId, srcProtocolVersion);
7779
+ assertProtocolVersion(dstChainId, dstProtocolVersion);
7780
+ }
7781
+ async function commonCtx3(dstProvider, params, client, tokens, contracts, attributes) {
7782
+ const sender = await client.signer.getAddress();
7783
+ const chainId = (await client.l2.getNetwork()).chainId;
7784
+ const dstChainId = (await dstProvider.getNetwork()).chainId;
7785
+ const {
7786
+ bridgehub,
7787
+ l2AssetRouter,
7788
+ l2NativeTokenVault,
7789
+ interopCenter,
7790
+ interopHandler,
7791
+ l2MessageVerification
7792
+ } = await contracts.addresses();
7793
+ await assertInteropProtocolVersion(client, chainId, dstChainId);
7794
+ const [srcBaseToken, dstBaseToken] = await Promise.all([
7795
+ client.baseToken(chainId),
7796
+ client.baseToken(dstChainId)
7797
+ ]);
7798
+ const interopCenterIface = new ethers.Interface(InteropCenter_default);
7799
+ const interopHandlerIface = new ethers.Interface(IInteropHandler_default);
7800
+ const baseMatches = srcBaseToken.toLowerCase() === dstBaseToken.toLowerCase();
7801
+ return {
7802
+ client,
7803
+ tokens,
7804
+ contracts,
7805
+ sender,
7806
+ chainIdL2: chainId,
7807
+ chainId,
7808
+ bridgehub,
7809
+ dstChainId,
7810
+ dstProvider,
7811
+ interopCenter,
7812
+ interopHandler,
7813
+ l2MessageVerification,
7814
+ l2AssetRouter,
7815
+ l2NativeTokenVault,
7816
+ baseTokens: { src: srcBaseToken, dst: dstBaseToken, matches: baseMatches },
7817
+ ifaces: { interopCenter: interopCenterIface, interopHandler: interopHandlerIface },
7818
+ attributes,
7819
+ gasOverrides: params.txOverrides ? toGasOverrides(params.txOverrides) : void 0
7820
+ };
7821
+ }
7822
+ function getTopics() {
7823
+ const centerIface = new ethers.Interface(InteropCenter_default);
7824
+ const handlerIface = new ethers.Interface(IInteropHandler_default);
7825
+ const topics = {
7826
+ interopBundleSent: centerIface.getEvent("InteropBundleSent").topicHash,
7827
+ bundleVerified: handlerIface.getEvent("BundleVerified").topicHash,
7828
+ bundleExecuted: handlerIface.getEvent("BundleExecuted").topicHash,
7829
+ bundleUnbundled: handlerIface.getEvent("BundleUnbundled").topicHash
7830
+ };
7831
+ return { topics, centerIface };
7832
+ }
7833
+ var { wrap: wrap2 } = createErrorHandlers("interop");
7834
+ var DEFAULT_BLOCKS_RANGE_SIZE = 1e4;
7835
+ var DEFAULT_MAX_BLOCKS_BACK = 2e4;
7836
+ var SAFE_BLOCKS_RANGE_SIZE = 1e3;
7837
+ function parseMaxBlockRangeLimit(error) {
7838
+ if (!ethers.isError(error, "UNKNOWN_ERROR")) return null;
7839
+ if (!error.error || typeof error.error !== "object") return null;
7840
+ const match = /query exceeds max block range\s+(\d+)/i.exec(error.error?.message);
7841
+ if (!match) return null;
7842
+ const limit = Number.parseInt(match[1], 10);
7843
+ return Number.isInteger(limit) && limit > 0 ? limit : null;
7844
+ }
7845
+ async function getTxReceipt(provider, txHash) {
7846
+ const receipt = await wrap2(
7847
+ OP_INTEROP.svc.status.sourceReceipt,
7848
+ () => provider.getTransactionReceipt(txHash),
7849
+ {
7850
+ ctx: { where: "l2.getTransactionReceipt", l2SrcTxHash: txHash },
7851
+ message: "Failed to fetch source L2 receipt for interop tx."
7852
+ }
7853
+ );
7854
+ if (!receipt) return null;
7855
+ return {
7856
+ logs: receipt.logs.map((log) => ({
7857
+ address: log.address,
7858
+ topics: log.topics,
7859
+ data: log.data,
7860
+ transactionHash: log.transactionHash
7861
+ }))
7862
+ };
7863
+ }
7864
+ async function getLogs(provider, address, topics, opts) {
7865
+ const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
7866
+ const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
7867
+ return await wrap2(
7868
+ OP_INTEROP.svc.status.dstLogs,
7869
+ async () => {
7870
+ const currentBlock = await provider.getBlockNumber();
7871
+ const minBlock = Math.max(0, currentBlock - maxBlocksBack);
7872
+ let toBlock = currentBlock;
7873
+ let chunkSize = initialChunkSize;
7874
+ while (toBlock >= minBlock) {
7875
+ const fromBlock = Math.max(minBlock, toBlock - chunkSize + 1);
7876
+ try {
7877
+ const rawLogs = await provider.getLogs({
7878
+ address,
7879
+ topics,
7880
+ fromBlock,
7881
+ toBlock
7882
+ });
7883
+ if (rawLogs.length > 0) {
7884
+ return rawLogs.map((log) => ({
7885
+ address: log.address,
7886
+ topics: log.topics,
7887
+ data: log.data,
7888
+ transactionHash: log.transactionHash
7889
+ }));
7890
+ }
7891
+ toBlock = fromBlock - 1;
7892
+ } catch (error) {
7893
+ const serverLimit = parseMaxBlockRangeLimit(error);
7894
+ if (serverLimit == null) {
7895
+ if (chunkSize > SAFE_BLOCKS_RANGE_SIZE) {
7896
+ chunkSize = SAFE_BLOCKS_RANGE_SIZE;
7897
+ } else {
7898
+ throw error;
7899
+ }
7900
+ } else {
7901
+ chunkSize = Math.min(chunkSize, serverLimit);
7902
+ }
5712
7903
  }
7904
+ }
7905
+ return [];
7906
+ },
7907
+ {
7908
+ ctx: { address, maxBlocksBack, logChunkSize: initialChunkSize },
7909
+ message: "Failed to query destination bundle lifecycle logs."
7910
+ }
7911
+ );
7912
+ }
7913
+ async function getInteropRoot(provider, rootChainId, batchNumber) {
7914
+ return await wrap2(
7915
+ OP_INTEROP.svc.status.getRoot,
7916
+ async () => {
7917
+ const rootStorage = new ethers.Contract(
7918
+ L2_INTEROP_ROOT_STORAGE_ADDRESS,
7919
+ InteropRootStorage_default,
7920
+ provider
5713
7921
  );
5714
- const feeData = await wrapAs8("RPC", OP_WITHDRAWALS.finalize.estimate, () => l1.getFeeData(), {
5715
- ctx: { where: "l1.getFeeData" },
5716
- message: "Failed to estimate fee data for finalizeDeposit."
5717
- });
5718
- const maxFeePerGas = feeData.maxFeePerGas ?? feeData.gasPrice ?? // legacy-style gas price if present
5719
- (() => {
5720
- throw createError("RPC", {
5721
- resource: "withdrawals",
5722
- operation: OP_WITHDRAWALS.finalize.estimate,
5723
- message: "Provider did not return gas price or EIP-1559 fields.",
5724
- context: { feeData }
5725
- });
5726
- })();
5727
- const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? 0n;
5728
- return {
5729
- gasLimit,
5730
- maxFeePerGas,
5731
- maxPriorityFeePerGas
5732
- };
7922
+ return await rootStorage.interopRoots(rootChainId, batchNumber);
5733
7923
  },
5734
- async finalizeDeposit(params) {
5735
- const { l1Nullifier } = await wrapAs8(
5736
- "INTERNAL",
5737
- OP_WITHDRAWALS.finalize.fetchParams.ensureAddresses,
5738
- () => client.ensureAddresses(),
5739
- {
5740
- ctx: { where: "ensureAddresses" },
5741
- message: "Failed to ensure L1 Nullifier address."
7924
+ {
7925
+ ctx: { rootChainId, batchNumber },
7926
+ message: "Failed to get interop root from the destination chain."
7927
+ }
7928
+ );
7929
+ }
7930
+
7931
+ // src/adapters/ethers/resources/interop/services/finalization/bundle.ts
7932
+ var { wrap: wrap3 } = createErrorHandlers("interop");
7933
+ async function getBundleStatus(client, dstProvider, topics, bundleHash, opts) {
7934
+ const { interopHandler } = await client.ensureAddresses();
7935
+ const bundleLogs = await getLogs(dstProvider, interopHandler, [null, bundleHash], opts);
7936
+ const findLastByTopic = (eventTopic) => bundleLogs.findLast((log) => log.topics[0].toLowerCase() === eventTopic.toLowerCase());
7937
+ const lifecycleChecks = [
7938
+ { phase: "UNBUNDLED", topic: topics.bundleUnbundled, includeTxHash: true },
7939
+ { phase: "EXECUTED", topic: topics.bundleExecuted, includeTxHash: true },
7940
+ { phase: "VERIFIED", topic: topics.bundleVerified }
7941
+ ];
7942
+ for (const check of lifecycleChecks) {
7943
+ const match = findLastByTopic(check.topic);
7944
+ if (!match) continue;
7945
+ if (check.includeTxHash) {
7946
+ return { phase: check.phase, dstExecTxHash: match.transactionHash };
7947
+ }
7948
+ return { phase: check.phase };
7949
+ }
7950
+ return { phase: "SENT" };
7951
+ }
7952
+ async function executeBundle(client, dstProvider, info, opts) {
7953
+ const { topics } = getTopics();
7954
+ const { bundleHash, encodedData, proof } = info;
7955
+ const dstStatus = await getBundleStatus(client, dstProvider, topics, bundleHash, opts);
7956
+ if (["EXECUTED", "UNBUNDLED"].includes(dstStatus.phase)) {
7957
+ throw createError("STATE", {
7958
+ resource: "interop",
7959
+ operation: OP_INTEROP.finalize,
7960
+ message: `Interop bundle has already been ${dstStatus.phase.toLowerCase()}.`,
7961
+ context: { bundleHash }
7962
+ });
7963
+ }
7964
+ const signer = await wrap3(OP_INTEROP.exec.sendStep, () => client.signerFor(dstProvider), {
7965
+ message: "Failed to resolve destination signer."
7966
+ });
7967
+ const { interopHandler } = await client.ensureAddresses();
7968
+ const handler = new ethers.Contract(interopHandler, IInteropHandler_default, signer);
7969
+ try {
7970
+ const txResponse = await handler.executeBundle(encodedData, proof);
7971
+ const hash = txResponse.hash;
7972
+ return {
7973
+ hash,
7974
+ wait: async () => {
7975
+ try {
7976
+ const receipt = await txResponse.wait();
7977
+ if (!receipt || receipt.status !== 1) {
7978
+ throw createError("EXECUTION", {
7979
+ resource: "interop",
7980
+ operation: OP_INTEROP.exec.waitStep,
7981
+ message: "Interop bundle execution reverted on destination.",
7982
+ context: { txHash: hash }
7983
+ });
7984
+ }
7985
+ return receipt;
7986
+ } catch (e) {
7987
+ if (isZKsyncError(e)) throw e;
7988
+ throw toZKsyncError(
7989
+ "EXECUTION",
7990
+ {
7991
+ resource: "interop",
7992
+ operation: OP_INTEROP.exec.waitStep,
7993
+ message: "Failed while waiting for executeBundle transaction on destination.",
7994
+ context: { txHash: hash }
7995
+ },
7996
+ e
7997
+ );
5742
7998
  }
5743
- );
5744
- const c = new ethers.Contract(l1Nullifier, IL1Nullifier_default, signer);
7999
+ }
8000
+ };
8001
+ } catch (e) {
8002
+ throw toZKsyncError(
8003
+ "EXECUTION",
8004
+ {
8005
+ resource: "interop",
8006
+ operation: OP_INTEROP.exec.sendStep,
8007
+ message: "Failed to send executeBundle transaction on destination chain."
8008
+ },
8009
+ e
8010
+ );
8011
+ }
8012
+ }
8013
+
8014
+ // src/core/resources/interop/finalization.ts
8015
+ var DEFAULT_POLL_MS = 1e3;
8016
+ var DEFAULT_TIMEOUT_MS = 3e5;
8017
+ function resolveIdsFromWaitable(input) {
8018
+ if (typeof input === "string") {
8019
+ return { l2SrcTxHash: input };
8020
+ }
8021
+ return {
8022
+ l2SrcTxHash: input.l2SrcTxHash,
8023
+ bundleHash: input.bundleHash,
8024
+ dstExecTxHash: input.dstExecTxHash
8025
+ };
8026
+ }
8027
+ function parseBundleSentFromReceipt(input) {
8028
+ const { receipt, interopCenter, interopBundleSentTopic, decodeInteropBundleSent: decodeInteropBundleSent2 } = input;
8029
+ const bundleSentLog = receipt.logs.find(
8030
+ (log) => log.address.toLowerCase() === interopCenter.toLowerCase() && log.topics[0].toLowerCase() === interopBundleSentTopic.toLowerCase()
8031
+ );
8032
+ if (!bundleSentLog) {
8033
+ throw createError("STATE", {
8034
+ resource: "interop",
8035
+ operation: OP_INTEROP.svc.status.parseSentLog,
8036
+ message: "Failed to locate InteropBundleSent event in source receipt.",
8037
+ context: { receipt, interopCenter }
8038
+ });
8039
+ }
8040
+ const decoded = decodeInteropBundleSent2({
8041
+ data: bundleSentLog.data,
8042
+ topics: bundleSentLog.topics
8043
+ });
8044
+ return { bundleHash: decoded.bundleHash, dstChainId: decoded.destinationChainId };
8045
+ }
8046
+ function parseBundleReceiptInfo(params) {
8047
+ const {
8048
+ rawReceipt,
8049
+ interopCenter,
8050
+ interopBundleSentTopic,
8051
+ decodeInteropBundleSent: decodeInteropBundleSent2,
8052
+ decodeL1MessageData: decodeL1MessageData2,
8053
+ l2SrcTxHash
8054
+ } = params;
8055
+ let l2ToL1LogIndex = -1;
8056
+ let l1MessageData = null;
8057
+ let found;
8058
+ for (const log of rawReceipt.logs) {
8059
+ if (isL1MessageSentLog(log)) {
8060
+ l2ToL1LogIndex += 1;
5745
8061
  try {
5746
- const receipt = await c.finalizeDeposit(params);
5747
- const hash = receipt.hash;
5748
- return {
5749
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
5750
- hash,
5751
- wait: async () => {
5752
- try {
5753
- return await receipt.wait();
5754
- } catch (e) {
5755
- throw toZKsyncError(
5756
- "EXECUTION",
5757
- {
5758
- resource: "withdrawals",
5759
- operation: OP_WITHDRAWALS.finalize.wait,
5760
- message: "Failed while waiting for finalizeDeposit transaction.",
5761
- context: { txHash: hash }
5762
- },
5763
- e
5764
- );
5765
- }
5766
- }
5767
- };
8062
+ l1MessageData = decodeL1MessageData2(log);
5768
8063
  } catch (e) {
5769
- throw toZKsyncError(
5770
- "EXECUTION",
5771
- {
5772
- resource: "withdrawals",
5773
- operation: OP_WITHDRAWALS.finalize.send,
5774
- message: "Failed to send finalizeDeposit transaction.",
5775
- context: {
5776
- chainIdL2: params.chainId,
5777
- l2BatchNumber: params.l2BatchNumber,
5778
- l2MessageIndex: params.l2MessageIndex,
5779
- l1Nullifier
5780
- }
5781
- },
5782
- e
5783
- );
8064
+ throw createError("STATE", {
8065
+ resource: "interop",
8066
+ operation: OP_INTEROP.svc.status.parseSentLog,
8067
+ message: "Failed to decode L1MessageSent log data for interop bundle.",
8068
+ context: { l2SrcTxHash, l2ToL1LogIndex },
8069
+ cause: e
8070
+ });
5784
8071
  }
8072
+ continue;
5785
8073
  }
8074
+ if (log.address.toLowerCase() !== interopCenter.toLowerCase() || log.topics[0].toLowerCase() !== interopBundleSentTopic.toLowerCase())
8075
+ continue;
8076
+ const decoded = decodeInteropBundleSent2({
8077
+ data: log.data,
8078
+ topics: log.topics
8079
+ });
8080
+ found = {
8081
+ bundleHash: decoded.bundleHash,
8082
+ dstChainId: decoded.destinationChainId,
8083
+ sourceChainId: decoded.sourceChainId
8084
+ };
8085
+ break;
8086
+ }
8087
+ if (!found) {
8088
+ throw createError("STATE", {
8089
+ resource: "interop",
8090
+ operation: OP_INTEROP.svc.status.parseSentLog,
8091
+ message: "Failed to locate InteropBundleSent event in source receipt.",
8092
+ context: { l2SrcTxHash, interopCenter }
8093
+ });
8094
+ }
8095
+ if (!l1MessageData) {
8096
+ throw createError("STATE", {
8097
+ resource: "interop",
8098
+ operation: OP_INTEROP.svc.status.parseSentLog,
8099
+ message: "Failed to locate L1MessageSent log data for interop bundle.",
8100
+ context: { l2SrcTxHash, interopCenter }
8101
+ });
8102
+ }
8103
+ return {
8104
+ bundleHash: found.bundleHash,
8105
+ dstChainId: found.dstChainId,
8106
+ sourceChainId: found.sourceChainId,
8107
+ l1MessageData,
8108
+ l2ToL1LogIndex,
8109
+ txNumberInBatch: Number(rawReceipt.transactionIndex),
8110
+ rawReceipt
8111
+ };
8112
+ }
8113
+ function getBundleEncodedData(messageData) {
8114
+ const prefix = `0x${messageData.slice(2, 4)}`;
8115
+ if (prefix !== BUNDLE_IDENTIFIER) {
8116
+ throw createError("STATE", {
8117
+ resource: "interop",
8118
+ operation: OP_INTEROP.wait,
8119
+ message: "Unexpected bundle prefix in L1MessageSent data.",
8120
+ context: { prefix, expected: BUNDLE_IDENTIFIER }
8121
+ });
8122
+ }
8123
+ return `0x${messageData.slice(4)}`;
8124
+ }
8125
+ function buildFinalizationInfo(ids, bundleInfo, proof, messageData) {
8126
+ const expectedRoot = {
8127
+ rootChainId: bundleInfo.sourceChainId,
8128
+ batchNumber: proof.batchNumber,
8129
+ expectedRoot: proof.root
8130
+ };
8131
+ const messageProof = {
8132
+ chainId: bundleInfo.sourceChainId,
8133
+ l1BatchNumber: proof.batchNumber,
8134
+ l2MessageIndex: proof.id,
8135
+ message: {
8136
+ txNumberInBatch: bundleInfo.txNumberInBatch,
8137
+ sender: L2_INTEROP_CENTER_ADDRESS,
8138
+ data: messageData
8139
+ },
8140
+ proof: proof.proof
8141
+ };
8142
+ return {
8143
+ l2SrcTxHash: ids.l2SrcTxHash,
8144
+ bundleHash: bundleInfo.bundleHash,
8145
+ dstChainId: bundleInfo.dstChainId,
8146
+ expectedRoot,
8147
+ proof: messageProof,
8148
+ encodedData: getBundleEncodedData(messageData)
8149
+ };
8150
+ }
8151
+ function decodeInteropBundleSent(centerIface, log) {
8152
+ const decoded = centerIface.decodeEventLog(
8153
+ "InteropBundleSent",
8154
+ log.data,
8155
+ log.topics
8156
+ );
8157
+ return {
8158
+ bundleHash: decoded.interopBundleHash,
8159
+ sourceChainId: decoded.interopBundle.sourceChainId,
8160
+ destinationChainId: decoded.interopBundle.destinationChainId
5786
8161
  };
5787
8162
  }
8163
+ function decodeL1MessageData(log) {
8164
+ const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], log.data);
8165
+ return decoded[0];
8166
+ }
5788
8167
 
5789
- // src/adapters/ethers/resources/withdrawals/index.ts
5790
- var ROUTES2 = {
5791
- base: routeEthBase(),
5792
- // BaseTokenSystem.withdraw, chain base = ETH
5793
- "erc20-nonbase": routeErc20NonBase2()
5794
- // AssetRouter.withdraw for non-base ERC-20s
5795
- };
5796
- function createWithdrawalsResource(client, tokens, contracts) {
5797
- const svc = createFinalizationServices(client);
5798
- const { wrap: wrap2, toResult: toResult2 } = createErrorHandlers("withdrawals");
5799
- const tokensResource = tokens ?? createTokensResource(client);
5800
- const contractsResource = contracts ?? createContractsResource(client);
5801
- async function buildPlan(p) {
5802
- const ctx = await commonCtx2(p, client, tokensResource, contractsResource);
5803
- await ROUTES2[ctx.route].preflight?.(p, ctx);
5804
- const { steps, approvals, fees } = await ROUTES2[ctx.route].build(p, ctx);
8168
+ // src/adapters/ethers/resources/interop/services/finalization/polling.ts
8169
+ var { wrap: wrap4 } = createErrorHandlers("interop");
8170
+ function isProofNotReadyError(error) {
8171
+ return isZKsyncError(error, {
8172
+ operation: "zksrpc.getL2ToL1LogProof",
8173
+ messageIncludes: "proof not yet available"
8174
+ });
8175
+ }
8176
+ function shouldRetryRootFetch(error) {
8177
+ if (!isZKsyncError(error)) return false;
8178
+ return error.envelope.operation === OP_INTEROP.svc.status.getRoot;
8179
+ }
8180
+ async function waitForProof(client, l2SrcTxHash, logIndex, blockNumber, pollMs, deadline) {
8181
+ while (true) {
8182
+ if (Date.now() > deadline) {
8183
+ throw createError("TIMEOUT", {
8184
+ resource: "interop",
8185
+ operation: OP_INTEROP.svc.wait.timeout,
8186
+ message: "Timed out waiting for block to be finalized.",
8187
+ context: { l2SrcTxHash, logIndex, blockNumber }
8188
+ });
8189
+ }
8190
+ const finalizedBlock = await client.l2.getBlock("finalized");
8191
+ if (finalizedBlock && BigInt(finalizedBlock.number) >= blockNumber) {
8192
+ break;
8193
+ }
8194
+ await sleep(pollMs);
8195
+ }
8196
+ while (true) {
8197
+ if (Date.now() > deadline) {
8198
+ throw createError("TIMEOUT", {
8199
+ resource: "interop",
8200
+ operation: OP_INTEROP.svc.wait.timeout,
8201
+ message: "Timed out waiting for L2->L1 log proof to become available.",
8202
+ context: { l2SrcTxHash, logIndex }
8203
+ });
8204
+ }
8205
+ try {
8206
+ return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex);
8207
+ } catch (error) {
8208
+ if (!isProofNotReadyError(error)) throw error;
8209
+ }
8210
+ await sleep(pollMs);
8211
+ }
8212
+ }
8213
+ async function waitForRoot(provider, expectedRoot, pollMs, deadline) {
8214
+ while (true) {
8215
+ if (Date.now() > deadline) {
8216
+ throw createError("TIMEOUT", {
8217
+ resource: "interop",
8218
+ operation: OP_INTEROP.svc.wait.timeout,
8219
+ message: "Timed out waiting for interop root to become available.",
8220
+ context: { expectedRoot }
8221
+ });
8222
+ }
8223
+ let interopRoot = null;
8224
+ try {
8225
+ const root = await getInteropRoot(
8226
+ provider,
8227
+ expectedRoot.rootChainId,
8228
+ expectedRoot.batchNumber
8229
+ );
8230
+ if (root !== ZERO_HASH) {
8231
+ interopRoot = root;
8232
+ }
8233
+ } catch (error) {
8234
+ if (!shouldRetryRootFetch(error)) throw error;
8235
+ interopRoot = null;
8236
+ }
8237
+ if (interopRoot) {
8238
+ if (interopRoot.toLowerCase() === expectedRoot.expectedRoot.toLowerCase()) {
8239
+ return;
8240
+ }
8241
+ throw createError("STATE", {
8242
+ resource: "interop",
8243
+ operation: OP_INTEROP.wait,
8244
+ message: "Interop root mismatch.",
8245
+ context: {
8246
+ expected: expectedRoot.expectedRoot,
8247
+ got: interopRoot
8248
+ }
8249
+ });
8250
+ }
8251
+ await sleep(pollMs);
8252
+ }
8253
+ }
8254
+ async function waitForTxReceipt(client, txHash, pollMs, deadline) {
8255
+ while (true) {
8256
+ if (Date.now() > deadline) {
8257
+ throw createError("TIMEOUT", {
8258
+ resource: "interop",
8259
+ operation: OP_INTEROP.svc.wait.timeout,
8260
+ message: "Timed out waiting for source receipt to be available.",
8261
+ context: { txHash }
8262
+ });
8263
+ }
8264
+ const receipt = await wrap4(
8265
+ OP_INTEROP.svc.status.sourceReceipt,
8266
+ () => client.zks.getReceiptWithL2ToL1(txHash),
8267
+ {
8268
+ ctx: { where: "zks.getReceiptWithL2ToL1", txHash },
8269
+ message: "Failed to fetch source L2 receipt (with L2->L1 logs) for interop tx."
8270
+ }
8271
+ );
8272
+ if (receipt) {
8273
+ return receipt;
8274
+ }
8275
+ await sleep(pollMs);
8276
+ }
8277
+ }
8278
+ async function waitForFinalization(client, dstProvider, input, opts) {
8279
+ const { topics, centerIface } = getTopics();
8280
+ const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
8281
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8282
+ const deadline = Date.now() + timeoutMs;
8283
+ const ids = resolveIdsFromWaitable(input);
8284
+ if (!ids.l2SrcTxHash) {
8285
+ throw createError("STATE", {
8286
+ resource: "interop",
8287
+ operation: OP_INTEROP.svc.status.sourceReceipt,
8288
+ message: "Cannot wait for interop finalization: missing l2SrcTxHash.",
8289
+ context: { input }
8290
+ });
8291
+ }
8292
+ const { interopCenter } = await client.ensureAddresses();
8293
+ let bundleInfo = null;
8294
+ while (!bundleInfo) {
8295
+ if (Date.now() > deadline) {
8296
+ throw createError("TIMEOUT", {
8297
+ resource: "interop",
8298
+ operation: OP_INTEROP.svc.wait.timeout,
8299
+ message: "Timed out waiting for source receipt to be available.",
8300
+ context: { l2SrcTxHash: ids.l2SrcTxHash }
8301
+ });
8302
+ }
8303
+ const txReceipt = await waitForTxReceipt(client, ids.l2SrcTxHash, pollMs, deadline);
8304
+ bundleInfo = parseBundleReceiptInfo({
8305
+ rawReceipt: txReceipt,
8306
+ interopCenter,
8307
+ interopBundleSentTopic: topics.interopBundleSent,
8308
+ decodeInteropBundleSent: (log) => decodeInteropBundleSent(centerIface, log),
8309
+ decodeL1MessageData,
8310
+ l2SrcTxHash: ids.l2SrcTxHash
8311
+ });
8312
+ }
8313
+ const proof = await waitForProof(
8314
+ client,
8315
+ ids.l2SrcTxHash,
8316
+ bundleInfo.l2ToL1LogIndex,
8317
+ BigInt(bundleInfo.rawReceipt.blockNumber),
8318
+ pollMs,
8319
+ deadline
8320
+ );
8321
+ const finalizationInfo = buildFinalizationInfo(
8322
+ { l2SrcTxHash: ids.l2SrcTxHash, bundleHash: ids.bundleHash },
8323
+ bundleInfo,
8324
+ proof,
8325
+ bundleInfo.l1MessageData
8326
+ );
8327
+ await waitForRoot(dstProvider, finalizationInfo.expectedRoot, pollMs, deadline);
8328
+ return finalizationInfo;
8329
+ }
8330
+
8331
+ // src/adapters/ethers/resources/interop/services/finalization/status.ts
8332
+ async function getStatus(client, dstProvider, input, opts) {
8333
+ const { topics, centerIface } = getTopics();
8334
+ const baseIds = resolveIdsFromWaitable(input);
8335
+ const enrichedIds = await (async () => {
8336
+ if (baseIds.bundleHash) return baseIds;
8337
+ if (!baseIds.l2SrcTxHash) return baseIds;
8338
+ const { interopCenter } = await client.ensureAddresses();
8339
+ const receipt = await getTxReceipt(client.l2, baseIds.l2SrcTxHash);
8340
+ if (!receipt) return baseIds;
8341
+ const { bundleHash } = parseBundleSentFromReceipt({
8342
+ receipt: { logs: receipt.logs },
8343
+ interopCenter,
8344
+ interopBundleSentTopic: topics.interopBundleSent,
8345
+ decodeInteropBundleSent: (log) => decodeInteropBundleSent(centerIface, log)
8346
+ });
8347
+ return { ...baseIds, bundleHash };
8348
+ })();
8349
+ if (!enrichedIds.bundleHash) {
8350
+ const phase = enrichedIds.l2SrcTxHash ? "SENT" : "UNKNOWN";
5805
8351
  return {
5806
- route: ctx.route,
5807
- summary: {
5808
- route: ctx.route,
5809
- approvalsNeeded: approvals,
5810
- amounts: {
5811
- transfer: { token: p.token, amount: p.amount }
5812
- },
5813
- fees
5814
- },
5815
- steps
8352
+ phase,
8353
+ l2SrcTxHash: enrichedIds.l2SrcTxHash,
8354
+ bundleHash: enrichedIds.bundleHash,
8355
+ dstExecTxHash: enrichedIds.dstExecTxHash
5816
8356
  };
5817
8357
  }
5818
- const finalizeCache = /* @__PURE__ */ new Map();
5819
- const quote = (p) => wrap2(
5820
- OP_WITHDRAWALS.quote,
5821
- async () => {
5822
- const plan = await buildPlan(p);
5823
- return plan.summary;
8358
+ const dstInfo = await getBundleStatus(client, dstProvider, topics, enrichedIds.bundleHash, opts);
8359
+ return {
8360
+ phase: dstInfo.phase,
8361
+ l2SrcTxHash: enrichedIds.l2SrcTxHash,
8362
+ bundleHash: enrichedIds.bundleHash,
8363
+ dstExecTxHash: dstInfo.dstExecTxHash ?? enrichedIds.dstExecTxHash
8364
+ };
8365
+ }
8366
+
8367
+ // src/adapters/ethers/resources/interop/services/finalization/index.ts
8368
+ function createInteropFinalizationServices(client) {
8369
+ return {
8370
+ status(dstProvider, input, opts) {
8371
+ return getStatus(client, dstProvider, input, opts);
5824
8372
  },
5825
- {
5826
- message: "Internal error while preparing a withdrawal quote.",
5827
- ctx: { token: p.token, where: "withdrawals.quote" }
5828
- }
5829
- );
5830
- const tryQuote = (p) => toResult2(
5831
- OP_WITHDRAWALS.tryQuote,
5832
- async () => {
5833
- const plan = await buildPlan(p);
5834
- return plan.summary;
8373
+ wait(dstProvider, input, opts) {
8374
+ return waitForFinalization(client, dstProvider, input, opts);
5835
8375
  },
5836
- {
5837
- message: "Internal error while preparing a withdrawal quote.",
5838
- ctx: { token: p.token, where: "withdrawals.tryQuote" }
8376
+ async finalize(dstProvider, info, opts) {
8377
+ const execResult = await executeBundle(client, dstProvider, info, opts);
8378
+ await execResult.wait();
8379
+ return {
8380
+ bundleHash: info.bundleHash,
8381
+ dstExecTxHash: execResult.hash
8382
+ };
5839
8383
  }
5840
- );
5841
- const prepare = (p) => wrap2(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
5842
- message: "Internal error while preparing a withdrawal plan.",
5843
- ctx: { token: p.token, where: "withdrawals.prepare" }
8384
+ };
8385
+ }
8386
+ function resolveDstProvider(dstChain) {
8387
+ return typeof dstChain === "string" ? new ethers.JsonRpcProvider(dstChain) : dstChain;
8388
+ }
8389
+ function resolveWaitableInput(waitableInput) {
8390
+ const input = waitableInput;
8391
+ return {
8392
+ dstProvider: resolveDstProvider(waitableInput.dstChain),
8393
+ waitable: input.waitable ? input.waitable : waitableInput
8394
+ };
8395
+ }
8396
+
8397
+ // src/adapters/ethers/resources/interop/index.ts
8398
+ var { wrap: wrap5, toResult: toResult2 } = createErrorHandlers("interop");
8399
+ var ROUTES3 = {
8400
+ direct: routeDirect(),
8401
+ indirect: routeIndirect()
8402
+ };
8403
+ function createInteropResource(client, tokens, contracts, attributes) {
8404
+ const svc = createInteropFinalizationServices(client);
8405
+ const tokensResource = createTokensResource(client);
8406
+ const contractsResource = createContractsResource(client);
8407
+ const attributesResource = createEthersAttributesResource();
8408
+ async function buildPlanWithCtx(dstProvider, params) {
8409
+ const ctx = await commonCtx3(
8410
+ dstProvider,
8411
+ params,
8412
+ client,
8413
+ tokensResource,
8414
+ contractsResource,
8415
+ attributesResource
8416
+ );
8417
+ const route = pickInteropRoute({
8418
+ actions: params.actions,
8419
+ ctx: {
8420
+ sender: ctx.sender,
8421
+ srcChainId: ctx.chainId,
8422
+ dstChainId: ctx.dstChainId,
8423
+ baseTokenSrc: ctx.baseTokens.src,
8424
+ baseTokenDst: ctx.baseTokens.dst
8425
+ }
8426
+ });
8427
+ await wrap5(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
8428
+ message: "Interop preflight failed.",
8429
+ ctx: { where: `routes.${route}.preflight` }
8430
+ });
8431
+ const { steps, approvals, quoteExtras } = await wrap5(
8432
+ OP_INTEROP.routes[route].build,
8433
+ () => ROUTES3[route].build(params, ctx),
8434
+ {
8435
+ message: "Failed to build interop route plan.",
8436
+ ctx: { where: `routes.${route}.build` }
8437
+ }
8438
+ );
8439
+ const summary = {
8440
+ route,
8441
+ approvalsNeeded: approvals,
8442
+ totalActionValue: quoteExtras.totalActionValue,
8443
+ bridgedTokenTotal: quoteExtras.bridgedTokenTotal
8444
+ };
8445
+ return { plan: { route, summary, steps }, ctx };
8446
+ }
8447
+ async function buildPlan(dstProvider, params) {
8448
+ const { plan } = await buildPlanWithCtx(dstProvider, params);
8449
+ return plan;
8450
+ }
8451
+ const quote = (params) => wrap5(OP_INTEROP.quote, async () => {
8452
+ const plan = await buildPlan(resolveDstProvider(params.dstChain), params);
8453
+ return plan.summary;
5844
8454
  });
5845
- const tryPrepare = (p) => toResult2(OP_WITHDRAWALS.tryPrepare, () => buildPlan(p), {
5846
- message: "Internal error while preparing a withdrawal plan.",
5847
- ctx: { token: p.token, where: "withdrawals.tryPrepare" }
8455
+ const tryQuote = (params) => toResult2(OP_INTEROP.tryQuote, () => quote(params));
8456
+ const prepare = (params) => wrap5(OP_INTEROP.prepare, () => buildPlan(resolveDstProvider(params.dstChain), params), {
8457
+ message: "Internal error while preparing an interop plan.",
8458
+ ctx: { where: "interop.prepare" }
5848
8459
  });
5849
- const create = (p) => wrap2(
5850
- OP_WITHDRAWALS.create,
8460
+ const tryPrepare = (params) => toResult2(OP_INTEROP.tryPrepare, () => prepare(params));
8461
+ const create = (params) => wrap5(
8462
+ OP_INTEROP.create,
5851
8463
  async () => {
5852
- const plan = await prepare(p);
8464
+ const { plan, ctx } = await buildPlanWithCtx(resolveDstProvider(params.dstChain), params);
8465
+ const signer = ctx.client.signerFor(ctx.client.l2);
8466
+ const srcProvider = ctx.client.l2;
8467
+ const from = await signer.getAddress();
8468
+ let next;
8469
+ if (typeof params.txOverrides?.nonce === "number") {
8470
+ next = params.txOverrides.nonce;
8471
+ } else {
8472
+ const blockTag = params.txOverrides?.nonce ?? "pending";
8473
+ next = await srcProvider.getTransactionCount(from, blockTag);
8474
+ }
5853
8475
  const stepHashes = {};
5854
- const managed = new ethers.NonceManager(client.getL2Signer());
5855
- const from = await managed.getAddress();
5856
- let next = await client.l2.getTransactionCount(from, "pending");
5857
8476
  for (const step of plan.steps) {
5858
8477
  step.tx.nonce = next++;
5859
- if (p.l2TxOverrides) {
5860
- const overrides = p.l2TxOverrides;
5861
- if (overrides.gasLimit != null) step.tx.gasLimit = overrides.gasLimit;
5862
- if (overrides.maxFeePerGas != null) step.tx.maxFeePerGas = overrides.maxFeePerGas;
5863
- if (overrides.maxPriorityFeePerGas != null) {
5864
- step.tx.maxPriorityFeePerGas = overrides.maxPriorityFeePerGas;
5865
- }
8478
+ if (!step.tx.chainId) {
8479
+ step.tx.chainId = Number(ctx.chainId);
5866
8480
  }
5867
8481
  if (!step.tx.gasLimit) {
5868
8482
  try {
5869
- const est = await client.l2.estimateGas(step.tx);
8483
+ const est = await srcProvider.estimateGas({
8484
+ ...step.tx,
8485
+ from
8486
+ });
5870
8487
  step.tx.gasLimit = BigInt(est) * 115n / 100n;
5871
8488
  } catch {
5872
8489
  }
5873
8490
  }
5874
8491
  let hash;
5875
8492
  try {
5876
- const sent = await managed.sendTransaction(step.tx);
8493
+ const sent = await signer.sendTransaction(step.tx);
5877
8494
  hash = sent.hash;
5878
8495
  stepHashes[step.key] = hash;
5879
8496
  const rcpt = await sent.wait();
5880
8497
  if (rcpt?.status === 0) {
5881
8498
  throw createError("EXECUTION", {
5882
- resource: "withdrawals",
5883
- operation: "withdrawals.create.sendTransaction",
5884
- message: "Withdrawal transaction reverted on L2 during a step.",
5885
- context: { step: step.key, txHash: hash, nonce: Number(step.tx.nonce ?? -1) }
8499
+ resource: "interop",
8500
+ operation: "interop.create.sendTransaction",
8501
+ message: "Interop transaction reverted on source L2.",
8502
+ context: { step: step.key, txHash: hash }
5886
8503
  });
5887
8504
  }
5888
8505
  } catch (e) {
5889
- throw toZKsyncError(
5890
- "EXECUTION",
5891
- {
5892
- resource: "withdrawals",
5893
- operation: "withdrawals.create.sendTransaction",
5894
- message: "Failed to send or confirm a withdrawal transaction step.",
5895
- context: { step: step.key, txHash: hash, nonce: Number(step.tx.nonce ?? -1) }
8506
+ if (isZKsyncError(e)) throw e;
8507
+ throw createError("EXECUTION", {
8508
+ resource: "interop",
8509
+ operation: "interop.create.sendTransaction",
8510
+ message: "Failed to send or confirm an interop transaction step.",
8511
+ context: {
8512
+ step: step.key,
8513
+ txHash: hash,
8514
+ nonce: Number(step.tx.nonce ?? -1)
5896
8515
  },
5897
- e
5898
- );
5899
- }
5900
- }
5901
- const keys = Object.keys(stepHashes);
5902
- const l2TxHash = stepHashes[keys[keys.length - 1]];
5903
- return { kind: "withdrawal", l2TxHash, stepHashes, plan };
5904
- },
5905
- {
5906
- message: "Internal error while creating withdrawal transactions.",
5907
- ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.create" }
5908
- }
5909
- );
5910
- const tryCreate = (p) => toResult2(OP_WITHDRAWALS.tryCreate, () => create(p), {
5911
- message: "Internal error while creating withdrawal transactions.",
5912
- ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
5913
- });
5914
- const status = (h) => wrap2(
5915
- OP_WITHDRAWALS.status,
5916
- async () => {
5917
- const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
5918
- if (!l2TxHash || l2TxHash === "0x") {
5919
- return { phase: "UNKNOWN", l2TxHash: "0x" };
5920
- }
5921
- let l2Rcpt;
5922
- try {
5923
- l2Rcpt = await client.l2.getTransactionReceipt(l2TxHash);
5924
- } catch (e) {
5925
- if (isReceiptNotFound(e)) {
5926
- return { phase: "L2_PENDING", l2TxHash };
8516
+ cause: e
8517
+ });
5927
8518
  }
5928
- throw toZKsyncError(
5929
- "RPC",
5930
- {
5931
- resource: "withdrawals",
5932
- operation: "withdrawals.status.getTransactionReceipt",
5933
- message: "Failed to fetch L2 transaction receipt.",
5934
- context: { l2TxHash, where: "l2.getTransactionReceipt" }
5935
- },
5936
- e
5937
- );
5938
- }
5939
- if (!l2Rcpt) return { phase: "L2_PENDING", l2TxHash };
5940
- let pack;
5941
- try {
5942
- pack = await svc.fetchFinalizeDepositParams(l2TxHash);
5943
- } catch {
5944
- return { phase: "PENDING", l2TxHash };
5945
8519
  }
5946
- const key = {
5947
- chainIdL2: pack.params.chainId,
5948
- l2BatchNumber: pack.params.l2BatchNumber,
5949
- l2MessageIndex: pack.params.l2MessageIndex
8520
+ const last = Object.values(stepHashes).pop();
8521
+ return {
8522
+ kind: "interop",
8523
+ dstChain: params.dstChain,
8524
+ stepHashes,
8525
+ plan,
8526
+ l2SrcTxHash: last ?? "0x"
5950
8527
  };
5951
- try {
5952
- const done = await svc.isWithdrawalFinalized(key);
5953
- if (done) return { phase: "FINALIZED", l2TxHash, key };
5954
- } catch {
5955
- }
5956
- const readiness = await svc.simulateFinalizeReadiness(pack.params);
5957
- if (readiness.kind === "FINALIZED") return { phase: "FINALIZED", l2TxHash, key };
5958
- if (readiness.kind === "READY") return { phase: "READY_TO_FINALIZE", l2TxHash, key };
5959
- return { phase: "PENDING", l2TxHash, key };
5960
8528
  },
5961
8529
  {
5962
- message: "Internal error while checking withdrawal status.",
5963
- ctx: { where: "withdrawals.status", l2TxHash: typeof h === "string" ? h : h.l2TxHash }
8530
+ message: "Internal error while creating interop bundle.",
8531
+ ctx: { where: "interop.create" }
5964
8532
  }
5965
8533
  );
5966
- const wait = (h, opts = {
5967
- for: "l2",
5968
- pollMs: 5500
5969
- }) => wrap2(
5970
- OP_WITHDRAWALS.wait,
5971
- async () => {
5972
- const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
5973
- if (!l2Hash || l2Hash === "0x") return null;
5974
- if (opts.for === "l2") {
5975
- let rcpt;
5976
- try {
5977
- rcpt = await client.l2.waitForTransaction(l2Hash);
5978
- } catch (e) {
5979
- throw toZKsyncError(
5980
- "RPC",
5981
- {
5982
- resource: "withdrawals",
5983
- operation: "withdrawals.wait.l2.waitForTransaction",
5984
- message: "Failed while waiting for L2 transaction.",
5985
- context: { l2TxHash: l2Hash }
5986
- },
5987
- e
5988
- );
5989
- }
5990
- if (!rcpt) return null;
5991
- try {
5992
- const raw = await client.zks.getReceiptWithL2ToL1(l2Hash);
5993
- rcpt.l2ToL1Logs = raw?.l2ToL1Logs ?? [];
5994
- } catch {
5995
- rcpt.l2ToL1Logs = rcpt.l2ToL1Logs ?? [];
5996
- }
5997
- return rcpt;
5998
- }
5999
- const poll = Math.max(1e3, opts.pollMs ?? 2500);
6000
- const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : void 0;
6001
- while (true) {
6002
- const s = await status(l2Hash);
6003
- if (opts.for === "ready") {
6004
- if (s.phase === "READY_TO_FINALIZE" || s.phase === "FINALIZED") return null;
6005
- } else {
6006
- if (s.phase === "FINALIZED") {
6007
- const l1Hash = finalizeCache.get(l2Hash);
6008
- if (l1Hash) {
6009
- try {
6010
- const l1Rcpt = await client.l1.getTransactionReceipt(l1Hash);
6011
- if (l1Rcpt) {
6012
- finalizeCache.delete(l2Hash);
6013
- return l1Rcpt;
6014
- }
6015
- } catch {
6016
- }
6017
- }
6018
- return null;
6019
- }
6020
- }
6021
- if (deadline && Date.now() > deadline) return null;
6022
- await new Promise((r) => setTimeout(r, poll));
6023
- }
6024
- },
6025
- {
6026
- message: "Internal error while waiting for withdrawal.",
6027
- ctx: {
6028
- where: "withdrawals.wait",
6029
- l2TxHash: typeof h === "string" ? h : h.l2TxHash,
6030
- for: opts.for
8534
+ const tryCreate = (params) => toResult2(OP_INTEROP.tryCreate, () => create(params));
8535
+ const status = (h, opts) => {
8536
+ const { dstProvider, waitable } = resolveWaitableInput(h);
8537
+ return wrap5(OP_INTEROP.status, () => svc.status(dstProvider, waitable, opts), {
8538
+ message: "Internal error while checking interop status.",
8539
+ ctx: { where: "interop.status" }
8540
+ });
8541
+ };
8542
+ const wait = (h, opts) => {
8543
+ const { dstProvider, waitable } = resolveWaitableInput(h);
8544
+ return wrap5(
8545
+ OP_INTEROP.wait,
8546
+ async () => {
8547
+ const info = await svc.wait(dstProvider, waitable, opts);
8548
+ return { ...info, dstChain: h.dstChain };
8549
+ },
8550
+ {
8551
+ message: "Internal error while waiting for interop finalization.",
8552
+ ctx: { where: "interop.wait" }
6031
8553
  }
6032
- }
6033
- );
6034
- const finalize = (l2TxHash) => wrap2(
6035
- OP_WITHDRAWALS.finalize.send,
8554
+ );
8555
+ };
8556
+ const tryWait = (h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(h, opts));
8557
+ const finalize = (h, opts) => wrap5(
8558
+ OP_INTEROP.finalize,
6036
8559
  async () => {
6037
- const pack = await (async () => {
6038
- try {
6039
- return await svc.fetchFinalizeDepositParams(l2TxHash);
6040
- } catch (e) {
8560
+ if (isInteropFinalizationInfo(h)) {
8561
+ if (h.dstChain == null) {
6041
8562
  throw createError("STATE", {
6042
- resource: "withdrawals",
6043
- operation: OP_WITHDRAWALS.finalize.fetchParams.receipt,
6044
- message: "Withdrawal not ready: finalize params unavailable.",
6045
- context: { l2TxHash },
6046
- cause: e
8563
+ resource: "interop",
8564
+ operation: OP_INTEROP.finalize,
8565
+ message: "Missing dstChain in interop finalization info.",
8566
+ context: { input: h }
6047
8567
  });
6048
8568
  }
6049
- })();
6050
- const { params } = pack;
6051
- const key = {
6052
- chainIdL2: params.chainId,
6053
- l2BatchNumber: params.l2BatchNumber,
6054
- l2MessageIndex: params.l2MessageIndex
6055
- };
6056
- try {
6057
- const done = await svc.isWithdrawalFinalized(key);
6058
- if (done) {
6059
- const statusNow = await status(l2TxHash);
6060
- return { status: statusNow };
6061
- }
6062
- } catch {
6063
- }
6064
- const readiness = await svc.simulateFinalizeReadiness(params);
6065
- if (readiness.kind === "FINALIZED") {
6066
- const statusNow = await status(l2TxHash);
6067
- return { status: statusNow };
6068
- }
6069
- if (readiness.kind === "NOT_READY") {
6070
- throw createError("STATE", {
6071
- resource: "withdrawals",
6072
- operation: OP_WITHDRAWALS.finalize.readiness.simulate,
6073
- message: "Withdrawal not ready to finalize.",
6074
- context: readiness
6075
- });
6076
- }
6077
- try {
6078
- const tx = await svc.finalizeDeposit(params);
6079
- finalizeCache.set(l2TxHash, tx.hash);
6080
- const rcpt = await tx.wait();
6081
- const statusNow = await status(l2TxHash);
6082
- return { status: statusNow, receipt: rcpt };
6083
- } catch (e) {
6084
- const statusNow = await status(l2TxHash);
6085
- if (statusNow.phase === "FINALIZED") return { status: statusNow };
6086
- try {
6087
- const again = await svc.simulateFinalizeReadiness(params);
6088
- if (again.kind === "NOT_READY") {
6089
- throw createError("STATE", {
6090
- resource: "withdrawals",
6091
- operation: OP_WITHDRAWALS.finalize.readiness.simulate,
6092
- message: "Withdrawal not ready to finalize.",
6093
- context: again
6094
- });
6095
- }
6096
- } catch {
6097
- }
6098
- throw e;
8569
+ const dstProvider2 = resolveDstProvider(h.dstChain);
8570
+ return svc.finalize(dstProvider2, h, opts);
6099
8571
  }
8572
+ const { dstProvider, waitable } = resolveWaitableInput(h);
8573
+ const info = await svc.wait(dstProvider, waitable);
8574
+ return svc.finalize(dstProvider, info, opts);
6100
8575
  },
6101
8576
  {
6102
- message: "Internal error while attempting to finalize withdrawal.",
6103
- ctx: { l2TxHash, where: "withdrawals.finalize" }
6104
- }
6105
- );
6106
- const tryFinalize = (l2TxHash) => toResult2("withdrawals.tryFinalize", () => finalize(l2TxHash), {
6107
- message: "Internal error while attempting to tryFinalize withdrawal.",
6108
- ctx: { l2TxHash, where: "withdrawals.tryFinalize" }
6109
- });
6110
- const tryWait = (h, opts) => toResult2(
6111
- OP_WITHDRAWALS.tryWait,
6112
- async () => {
6113
- const v = await wait(h, opts);
6114
- if (v) return v;
6115
- throw createError("STATE", {
6116
- resource: "withdrawals",
6117
- operation: "withdrawals.tryWait",
6118
- message: opts.for === "l2" ? "No L2 receipt yet; the withdrawal has not executed on L2." : "No L1 receipt yet; the withdrawal has not been included on L1.",
6119
- context: {
6120
- for: opts.for,
6121
- l2TxHash: typeof h === "string" ? h : "l2TxHash" in h ? h.l2TxHash : void 0,
6122
- where: "withdrawals.tryWait"
6123
- }
6124
- });
6125
- },
6126
- {
6127
- message: "Internal error while waiting for withdrawal.",
6128
- ctx: { input: h, for: opts?.for, where: "withdrawals.tryWait" }
8577
+ message: "Failed to finalize/execute interop bundle on destination.",
8578
+ ctx: { where: "interop.finalize" }
6129
8579
  }
6130
8580
  );
8581
+ const tryFinalize = (h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(h, opts));
6131
8582
  return {
6132
8583
  quote,
6133
8584
  tryQuote,
@@ -6137,9 +8588,9 @@ function createWithdrawalsResource(client, tokens, contracts) {
6137
8588
  tryCreate,
6138
8589
  status,
6139
8590
  wait,
8591
+ tryWait,
6140
8592
  finalize,
6141
- tryFinalize,
6142
- tryWait
8593
+ tryFinalize
6143
8594
  };
6144
8595
  }
6145
8596
 
@@ -6147,11 +8598,13 @@ function createWithdrawalsResource(client, tokens, contracts) {
6147
8598
  function createEthersSdk(client) {
6148
8599
  const tokens = createTokensResource(client);
6149
8600
  const contracts = createContractsResource(client);
8601
+ const interop = createInteropResource(client);
6150
8602
  return {
6151
8603
  deposits: createDepositsResource(client, tokens, contracts),
6152
8604
  withdrawals: createWithdrawalsResource(client, tokens, contracts),
6153
8605
  tokens,
6154
- contracts
8606
+ contracts,
8607
+ interop
6155
8608
  };
6156
8609
  }
6157
8610