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