@matterlabs/zksync-js 0.0.14 → 0.0.16
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.
- package/dist/adapters/ethers/client.cjs.map +1 -1
- package/dist/adapters/ethers/client.d.ts +1 -2
- package/dist/adapters/ethers/client.js +4 -5
- package/dist/adapters/ethers/index.cjs +200 -32
- package/dist/adapters/ethers/index.cjs.map +1 -1
- package/dist/adapters/ethers/index.js +6 -7
- package/dist/adapters/ethers/resources/interop/index.d.ts +4 -1
- package/dist/adapters/ethers/resources/interop/services/finalization/bundle.d.ts +4 -0
- package/dist/adapters/ethers/resources/interop/services/gas.d.ts +12 -0
- package/dist/adapters/ethers/sdk.cjs +200 -32
- package/dist/adapters/ethers/sdk.cjs.map +1 -1
- package/dist/adapters/ethers/sdk.js +5 -6
- package/dist/adapters/viem/client.cjs +787 -3
- package/dist/adapters/viem/client.cjs.map +1 -1
- package/dist/adapters/viem/client.d.ts +6 -1
- package/dist/adapters/viem/client.js +4 -5
- package/dist/adapters/viem/index.cjs +6502 -2966
- package/dist/adapters/viem/index.cjs.map +1 -1
- package/dist/adapters/viem/index.d.ts +5 -0
- package/dist/adapters/viem/index.js +6 -7
- package/dist/adapters/viem/resources/interop/address.d.ts +18 -0
- package/dist/adapters/viem/resources/interop/attributes/resource.d.ts +6 -0
- package/dist/adapters/viem/resources/interop/context.d.ts +31 -0
- package/dist/adapters/viem/resources/interop/index.d.ts +65 -0
- package/dist/adapters/viem/resources/interop/resolvers.d.ts +4 -0
- package/dist/adapters/viem/resources/interop/routes/direct.d.ts +2 -0
- package/dist/adapters/viem/resources/interop/routes/indirect.d.ts +2 -0
- package/dist/adapters/viem/resources/interop/routes/types.d.ts +23 -0
- package/dist/adapters/viem/resources/interop/services/erc20.d.ts +25 -0
- package/dist/adapters/viem/resources/interop/services/fee.d.ts +12 -0
- package/dist/adapters/viem/resources/interop/services/finalization/bundle.d.ts +19 -0
- package/dist/adapters/viem/resources/interop/services/finalization/data-fetchers.d.ts +17 -0
- package/dist/adapters/viem/resources/interop/services/finalization/decoders.d.ts +11 -0
- package/dist/adapters/viem/resources/interop/services/finalization/index.d.ts +13 -0
- package/dist/adapters/viem/resources/interop/services/finalization/polling.d.ts +7 -0
- package/dist/adapters/viem/resources/interop/services/finalization/status.d.ts +5 -0
- package/dist/adapters/viem/resources/interop/services/finalization/topics.d.ts +4 -0
- package/dist/adapters/viem/resources/interop/services/gas.d.ts +12 -0
- package/dist/adapters/viem/resources/interop/services/starter-data.d.ts +6 -0
- package/dist/adapters/viem/resources/interop/types.d.ts +8 -0
- package/dist/adapters/viem/sdk.cjs +6665 -3173
- package/dist/adapters/viem/sdk.cjs.map +1 -1
- package/dist/adapters/viem/sdk.d.ts +8 -1
- package/dist/adapters/viem/sdk.js +5 -5
- package/dist/{chunk-7CAVFIMW.js → chunk-24TE2NNJ.js} +2 -3
- package/dist/{chunk-75IOOODG.js → chunk-3KH5PCD6.js} +1233 -31
- package/dist/{chunk-XKRNLFET.js → chunk-5HG2DUYW.js} +150 -375
- package/dist/{chunk-OTXPSNNC.js → chunk-CK5UFAZK.js} +64 -7
- package/dist/{chunk-HP3EWKJL.js → chunk-JSBMIT4S.js} +1 -1
- package/dist/{chunk-5RRJDPAJ.js → chunk-NJK325XV.js} +2 -2
- package/dist/chunk-NLUCYVMX.js +658 -0
- package/dist/{chunk-XDRCN4FC.js → chunk-SBGBYZJM.js} +10 -2
- package/dist/{chunk-J47RI3G7.js → chunk-TYYUG5GA.js} +1 -1
- package/dist/{chunk-JY62QO3W.js → chunk-UEKFQAOS.js} +420 -6
- package/dist/core/index.js +2 -3
- package/dist/core/resources/deposits/chains.d.ts +1 -0
- package/dist/core/resources/deposits/gas.d.ts +7 -0
- package/dist/core/resources/deposits/priority.d.ts +4 -0
- package/dist/core/resources/interop/protocol.d.ts +3 -0
- package/dist/core/types/errors.d.ts +1 -0
- package/dist/core/types/flows/interop.d.ts +0 -2
- package/dist/core/types/primitives.d.ts +2 -0
- package/dist/index.js +2 -3
- package/package.json +1 -1
- package/dist/chunk-DYJKK5FW.js +0 -417
- package/dist/chunk-EOBXYHTZ.js +0 -265
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createNTVCodec, toGasOverrides, buildFeeBreakdown, derivePriorityTxGasBreakdown, quoteL2Gas, quoteL1Gas, derivePriorityBodyGasEstimateCap, quoteL2Gas2 } from './chunk-
|
|
1
|
+
import { createNTVCodec, resolveCreateDepositL1GasLimit, isInteropFinalizationInfo, DEFAULT_POLL_MS, DEFAULT_TIMEOUT_MS, resolveIdsFromWaitable, parseBundleReceiptInfo, buildFinalizationInfo, parseBundleSentFromReceipt, createAttributesResource, pickInteropRoute, ZERO_HASH, toGasOverrides, applyPriorityL2GasLimitBuffer, buildFeeBreakdown, buildIndirectBundle, preflightIndirect, buildDirectBundle, preflightDirect, derivePriorityTxGasBreakdown, quoteL2Gas, quoteL1Gas, derivePriorityBodyGasEstimateCap, quoteL2Gas2, assertProtocolVersion } from './chunk-NLUCYVMX.js';
|
|
2
2
|
import { findL1MessageSentLog, messengerLogIndex, pickWithdrawRoute } from './chunk-3HHUZXSV.js';
|
|
3
|
-
import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-
|
|
4
|
-
import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, IBridgehub_default, isETH, normalizeAddrEq, L2NativeTokenVault_default, IL2AssetRouter_default, IBaseToken_default } from './chunk-
|
|
5
|
-
import { ETH_ADDRESS, TOPIC_CANONICAL_ASSIGNED, TOPIC_CANONICAL_SUCCESS, L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, SAFE_L1_BRIDGE_GAS, L2_ASSET_ROUTER_ADDRESS, FORMAL_ETH_ADDRESS } from './chunk-MT4X5FEO.js';
|
|
6
|
-
import { keccak256, encodeAbiParameters, concat, decodeEventLog, decodeAbiParameters, encodeFunctionData, zeroAddress } from 'viem';
|
|
3
|
+
import { createErrorHandlers, toZKsyncError, classifyReadinessFromRevert } from './chunk-SBGBYZJM.js';
|
|
4
|
+
import { isHash66, IL1Nullifier_default, OP_WITHDRAWALS, createError, normalizeL1Token, isAddressEq, hexEq, OP_DEPOSITS, IERC20_default, isZKsyncError, isReceiptNotFound, OP_INTEROP, IInteropHandler_default, IInteropRootStorage_default, IInteropCenter_default, sleep, IERC7786Attributes_default, IBridgehub_default, isETH, normalizeAddrEq, L2NativeTokenVault_default, IL2AssetRouter_default, IBaseToken_default, assertNever } from './chunk-UEKFQAOS.js';
|
|
5
|
+
import { ETH_ADDRESS, TOPIC_CANONICAL_ASSIGNED, TOPIC_CANONICAL_SUCCESS, L1_MESSENGER_ADDRESS, L2_BASE_TOKEN_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, L2_INTEROP_ROOT_STORAGE_ADDRESS, BUFFER, SAFE_L1_BRIDGE_GAS, L2_ASSET_ROUTER_ADDRESS, FORMAL_ETH_ADDRESS } from './chunk-MT4X5FEO.js';
|
|
6
|
+
import { keccak256, encodeAbiParameters, toBytes, concat, decodeEventLog, decodeAbiParameters, createWalletClient, custom, encodeFunctionData, createPublicClient, http, encodeEventTopics, numberToHex, zeroAddress, getAddress, toHex } from 'viem';
|
|
7
7
|
|
|
8
8
|
// src/adapters/viem/resources/deposits/context.ts
|
|
9
9
|
async function commonCtx(p, client, tokens, contracts) {
|
|
@@ -372,7 +372,10 @@ function routeEthDirect() {
|
|
|
372
372
|
l2Calldata,
|
|
373
373
|
gasPerPubdata: ctx.gasPerPubdata
|
|
374
374
|
});
|
|
375
|
-
const quotedL2GasLimit = ctx.l2GasLimit ??
|
|
375
|
+
const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
|
|
376
|
+
chainIdL2: ctx.chainIdL2,
|
|
377
|
+
gasLimit: priorityFloorBreakdown.derivedL2GasLimit
|
|
378
|
+
});
|
|
376
379
|
const l2GasParams = await quoteL2Gas3({
|
|
377
380
|
ctx,
|
|
378
381
|
route: "eth-base",
|
|
@@ -425,12 +428,21 @@ function routeEthDirect() {
|
|
|
425
428
|
tx: l1TxCandidate,
|
|
426
429
|
overrides: ctx.gasOverrides
|
|
427
430
|
});
|
|
431
|
+
let bridgeTx = { ...sim.request };
|
|
432
|
+
if (l1Gas) {
|
|
433
|
+
bridgeTx = {
|
|
434
|
+
...bridgeTx,
|
|
435
|
+
gas: l1Gas.gasLimit,
|
|
436
|
+
maxFeePerGas: l1Gas.maxFeePerGas,
|
|
437
|
+
maxPriorityFeePerGas: l1Gas.maxPriorityFeePerGas
|
|
438
|
+
};
|
|
439
|
+
}
|
|
428
440
|
const steps = [
|
|
429
441
|
{
|
|
430
442
|
key: "bridgehub:direct",
|
|
431
443
|
kind: "bridgehub:direct",
|
|
432
444
|
description: "Bridge ETH via Bridgehub.requestL2TransactionDirect",
|
|
433
|
-
tx:
|
|
445
|
+
tx: bridgeTx
|
|
434
446
|
}
|
|
435
447
|
];
|
|
436
448
|
const fees = buildFeeBreakdown({
|
|
@@ -491,7 +503,10 @@ async function getPriorityGasModel(input) {
|
|
|
491
503
|
gasPerPubdata: input.ctx.gasPerPubdata
|
|
492
504
|
});
|
|
493
505
|
const model = {
|
|
494
|
-
priorityFloorGasLimit:
|
|
506
|
+
priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
|
|
507
|
+
chainIdL2: input.ctx.chainIdL2,
|
|
508
|
+
gasLimit: priorityFloorBreakdown.derivedL2GasLimit
|
|
509
|
+
})
|
|
495
510
|
};
|
|
496
511
|
if (isFirstBridge || input.ctx.resolvedToken.l2.toLowerCase() === zeroAddress) {
|
|
497
512
|
model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
|
|
@@ -780,7 +795,10 @@ async function getPriorityGasModel2(input) {
|
|
|
780
795
|
gasPerPubdata: input.ctx.gasPerPubdata
|
|
781
796
|
});
|
|
782
797
|
const model = {
|
|
783
|
-
priorityFloorGasLimit:
|
|
798
|
+
priorityFloorGasLimit: applyPriorityL2GasLimitBuffer({
|
|
799
|
+
chainIdL2: input.ctx.chainIdL2,
|
|
800
|
+
gasLimit: priorityFloorBreakdown.derivedL2GasLimit
|
|
801
|
+
})
|
|
784
802
|
};
|
|
785
803
|
if (input.ctx.resolvedToken.l2.toLowerCase() === zeroAddress) {
|
|
786
804
|
model.undeployedGasLimit = derivePriorityBodyGasEstimateCap({
|
|
@@ -916,7 +934,7 @@ function routeEthNonBase() {
|
|
|
916
934
|
const requestStruct = {
|
|
917
935
|
chainId: ctx.chainIdL2,
|
|
918
936
|
mintValue,
|
|
919
|
-
l2Value:
|
|
937
|
+
l2Value: 0n,
|
|
920
938
|
l2GasLimit: l2Gas.gasLimit,
|
|
921
939
|
l2GasPerPubdataByteLimit: ctx.gasPerPubdata,
|
|
922
940
|
refundRecipient: ctx.refundRecipient,
|
|
@@ -924,6 +942,7 @@ function routeEthNonBase() {
|
|
|
924
942
|
secondBridgeValue: p.amount,
|
|
925
943
|
secondBridgeCalldata
|
|
926
944
|
};
|
|
945
|
+
const bridgehubValue = p.amount;
|
|
927
946
|
let bridgeTx;
|
|
928
947
|
let calldata;
|
|
929
948
|
if (needsApprove) {
|
|
@@ -932,8 +951,7 @@ function routeEthNonBase() {
|
|
|
932
951
|
abi: IBridgehub_default,
|
|
933
952
|
functionName: "requestL2TransactionTwoBridges",
|
|
934
953
|
args: [requestStruct],
|
|
935
|
-
value:
|
|
936
|
-
// base ≠ ETH ⇒ msg.value == secondBridgeValue
|
|
954
|
+
value: bridgehubValue,
|
|
937
955
|
account: ctx.client.account
|
|
938
956
|
};
|
|
939
957
|
calldata = encodeFunctionData({
|
|
@@ -950,7 +968,7 @@ function routeEthNonBase() {
|
|
|
950
968
|
abi: IBridgehub_default,
|
|
951
969
|
functionName: "requestL2TransactionTwoBridges",
|
|
952
970
|
args: [requestStruct],
|
|
953
|
-
value:
|
|
971
|
+
value: bridgehubValue,
|
|
954
972
|
account: ctx.client.account
|
|
955
973
|
}),
|
|
956
974
|
{
|
|
@@ -968,7 +986,7 @@ function routeEthNonBase() {
|
|
|
968
986
|
const l1TxCandidate = {
|
|
969
987
|
to: ctx.bridgehub,
|
|
970
988
|
data: calldata,
|
|
971
|
-
value:
|
|
989
|
+
value: bridgehubValue,
|
|
972
990
|
from: ctx.sender,
|
|
973
991
|
...ctx.gasOverrides
|
|
974
992
|
};
|
|
@@ -1047,7 +1065,10 @@ function routeErc20Base() {
|
|
|
1047
1065
|
l2Calldata,
|
|
1048
1066
|
gasPerPubdata: ctx.gasPerPubdata
|
|
1049
1067
|
});
|
|
1050
|
-
const quotedL2GasLimit = ctx.l2GasLimit ??
|
|
1068
|
+
const quotedL2GasLimit = ctx.l2GasLimit ?? applyPriorityL2GasLimitBuffer({
|
|
1069
|
+
chainIdL2: ctx.chainIdL2,
|
|
1070
|
+
gasLimit: priorityFloorBreakdown.derivedL2GasLimit
|
|
1071
|
+
});
|
|
1051
1072
|
const l2Gas = await quoteL2Gas3({
|
|
1052
1073
|
ctx,
|
|
1053
1074
|
route: "erc20-base",
|
|
@@ -1579,6 +1600,7 @@ function createDepositsResource(client, tokens, contracts) {
|
|
|
1579
1600
|
async () => {
|
|
1580
1601
|
const plan = await prepare(p);
|
|
1581
1602
|
const stepHashes = {};
|
|
1603
|
+
const chainIdL2 = BigInt(await client.l2.getChainId());
|
|
1582
1604
|
const from = client.account.address;
|
|
1583
1605
|
let next;
|
|
1584
1606
|
if (typeof p.l1TxOverrides?.nonce === "number") {
|
|
@@ -1630,6 +1652,7 @@ function createDepositsResource(client, tokens, contracts) {
|
|
|
1630
1652
|
}
|
|
1631
1653
|
if (!p.l1TxOverrides?.gasLimit) {
|
|
1632
1654
|
try {
|
|
1655
|
+
const preparedGasLimit = step.tx.gas;
|
|
1633
1656
|
const feePart = step.tx.maxFeePerGas != null && step.tx.maxPriorityFeePerGas != null ? {
|
|
1634
1657
|
maxFeePerGas: step.tx.maxFeePerGas,
|
|
1635
1658
|
maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas
|
|
@@ -1648,7 +1671,12 @@ function createDepositsResource(client, tokens, contracts) {
|
|
|
1648
1671
|
const gas = await client.l1.estimateContractGas({
|
|
1649
1672
|
...params
|
|
1650
1673
|
});
|
|
1651
|
-
step.tx.gas =
|
|
1674
|
+
step.tx.gas = resolveCreateDepositL1GasLimit({
|
|
1675
|
+
chainIdL2,
|
|
1676
|
+
stepKey: step.key,
|
|
1677
|
+
preparedGasLimit,
|
|
1678
|
+
estimatedGasLimit: gas
|
|
1679
|
+
});
|
|
1652
1680
|
} catch {
|
|
1653
1681
|
}
|
|
1654
1682
|
}
|
|
@@ -2409,7 +2437,7 @@ var ROUTES2 = {
|
|
|
2409
2437
|
};
|
|
2410
2438
|
function createWithdrawalsResource(client, tokens, contracts) {
|
|
2411
2439
|
const svc = createFinalizationServices(client);
|
|
2412
|
-
const { wrap:
|
|
2440
|
+
const { wrap: wrap7, toResult: toResult3 } = createErrorHandlers("withdrawals");
|
|
2413
2441
|
const tokensResource = tokens ?? createTokensResource(client);
|
|
2414
2442
|
const contractsResource = contracts ?? createContractsResource(client);
|
|
2415
2443
|
async function buildPlan(p) {
|
|
@@ -2430,23 +2458,23 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2430
2458
|
};
|
|
2431
2459
|
}
|
|
2432
2460
|
const finalizeCache = /* @__PURE__ */ new Map();
|
|
2433
|
-
const quote = (p) =>
|
|
2461
|
+
const quote = (p) => wrap7(OP_WITHDRAWALS.quote, async () => (await buildPlan(p)).summary, {
|
|
2434
2462
|
message: "Internal error while preparing a withdrawal quote.",
|
|
2435
2463
|
ctx: { token: p.token, where: "withdrawals.quote" }
|
|
2436
2464
|
});
|
|
2437
|
-
const tryQuote = (p) =>
|
|
2465
|
+
const tryQuote = (p) => toResult3(OP_WITHDRAWALS.tryQuote, () => quote(p), {
|
|
2438
2466
|
message: "Internal error while preparing a withdrawal quote.",
|
|
2439
2467
|
ctx: { token: p.token, where: "withdrawals.tryQuote" }
|
|
2440
2468
|
});
|
|
2441
|
-
const prepare = (p) =>
|
|
2469
|
+
const prepare = (p) => wrap7(OP_WITHDRAWALS.prepare, () => buildPlan(p), {
|
|
2442
2470
|
message: "Internal error while preparing a withdrawal plan.",
|
|
2443
2471
|
ctx: { token: p.token, where: "withdrawals.prepare" }
|
|
2444
2472
|
});
|
|
2445
|
-
const tryPrepare = (p) =>
|
|
2473
|
+
const tryPrepare = (p) => toResult3(OP_WITHDRAWALS.tryPrepare, () => prepare(p), {
|
|
2446
2474
|
message: "Internal error while preparing a withdrawal plan.",
|
|
2447
2475
|
ctx: { token: p.token, where: "withdrawals.tryPrepare" }
|
|
2448
2476
|
});
|
|
2449
|
-
const create = (p) =>
|
|
2477
|
+
const create = (p) => wrap7(
|
|
2450
2478
|
OP_WITHDRAWALS.create,
|
|
2451
2479
|
async () => {
|
|
2452
2480
|
const plan = await prepare(p);
|
|
@@ -2550,11 +2578,11 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2550
2578
|
ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.create" }
|
|
2551
2579
|
}
|
|
2552
2580
|
);
|
|
2553
|
-
const tryCreate = (p) =>
|
|
2581
|
+
const tryCreate = (p) => toResult3(OP_WITHDRAWALS.tryCreate, () => create(p), {
|
|
2554
2582
|
message: "Internal error while creating withdrawal transactions.",
|
|
2555
2583
|
ctx: { token: p.token, amount: p.amount, to: p.to, where: "withdrawals.tryCreate" }
|
|
2556
2584
|
});
|
|
2557
|
-
const status = (h) =>
|
|
2585
|
+
const status = (h) => wrap7(
|
|
2558
2586
|
OP_WITHDRAWALS.status,
|
|
2559
2587
|
async () => {
|
|
2560
2588
|
const l2TxHash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
|
|
@@ -2609,7 +2637,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2609
2637
|
const wait = (h, opts = {
|
|
2610
2638
|
for: "l2",
|
|
2611
2639
|
pollMs: 5500
|
|
2612
|
-
}) =>
|
|
2640
|
+
}) => wrap7(
|
|
2613
2641
|
OP_WITHDRAWALS.wait,
|
|
2614
2642
|
async () => {
|
|
2615
2643
|
const l2Hash = typeof h === "string" ? h : "l2TxHash" in h && h.l2TxHash ? h.l2TxHash : "0x";
|
|
@@ -2678,7 +2706,7 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2678
2706
|
}
|
|
2679
2707
|
}
|
|
2680
2708
|
);
|
|
2681
|
-
const finalize = (l2TxHash) =>
|
|
2709
|
+
const finalize = (l2TxHash) => wrap7(
|
|
2682
2710
|
OP_WITHDRAWALS.finalize.send,
|
|
2683
2711
|
async () => {
|
|
2684
2712
|
const pack = await (async () => {
|
|
@@ -2750,11 +2778,11 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2750
2778
|
ctx: { l2TxHash, where: "withdrawals.finalize" }
|
|
2751
2779
|
}
|
|
2752
2780
|
);
|
|
2753
|
-
const tryFinalize = (l2TxHash) =>
|
|
2781
|
+
const tryFinalize = (l2TxHash) => toResult3("withdrawals.tryFinalize", () => finalize(l2TxHash), {
|
|
2754
2782
|
message: "Internal error while attempting to tryFinalize withdrawal.",
|
|
2755
2783
|
ctx: { l2TxHash, where: "withdrawals.tryFinalize" }
|
|
2756
2784
|
});
|
|
2757
|
-
const tryWait = (h, opts) =>
|
|
2785
|
+
const tryWait = (h, opts) => toResult3(
|
|
2758
2786
|
OP_WITHDRAWALS.tryWait,
|
|
2759
2787
|
async () => {
|
|
2760
2788
|
const v = await wait(h, opts);
|
|
@@ -2789,17 +2817,1191 @@ function createWithdrawalsResource(client, tokens, contracts) {
|
|
|
2789
2817
|
tryWait
|
|
2790
2818
|
};
|
|
2791
2819
|
}
|
|
2820
|
+
function getInteropAttributes(params, ctx) {
|
|
2821
|
+
const bundleAttributes = [];
|
|
2822
|
+
if (params.execution?.only) {
|
|
2823
|
+
bundleAttributes.push(ctx.attributes.bundle.executionAddress(params.execution.only));
|
|
2824
|
+
}
|
|
2825
|
+
if (params.unbundling?.by) {
|
|
2826
|
+
bundleAttributes.push(ctx.attributes.bundle.unbundlerAddress(params.unbundling.by));
|
|
2827
|
+
}
|
|
2828
|
+
bundleAttributes.push(ctx.attributes.bundle.useFixedFee(params.fee?.useFixed ?? false));
|
|
2829
|
+
const callAttributes = params.actions.map((action) => {
|
|
2830
|
+
switch (action.type) {
|
|
2831
|
+
case "sendNative": {
|
|
2832
|
+
const baseMatches = ctx.baseTokens.src.toLowerCase() === ctx.baseTokens.dst.toLowerCase();
|
|
2833
|
+
if (baseMatches) {
|
|
2834
|
+
return [ctx.attributes.call.interopCallValue(action.amount)];
|
|
2835
|
+
}
|
|
2836
|
+
return [ctx.attributes.call.indirectCall(action.amount)];
|
|
2837
|
+
}
|
|
2838
|
+
case "call":
|
|
2839
|
+
if (action.value && action.value > 0n) {
|
|
2840
|
+
return [ctx.attributes.call.interopCallValue(action.value)];
|
|
2841
|
+
}
|
|
2842
|
+
return [];
|
|
2843
|
+
case "sendErc20":
|
|
2844
|
+
return [ctx.attributes.call.indirectCall(0n)];
|
|
2845
|
+
default:
|
|
2846
|
+
assertNever(action);
|
|
2847
|
+
}
|
|
2848
|
+
});
|
|
2849
|
+
return { bundleAttributes, callAttributes };
|
|
2850
|
+
}
|
|
2851
|
+
function createViemAttributesResource() {
|
|
2852
|
+
const encode = (fn, args) => encodeFunctionData({
|
|
2853
|
+
abi: IERC7786Attributes_default,
|
|
2854
|
+
functionName: fn,
|
|
2855
|
+
args
|
|
2856
|
+
});
|
|
2857
|
+
return createAttributesResource({ encode });
|
|
2858
|
+
}
|
|
2859
|
+
var PREFIX_EVM_CHAIN = toBytes("0x00010000");
|
|
2860
|
+
var PREFIX_EVM_ADDRESS = toBytes("0x000100000014");
|
|
2861
|
+
function formatInteropEvmChain(chainId) {
|
|
2862
|
+
const chainRef = toBytes(toHex(chainId));
|
|
2863
|
+
const chainRefLength = toBytes(toHex(chainRef.length, { size: 1 }));
|
|
2864
|
+
const payload = concat([PREFIX_EVM_CHAIN, chainRefLength, chainRef, new Uint8Array([0])]);
|
|
2865
|
+
return toHex(payload);
|
|
2866
|
+
}
|
|
2867
|
+
function formatInteropEvmAddress(address) {
|
|
2868
|
+
const normalized = getAddress(address);
|
|
2869
|
+
const addrBytes = toBytes(normalized);
|
|
2870
|
+
const payload = concat([PREFIX_EVM_ADDRESS, addrBytes]);
|
|
2871
|
+
return toHex(payload);
|
|
2872
|
+
}
|
|
2873
|
+
var interopCodec = {
|
|
2874
|
+
formatChain: formatInteropEvmChain,
|
|
2875
|
+
formatAddress: formatInteropEvmAddress
|
|
2876
|
+
};
|
|
2877
|
+
function getErc20Tokens(params) {
|
|
2878
|
+
const erc20Tokens = /* @__PURE__ */ new Map();
|
|
2879
|
+
for (const action of params.actions) {
|
|
2880
|
+
if (action.type !== "sendErc20") continue;
|
|
2881
|
+
erc20Tokens.set(action.token.toLowerCase(), action.token);
|
|
2882
|
+
}
|
|
2883
|
+
return Array.from(erc20Tokens.values());
|
|
2884
|
+
}
|
|
2885
|
+
function buildEnsureTokenSteps(erc20Tokens, ctx) {
|
|
2886
|
+
if (erc20Tokens.length === 0) return [];
|
|
2887
|
+
return erc20Tokens.map((token) => ({
|
|
2888
|
+
key: `ensure-token:${token.toLowerCase()}`,
|
|
2889
|
+
kind: "interop.ntv.ensure-token",
|
|
2890
|
+
description: `Ensure ${token} is registered in the native token vault`,
|
|
2891
|
+
tx: {
|
|
2892
|
+
to: ctx.l2NativeTokenVault,
|
|
2893
|
+
data: encodeFunctionData({
|
|
2894
|
+
abi: L2NativeTokenVault_default,
|
|
2895
|
+
functionName: "ensureTokenIsRegistered",
|
|
2896
|
+
args: [token]
|
|
2897
|
+
}),
|
|
2898
|
+
...ctx.gasOverrides
|
|
2899
|
+
}
|
|
2900
|
+
}));
|
|
2901
|
+
}
|
|
2902
|
+
async function buildApproveSteps(approvals, ctx) {
|
|
2903
|
+
const steps = [];
|
|
2904
|
+
for (const approval of approvals) {
|
|
2905
|
+
const currentAllowance = await ctx.client.l2.readContract({
|
|
2906
|
+
address: approval.token,
|
|
2907
|
+
abi: IERC20_default,
|
|
2908
|
+
functionName: "allowance",
|
|
2909
|
+
args: [ctx.sender, approval.spender]
|
|
2910
|
+
});
|
|
2911
|
+
if (currentAllowance < approval.amount) {
|
|
2912
|
+
steps.push({
|
|
2913
|
+
key: `approve:${approval.token}:${approval.spender}`,
|
|
2914
|
+
kind: "approve",
|
|
2915
|
+
description: `Approve ${approval.spender} to spend ${approval.amount} of ${approval.token}`,
|
|
2916
|
+
tx: {
|
|
2917
|
+
to: approval.token,
|
|
2918
|
+
data: encodeFunctionData({
|
|
2919
|
+
abi: IERC20_default,
|
|
2920
|
+
functionName: "approve",
|
|
2921
|
+
args: [approval.spender, approval.amount]
|
|
2922
|
+
}),
|
|
2923
|
+
...ctx.gasOverrides
|
|
2924
|
+
}
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return steps;
|
|
2929
|
+
}
|
|
2930
|
+
async function resolveErc20AssetIds(erc20Tokens, ctx) {
|
|
2931
|
+
const assetIds = /* @__PURE__ */ new Map();
|
|
2932
|
+
if (erc20Tokens.length === 0) return assetIds;
|
|
2933
|
+
for (const token of erc20Tokens) {
|
|
2934
|
+
const { result: assetId } = await ctx.client.l2.simulateContract({
|
|
2935
|
+
address: ctx.l2NativeTokenVault,
|
|
2936
|
+
abi: L2NativeTokenVault_default,
|
|
2937
|
+
functionName: "ensureTokenIsRegistered",
|
|
2938
|
+
args: [token],
|
|
2939
|
+
account: ctx.sender
|
|
2940
|
+
});
|
|
2941
|
+
assetIds.set(token.toLowerCase(), assetId);
|
|
2942
|
+
}
|
|
2943
|
+
return assetIds;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
// src/adapters/viem/resources/interop/services/starter-data.ts
|
|
2947
|
+
async function getStarterData(params, ctx, erc20AssetIds) {
|
|
2948
|
+
const starterData = [];
|
|
2949
|
+
for (const action of params.actions) {
|
|
2950
|
+
switch (action.type) {
|
|
2951
|
+
case "sendErc20": {
|
|
2952
|
+
const assetId = erc20AssetIds.get(action.token.toLowerCase());
|
|
2953
|
+
if (!assetId) {
|
|
2954
|
+
throw new Error(`Missing precomputed assetId for token ${action.token}.`);
|
|
2955
|
+
}
|
|
2956
|
+
const transferData = encodeNativeTokenVaultTransferData(
|
|
2957
|
+
action.amount,
|
|
2958
|
+
action.to,
|
|
2959
|
+
action.token
|
|
2960
|
+
);
|
|
2961
|
+
const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
|
|
2962
|
+
starterData.push({ assetRouterPayload });
|
|
2963
|
+
break;
|
|
2964
|
+
}
|
|
2965
|
+
case "sendNative":
|
|
2966
|
+
if (!ctx.baseTokens.matches) {
|
|
2967
|
+
const assetId = await ctx.tokens.baseTokenAssetId();
|
|
2968
|
+
const transferData = encodeNativeTokenVaultTransferData(
|
|
2969
|
+
action.amount,
|
|
2970
|
+
action.to,
|
|
2971
|
+
ctx.baseTokens.src
|
|
2972
|
+
);
|
|
2973
|
+
const assetRouterPayload = encodeSecondBridgeDataV1(assetId, transferData);
|
|
2974
|
+
starterData.push({ assetRouterPayload });
|
|
2975
|
+
} else {
|
|
2976
|
+
starterData.push({});
|
|
2977
|
+
}
|
|
2978
|
+
break;
|
|
2979
|
+
case "call":
|
|
2980
|
+
starterData.push({});
|
|
2981
|
+
break;
|
|
2982
|
+
default:
|
|
2983
|
+
assertNever(action);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
return starterData;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
// src/adapters/viem/resources/interop/services/fee.ts
|
|
2990
|
+
var { wrap: wrap2 } = createErrorHandlers("interop");
|
|
2991
|
+
async function buildFeeInfo(params, ctx, numStarters) {
|
|
2992
|
+
const useFixed = params.fee?.useFixed ?? false;
|
|
2993
|
+
if (useFixed) {
|
|
2994
|
+
const zkFeePerCall = await wrap2(
|
|
2995
|
+
OP_INTEROP.svc.fees.zkInteropFee,
|
|
2996
|
+
() => ctx.client.l2.readContract({
|
|
2997
|
+
address: ctx.interopCenter,
|
|
2998
|
+
abi: IInteropCenter_default,
|
|
2999
|
+
functionName: "ZK_INTEROP_FEE"
|
|
3000
|
+
}),
|
|
3001
|
+
{ message: "Failed to fetch ZK interop fee from InteropCenter." }
|
|
3002
|
+
);
|
|
3003
|
+
const zkFeeTotal = zkFeePerCall * BigInt(numStarters);
|
|
3004
|
+
const zkTokenAddress = await wrap2(
|
|
3005
|
+
OP_INTEROP.svc.fees.zkToken,
|
|
3006
|
+
() => ctx.client.l2.readContract({
|
|
3007
|
+
address: ctx.interopCenter,
|
|
3008
|
+
abi: IInteropCenter_default,
|
|
3009
|
+
functionName: "zkToken"
|
|
3010
|
+
}),
|
|
3011
|
+
{ message: "Failed to fetch ZK token address from InteropCenter." }
|
|
3012
|
+
);
|
|
3013
|
+
const approval = {
|
|
3014
|
+
token: zkTokenAddress,
|
|
3015
|
+
spender: ctx.interopCenter,
|
|
3016
|
+
amount: zkFeeTotal
|
|
3017
|
+
};
|
|
3018
|
+
return {
|
|
3019
|
+
approval,
|
|
3020
|
+
fee: { token: zkTokenAddress, amount: zkFeeTotal }
|
|
3021
|
+
};
|
|
3022
|
+
} else {
|
|
3023
|
+
const protocolFeePerCall = await wrap2(
|
|
3024
|
+
OP_INTEROP.svc.fees.protocolFee,
|
|
3025
|
+
() => ctx.client.l2.readContract({
|
|
3026
|
+
address: ctx.interopCenter,
|
|
3027
|
+
abi: IInteropCenter_default,
|
|
3028
|
+
functionName: "interopProtocolFee"
|
|
3029
|
+
}),
|
|
3030
|
+
{ message: "Failed to fetch interop protocol fee from InteropCenter." }
|
|
3031
|
+
);
|
|
3032
|
+
const totalFee = protocolFeePerCall * BigInt(numStarters);
|
|
3033
|
+
return {
|
|
3034
|
+
approval: null,
|
|
3035
|
+
fee: { token: ctx.baseTokens.src, amount: totalFee }
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
|
|
3040
|
+
// src/adapters/viem/resources/interop/routes/indirect.ts
|
|
3041
|
+
function routeIndirect() {
|
|
3042
|
+
return {
|
|
3043
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3044
|
+
async preflight(params, ctx) {
|
|
3045
|
+
preflightIndirect(params, {
|
|
3046
|
+
dstChainId: ctx.dstChainId,
|
|
3047
|
+
baseTokens: ctx.baseTokens,
|
|
3048
|
+
l2AssetRouter: ctx.l2AssetRouter,
|
|
3049
|
+
l2NativeTokenVault: ctx.l2NativeTokenVault,
|
|
3050
|
+
codec: interopCodec
|
|
3051
|
+
});
|
|
3052
|
+
},
|
|
3053
|
+
async build(params, ctx) {
|
|
3054
|
+
const steps = [];
|
|
3055
|
+
const erc20Tokens = getErc20Tokens(params);
|
|
3056
|
+
const [erc20AssetIds, feeInfo] = await Promise.all([
|
|
3057
|
+
resolveErc20AssetIds(erc20Tokens, ctx),
|
|
3058
|
+
buildFeeInfo(params, ctx, params.actions.length)
|
|
3059
|
+
]);
|
|
3060
|
+
const attributes = getInteropAttributes(params, ctx);
|
|
3061
|
+
const starterData = await getStarterData(params, ctx, erc20AssetIds);
|
|
3062
|
+
const bundle = buildIndirectBundle(
|
|
3063
|
+
params,
|
|
3064
|
+
{
|
|
3065
|
+
dstChainId: ctx.dstChainId,
|
|
3066
|
+
baseTokens: ctx.baseTokens,
|
|
3067
|
+
l2AssetRouter: ctx.l2AssetRouter,
|
|
3068
|
+
l2NativeTokenVault: ctx.l2NativeTokenVault,
|
|
3069
|
+
codec: interopCodec
|
|
3070
|
+
},
|
|
3071
|
+
attributes,
|
|
3072
|
+
starterData,
|
|
3073
|
+
feeInfo
|
|
3074
|
+
);
|
|
3075
|
+
steps.push(...buildEnsureTokenSteps(erc20Tokens, ctx));
|
|
3076
|
+
steps.push(...await buildApproveSteps(bundle.approvals, ctx));
|
|
3077
|
+
const data = encodeFunctionData({
|
|
3078
|
+
abi: IInteropCenter_default,
|
|
3079
|
+
functionName: "sendBundle",
|
|
3080
|
+
args: [bundle.dstChain, bundle.starters, bundle.bundleAttributes]
|
|
3081
|
+
});
|
|
3082
|
+
steps.push({
|
|
3083
|
+
key: "sendBundle",
|
|
3084
|
+
kind: "interop.center",
|
|
3085
|
+
description: "Send interop bundle (indirect route)",
|
|
3086
|
+
tx: {
|
|
3087
|
+
to: ctx.interopCenter,
|
|
3088
|
+
data,
|
|
3089
|
+
value: bundle.quoteExtras.totalActionValue + feeInfo.fee.amount,
|
|
3090
|
+
...ctx.gasOverrides
|
|
3091
|
+
}
|
|
3092
|
+
});
|
|
3093
|
+
return {
|
|
3094
|
+
steps,
|
|
3095
|
+
approvals: bundle.approvals,
|
|
3096
|
+
quoteExtras: bundle.quoteExtras,
|
|
3097
|
+
interopFee: feeInfo.fee
|
|
3098
|
+
};
|
|
3099
|
+
}
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
function routeDirect() {
|
|
3103
|
+
return {
|
|
3104
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3105
|
+
async preflight(params, ctx) {
|
|
3106
|
+
preflightDirect(params, {
|
|
3107
|
+
dstChainId: ctx.dstChainId,
|
|
3108
|
+
baseTokens: ctx.baseTokens,
|
|
3109
|
+
l2AssetRouter: ctx.l2AssetRouter,
|
|
3110
|
+
l2NativeTokenVault: ctx.l2NativeTokenVault,
|
|
3111
|
+
codec: interopCodec
|
|
3112
|
+
});
|
|
3113
|
+
},
|
|
3114
|
+
async build(params, ctx) {
|
|
3115
|
+
const steps = [];
|
|
3116
|
+
const attrs = getInteropAttributes(params, ctx);
|
|
3117
|
+
const feeInfo = await buildFeeInfo(params, ctx, params.actions.length);
|
|
3118
|
+
const built = buildDirectBundle(
|
|
3119
|
+
params,
|
|
3120
|
+
{
|
|
3121
|
+
dstChainId: ctx.dstChainId,
|
|
3122
|
+
baseTokens: ctx.baseTokens,
|
|
3123
|
+
l2AssetRouter: ctx.l2AssetRouter,
|
|
3124
|
+
l2NativeTokenVault: ctx.l2NativeTokenVault,
|
|
3125
|
+
codec: interopCodec
|
|
3126
|
+
},
|
|
3127
|
+
attrs,
|
|
3128
|
+
feeInfo
|
|
3129
|
+
);
|
|
3130
|
+
steps.push(...await buildApproveSteps(built.approvals, ctx));
|
|
3131
|
+
const data = encodeFunctionData({
|
|
3132
|
+
abi: IInteropCenter_default,
|
|
3133
|
+
functionName: "sendBundle",
|
|
3134
|
+
args: [built.dstChain, built.starters, built.bundleAttributes]
|
|
3135
|
+
});
|
|
3136
|
+
steps.push({
|
|
3137
|
+
key: "sendBundle",
|
|
3138
|
+
kind: "interop.center",
|
|
3139
|
+
description: `Send interop bundle (direct route; ${params.actions.length} actions)`,
|
|
3140
|
+
tx: {
|
|
3141
|
+
to: ctx.interopCenter,
|
|
3142
|
+
data,
|
|
3143
|
+
value: built.quoteExtras.totalActionValue + feeInfo.fee.amount,
|
|
3144
|
+
...ctx.gasOverrides
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3147
|
+
return {
|
|
3148
|
+
steps,
|
|
3149
|
+
approvals: built.approvals,
|
|
3150
|
+
quoteExtras: built.quoteExtras,
|
|
3151
|
+
interopFee: feeInfo.fee
|
|
3152
|
+
};
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
// src/adapters/viem/resources/interop/context.ts
|
|
3158
|
+
async function assertInteropProtocolVersion(client, srcChainId, dstChainId) {
|
|
3159
|
+
const [srcProtocolVersion, dstProtocolVersion] = await Promise.all([
|
|
3160
|
+
client.getProtocolVersion(srcChainId),
|
|
3161
|
+
client.getProtocolVersion(dstChainId)
|
|
3162
|
+
]);
|
|
3163
|
+
assertProtocolVersion(srcChainId, srcProtocolVersion);
|
|
3164
|
+
assertProtocolVersion(dstChainId, dstProtocolVersion);
|
|
3165
|
+
}
|
|
3166
|
+
async function commonCtx3(dstPublicClient, params, client, tokens, contracts, attributes) {
|
|
3167
|
+
const sender = client.account.address;
|
|
3168
|
+
const chainId = BigInt(await client.l2.getChainId());
|
|
3169
|
+
const dstChainId = BigInt(await dstPublicClient.getChainId());
|
|
3170
|
+
const {
|
|
3171
|
+
bridgehub,
|
|
3172
|
+
l2AssetRouter,
|
|
3173
|
+
l2NativeTokenVault,
|
|
3174
|
+
interopCenter,
|
|
3175
|
+
interopHandler,
|
|
3176
|
+
l2MessageVerification
|
|
3177
|
+
} = await contracts.addresses();
|
|
3178
|
+
await assertInteropProtocolVersion(client, chainId, dstChainId);
|
|
3179
|
+
const [srcBaseToken, dstBaseToken] = await Promise.all([
|
|
3180
|
+
client.baseToken(chainId),
|
|
3181
|
+
client.baseToken(dstChainId)
|
|
3182
|
+
]);
|
|
3183
|
+
const baseMatches = srcBaseToken.toLowerCase() === dstBaseToken.toLowerCase();
|
|
3184
|
+
return {
|
|
3185
|
+
client,
|
|
3186
|
+
tokens,
|
|
3187
|
+
contracts,
|
|
3188
|
+
sender,
|
|
3189
|
+
chainIdL2: chainId,
|
|
3190
|
+
chainId,
|
|
3191
|
+
bridgehub,
|
|
3192
|
+
dstChainId,
|
|
3193
|
+
dstPublicClient,
|
|
3194
|
+
interopCenter,
|
|
3195
|
+
interopHandler,
|
|
3196
|
+
l2MessageVerification,
|
|
3197
|
+
l2AssetRouter,
|
|
3198
|
+
l2NativeTokenVault,
|
|
3199
|
+
baseTokens: { src: srcBaseToken, dst: dstBaseToken, matches: baseMatches },
|
|
3200
|
+
attributes,
|
|
3201
|
+
gasOverrides: params.txOverrides ? toGasOverrides(params.txOverrides) : void 0
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3204
|
+
function getTopics() {
|
|
3205
|
+
const topics = {
|
|
3206
|
+
interopBundleSent: encodeEventTopics({
|
|
3207
|
+
abi: IInteropCenter_default,
|
|
3208
|
+
eventName: "InteropBundleSent"
|
|
3209
|
+
})[0],
|
|
3210
|
+
bundleVerified: encodeEventTopics({
|
|
3211
|
+
abi: IInteropHandler_default,
|
|
3212
|
+
eventName: "BundleVerified"
|
|
3213
|
+
})[0],
|
|
3214
|
+
bundleExecuted: encodeEventTopics({
|
|
3215
|
+
abi: IInteropHandler_default,
|
|
3216
|
+
eventName: "BundleExecuted"
|
|
3217
|
+
})[0],
|
|
3218
|
+
bundleUnbundled: encodeEventTopics({
|
|
3219
|
+
abi: IInteropHandler_default,
|
|
3220
|
+
eventName: "BundleUnbundled"
|
|
3221
|
+
})[0]
|
|
3222
|
+
};
|
|
3223
|
+
return { topics };
|
|
3224
|
+
}
|
|
3225
|
+
var { wrap: wrap3 } = createErrorHandlers("interop");
|
|
3226
|
+
var DEFAULT_BLOCKS_RANGE_SIZE = 1e4;
|
|
3227
|
+
var DEFAULT_MAX_BLOCKS_BACK = 2e4;
|
|
3228
|
+
var SAFE_BLOCKS_RANGE_SIZE = 1e3;
|
|
3229
|
+
function parseMaxBlockRangeLimit(error) {
|
|
3230
|
+
const msg = typeof error === "object" && error !== null && "message" in error ? String(error.message) : String(error);
|
|
3231
|
+
const match = /query exceeds max block range\s+(\d+)/i.exec(msg);
|
|
3232
|
+
if (!match) return null;
|
|
3233
|
+
const limit = Number.parseInt(match[1], 10);
|
|
3234
|
+
return Number.isInteger(limit) && limit > 0 ? limit : null;
|
|
3235
|
+
}
|
|
3236
|
+
async function getTxReceipt(provider, txHash) {
|
|
3237
|
+
const receipt = await wrap3(
|
|
3238
|
+
OP_INTEROP.svc.status.sourceReceipt,
|
|
3239
|
+
async () => {
|
|
3240
|
+
try {
|
|
3241
|
+
return await provider.getTransactionReceipt({ hash: txHash });
|
|
3242
|
+
} catch (error) {
|
|
3243
|
+
if (isReceiptNotFound(error)) return null;
|
|
3244
|
+
throw error;
|
|
3245
|
+
}
|
|
3246
|
+
},
|
|
3247
|
+
{
|
|
3248
|
+
ctx: { where: "l2.getTransactionReceipt", l2SrcTxHash: txHash },
|
|
3249
|
+
message: "Failed to fetch source L2 receipt for interop tx."
|
|
3250
|
+
}
|
|
3251
|
+
);
|
|
3252
|
+
if (!receipt) return null;
|
|
3253
|
+
return {
|
|
3254
|
+
logs: receipt.logs.map((log) => ({
|
|
3255
|
+
address: log.address,
|
|
3256
|
+
topics: log.topics,
|
|
3257
|
+
data: log.data,
|
|
3258
|
+
transactionHash: log.transactionHash
|
|
3259
|
+
}))
|
|
3260
|
+
};
|
|
3261
|
+
}
|
|
3262
|
+
async function getLogs(provider, address, topics, opts) {
|
|
3263
|
+
const maxBlocksBack = opts?.maxBlocksBack ?? DEFAULT_MAX_BLOCKS_BACK;
|
|
3264
|
+
const initialChunkSize = opts?.logChunkSize ?? DEFAULT_BLOCKS_RANGE_SIZE;
|
|
3265
|
+
return await wrap3(
|
|
3266
|
+
OP_INTEROP.svc.status.dstLogs,
|
|
3267
|
+
async () => {
|
|
3268
|
+
const currentBlock = await provider.getBlockNumber();
|
|
3269
|
+
const minBlock = BigInt(Math.max(0, Number(currentBlock) - maxBlocksBack));
|
|
3270
|
+
let toBlock = currentBlock;
|
|
3271
|
+
let chunkSize = initialChunkSize;
|
|
3272
|
+
while (toBlock >= minBlock) {
|
|
3273
|
+
const fromBlock = toBlock - BigInt(chunkSize) + 1n > minBlock ? toBlock - BigInt(chunkSize) + 1n : minBlock;
|
|
3274
|
+
try {
|
|
3275
|
+
const rawLogs = await provider.request({
|
|
3276
|
+
method: "eth_getLogs",
|
|
3277
|
+
params: [
|
|
3278
|
+
{
|
|
3279
|
+
address,
|
|
3280
|
+
topics,
|
|
3281
|
+
fromBlock: numberToHex(fromBlock),
|
|
3282
|
+
toBlock: numberToHex(toBlock)
|
|
3283
|
+
}
|
|
3284
|
+
]
|
|
3285
|
+
});
|
|
3286
|
+
if (rawLogs.length > 0) {
|
|
3287
|
+
return rawLogs.map((log) => ({
|
|
3288
|
+
address: log.address,
|
|
3289
|
+
topics: log.topics,
|
|
3290
|
+
data: log.data,
|
|
3291
|
+
transactionHash: log.transactionHash
|
|
3292
|
+
}));
|
|
3293
|
+
}
|
|
3294
|
+
toBlock = fromBlock - 1n;
|
|
3295
|
+
} catch (error) {
|
|
3296
|
+
const serverLimit = parseMaxBlockRangeLimit(error);
|
|
3297
|
+
if (serverLimit == null) {
|
|
3298
|
+
if (chunkSize > SAFE_BLOCKS_RANGE_SIZE) {
|
|
3299
|
+
chunkSize = SAFE_BLOCKS_RANGE_SIZE;
|
|
3300
|
+
} else {
|
|
3301
|
+
throw error;
|
|
3302
|
+
}
|
|
3303
|
+
} else {
|
|
3304
|
+
chunkSize = Math.min(chunkSize, serverLimit);
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
return [];
|
|
3309
|
+
},
|
|
3310
|
+
{
|
|
3311
|
+
ctx: { address, maxBlocksBack, logChunkSize: initialChunkSize },
|
|
3312
|
+
message: "Failed to query destination bundle lifecycle logs."
|
|
3313
|
+
}
|
|
3314
|
+
);
|
|
3315
|
+
}
|
|
3316
|
+
async function getInteropRoot(provider, rootChainId, batchNumber) {
|
|
3317
|
+
return await wrap3(
|
|
3318
|
+
OP_INTEROP.svc.status.getRoot,
|
|
3319
|
+
async () => {
|
|
3320
|
+
return await provider.readContract({
|
|
3321
|
+
address: L2_INTEROP_ROOT_STORAGE_ADDRESS,
|
|
3322
|
+
abi: IInteropRootStorage_default,
|
|
3323
|
+
functionName: "interopRoots",
|
|
3324
|
+
args: [rootChainId, batchNumber]
|
|
3325
|
+
});
|
|
3326
|
+
},
|
|
3327
|
+
{
|
|
3328
|
+
ctx: { rootChainId, batchNumber },
|
|
3329
|
+
message: "Failed to get interop root from the destination chain."
|
|
3330
|
+
}
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/adapters/viem/resources/interop/services/finalization/bundle.ts
|
|
3335
|
+
var { wrap: wrap4 } = createErrorHandlers("interop");
|
|
3336
|
+
async function getBundleStatus(client, dstProvider, topics, bundleHash, opts) {
|
|
3337
|
+
const { interopHandler } = await client.ensureAddresses();
|
|
3338
|
+
const bundleLogs = await getLogs(dstProvider, interopHandler, [null, bundleHash], opts);
|
|
3339
|
+
const findLastByTopic = (eventTopic) => bundleLogs.findLast((log) => log.topics[0].toLowerCase() === eventTopic.toLowerCase());
|
|
3340
|
+
const lifecycleChecks = [
|
|
3341
|
+
{ phase: "UNBUNDLED", topic: topics.bundleUnbundled, includeTxHash: true },
|
|
3342
|
+
{ phase: "EXECUTED", topic: topics.bundleExecuted, includeTxHash: true },
|
|
3343
|
+
{ phase: "VERIFIED", topic: topics.bundleVerified }
|
|
3344
|
+
];
|
|
3345
|
+
for (const check of lifecycleChecks) {
|
|
3346
|
+
const match = findLastByTopic(check.topic);
|
|
3347
|
+
if (!match) continue;
|
|
3348
|
+
if (check.includeTxHash) {
|
|
3349
|
+
return { phase: check.phase, dstExecTxHash: match.transactionHash };
|
|
3350
|
+
}
|
|
3351
|
+
return { phase: check.phase };
|
|
3352
|
+
}
|
|
3353
|
+
return { phase: "SENT" };
|
|
3354
|
+
}
|
|
3355
|
+
async function executeBundle(client, dstProvider, info, opts) {
|
|
3356
|
+
const { topics } = getTopics();
|
|
3357
|
+
const { bundleHash, encodedData, proof } = info;
|
|
3358
|
+
const dstStatus = await getBundleStatus(client, dstProvider, topics, bundleHash, opts);
|
|
3359
|
+
if (["EXECUTED", "UNBUNDLED"].includes(dstStatus.phase)) {
|
|
3360
|
+
throw createError("STATE", {
|
|
3361
|
+
resource: "interop",
|
|
3362
|
+
operation: OP_INTEROP.finalize,
|
|
3363
|
+
message: `Interop bundle has already been ${dstStatus.phase.toLowerCase()}.`,
|
|
3364
|
+
context: { bundleHash }
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3367
|
+
const dstWallet = await wrap4(
|
|
3368
|
+
OP_INTEROP.exec.sendStep,
|
|
3369
|
+
() => createWalletClient({
|
|
3370
|
+
account: client.account,
|
|
3371
|
+
transport: custom(dstProvider.transport),
|
|
3372
|
+
chain: dstProvider.chain
|
|
3373
|
+
}),
|
|
3374
|
+
{ message: "Failed to create destination wallet client." }
|
|
3375
|
+
);
|
|
3376
|
+
const { interopHandler } = await client.ensureAddresses();
|
|
3377
|
+
try {
|
|
3378
|
+
const hash = await dstWallet.writeContract({
|
|
3379
|
+
address: interopHandler,
|
|
3380
|
+
abi: IInteropHandler_default,
|
|
3381
|
+
functionName: "executeBundle",
|
|
3382
|
+
args: [encodedData, proof],
|
|
3383
|
+
account: client.account,
|
|
3384
|
+
chain: dstProvider.chain ?? null
|
|
3385
|
+
});
|
|
3386
|
+
return {
|
|
3387
|
+
hash,
|
|
3388
|
+
wait: async () => {
|
|
3389
|
+
try {
|
|
3390
|
+
const receipt = await dstProvider.waitForTransactionReceipt({ hash });
|
|
3391
|
+
if (receipt.status === "reverted") {
|
|
3392
|
+
throw createError("EXECUTION", {
|
|
3393
|
+
resource: "interop",
|
|
3394
|
+
operation: OP_INTEROP.exec.waitStep,
|
|
3395
|
+
message: "Interop bundle execution reverted on destination.",
|
|
3396
|
+
context: { txHash: hash }
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
return receipt;
|
|
3400
|
+
} catch (e) {
|
|
3401
|
+
if (isZKsyncError(e)) throw e;
|
|
3402
|
+
throw toZKsyncError(
|
|
3403
|
+
"EXECUTION",
|
|
3404
|
+
{
|
|
3405
|
+
resource: "interop",
|
|
3406
|
+
operation: OP_INTEROP.exec.waitStep,
|
|
3407
|
+
message: "Failed while waiting for executeBundle transaction on destination.",
|
|
3408
|
+
context: { txHash: hash }
|
|
3409
|
+
},
|
|
3410
|
+
e
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
} catch (e) {
|
|
3416
|
+
throw toZKsyncError(
|
|
3417
|
+
"EXECUTION",
|
|
3418
|
+
{
|
|
3419
|
+
resource: "interop",
|
|
3420
|
+
operation: OP_INTEROP.exec.sendStep,
|
|
3421
|
+
message: "Failed to send executeBundle transaction on destination chain."
|
|
3422
|
+
},
|
|
3423
|
+
e
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
async function verifyBundle(client, dstProvider, info) {
|
|
3428
|
+
const { interopHandler } = await client.ensureAddresses();
|
|
3429
|
+
const dstWallet = await wrap4(
|
|
3430
|
+
OP_INTEROP.verify,
|
|
3431
|
+
() => createWalletClient({
|
|
3432
|
+
account: client.account,
|
|
3433
|
+
transport: custom(dstProvider.transport),
|
|
3434
|
+
chain: dstProvider.chain
|
|
3435
|
+
}),
|
|
3436
|
+
{ message: "Failed to create destination wallet client for verifyBundle." }
|
|
3437
|
+
);
|
|
3438
|
+
try {
|
|
3439
|
+
const hash = await dstWallet.writeContract({
|
|
3440
|
+
address: interopHandler,
|
|
3441
|
+
abi: IInteropHandler_default,
|
|
3442
|
+
functionName: "verifyBundle",
|
|
3443
|
+
args: [info.encodedData, info.proof],
|
|
3444
|
+
account: client.account,
|
|
3445
|
+
chain: dstProvider.chain ?? null
|
|
3446
|
+
});
|
|
3447
|
+
return {
|
|
3448
|
+
hash,
|
|
3449
|
+
wait: async () => {
|
|
3450
|
+
try {
|
|
3451
|
+
const receipt = await dstProvider.waitForTransactionReceipt({ hash });
|
|
3452
|
+
if (receipt.status === "reverted") {
|
|
3453
|
+
throw createError("EXECUTION", {
|
|
3454
|
+
resource: "interop",
|
|
3455
|
+
operation: OP_INTEROP.verify,
|
|
3456
|
+
message: "Interop bundle verification reverted on destination.",
|
|
3457
|
+
context: { txHash: hash }
|
|
3458
|
+
});
|
|
3459
|
+
}
|
|
3460
|
+
return receipt;
|
|
3461
|
+
} catch (e) {
|
|
3462
|
+
if (isZKsyncError(e)) throw e;
|
|
3463
|
+
throw toZKsyncError(
|
|
3464
|
+
"EXECUTION",
|
|
3465
|
+
{
|
|
3466
|
+
resource: "interop",
|
|
3467
|
+
operation: OP_INTEROP.verify,
|
|
3468
|
+
message: "Failed while waiting for verifyBundle transaction on destination.",
|
|
3469
|
+
context: { txHash: hash }
|
|
3470
|
+
},
|
|
3471
|
+
e
|
|
3472
|
+
);
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
};
|
|
3476
|
+
} catch (e) {
|
|
3477
|
+
if (isZKsyncError(e)) throw e;
|
|
3478
|
+
throw toZKsyncError(
|
|
3479
|
+
"EXECUTION",
|
|
3480
|
+
{
|
|
3481
|
+
resource: "interop",
|
|
3482
|
+
operation: OP_INTEROP.verify,
|
|
3483
|
+
message: "Failed to send verifyBundle transaction on destination chain."
|
|
3484
|
+
},
|
|
3485
|
+
e
|
|
3486
|
+
);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
function decodeInteropBundleSent(log) {
|
|
3490
|
+
const { args } = decodeEventLog({
|
|
3491
|
+
abi: IInteropCenter_default,
|
|
3492
|
+
eventName: "InteropBundleSent",
|
|
3493
|
+
data: log.data,
|
|
3494
|
+
topics: log.topics
|
|
3495
|
+
});
|
|
3496
|
+
return {
|
|
3497
|
+
bundleHash: args.interopBundleHash,
|
|
3498
|
+
sourceChainId: args.interopBundle.sourceChainId,
|
|
3499
|
+
destinationChainId: args.interopBundle.destinationChainId
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
function decodeL1MessageData(log) {
|
|
3503
|
+
const [decoded] = decodeAbiParameters([{ type: "bytes" }], log.data);
|
|
3504
|
+
return decoded;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// src/adapters/viem/resources/interop/services/finalization/polling.ts
|
|
3508
|
+
var { wrap: wrap5 } = createErrorHandlers("interop");
|
|
3509
|
+
function isProofNotReadyError(error) {
|
|
3510
|
+
return isZKsyncError(error, {
|
|
3511
|
+
operation: "zksrpc.getL2ToL1LogProof",
|
|
3512
|
+
messageIncludes: "proof not yet available"
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
function shouldRetryRootFetch(error) {
|
|
3516
|
+
if (!isZKsyncError(error)) return false;
|
|
3517
|
+
return error.envelope.operation === OP_INTEROP.svc.status.getRoot;
|
|
3518
|
+
}
|
|
3519
|
+
async function waitForProof(client, l2SrcTxHash, logIndex, blockNumber, pollMs, deadline) {
|
|
3520
|
+
while (true) {
|
|
3521
|
+
if (Date.now() > deadline) {
|
|
3522
|
+
throw createError("TIMEOUT", {
|
|
3523
|
+
resource: "interop",
|
|
3524
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3525
|
+
message: "Timed out waiting for block to be finalized.",
|
|
3526
|
+
context: { l2SrcTxHash, logIndex, blockNumber }
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
const finalizedBlock = await client.l2.getBlock({ blockTag: "finalized" });
|
|
3530
|
+
if (finalizedBlock && finalizedBlock.number !== null && finalizedBlock.number >= blockNumber) {
|
|
3531
|
+
break;
|
|
3532
|
+
}
|
|
3533
|
+
await sleep(pollMs);
|
|
3534
|
+
}
|
|
3535
|
+
while (true) {
|
|
3536
|
+
if (Date.now() > deadline) {
|
|
3537
|
+
throw createError("TIMEOUT", {
|
|
3538
|
+
resource: "interop",
|
|
3539
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3540
|
+
message: "Timed out waiting for L2->L1 log proof to become available.",
|
|
3541
|
+
context: { l2SrcTxHash, logIndex }
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
try {
|
|
3545
|
+
return await client.zks.getL2ToL1LogProof(l2SrcTxHash, logIndex, "messageRoot" /* MessageRoot */);
|
|
3546
|
+
} catch (error) {
|
|
3547
|
+
if (!isProofNotReadyError(error)) throw error;
|
|
3548
|
+
}
|
|
3549
|
+
await sleep(pollMs);
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
async function waitForRoot(provider, chainId, batchNumber, pollMs, deadline) {
|
|
3553
|
+
while (true) {
|
|
3554
|
+
if (Date.now() > deadline) {
|
|
3555
|
+
throw createError("TIMEOUT", {
|
|
3556
|
+
resource: "interop",
|
|
3557
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3558
|
+
message: "Timed out waiting for interop root to become available.",
|
|
3559
|
+
context: { chainId, batchNumber }
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
let interopRoot = null;
|
|
3563
|
+
try {
|
|
3564
|
+
const root = await getInteropRoot(provider, chainId, batchNumber);
|
|
3565
|
+
if (root !== ZERO_HASH) {
|
|
3566
|
+
interopRoot = root;
|
|
3567
|
+
}
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
if (!shouldRetryRootFetch(error)) throw error;
|
|
3570
|
+
interopRoot = null;
|
|
3571
|
+
}
|
|
3572
|
+
if (interopRoot) {
|
|
3573
|
+
return interopRoot;
|
|
3574
|
+
}
|
|
3575
|
+
await sleep(pollMs);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
async function waitForTxReceipt(client, txHash, pollMs, deadline) {
|
|
3579
|
+
while (true) {
|
|
3580
|
+
if (Date.now() > deadline) {
|
|
3581
|
+
throw createError("TIMEOUT", {
|
|
3582
|
+
resource: "interop",
|
|
3583
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3584
|
+
message: "Timed out waiting for source receipt to be available.",
|
|
3585
|
+
context: { txHash }
|
|
3586
|
+
});
|
|
3587
|
+
}
|
|
3588
|
+
const receipt = await wrap5(
|
|
3589
|
+
OP_INTEROP.svc.status.sourceReceipt,
|
|
3590
|
+
() => client.zks.getReceiptWithL2ToL1(txHash),
|
|
3591
|
+
{
|
|
3592
|
+
ctx: { where: "zks.getReceiptWithL2ToL1", txHash },
|
|
3593
|
+
message: "Failed to fetch source L2 receipt (with L2->L1 logs) for interop tx."
|
|
3594
|
+
}
|
|
3595
|
+
);
|
|
3596
|
+
if (receipt) {
|
|
3597
|
+
return receipt;
|
|
3598
|
+
}
|
|
3599
|
+
await sleep(pollMs);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
async function waitForFinalization(client, dstProvider, gwProvider, input, opts) {
|
|
3603
|
+
const { topics } = getTopics();
|
|
3604
|
+
const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
|
|
3605
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3606
|
+
const deadline = Date.now() + timeoutMs;
|
|
3607
|
+
const ids = resolveIdsFromWaitable(input);
|
|
3608
|
+
if (!ids.l2SrcTxHash) {
|
|
3609
|
+
throw createError("STATE", {
|
|
3610
|
+
resource: "interop",
|
|
3611
|
+
operation: OP_INTEROP.svc.status.sourceReceipt,
|
|
3612
|
+
message: "Cannot wait for interop finalization: missing l2SrcTxHash.",
|
|
3613
|
+
context: { input }
|
|
3614
|
+
});
|
|
3615
|
+
}
|
|
3616
|
+
const { interopCenter } = await client.ensureAddresses();
|
|
3617
|
+
let bundleInfo = null;
|
|
3618
|
+
while (!bundleInfo) {
|
|
3619
|
+
if (Date.now() > deadline) {
|
|
3620
|
+
throw createError("TIMEOUT", {
|
|
3621
|
+
resource: "interop",
|
|
3622
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3623
|
+
message: "Timed out waiting for source receipt to be available.",
|
|
3624
|
+
context: { l2SrcTxHash: ids.l2SrcTxHash }
|
|
3625
|
+
});
|
|
3626
|
+
}
|
|
3627
|
+
const txReceipt = await waitForTxReceipt(client, ids.l2SrcTxHash, pollMs, deadline);
|
|
3628
|
+
bundleInfo = parseBundleReceiptInfo({
|
|
3629
|
+
rawReceipt: txReceipt,
|
|
3630
|
+
interopCenter,
|
|
3631
|
+
interopBundleSentTopic: topics.interopBundleSent,
|
|
3632
|
+
decodeInteropBundleSent: (log) => decodeInteropBundleSent(log),
|
|
3633
|
+
decodeL1MessageData,
|
|
3634
|
+
l2SrcTxHash: ids.l2SrcTxHash
|
|
3635
|
+
});
|
|
3636
|
+
}
|
|
3637
|
+
const proof = await waitForProof(
|
|
3638
|
+
client,
|
|
3639
|
+
ids.l2SrcTxHash,
|
|
3640
|
+
bundleInfo.l2ToL1LogIndex,
|
|
3641
|
+
BigInt(bundleInfo.rawReceipt.blockNumber),
|
|
3642
|
+
pollMs,
|
|
3643
|
+
deadline
|
|
3644
|
+
);
|
|
3645
|
+
const finalizationInfo = buildFinalizationInfo(
|
|
3646
|
+
{ l2SrcTxHash: ids.l2SrcTxHash, bundleHash: ids.bundleHash },
|
|
3647
|
+
bundleInfo,
|
|
3648
|
+
proof,
|
|
3649
|
+
bundleInfo.l1MessageData
|
|
3650
|
+
);
|
|
3651
|
+
if (proof.gatewayBlockNumber == null) {
|
|
3652
|
+
throw createError("STATE", {
|
|
3653
|
+
resource: "interop",
|
|
3654
|
+
operation: OP_INTEROP.svc.wait.timeout,
|
|
3655
|
+
message: "Proof missing gatewayBlockNumber required for interop finalization.",
|
|
3656
|
+
context: { l2SrcTxHash: ids.l2SrcTxHash }
|
|
3657
|
+
});
|
|
3658
|
+
}
|
|
3659
|
+
const gwChainId = BigInt(await gwProvider.getChainId());
|
|
3660
|
+
await waitForRoot(dstProvider, gwChainId, proof.gatewayBlockNumber, pollMs, deadline);
|
|
3661
|
+
return finalizationInfo;
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
// src/adapters/viem/resources/interop/services/finalization/status.ts
|
|
3665
|
+
async function getStatus(client, dstProvider, input, opts) {
|
|
3666
|
+
const { topics } = getTopics();
|
|
3667
|
+
const baseIds = resolveIdsFromWaitable(input);
|
|
3668
|
+
const enrichedIds = await (async () => {
|
|
3669
|
+
if (baseIds.bundleHash) return baseIds;
|
|
3670
|
+
if (!baseIds.l2SrcTxHash) return baseIds;
|
|
3671
|
+
const { interopCenter } = await client.ensureAddresses();
|
|
3672
|
+
const receipt = await getTxReceipt(client.l2, baseIds.l2SrcTxHash);
|
|
3673
|
+
if (!receipt) return baseIds;
|
|
3674
|
+
const { bundleHash } = parseBundleSentFromReceipt({
|
|
3675
|
+
receipt: { logs: receipt.logs },
|
|
3676
|
+
interopCenter,
|
|
3677
|
+
interopBundleSentTopic: topics.interopBundleSent,
|
|
3678
|
+
decodeInteropBundleSent: (log) => decodeInteropBundleSent(log)
|
|
3679
|
+
});
|
|
3680
|
+
return { ...baseIds, bundleHash };
|
|
3681
|
+
})();
|
|
3682
|
+
if (!enrichedIds.bundleHash) {
|
|
3683
|
+
const phase = enrichedIds.l2SrcTxHash ? "SENT" : "UNKNOWN";
|
|
3684
|
+
return {
|
|
3685
|
+
phase,
|
|
3686
|
+
l2SrcTxHash: enrichedIds.l2SrcTxHash,
|
|
3687
|
+
bundleHash: enrichedIds.bundleHash,
|
|
3688
|
+
dstExecTxHash: enrichedIds.dstExecTxHash
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3691
|
+
const dstInfo = await getBundleStatus(client, dstProvider, topics, enrichedIds.bundleHash, opts);
|
|
3692
|
+
return {
|
|
3693
|
+
phase: dstInfo.phase,
|
|
3694
|
+
l2SrcTxHash: enrichedIds.l2SrcTxHash,
|
|
3695
|
+
bundleHash: enrichedIds.bundleHash,
|
|
3696
|
+
dstExecTxHash: dstInfo.dstExecTxHash ?? enrichedIds.dstExecTxHash
|
|
3697
|
+
};
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// src/adapters/viem/resources/interop/services/finalization/index.ts
|
|
3701
|
+
function createInteropFinalizationServices(client) {
|
|
3702
|
+
return {
|
|
3703
|
+
status(dstProvider, input, opts) {
|
|
3704
|
+
return getStatus(client, dstProvider, input, opts);
|
|
3705
|
+
},
|
|
3706
|
+
wait(dstProvider, gwProvider, input, opts) {
|
|
3707
|
+
return waitForFinalization(client, dstProvider, gwProvider, input, opts);
|
|
3708
|
+
},
|
|
3709
|
+
async finalize(dstProvider, info, opts) {
|
|
3710
|
+
const execResult = await executeBundle(client, dstProvider, info, opts);
|
|
3711
|
+
await execResult.wait();
|
|
3712
|
+
return {
|
|
3713
|
+
bundleHash: info.bundleHash,
|
|
3714
|
+
dstExecTxHash: execResult.hash
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
function resolveChainRef(ref) {
|
|
3720
|
+
if (typeof ref === "string") {
|
|
3721
|
+
return createPublicClient({ transport: http(ref) });
|
|
3722
|
+
}
|
|
3723
|
+
return ref;
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
// src/adapters/viem/resources/interop/services/gas.ts
|
|
3727
|
+
async function quoteStepsL2Fee(steps, ctx) {
|
|
3728
|
+
if (steps.length === 0) return 0n;
|
|
3729
|
+
const estimator = viemToGasEstimator(ctx.client.l2);
|
|
3730
|
+
let maxFeePerGas;
|
|
3731
|
+
try {
|
|
3732
|
+
const fees = await estimator.estimateFeesPerGas();
|
|
3733
|
+
maxFeePerGas = fees.maxFeePerGas ?? fees.gasPrice ?? await estimator.getGasPrice();
|
|
3734
|
+
} catch {
|
|
3735
|
+
return void 0;
|
|
3736
|
+
}
|
|
3737
|
+
let total = 0n;
|
|
3738
|
+
for (const step of steps) {
|
|
3739
|
+
try {
|
|
3740
|
+
const coreTx = {
|
|
3741
|
+
to: step.tx.to,
|
|
3742
|
+
from: ctx.sender,
|
|
3743
|
+
data: step.tx.data,
|
|
3744
|
+
value: step.tx.value,
|
|
3745
|
+
gasLimit: step.tx.gas ?? step.tx.gasLimit,
|
|
3746
|
+
maxFeePerGas: step.tx.maxFeePerGas,
|
|
3747
|
+
maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas
|
|
3748
|
+
};
|
|
3749
|
+
const est = await estimator.estimateGas(coreTx);
|
|
3750
|
+
const buffered = BigInt(est) * (100n + BUFFER) / 100n;
|
|
3751
|
+
total += buffered * maxFeePerGas;
|
|
3752
|
+
} catch {
|
|
3753
|
+
return void 0;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
return total;
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
// src/adapters/viem/resources/interop/index.ts
|
|
3760
|
+
var { wrap: wrap6, toResult: toResult2 } = createErrorHandlers("interop");
|
|
3761
|
+
var ROUTES3 = {
|
|
3762
|
+
direct: routeDirect(),
|
|
3763
|
+
indirect: routeIndirect()
|
|
3764
|
+
};
|
|
3765
|
+
function createInteropResource(client, config, tokens, contracts, attributes) {
|
|
3766
|
+
let gwProviderCache;
|
|
3767
|
+
function requireConfig() {
|
|
3768
|
+
if (!config)
|
|
3769
|
+
throw createError("STATE", {
|
|
3770
|
+
resource: "interop",
|
|
3771
|
+
operation: "interop.init",
|
|
3772
|
+
message: "Interop is not configured. Pass gwChain in createViemSdk options."
|
|
3773
|
+
});
|
|
3774
|
+
return config;
|
|
3775
|
+
}
|
|
3776
|
+
function getGwProvider() {
|
|
3777
|
+
if (!gwProviderCache) gwProviderCache = resolveChainRef(requireConfig().gwChain);
|
|
3778
|
+
return gwProviderCache;
|
|
3779
|
+
}
|
|
3780
|
+
const svc = createInteropFinalizationServices(client);
|
|
3781
|
+
const tokensResource = tokens ?? createTokensResource(client);
|
|
3782
|
+
const contractsResource = contracts ?? createContractsResource(client);
|
|
3783
|
+
const attributesResource = attributes ?? createViemAttributesResource();
|
|
3784
|
+
async function buildPlanWithCtx(dstPublicClient, params) {
|
|
3785
|
+
const ctx = await commonCtx3(
|
|
3786
|
+
dstPublicClient,
|
|
3787
|
+
params,
|
|
3788
|
+
client,
|
|
3789
|
+
tokensResource,
|
|
3790
|
+
contractsResource,
|
|
3791
|
+
attributesResource
|
|
3792
|
+
);
|
|
3793
|
+
const route = pickInteropRoute({
|
|
3794
|
+
actions: params.actions,
|
|
3795
|
+
ctx: {
|
|
3796
|
+
sender: ctx.sender,
|
|
3797
|
+
srcChainId: ctx.chainId,
|
|
3798
|
+
dstChainId: ctx.dstChainId,
|
|
3799
|
+
baseTokenSrc: ctx.baseTokens.src,
|
|
3800
|
+
baseTokenDst: ctx.baseTokens.dst
|
|
3801
|
+
}
|
|
3802
|
+
});
|
|
3803
|
+
await wrap6(OP_INTEROP.routes[route].preflight, () => ROUTES3[route].preflight?.(params, ctx), {
|
|
3804
|
+
message: "Interop preflight failed.",
|
|
3805
|
+
ctx: { where: `routes.${route}.preflight` }
|
|
3806
|
+
});
|
|
3807
|
+
const { steps, approvals, quoteExtras, interopFee } = await wrap6(
|
|
3808
|
+
OP_INTEROP.routes[route].build,
|
|
3809
|
+
() => ROUTES3[route].build(params, ctx),
|
|
3810
|
+
{
|
|
3811
|
+
message: "Failed to build interop route plan.",
|
|
3812
|
+
ctx: { where: `routes.${route}.build` }
|
|
3813
|
+
}
|
|
3814
|
+
);
|
|
3815
|
+
const l2Fee = await quoteStepsL2Fee(steps, ctx).catch(() => void 0);
|
|
3816
|
+
const summary = {
|
|
3817
|
+
route,
|
|
3818
|
+
approvalsNeeded: approvals,
|
|
3819
|
+
totalActionValue: quoteExtras.totalActionValue,
|
|
3820
|
+
bridgedTokenTotal: quoteExtras.bridgedTokenTotal,
|
|
3821
|
+
interopFee,
|
|
3822
|
+
l2Fee
|
|
3823
|
+
};
|
|
3824
|
+
return { plan: { route, summary, steps }, ctx };
|
|
3825
|
+
}
|
|
3826
|
+
async function buildPlan(dstPublicClient, params) {
|
|
3827
|
+
const { plan } = await buildPlanWithCtx(dstPublicClient, params);
|
|
3828
|
+
return plan;
|
|
3829
|
+
}
|
|
3830
|
+
const quote = (dstChain, params) => wrap6(OP_INTEROP.quote, async () => {
|
|
3831
|
+
const plan = await buildPlan(resolveChainRef(dstChain), params);
|
|
3832
|
+
return plan.summary;
|
|
3833
|
+
});
|
|
3834
|
+
const tryQuote = (dstChain, params) => toResult2(OP_INTEROP.tryQuote, () => quote(dstChain, params));
|
|
3835
|
+
const prepare = (dstChain, params) => wrap6(OP_INTEROP.prepare, () => buildPlan(resolveChainRef(dstChain), params), {
|
|
3836
|
+
message: "Internal error while preparing an interop plan.",
|
|
3837
|
+
ctx: { where: "interop.prepare" }
|
|
3838
|
+
});
|
|
3839
|
+
const tryPrepare = (dstChain, params) => toResult2(
|
|
3840
|
+
OP_INTEROP.tryPrepare,
|
|
3841
|
+
() => prepare(dstChain, params)
|
|
3842
|
+
);
|
|
3843
|
+
const create = (dstChain, params) => wrap6(
|
|
3844
|
+
OP_INTEROP.create,
|
|
3845
|
+
async () => {
|
|
3846
|
+
const { plan } = await buildPlanWithCtx(resolveChainRef(dstChain), params);
|
|
3847
|
+
const l2Wallet = client.getL2Wallet();
|
|
3848
|
+
const from = client.account.address;
|
|
3849
|
+
let next;
|
|
3850
|
+
if (typeof params.txOverrides?.nonce === "number") {
|
|
3851
|
+
next = params.txOverrides.nonce;
|
|
3852
|
+
} else {
|
|
3853
|
+
const blockTag = params.txOverrides?.nonce ?? "pending";
|
|
3854
|
+
next = await client.l2.getTransactionCount({ address: from, blockTag });
|
|
3855
|
+
}
|
|
3856
|
+
const stepHashes = {};
|
|
3857
|
+
for (const step of plan.steps) {
|
|
3858
|
+
let gasLimit = step.tx.gas ?? step.tx.gasLimit;
|
|
3859
|
+
if (!gasLimit) {
|
|
3860
|
+
try {
|
|
3861
|
+
const est = await client.l2.estimateGas({
|
|
3862
|
+
account: from,
|
|
3863
|
+
to: step.tx.to,
|
|
3864
|
+
data: step.tx.data,
|
|
3865
|
+
value: step.tx.value
|
|
3866
|
+
});
|
|
3867
|
+
gasLimit = est * 115n / 100n;
|
|
3868
|
+
} catch {
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
let hash;
|
|
3872
|
+
try {
|
|
3873
|
+
hash = await l2Wallet.sendTransaction({
|
|
3874
|
+
to: step.tx.to,
|
|
3875
|
+
data: step.tx.data,
|
|
3876
|
+
value: step.tx.value,
|
|
3877
|
+
gas: gasLimit,
|
|
3878
|
+
maxFeePerGas: step.tx.maxFeePerGas,
|
|
3879
|
+
maxPriorityFeePerGas: step.tx.maxPriorityFeePerGas,
|
|
3880
|
+
nonce: next++,
|
|
3881
|
+
account: client.account,
|
|
3882
|
+
chain: null
|
|
3883
|
+
});
|
|
3884
|
+
stepHashes[step.key] = hash;
|
|
3885
|
+
const rcpt = await client.l2.waitForTransactionReceipt({ hash });
|
|
3886
|
+
if (rcpt.status === "reverted") {
|
|
3887
|
+
throw createError("EXECUTION", {
|
|
3888
|
+
resource: "interop",
|
|
3889
|
+
operation: "interop.create.sendTransaction",
|
|
3890
|
+
message: "Interop transaction reverted on source L2.",
|
|
3891
|
+
context: { step: step.key, txHash: hash }
|
|
3892
|
+
});
|
|
3893
|
+
}
|
|
3894
|
+
} catch (e) {
|
|
3895
|
+
if (isZKsyncError(e)) throw e;
|
|
3896
|
+
throw toZKsyncError(
|
|
3897
|
+
"EXECUTION",
|
|
3898
|
+
{
|
|
3899
|
+
resource: "interop",
|
|
3900
|
+
operation: "interop.create.sendTransaction",
|
|
3901
|
+
message: "Failed to send or confirm an interop transaction step.",
|
|
3902
|
+
context: {
|
|
3903
|
+
step: step.key,
|
|
3904
|
+
txHash: hash,
|
|
3905
|
+
nonce: next - 1
|
|
3906
|
+
}
|
|
3907
|
+
},
|
|
3908
|
+
e
|
|
3909
|
+
);
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
const last = Object.values(stepHashes).pop();
|
|
3913
|
+
return {
|
|
3914
|
+
kind: "interop",
|
|
3915
|
+
stepHashes,
|
|
3916
|
+
plan,
|
|
3917
|
+
l2SrcTxHash: last ?? "0x"
|
|
3918
|
+
};
|
|
3919
|
+
},
|
|
3920
|
+
{
|
|
3921
|
+
message: "Internal error while creating interop bundle.",
|
|
3922
|
+
ctx: { where: "interop.create" }
|
|
3923
|
+
}
|
|
3924
|
+
);
|
|
3925
|
+
const tryCreate = (dstChain, params) => toResult2(
|
|
3926
|
+
OP_INTEROP.tryCreate,
|
|
3927
|
+
() => create(dstChain, params)
|
|
3928
|
+
);
|
|
3929
|
+
const status = (dstChain, h, opts) => wrap6(OP_INTEROP.status, () => svc.status(resolveChainRef(dstChain), h, opts), {
|
|
3930
|
+
message: "Internal error while checking interop status.",
|
|
3931
|
+
ctx: { where: "interop.status" }
|
|
3932
|
+
});
|
|
3933
|
+
const wait = (dstChain, h, opts) => wrap6(OP_INTEROP.wait, () => svc.wait(resolveChainRef(dstChain), getGwProvider(), h, opts), {
|
|
3934
|
+
message: "Internal error while waiting for interop finalization.",
|
|
3935
|
+
ctx: { where: "interop.wait" }
|
|
3936
|
+
});
|
|
3937
|
+
const tryWait = (dstChain, h, opts) => toResult2(OP_INTEROP.tryWait, () => wait(dstChain, h, opts));
|
|
3938
|
+
const finalize = (dstChain, h, opts) => wrap6(
|
|
3939
|
+
OP_INTEROP.finalize,
|
|
3940
|
+
async () => {
|
|
3941
|
+
const dstProvider = resolveChainRef(dstChain);
|
|
3942
|
+
if (isInteropFinalizationInfo(h)) {
|
|
3943
|
+
return svc.finalize(dstProvider, h, opts);
|
|
3944
|
+
}
|
|
3945
|
+
const info = await svc.wait(dstProvider, getGwProvider(), h);
|
|
3946
|
+
return svc.finalize(dstProvider, info, opts);
|
|
3947
|
+
},
|
|
3948
|
+
{
|
|
3949
|
+
message: "Failed to finalize/execute interop bundle on destination.",
|
|
3950
|
+
ctx: { where: "interop.finalize" }
|
|
3951
|
+
}
|
|
3952
|
+
);
|
|
3953
|
+
const tryFinalize = (dstChain, h, opts) => toResult2(OP_INTEROP.tryFinalize, () => finalize(dstChain, h, opts));
|
|
3954
|
+
const interopGetRoot = (dstChain, rootChainId, batchNumber) => wrap6(
|
|
3955
|
+
OP_INTEROP.svc.status.getRoot,
|
|
3956
|
+
() => getInteropRoot(resolveChainRef(dstChain), rootChainId, batchNumber),
|
|
3957
|
+
{
|
|
3958
|
+
message: "Failed to get interop root from the destination chain.",
|
|
3959
|
+
ctx: { where: "interop.getInteropRoot" }
|
|
3960
|
+
}
|
|
3961
|
+
);
|
|
3962
|
+
const verifyBundle2 = (dstChain, h) => wrap6(
|
|
3963
|
+
OP_INTEROP.verify,
|
|
3964
|
+
async () => {
|
|
3965
|
+
const dstProvider = resolveChainRef(dstChain);
|
|
3966
|
+
const info = isInteropFinalizationInfo(h) ? h : await svc.wait(dstProvider, getGwProvider(), h);
|
|
3967
|
+
const result = await verifyBundle(client, dstProvider, info);
|
|
3968
|
+
await result.wait();
|
|
3969
|
+
return { bundleHash: info.bundleHash, dstExecTxHash: result.hash };
|
|
3970
|
+
},
|
|
3971
|
+
{
|
|
3972
|
+
message: "Failed to verify interop bundle on destination.",
|
|
3973
|
+
ctx: { where: "interop.verifyBundle" }
|
|
3974
|
+
}
|
|
3975
|
+
);
|
|
3976
|
+
return {
|
|
3977
|
+
quote,
|
|
3978
|
+
tryQuote,
|
|
3979
|
+
prepare,
|
|
3980
|
+
tryPrepare,
|
|
3981
|
+
create,
|
|
3982
|
+
tryCreate,
|
|
3983
|
+
status,
|
|
3984
|
+
wait,
|
|
3985
|
+
tryWait,
|
|
3986
|
+
finalize,
|
|
3987
|
+
tryFinalize,
|
|
3988
|
+
getInteropRoot: interopGetRoot,
|
|
3989
|
+
verifyBundle: verifyBundle2
|
|
3990
|
+
};
|
|
3991
|
+
}
|
|
2792
3992
|
|
|
2793
3993
|
// src/adapters/viem/sdk.ts
|
|
2794
|
-
function createViemSdk(client) {
|
|
3994
|
+
function createViemSdk(client, options) {
|
|
2795
3995
|
const tokens = createTokensResource(client);
|
|
2796
3996
|
const contracts = createContractsResource(client);
|
|
3997
|
+
const interop = createInteropResource(client, options?.interop, tokens, contracts);
|
|
2797
3998
|
return {
|
|
2798
3999
|
deposits: createDepositsResource(client, tokens, contracts),
|
|
2799
4000
|
withdrawals: createWithdrawalsResource(client, tokens, contracts),
|
|
2800
4001
|
tokens,
|
|
2801
|
-
contracts
|
|
4002
|
+
contracts,
|
|
4003
|
+
interop
|
|
2802
4004
|
};
|
|
2803
4005
|
}
|
|
2804
4006
|
|
|
2805
|
-
export { buildDirectRequestStruct, createContractsResource, createDepositsResource, createFinalizationServices, createTokensResource, createViemSdk, createWithdrawalsResource, encodeNativeTokenVaultTransferData, encodeSecondBridgeArgs, encodeSecondBridgeDataV1, encodeSecondBridgeErc20Args, encodeSecondBridgeEthArgs, getL2TransactionHashFromLogs };
|
|
4007
|
+
export { buildDirectRequestStruct, createContractsResource, createDepositsResource, createFinalizationServices, createInteropFinalizationServices, createInteropResource, createTokensResource, createViemSdk, createWithdrawalsResource, encodeNativeTokenVaultTransferData, encodeSecondBridgeArgs, encodeSecondBridgeDataV1, encodeSecondBridgeErc20Args, encodeSecondBridgeEthArgs, getL2TransactionHashFromLogs };
|