@continuumdao/ctm-mpc-defi 0.2.0 → 0.2.2
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/README.md +20 -78
- package/dist/agent/catalog.cjs +563 -5
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +166 -20
- package/dist/agent/catalog.js +551 -7
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/aave-v4/SKILL.md +43 -0
- package/dist/agent/skills/curve-dao/SKILL.md +13 -0
- package/dist/agent/skills/ethena/SKILL.md +10 -0
- package/dist/agent/skills/euler-v2/SKILL.md +10 -0
- package/dist/agent/skills/lido/SKILL.md +22 -0
- package/dist/agent/skills/maple-syrup/SKILL.md +10 -0
- package/dist/agent/skills/sky/SKILL.md +10 -0
- package/dist/agent/skills/uniswap-v4/SKILL.md +22 -0
- package/dist/chains/evm/index.cjs +79 -224
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +26 -26
- package/dist/chains/evm/index.js +69 -209
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/chains/near/index.d.ts +1 -1
- package/dist/chains/solana/index.d.ts +1 -1
- package/dist/core/index.cjs +68 -106
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +21 -36
- package/dist/core/index.js +57 -96
- package/dist/core/index.js.map +1 -1
- package/dist/{envelope-CcE5Cz_q.d.ts → envelope-CpBUh9eP.d.ts} +1 -1
- package/dist/index.cjs +356 -1855
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +7 -11
- package/dist/index.js +332 -1826
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs +1152 -669
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +418 -3
- package/dist/protocols/evm/aave-v4/index.js +1126 -670
- package/dist/protocols/evm/aave-v4/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.cjs +257 -131
- package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +69 -5
- package/dist/protocols/evm/curve-dao/index.js +242 -124
- package/dist/protocols/evm/curve-dao/index.js.map +1 -1
- package/dist/protocols/evm/ethena/index.cjs +394 -402
- package/dist/protocols/evm/ethena/index.cjs.map +1 -1
- package/dist/protocols/evm/ethena/index.d.ts +47 -3
- package/dist/protocols/evm/ethena/index.js +390 -404
- package/dist/protocols/evm/ethena/index.js.map +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +2810 -1191
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +465 -3
- package/dist/protocols/evm/euler-v2/index.js +2761 -1192
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/lido/index.cjs +351 -236
- package/dist/protocols/evm/lido/index.cjs.map +1 -1
- package/dist/protocols/evm/lido/index.d.ts +34 -4
- package/dist/protocols/evm/lido/index.js +348 -238
- package/dist/protocols/evm/lido/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs +390 -395
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.d.ts +23 -3
- package/dist/protocols/evm/maple/index.js +390 -397
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/sky/index.cjs +454 -232
- package/dist/protocols/evm/sky/index.cjs.map +1 -1
- package/dist/protocols/evm/sky/index.d.ts +57 -3
- package/dist/protocols/evm/sky/index.js +444 -231
- package/dist/protocols/evm/sky/index.js.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.cjs +423 -658
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -4
- package/dist/protocols/evm/uniswap-v4/index.js +422 -657
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- package/dist/{registry-oMKlO_5z.d.ts → registry-Bv5o37_w.d.ts} +1 -1
- package/dist/{types-Ce2qNHai.d.cts → types-BfjWdw1j.d.ts} +3 -1
- package/dist/{types-5u863Fd9.d.ts → types-DUeNJLr9.d.ts} +1 -1
- package/package.json +7 -6
- package/dist/agent/catalog.d.cts +0 -939
- package/dist/chains/evm/index.d.cts +0 -64
- package/dist/chains/near/index.d.cts +0 -37
- package/dist/chains/solana/index.d.cts +0 -40
- package/dist/core/index.d.cts +0 -43
- package/dist/envelope-DYDPnrHZ.d.cts +0 -35
- package/dist/index.d.cts +0 -16
- package/dist/keygen-CfNp8yKJ.d.cts +0 -9
- package/dist/keygen-DsINazx8.d.ts +0 -9
- package/dist/nodeRead-BnmSaMGO.d.cts +0 -8
- package/dist/nodeRead-BnmSaMGO.d.ts +0 -8
- package/dist/protocols/evm/aave-v4/index.d.cts +0 -500
- package/dist/protocols/evm/curve-dao/index.d.cts +0 -147
- package/dist/protocols/evm/ethena/index.d.cts +0 -161
- package/dist/protocols/evm/euler-v2/index.d.cts +0 -317
- package/dist/protocols/evm/lido/index.d.cts +0 -120
- package/dist/protocols/evm/maple/index.d.cts +0 -109
- package/dist/protocols/evm/sky/index.d.cts +0 -218
- package/dist/protocols/evm/uniswap-v4/index.d.cts +0 -324
- package/dist/registry-BwZoE668.d.cts +0 -8
- package/dist/txParams-BC7ogvdR.d.cts +0 -19
- package/dist/txParams-BC7ogvdR.d.ts +0 -19
- package/dist/types-B8idm_gu.d.cts +0 -34
- package/dist/types-Ce2qNHai.d.ts +0 -57
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var viem = require('viem');
|
|
4
|
+
var continuumNodeSdk = require('@continuumdao/continuum-node-sdk');
|
|
4
5
|
|
|
5
6
|
// src/core/registry.ts
|
|
6
7
|
var modules = [];
|
|
@@ -92,18 +93,6 @@ var UNISWAP_TRADE_BASE_DEFAULT = "https://trade-api.gateway.uniswap.org/v1";
|
|
|
92
93
|
var UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT = "2.0";
|
|
93
94
|
var UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES = 30;
|
|
94
95
|
var UNISWAP_SWAP_DEFAULT_DEADLINE_SEC_OFFSET = UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES * 60;
|
|
95
|
-
|
|
96
|
-
// src/core/nodeRead.ts
|
|
97
|
-
function nodeFetchWithReadAuth(url, init, auth) {
|
|
98
|
-
const method = (init?.method ?? "GET").toUpperCase();
|
|
99
|
-
const headers = new Headers(init?.headers);
|
|
100
|
-
if (auth.bearerOnGet && method === "GET" && auth.jwt && auth.jwt.trim()) {
|
|
101
|
-
headers.set("Authorization", `Bearer ${auth.jwt.trim()}`);
|
|
102
|
-
}
|
|
103
|
-
return fetch(url, { ...init, headers });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/protocols/evm/uniswap-v4/quote.ts
|
|
107
96
|
var DEFAULT_TRADE_BASE = "https://trade-api.gateway.uniswap.org/v1";
|
|
108
97
|
var UNISWAP_QUOTE_HEADERS_BASE = {
|
|
109
98
|
"Content-Type": "application/json",
|
|
@@ -153,7 +142,7 @@ async function fetchEthereumAddressForKeyGen(managementNodeUrl, keyGenId, readAu
|
|
|
153
142
|
throw new Error("keyGen is required");
|
|
154
143
|
}
|
|
155
144
|
const url = `${base}/getKeyGenResultById?id=${encodeURIComponent(id)}`;
|
|
156
|
-
const res = await nodeFetchWithReadAuth(url, { ...init, method: "GET", cache: "no-store" }, readAuth);
|
|
145
|
+
const res = await continuumNodeSdk.nodeFetchWithReadAuth(url, { ...init, method: "GET", cache: "no-store" }, readAuth);
|
|
157
146
|
if (!res.ok) {
|
|
158
147
|
const t = await res.text().catch(() => "");
|
|
159
148
|
throw new Error(
|
|
@@ -457,100 +446,234 @@ function formatUniswapQuoteForDisplay(res, args) {
|
|
|
457
446
|
return { title: "Quote received", lines, rawJson };
|
|
458
447
|
}
|
|
459
448
|
|
|
460
|
-
// src/core/
|
|
461
|
-
function
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
449
|
+
// src/core/purpose.ts
|
|
450
|
+
function mergePurposeText(purposeText, purposeSuffix) {
|
|
451
|
+
const t = purposeText.trim();
|
|
452
|
+
const suffix = (purposeSuffix ?? "").trim();
|
|
453
|
+
if (!suffix) return t;
|
|
454
|
+
return t ? `${t}
|
|
455
|
+
|
|
456
|
+
${suffix}` : suffix;
|
|
468
457
|
}
|
|
469
458
|
|
|
470
|
-
// src/
|
|
471
|
-
function
|
|
472
|
-
|
|
473
|
-
|
|
459
|
+
// src/core/envelope.ts
|
|
460
|
+
function finalizeMultisign(input) {
|
|
461
|
+
const { keyGen, destinationChainID, legs } = input;
|
|
462
|
+
if (legs.length === 0) {
|
|
463
|
+
throw new Error("finalizeMultisign requires at least one leg");
|
|
464
|
+
}
|
|
465
|
+
const ph = (keyGen.pubkeyhex ?? "").trim();
|
|
466
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
467
|
+
const keyList = keyGen.keylist ?? [];
|
|
468
|
+
const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
|
|
469
|
+
const first = legs[0];
|
|
470
|
+
const messageHashes = legs.map((l) => l.msgHash);
|
|
471
|
+
const messageRawBatch = legs.map((l) => l.msgRaw);
|
|
472
|
+
const batchMeta = legs.map((l) => ({
|
|
473
|
+
destinationAddress: l.destinationAddress,
|
|
474
|
+
signatureText: l.signatureText,
|
|
475
|
+
...l.audit
|
|
476
|
+
}));
|
|
477
|
+
const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
|
|
478
|
+
const extraPayload = {
|
|
479
|
+
batchMeta,
|
|
480
|
+
...input.extraJSON ?? {}
|
|
481
|
+
};
|
|
482
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
483
|
+
const bodyForSign = {
|
|
484
|
+
keyList,
|
|
485
|
+
pubKey: ph,
|
|
486
|
+
msgHash: messageHashes[0],
|
|
487
|
+
msgRaw: first.msgRaw,
|
|
488
|
+
destinationChainID,
|
|
489
|
+
destinationAddress: input.destinationAddress ?? first.destinationAddress,
|
|
490
|
+
extraJSON,
|
|
491
|
+
signatureText: first.signatureText,
|
|
492
|
+
purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
|
|
493
|
+
...first.feeSnapshot
|
|
494
|
+
};
|
|
495
|
+
if (legs.length > 1) {
|
|
496
|
+
bodyForSign.messageHashes = messageHashes;
|
|
497
|
+
bodyForSign.messageRawBatch = messageRawBatch;
|
|
498
|
+
}
|
|
499
|
+
if (proposalTxParams.length > 0) {
|
|
500
|
+
bodyForSign.proposalTxParams = proposalTxParams;
|
|
501
|
+
}
|
|
502
|
+
const valueWei = first.valueWei;
|
|
503
|
+
if (valueWei != null && valueWei > 0n) {
|
|
504
|
+
bodyForSign.value = valueWei.toString();
|
|
474
505
|
}
|
|
475
|
-
|
|
476
|
-
return
|
|
506
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
507
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
477
508
|
}
|
|
478
509
|
function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
|
|
479
510
|
if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
|
|
480
|
-
return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
|
|
511
|
+
return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
|
|
481
512
|
}
|
|
482
513
|
return (estimatedGas * 12n + 9n) / 10n;
|
|
483
514
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
515
|
+
|
|
516
|
+
// src/chains/evm/buildBatch.ts
|
|
517
|
+
async function buildEvmMultisignBatch(args) {
|
|
518
|
+
const { context, steps } = args;
|
|
519
|
+
const {
|
|
520
|
+
chainId,
|
|
521
|
+
rpcUrl,
|
|
522
|
+
executorAddress,
|
|
523
|
+
chainDetail,
|
|
524
|
+
useCustomGas,
|
|
525
|
+
customGasChainDetails,
|
|
526
|
+
keyGen,
|
|
527
|
+
purposeText
|
|
528
|
+
} = context;
|
|
529
|
+
if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
|
|
530
|
+
const ch = viem.defineChain({
|
|
531
|
+
id: chainId,
|
|
532
|
+
name: "Destination",
|
|
492
533
|
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
493
|
-
rpcUrls: { default: { http: [
|
|
534
|
+
rpcUrls: { default: { http: [rpcUrl] } }
|
|
494
535
|
});
|
|
495
|
-
const publicClient = viem.createPublicClient({
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
});
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
536
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
|
|
537
|
+
const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
|
|
538
|
+
const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
|
|
539
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
540
|
+
const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
|
|
541
|
+
const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
|
|
542
|
+
const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
|
|
543
|
+
const executor = viem.getAddress(executorAddress);
|
|
544
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
545
|
+
const legs = [];
|
|
546
|
+
for (let i = 0; i < steps.length; i++) {
|
|
547
|
+
const step = steps[i];
|
|
548
|
+
const currentNonce = baseNonce + i;
|
|
549
|
+
let estimatedGas;
|
|
550
|
+
if (args.estimateGasForStep) {
|
|
551
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
|
|
552
|
+
} else {
|
|
553
|
+
try {
|
|
554
|
+
estimatedGas = await publicClient.estimateGas({
|
|
555
|
+
to: step.to,
|
|
556
|
+
data: step.data,
|
|
557
|
+
value: step.value,
|
|
558
|
+
account: executor
|
|
559
|
+
});
|
|
560
|
+
} catch {
|
|
561
|
+
estimatedGas = step.fallbackGas ?? 100000n;
|
|
562
|
+
}
|
|
509
563
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
564
|
+
let gasLimitI;
|
|
565
|
+
if (args.resolveGasLimit) {
|
|
566
|
+
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
|
|
567
|
+
} else if (step.routerSwap) {
|
|
568
|
+
gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
|
|
569
|
+
} else {
|
|
570
|
+
gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
516
571
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
572
|
+
let proposalTxParams;
|
|
573
|
+
let feeSnapshot;
|
|
574
|
+
let serialized;
|
|
575
|
+
if (legacy) {
|
|
576
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
577
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
578
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
579
|
+
}
|
|
580
|
+
if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
|
|
581
|
+
const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
|
|
582
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
583
|
+
}
|
|
584
|
+
serialized = viem.serializeTransaction({
|
|
585
|
+
type: "legacy",
|
|
586
|
+
to: step.to,
|
|
587
|
+
data: step.data,
|
|
588
|
+
value: step.value,
|
|
589
|
+
gas: gasLimitI,
|
|
590
|
+
gasPrice: gasPriceWei,
|
|
591
|
+
nonce: currentNonce,
|
|
592
|
+
chainId
|
|
593
|
+
});
|
|
594
|
+
proposalTxParams = {
|
|
595
|
+
nonce: currentNonce,
|
|
596
|
+
gasLimit: gasLimitI.toString(),
|
|
597
|
+
txType: "legacy",
|
|
598
|
+
gasPrice: gasPriceWei.toString()
|
|
599
|
+
};
|
|
600
|
+
feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
|
|
601
|
+
} else {
|
|
602
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
603
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
604
|
+
const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
|
|
605
|
+
const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
|
|
606
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
607
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
608
|
+
const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
|
|
609
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
610
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
611
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
612
|
+
let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
|
|
613
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
614
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
615
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
616
|
+
}
|
|
617
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
|
|
618
|
+
maxFeePerGas,
|
|
619
|
+
maxPriorityFeePerGas,
|
|
620
|
+
latestBaseFeeWei
|
|
621
|
+
));
|
|
622
|
+
serialized = viem.serializeTransaction({
|
|
623
|
+
type: "eip1559",
|
|
624
|
+
to: step.to,
|
|
625
|
+
data: step.data,
|
|
626
|
+
value: step.value,
|
|
627
|
+
gas: gasLimitI,
|
|
628
|
+
maxFeePerGas,
|
|
629
|
+
maxPriorityFeePerGas,
|
|
630
|
+
nonce: currentNonce,
|
|
631
|
+
chainId
|
|
632
|
+
});
|
|
633
|
+
proposalTxParams = {
|
|
634
|
+
nonce: currentNonce,
|
|
635
|
+
gasLimit: gasLimitI.toString(),
|
|
636
|
+
txType: "eip1559",
|
|
637
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
638
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
639
|
+
};
|
|
640
|
+
feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
|
|
641
|
+
}
|
|
642
|
+
const h = viem.keccak256(serialized);
|
|
643
|
+
const msgHash = h.startsWith("0x") ? h.slice(2) : h;
|
|
644
|
+
const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
|
|
645
|
+
legs.push({
|
|
646
|
+
msgHash,
|
|
647
|
+
msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
|
|
648
|
+
destinationAddress: step.to,
|
|
649
|
+
signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
|
|
650
|
+
audit: batchMetaExtra,
|
|
651
|
+
feeSnapshot: i === 0 ? feeSnapshot : {},
|
|
652
|
+
proposalTxParams,
|
|
653
|
+
valueWei: i === 0 ? step.value : void 0
|
|
654
|
+
});
|
|
655
|
+
if (i === 0 && args.firstMsgRawNo0x != null) {
|
|
656
|
+
legs[0].msgRaw = args.firstMsgRawNo0x;
|
|
531
657
|
}
|
|
532
658
|
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
659
|
+
const extraJSON = {};
|
|
660
|
+
if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
|
|
661
|
+
extraJSON.customGasChainDetails = customGasChainDetails;
|
|
662
|
+
}
|
|
663
|
+
const result = finalizeMultisign({
|
|
664
|
+
keyGen,
|
|
665
|
+
purposeText,
|
|
666
|
+
purposeSuffix: args.purposeSuffix,
|
|
667
|
+
destinationChainID: String(chainId),
|
|
668
|
+
destinationAddress: args.destinationAddress ?? steps[0].to,
|
|
669
|
+
legs,
|
|
670
|
+
extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
|
|
671
|
+
});
|
|
672
|
+
const pv = args.payableValueWei;
|
|
673
|
+
if (pv != null && pv > 0n) {
|
|
674
|
+
result.bodyForSign.value = pv.toString();
|
|
542
675
|
}
|
|
543
|
-
return
|
|
544
|
-
}
|
|
545
|
-
function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
|
|
546
|
-
return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
|
|
547
|
-
}
|
|
548
|
-
function gweiToDecimalString(n) {
|
|
549
|
-
if (!Number.isFinite(n)) return "0";
|
|
550
|
-
if (n === 0) return "0";
|
|
551
|
-
const s = String(n);
|
|
552
|
-
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
553
|
-
return s;
|
|
676
|
+
return result;
|
|
554
677
|
}
|
|
555
678
|
|
|
556
679
|
// src/protocols/evm/uniswap-v4/swapMultisign.ts
|
|
@@ -739,6 +862,35 @@ async function uniswapCreateSwap(args) {
|
|
|
739
862
|
}
|
|
740
863
|
return parsed;
|
|
741
864
|
}
|
|
865
|
+
async function estimateUniswapRouterSwapGas(args) {
|
|
866
|
+
const fromTradeApi = parseOptionalGasLimitString(args.swapRecord.gasLimit) ?? parseOptionalGasLimitString(args.swapRecord.gas);
|
|
867
|
+
if (fromTradeApi != null && fromTradeApi > 0n) {
|
|
868
|
+
return { baseGasUnits: fromTradeApi, source: "tradeApi" };
|
|
869
|
+
}
|
|
870
|
+
try {
|
|
871
|
+
const est = await args.publicClient.estimateGas({
|
|
872
|
+
to: args.to,
|
|
873
|
+
data: args.data,
|
|
874
|
+
value: args.value,
|
|
875
|
+
account: args.executor
|
|
876
|
+
});
|
|
877
|
+
return { baseGasUnits: est, source: "rpcEstimate" };
|
|
878
|
+
} catch (e) {
|
|
879
|
+
const estimateGasError = e instanceof Error ? e.message : String(e);
|
|
880
|
+
const minRouterGas = 500000n;
|
|
881
|
+
if (args.useCustomGas) {
|
|
882
|
+
const cfg = args.chainDetail?.gasLimit != null ? parseOptionalGasLimitString(String(args.chainDetail.gasLimit)) : null;
|
|
883
|
+
if (cfg != null && cfg >= minRouterGas) {
|
|
884
|
+
return { baseGasUnits: cfg, source: "estimateFailedFallback", estimateGasError };
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return {
|
|
888
|
+
baseGasUnits: DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK,
|
|
889
|
+
source: "estimateFailedFallback",
|
|
890
|
+
estimateGasError
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
}
|
|
742
894
|
var permit2ApproveRouterAbi = [
|
|
743
895
|
{
|
|
744
896
|
name: "approve",
|
|
@@ -839,10 +991,6 @@ function permit2SpenderAndApproveWeiFromSwapCalldata(dataHex, tokenIn, quoteInpu
|
|
|
839
991
|
}
|
|
840
992
|
}
|
|
841
993
|
async function buildEvmMultisignBodyUniswapV4NativeInOnly(args, quoteInputWei) {
|
|
842
|
-
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
843
|
-
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
844
|
-
const keyList = args.keyGen.keylist ?? [];
|
|
845
|
-
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
846
994
|
const toRouter = viem.getAddress(
|
|
847
995
|
(args.swap.to ?? "").trim().startsWith("0x") ? args.swap.to.trim() : `0x${args.swap.to.trim()}`
|
|
848
996
|
);
|
|
@@ -860,214 +1008,90 @@ async function buildEvmMultisignBodyUniswapV4NativeInOnly(args, quoteInputWei) {
|
|
|
860
1008
|
"Native (ETH) in swap: could not determine payable value (no `swap.value`, no `execute` amount in calldata, and quote input is zero). Refresh the quote and request /swap again."
|
|
861
1009
|
);
|
|
862
1010
|
}
|
|
863
|
-
const ch = viem.defineChain({
|
|
864
|
-
id: args.chainId,
|
|
865
|
-
name: "Destination",
|
|
866
|
-
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
867
|
-
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
868
|
-
});
|
|
869
|
-
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
870
|
-
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
871
|
-
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
872
|
-
const latestBaseFeeWeiNativeIn = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
873
|
-
const useCustomGas = args.useCustomGas;
|
|
874
|
-
const chainGasLimitRouter = args.chainDetail?.gasLimit != null && Number.isFinite(Number(args.chainDetail.gasLimit)) && Number(args.chainDetail.gasLimit) > 0 ? Number(args.chainDetail.gasLimit) : void 0;
|
|
875
|
-
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
876
|
-
const nonce = await publicClient.getTransactionCount({ address: args.executorAddress, blockTag: "pending" });
|
|
877
|
-
const proposalTxParamsBatch = [];
|
|
878
|
-
const messageHashes = [];
|
|
879
|
-
const messageRawBatch = [];
|
|
880
1011
|
const swapRecord = args.swap;
|
|
881
|
-
const fromTradeApi = parseOptionalGasLimitString(swapRecord.gasLimit) ?? parseOptionalGasLimitString(swapRecord.gas);
|
|
882
1012
|
let gasBuildSource = "rpcEstimate";
|
|
883
1013
|
let estimateGasError;
|
|
884
|
-
let
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1014
|
+
let swapBaseGasUnits = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
1015
|
+
const dataNo0x = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
|
|
1016
|
+
const purposeSuffix = "Uniswap V4: 1-tx batch \u2014 native gas token in (no ERC-20 approve) \u2014 single payable swap (Trade /swap).";
|
|
1017
|
+
return buildEvmMultisignBatch({
|
|
1018
|
+
context: {
|
|
1019
|
+
chainCategory: "evm",
|
|
1020
|
+
keyGen: args.keyGen,
|
|
1021
|
+
purposeText: args.purposeText,
|
|
1022
|
+
chainId: args.chainId,
|
|
1023
|
+
rpcUrl: args.rpcUrl,
|
|
1024
|
+
executorAddress: args.executorAddress,
|
|
1025
|
+
chainDetail: args.chainDetail,
|
|
1026
|
+
useCustomGas: args.useCustomGas,
|
|
1027
|
+
customGasChainDetails: args.customGasChainDetails
|
|
1028
|
+
},
|
|
1029
|
+
steps: [
|
|
1030
|
+
{
|
|
891
1031
|
to: toRouter,
|
|
892
1032
|
data: dataHex,
|
|
893
1033
|
value: valueWei,
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
gasBuildSource = "rpcEstimate";
|
|
897
|
-
} catch (e) {
|
|
898
|
-
estimateGasError = e instanceof Error ? e.message : String(e);
|
|
899
|
-
const minRouterGas = 500000n;
|
|
900
|
-
if (useCustomGas) {
|
|
901
|
-
const cfg = args.chainDetail?.gasLimit != null ? parseOptionalGasLimitString(String(args.chainDetail.gasLimit)) : null;
|
|
902
|
-
if (cfg != null && cfg >= minRouterGas) {
|
|
903
|
-
baseGasUnits1 = cfg;
|
|
904
|
-
} else {
|
|
905
|
-
baseGasUnits1 = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
906
|
-
}
|
|
907
|
-
} else {
|
|
908
|
-
baseGasUnits1 = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
909
|
-
}
|
|
910
|
-
gasBuildSource = "estimateFailedFallback";
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
const gasLimit1 = routerSwapGasLimitFromEstimate(baseGasUnits1, chainGasLimitRouter);
|
|
914
|
-
const currentNonce0 = nonce;
|
|
915
|
-
let firstTxFeePayload = {};
|
|
916
|
-
if (legacy) {
|
|
917
|
-
let gasPriceWei1 = await publicClient.getGasPrice();
|
|
918
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
919
|
-
gasPriceWei1 = gasPriceWei1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
920
|
-
}
|
|
921
|
-
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
922
|
-
const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
|
|
923
|
-
if (configured > gasPriceWei1) gasPriceWei1 = configured;
|
|
924
|
-
}
|
|
925
|
-
firstTxFeePayload = { txNonce: nonce, txGasLimit: gasLimit1.toString(), txGasPrice: gasPriceWei1.toString() };
|
|
926
|
-
const ser0 = viem.serializeTransaction({
|
|
927
|
-
type: "legacy",
|
|
928
|
-
to: toRouter,
|
|
929
|
-
data: dataHex,
|
|
930
|
-
value: valueWei,
|
|
931
|
-
gas: gasLimit1,
|
|
932
|
-
gasPrice: gasPriceWei1,
|
|
933
|
-
nonce: currentNonce0,
|
|
934
|
-
chainId: args.chainId
|
|
935
|
-
});
|
|
936
|
-
const h0 = viem.keccak256(ser0);
|
|
937
|
-
messageHashes.push(h0.startsWith("0x") ? h0.slice(2) : h0);
|
|
938
|
-
messageRawBatch.push(ser0);
|
|
939
|
-
proposalTxParamsBatch.push({
|
|
940
|
-
nonce: currentNonce0,
|
|
941
|
-
gasLimit: gasLimit1.toString(),
|
|
942
|
-
txType: "legacy",
|
|
943
|
-
gasPrice: gasPriceWei1.toString()
|
|
944
|
-
});
|
|
945
|
-
} else {
|
|
946
|
-
const fetchedBase1 = feeParams.baseFeeGwei ?? 0;
|
|
947
|
-
const fetchedPriority1 = feeParams.priorityFeeGwei ?? 0;
|
|
948
|
-
const configuredBase1 = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
949
|
-
const configuredPriority1 = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
950
|
-
const effectiveBaseFeeGwei1 = Math.max(fetchedBase1, configuredBase1);
|
|
951
|
-
const effectivePriorityFeeGwei1 = Math.max(fetchedPriority1, configuredPriority1);
|
|
952
|
-
const baseFeeMultiplierPct1 = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
953
|
-
const baseComponentGwei1 = effectiveBaseFeeGwei1 * baseFeeMultiplierPct1 / 100;
|
|
954
|
-
const maxFeePerGasGwei1 = baseComponentGwei1 + effectivePriorityFeeGwei1;
|
|
955
|
-
let maxPriorityFeePerGas1 = effectivePriorityFeeGwei1 > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei1)) : viem.parseGwei("1");
|
|
956
|
-
let maxFeePerGas1 = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei1));
|
|
957
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
958
|
-
maxPriorityFeePerGas1 = maxPriorityFeePerGas1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
959
|
-
maxFeePerGas1 = maxFeePerGas1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
960
|
-
}
|
|
961
|
-
({ maxFeePerGas: maxFeePerGas1, maxPriorityFeePerGas: maxPriorityFeePerGas1 } = alignEip1559FeesWithLatestBase(maxFeePerGas1, maxPriorityFeePerGas1, latestBaseFeeWeiNativeIn));
|
|
962
|
-
firstTxFeePayload = {
|
|
963
|
-
txNonce: nonce,
|
|
964
|
-
txGasLimit: gasLimit1.toString(),
|
|
965
|
-
txMaxFeePerGas: maxFeePerGas1.toString(),
|
|
966
|
-
txMaxPriorityFeePerGas: maxPriorityFeePerGas1.toString()
|
|
967
|
-
};
|
|
968
|
-
const ser0 = viem.serializeTransaction({
|
|
969
|
-
type: "eip1559",
|
|
970
|
-
to: toRouter,
|
|
971
|
-
data: dataHex,
|
|
972
|
-
value: valueWei,
|
|
973
|
-
gas: gasLimit1,
|
|
974
|
-
maxFeePerGas: maxFeePerGas1,
|
|
975
|
-
maxPriorityFeePerGas: maxPriorityFeePerGas1,
|
|
976
|
-
nonce: currentNonce0,
|
|
977
|
-
chainId: args.chainId
|
|
978
|
-
});
|
|
979
|
-
const h0 = viem.keccak256(ser0);
|
|
980
|
-
messageHashes.push(h0.startsWith("0x") ? h0.slice(2) : h0);
|
|
981
|
-
messageRawBatch.push(ser0);
|
|
982
|
-
proposalTxParamsBatch.push({
|
|
983
|
-
nonce: currentNonce0,
|
|
984
|
-
gasLimit: gasLimit1.toString(),
|
|
985
|
-
txType: "eip1559",
|
|
986
|
-
maxFeePerGas: maxFeePerGas1.toString(),
|
|
987
|
-
maxPriorityFeePerGas: maxPriorityFeePerGas1.toString()
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
const dataNo0x = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
|
|
991
|
-
const audit = {
|
|
992
|
-
skipPermit2Batch: true,
|
|
993
|
-
inputKind: "native_eth",
|
|
994
|
-
noErc20Approve: true,
|
|
995
|
-
quoteInputWei: quoteInputWei.toString(),
|
|
996
|
-
swapValueWei: valueWei.toString(),
|
|
997
|
-
uniswapCreateSwap: {
|
|
998
|
-
requestId: args.createSwapResponse.requestId,
|
|
999
|
-
gasFee: args.createSwapResponse.gasFee,
|
|
1000
|
-
gasBuildSwap: {
|
|
1001
|
-
useCustomGas,
|
|
1002
|
-
source: gasBuildSource,
|
|
1003
|
-
baseGasUnits: baseGasUnits1.toString(),
|
|
1004
|
-
...estimateGasError != null && estimateGasError !== "" ? { estimateGasError } : {}
|
|
1005
|
-
},
|
|
1006
|
-
swap: {
|
|
1007
|
-
to: args.createSwapResponse.swap.to,
|
|
1008
|
-
value: args.createSwapResponse.swap.value,
|
|
1009
|
-
dataNibbles: (() => {
|
|
1010
|
-
const t = (args.createSwapResponse.swap.data ?? "").toString().trim();
|
|
1011
|
-
return t.startsWith("0x") ? t.length - 2 : t.length;
|
|
1012
|
-
})()
|
|
1034
|
+
routerSwap: true,
|
|
1035
|
+
fallbackGas: DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK
|
|
1013
1036
|
}
|
|
1037
|
+
],
|
|
1038
|
+
purposeSuffix,
|
|
1039
|
+
firstMsgRawNo0x: dataNo0x,
|
|
1040
|
+
destinationAddress: toRouter,
|
|
1041
|
+
estimateGasForStep: async ({ publicClient, executor }) => {
|
|
1042
|
+
const r = await estimateUniswapRouterSwapGas({
|
|
1043
|
+
publicClient,
|
|
1044
|
+
executor,
|
|
1045
|
+
to: toRouter,
|
|
1046
|
+
data: dataHex,
|
|
1047
|
+
value: valueWei,
|
|
1048
|
+
swapRecord,
|
|
1049
|
+
useCustomGas: args.useCustomGas,
|
|
1050
|
+
chainDetail: args.chainDetail
|
|
1051
|
+
});
|
|
1052
|
+
gasBuildSource = r.source;
|
|
1053
|
+
estimateGasError = r.estimateGasError;
|
|
1054
|
+
swapBaseGasUnits = r.baseGasUnits;
|
|
1055
|
+
return r.baseGasUnits;
|
|
1014
1056
|
},
|
|
1015
|
-
|
|
1016
|
-
originalPurpose: args.purposeText
|
|
1017
|
-
};
|
|
1018
|
-
const batchMeta = [
|
|
1019
|
-
{
|
|
1020
|
-
destinationAddress: toRouter,
|
|
1057
|
+
buildBatchMeta: () => ({
|
|
1021
1058
|
signatureText: JSON.stringify({
|
|
1022
1059
|
kind: "UniswapV4",
|
|
1023
1060
|
name: "UniversalRouter (payable, native in)",
|
|
1024
1061
|
note: "Single tx from Trade POST /swap; no ERC-20 approve. Calldata in messageHashes[0] / messageRawBatch[0]."
|
|
1025
1062
|
}),
|
|
1026
1063
|
evm: { type: "uniswap_v4_swap_tx", version: 1, chainId: String(args.chainId) },
|
|
1027
|
-
uniswapV4:
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
})(),
|
|
1057
|
-
...firstTxFeePayload,
|
|
1058
|
-
proposalTxParams: proposalTxParamsBatch
|
|
1059
|
-
};
|
|
1060
|
-
if (valueWei > 0n) {
|
|
1061
|
-
bodyForSign.value = valueWei.toString();
|
|
1062
|
-
}
|
|
1063
|
-
if (clientId) bodyForSign.clientId = clientId;
|
|
1064
|
-
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1064
|
+
uniswapV4: {
|
|
1065
|
+
skipPermit2Batch: true,
|
|
1066
|
+
inputKind: "native_eth",
|
|
1067
|
+
noErc20Approve: true,
|
|
1068
|
+
quoteInputWei: quoteInputWei.toString(),
|
|
1069
|
+
swapValueWei: valueWei.toString(),
|
|
1070
|
+
uniswapCreateSwap: {
|
|
1071
|
+
requestId: args.createSwapResponse.requestId,
|
|
1072
|
+
gasFee: args.createSwapResponse.gasFee,
|
|
1073
|
+
gasBuildSwap: {
|
|
1074
|
+
useCustomGas: args.useCustomGas,
|
|
1075
|
+
source: gasBuildSource,
|
|
1076
|
+
baseGasUnits: swapBaseGasUnits.toString(),
|
|
1077
|
+
...estimateGasError != null && estimateGasError !== "" ? { estimateGasError } : {}
|
|
1078
|
+
},
|
|
1079
|
+
swap: {
|
|
1080
|
+
to: args.createSwapResponse.swap.to,
|
|
1081
|
+
value: args.createSwapResponse.swap.value,
|
|
1082
|
+
dataNibbles: (() => {
|
|
1083
|
+
const t = (args.createSwapResponse.swap.data ?? "").toString().trim();
|
|
1084
|
+
return t.startsWith("0x") ? t.length - 2 : t.length;
|
|
1085
|
+
})()
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
fullQuoteFromPermitSnapshot: args.fullQuoteSnapshot,
|
|
1089
|
+
originalPurpose: args.purposeText
|
|
1090
|
+
}
|
|
1091
|
+
})
|
|
1092
|
+
});
|
|
1065
1093
|
}
|
|
1066
1094
|
async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
1067
|
-
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
1068
|
-
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
1069
|
-
const keyList = args.keyGen.keylist ?? [];
|
|
1070
|
-
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
1071
1095
|
const tokenIn = viem.getAddress(args.tokenIn);
|
|
1072
1096
|
const parsedInOut = parseUniswapQuoteClassicInOut(args.fullQuoteSnapshot);
|
|
1073
1097
|
const quoteInputWei = parsedInOut?.inputWei;
|
|
@@ -1122,298 +1146,33 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
|
1122
1146
|
return 0n;
|
|
1123
1147
|
}
|
|
1124
1148
|
})();
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
1134
|
-
const latestBaseFeeWeiSkipBatch = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
1135
|
-
const useCustomGas = args.useCustomGas;
|
|
1136
|
-
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
1137
|
-
const chainGasLimitRouter = args.chainDetail?.gasLimit != null && Number.isFinite(Number(args.chainDetail.gasLimit)) && Number(args.chainDetail.gasLimit) > 0 ? Number(args.chainDetail.gasLimit) : void 0;
|
|
1138
|
-
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
1139
|
-
const nonce = await publicClient.getTransactionCount({ address: args.executorAddress, blockTag: "pending" });
|
|
1140
|
-
const proposalTxParamsBatch = [];
|
|
1141
|
-
const messageHashes = [];
|
|
1142
|
-
const messageRawBatch = [];
|
|
1143
|
-
const approveMsgRawNo0x = approveData.startsWith("0x") ? approveData.slice(2) : approveData;
|
|
1144
|
-
let firstTxFeePayload = {};
|
|
1145
|
-
const approveGas = await publicClient.estimateGas({
|
|
1146
|
-
to: tokenIn,
|
|
1147
|
-
data: approveData,
|
|
1148
|
-
value: 0n,
|
|
1149
|
-
account: args.executorAddress
|
|
1150
|
-
});
|
|
1151
|
-
const gasLimit0 = useCustomGas ? gasLimitFromEstimateAndChainConfig(approveGas, gasLimitConfig) : approveGas;
|
|
1152
|
-
const currentNonce0 = nonce;
|
|
1153
|
-
if (legacy) {
|
|
1154
|
-
let gasPriceWei = await publicClient.getGasPrice();
|
|
1155
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1156
|
-
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1157
|
-
}
|
|
1158
|
-
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1159
|
-
const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
|
|
1160
|
-
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
1161
|
-
}
|
|
1162
|
-
firstTxFeePayload = { txNonce: nonce, txGasLimit: gasLimit0.toString(), txGasPrice: gasPriceWei.toString() };
|
|
1163
|
-
const ser0 = viem.serializeTransaction({
|
|
1164
|
-
type: "legacy",
|
|
1165
|
-
to: tokenIn,
|
|
1166
|
-
data: approveData,
|
|
1167
|
-
value: 0n,
|
|
1168
|
-
gas: gasLimit0,
|
|
1169
|
-
gasPrice: gasPriceWei,
|
|
1170
|
-
nonce: currentNonce0,
|
|
1171
|
-
chainId: args.chainId
|
|
1172
|
-
});
|
|
1173
|
-
const h0 = viem.keccak256(ser0);
|
|
1174
|
-
messageHashes.push(h0.startsWith("0x") ? h0.slice(2) : h0);
|
|
1175
|
-
messageRawBatch.push(ser0);
|
|
1176
|
-
proposalTxParamsBatch.push({
|
|
1177
|
-
nonce: currentNonce0,
|
|
1178
|
-
gasLimit: gasLimit0.toString(),
|
|
1179
|
-
txType: "legacy",
|
|
1180
|
-
gasPrice: gasPriceWei.toString()
|
|
1181
|
-
});
|
|
1182
|
-
} else {
|
|
1183
|
-
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
1184
|
-
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
1185
|
-
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1186
|
-
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1187
|
-
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
1188
|
-
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
1189
|
-
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1190
|
-
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
1191
|
-
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
1192
|
-
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
1193
|
-
let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
|
|
1194
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1195
|
-
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1196
|
-
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1197
|
-
}
|
|
1198
|
-
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
1199
|
-
maxFeePerGas,
|
|
1200
|
-
maxPriorityFeePerGas,
|
|
1201
|
-
latestBaseFeeWeiSkipBatch
|
|
1202
|
-
));
|
|
1203
|
-
firstTxFeePayload = {
|
|
1204
|
-
txNonce: nonce,
|
|
1205
|
-
txGasLimit: gasLimit0.toString(),
|
|
1206
|
-
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
1207
|
-
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1208
|
-
};
|
|
1209
|
-
const ser0 = viem.serializeTransaction({
|
|
1210
|
-
type: "eip1559",
|
|
1211
|
-
to: tokenIn,
|
|
1212
|
-
data: approveData,
|
|
1213
|
-
value: 0n,
|
|
1214
|
-
gas: gasLimit0,
|
|
1215
|
-
maxFeePerGas,
|
|
1216
|
-
maxPriorityFeePerGas,
|
|
1217
|
-
nonce: currentNonce0,
|
|
1218
|
-
chainId: args.chainId
|
|
1219
|
-
});
|
|
1220
|
-
const h0 = viem.keccak256(ser0);
|
|
1221
|
-
messageHashes.push(h0.startsWith("0x") ? h0.slice(2) : h0);
|
|
1222
|
-
messageRawBatch.push(ser0);
|
|
1223
|
-
proposalTxParamsBatch.push({
|
|
1224
|
-
nonce: currentNonce0,
|
|
1225
|
-
gasLimit: gasLimit0.toString(),
|
|
1226
|
-
txType: "eip1559",
|
|
1227
|
-
maxFeePerGas: maxFeePerGas.toString(),
|
|
1228
|
-
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1149
|
+
const swapRecord = args.swap;
|
|
1150
|
+
const swapMsgIndex = usePermit2Triple ? 2 : 1;
|
|
1151
|
+
let gasBuildSource = "rpcEstimate";
|
|
1152
|
+
let estimateGasError;
|
|
1153
|
+
let swapBaseGasUnits = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
1154
|
+
const steps = [
|
|
1155
|
+
{ to: tokenIn, data: approveData, value: 0n, fallbackGas: 100000n }
|
|
1156
|
+
];
|
|
1231
1157
|
if (usePermit2Triple) {
|
|
1232
1158
|
const permit2ApproveData = viem.encodeFunctionData({
|
|
1233
1159
|
abi: permit2ApproveRouterAbi,
|
|
1234
1160
|
functionName: "approve",
|
|
1235
1161
|
args: [tokenIn, permit2Spender, approveAmountWei, Number(expiration48)]
|
|
1236
1162
|
});
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1251
|
-
const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
|
|
1252
|
-
if (configured > gasPriceWeiP2) gasPriceWeiP2 = configured;
|
|
1253
|
-
}
|
|
1254
|
-
const serP2 = viem.serializeTransaction({
|
|
1255
|
-
type: "legacy",
|
|
1256
|
-
to: PERMIT2_ADDRESS,
|
|
1257
|
-
data: permit2ApproveData,
|
|
1258
|
-
value: 0n,
|
|
1259
|
-
gas: gasLimitP2,
|
|
1260
|
-
gasPrice: gasPriceWeiP2,
|
|
1261
|
-
nonce: currentNonceP2,
|
|
1262
|
-
chainId: args.chainId
|
|
1263
|
-
});
|
|
1264
|
-
const hP2 = viem.keccak256(serP2);
|
|
1265
|
-
messageHashes.push(hP2.startsWith("0x") ? hP2.slice(2) : hP2);
|
|
1266
|
-
messageRawBatch.push(serP2);
|
|
1267
|
-
proposalTxParamsBatch.push({
|
|
1268
|
-
nonce: currentNonceP2,
|
|
1269
|
-
gasLimit: gasLimitP2.toString(),
|
|
1270
|
-
txType: "legacy",
|
|
1271
|
-
gasPrice: gasPriceWeiP2.toString()
|
|
1272
|
-
});
|
|
1273
|
-
} else {
|
|
1274
|
-
const fetchedBaseP2 = feeParams.baseFeeGwei ?? 0;
|
|
1275
|
-
const fetchedPriorityP2 = feeParams.priorityFeeGwei ?? 0;
|
|
1276
|
-
const configuredBaseP2 = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1277
|
-
const configuredPriorityP2 = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1278
|
-
const effectiveBaseFeeGweiP2 = Math.max(fetchedBaseP2, configuredBaseP2);
|
|
1279
|
-
const effectivePriorityFeeGweiP2 = Math.max(fetchedPriorityP2, configuredPriorityP2);
|
|
1280
|
-
const baseFeeMultiplierPctP2 = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1281
|
-
const baseComponentGweiP2 = effectiveBaseFeeGweiP2 * baseFeeMultiplierPctP2 / 100;
|
|
1282
|
-
const maxFeePerGasGweiP2 = baseComponentGweiP2 + effectivePriorityFeeGweiP2;
|
|
1283
|
-
let maxPriorityFeePerGasP2 = effectivePriorityFeeGweiP2 > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGweiP2)) : viem.parseGwei("1");
|
|
1284
|
-
let maxFeePerGasP2 = viem.parseGwei(gweiToDecimalString(maxFeePerGasGweiP2));
|
|
1285
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1286
|
-
maxPriorityFeePerGasP2 = maxPriorityFeePerGasP2 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1287
|
-
maxFeePerGasP2 = maxFeePerGasP2 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1288
|
-
}
|
|
1289
|
-
({ maxFeePerGas: maxFeePerGasP2, maxPriorityFeePerGas: maxPriorityFeePerGasP2 } = alignEip1559FeesWithLatestBase(maxFeePerGasP2, maxPriorityFeePerGasP2, latestBaseFeeWeiSkipBatch));
|
|
1290
|
-
const serP2 = viem.serializeTransaction({
|
|
1291
|
-
type: "eip1559",
|
|
1292
|
-
to: PERMIT2_ADDRESS,
|
|
1293
|
-
data: permit2ApproveData,
|
|
1294
|
-
value: 0n,
|
|
1295
|
-
gas: gasLimitP2,
|
|
1296
|
-
maxFeePerGas: maxFeePerGasP2,
|
|
1297
|
-
maxPriorityFeePerGas: maxPriorityFeePerGasP2,
|
|
1298
|
-
nonce: currentNonceP2,
|
|
1299
|
-
chainId: args.chainId
|
|
1300
|
-
});
|
|
1301
|
-
const hP2 = viem.keccak256(serP2);
|
|
1302
|
-
messageHashes.push(hP2.startsWith("0x") ? hP2.slice(2) : hP2);
|
|
1303
|
-
messageRawBatch.push(serP2);
|
|
1304
|
-
proposalTxParamsBatch.push({
|
|
1305
|
-
nonce: currentNonceP2,
|
|
1306
|
-
gasLimit: gasLimitP2.toString(),
|
|
1307
|
-
txType: "eip1559",
|
|
1308
|
-
maxFeePerGas: maxFeePerGasP2.toString(),
|
|
1309
|
-
maxPriorityFeePerGas: maxPriorityFeePerGasP2.toString()
|
|
1310
|
-
});
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
const swapRecord = args.swap;
|
|
1314
|
-
const fromTradeApi = parseOptionalGasLimitString(swapRecord.gasLimit) ?? parseOptionalGasLimitString(swapRecord.gas);
|
|
1315
|
-
let gasBuildSource = "rpcEstimate";
|
|
1316
|
-
let estimateGasError;
|
|
1317
|
-
let baseGasUnits1;
|
|
1318
|
-
if (fromTradeApi != null && fromTradeApi > 0n) {
|
|
1319
|
-
baseGasUnits1 = fromTradeApi;
|
|
1320
|
-
gasBuildSource = "tradeApi";
|
|
1321
|
-
} else {
|
|
1322
|
-
try {
|
|
1323
|
-
baseGasUnits1 = await publicClient.estimateGas({
|
|
1324
|
-
to: toRouter,
|
|
1325
|
-
data: dataHex,
|
|
1326
|
-
value: valueWei,
|
|
1327
|
-
account: args.executorAddress
|
|
1328
|
-
});
|
|
1329
|
-
gasBuildSource = "rpcEstimate";
|
|
1330
|
-
} catch (e) {
|
|
1331
|
-
estimateGasError = e instanceof Error ? e.message : String(e);
|
|
1332
|
-
const minRouterGas = 500000n;
|
|
1333
|
-
if (useCustomGas) {
|
|
1334
|
-
const cfg = args.chainDetail?.gasLimit != null ? parseOptionalGasLimitString(String(args.chainDetail.gasLimit)) : null;
|
|
1335
|
-
if (cfg != null && cfg >= minRouterGas) {
|
|
1336
|
-
baseGasUnits1 = cfg;
|
|
1337
|
-
} else {
|
|
1338
|
-
baseGasUnits1 = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
1339
|
-
}
|
|
1340
|
-
} else {
|
|
1341
|
-
baseGasUnits1 = DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK;
|
|
1342
|
-
}
|
|
1343
|
-
gasBuildSource = "estimateFailedFallback";
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
const gasLimit1 = routerSwapGasLimitFromEstimate(baseGasUnits1, chainGasLimitRouter);
|
|
1347
|
-
const currentNonce1 = nonce + (usePermit2Triple ? 2 : 1);
|
|
1348
|
-
if (legacy) {
|
|
1349
|
-
let gasPriceWei1 = await publicClient.getGasPrice();
|
|
1350
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1351
|
-
gasPriceWei1 = gasPriceWei1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1352
|
-
}
|
|
1353
|
-
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1354
|
-
const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
|
|
1355
|
-
if (configured > gasPriceWei1) gasPriceWei1 = configured;
|
|
1356
|
-
}
|
|
1357
|
-
const ser1 = viem.serializeTransaction({
|
|
1358
|
-
type: "legacy",
|
|
1359
|
-
to: toRouter,
|
|
1360
|
-
data: dataHex,
|
|
1361
|
-
value: valueWei,
|
|
1362
|
-
gas: gasLimit1,
|
|
1363
|
-
gasPrice: gasPriceWei1,
|
|
1364
|
-
nonce: currentNonce1,
|
|
1365
|
-
chainId: args.chainId
|
|
1366
|
-
});
|
|
1367
|
-
const h1 = viem.keccak256(ser1);
|
|
1368
|
-
messageHashes.push(h1.startsWith("0x") ? h1.slice(2) : h1);
|
|
1369
|
-
messageRawBatch.push(ser1);
|
|
1370
|
-
proposalTxParamsBatch.push({
|
|
1371
|
-
nonce: currentNonce1,
|
|
1372
|
-
gasLimit: gasLimit1.toString(),
|
|
1373
|
-
txType: "legacy",
|
|
1374
|
-
gasPrice: gasPriceWei1.toString()
|
|
1375
|
-
});
|
|
1376
|
-
} else {
|
|
1377
|
-
const fetchedBase1 = feeParams.baseFeeGwei ?? 0;
|
|
1378
|
-
const fetchedPriority1 = feeParams.priorityFeeGwei ?? 0;
|
|
1379
|
-
const configuredBase1 = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1380
|
-
const configuredPriority1 = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1381
|
-
const effectiveBaseFeeGwei1 = Math.max(fetchedBase1, configuredBase1);
|
|
1382
|
-
const effectivePriorityFeeGwei1 = Math.max(fetchedPriority1, configuredPriority1);
|
|
1383
|
-
const baseFeeMultiplierPct1 = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1384
|
-
const baseComponentGwei1 = effectiveBaseFeeGwei1 * baseFeeMultiplierPct1 / 100;
|
|
1385
|
-
const maxFeePerGasGwei1 = baseComponentGwei1 + effectivePriorityFeeGwei1;
|
|
1386
|
-
let maxPriorityFeePerGas1 = effectivePriorityFeeGwei1 > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei1)) : viem.parseGwei("1");
|
|
1387
|
-
let maxFeePerGas1 = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei1));
|
|
1388
|
-
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1389
|
-
maxPriorityFeePerGas1 = maxPriorityFeePerGas1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1390
|
-
maxFeePerGas1 = maxFeePerGas1 * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1391
|
-
}
|
|
1392
|
-
({ maxFeePerGas: maxFeePerGas1, maxPriorityFeePerGas: maxPriorityFeePerGas1 } = alignEip1559FeesWithLatestBase(maxFeePerGas1, maxPriorityFeePerGas1, latestBaseFeeWeiSkipBatch));
|
|
1393
|
-
const ser1 = viem.serializeTransaction({
|
|
1394
|
-
type: "eip1559",
|
|
1395
|
-
to: toRouter,
|
|
1396
|
-
data: dataHex,
|
|
1397
|
-
value: valueWei,
|
|
1398
|
-
gas: gasLimit1,
|
|
1399
|
-
maxFeePerGas: maxFeePerGas1,
|
|
1400
|
-
maxPriorityFeePerGas: maxPriorityFeePerGas1,
|
|
1401
|
-
nonce: currentNonce1,
|
|
1402
|
-
chainId: args.chainId
|
|
1403
|
-
});
|
|
1404
|
-
const h1 = viem.keccak256(ser1);
|
|
1405
|
-
messageHashes.push(h1.startsWith("0x") ? h1.slice(2) : h1);
|
|
1406
|
-
messageRawBatch.push(ser1);
|
|
1407
|
-
proposalTxParamsBatch.push({
|
|
1408
|
-
nonce: currentNonce1,
|
|
1409
|
-
gasLimit: gasLimit1.toString(),
|
|
1410
|
-
txType: "eip1559",
|
|
1411
|
-
maxFeePerGas: maxFeePerGas1.toString(),
|
|
1412
|
-
maxPriorityFeePerGas: maxPriorityFeePerGas1.toString()
|
|
1413
|
-
});
|
|
1414
|
-
}
|
|
1415
|
-
const swapMsgIndex = usePermit2Triple ? 2 : 1;
|
|
1416
|
-
const audit = {
|
|
1163
|
+
steps.push({ to: PERMIT2_ADDRESS, data: permit2ApproveData, value: 0n, fallbackGas: 100000n });
|
|
1164
|
+
}
|
|
1165
|
+
steps.push({
|
|
1166
|
+
to: toRouter,
|
|
1167
|
+
data: dataHex,
|
|
1168
|
+
value: valueWei,
|
|
1169
|
+
routerSwap: true,
|
|
1170
|
+
fallbackGas: DEFAULT_UNIVERSAL_ROUTER_GAS_FALLBACK
|
|
1171
|
+
});
|
|
1172
|
+
const swapStepIndex = steps.length - 1;
|
|
1173
|
+
const approveMsgRawNo0x = approveData.startsWith("0x") ? approveData.slice(2) : approveData;
|
|
1174
|
+
const purposeSuffix = usePermit2Triple ? "Uniswap V4: 3-tx batch (classic allowance) \u2014 (1) ERC-20 approve allowance hub, (2) hub approve(Universal Router), (3) swap (Trade /swap)." : "Uniswap V4: 2-tx batch (classic allowance, dispatcher) \u2014 (1) ERC-20 approve swap.to (dispatcher pulls tokens), (2) swap (Trade /swap).";
|
|
1175
|
+
const buildSwapAudit = () => ({
|
|
1417
1176
|
skipPermit2Batch: true,
|
|
1418
1177
|
approvalPath: usePermit2Triple ? "permit2_triple" : "dispatcher",
|
|
1419
1178
|
approveAmount: {
|
|
@@ -1427,9 +1186,9 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
|
1427
1186
|
requestId: args.createSwapResponse.requestId,
|
|
1428
1187
|
gasFee: args.createSwapResponse.gasFee,
|
|
1429
1188
|
gasBuildSwap: {
|
|
1430
|
-
useCustomGas,
|
|
1189
|
+
useCustomGas: args.useCustomGas,
|
|
1431
1190
|
source: gasBuildSource,
|
|
1432
|
-
baseGasUnits:
|
|
1191
|
+
baseGasUnits: swapBaseGasUnits.toString(),
|
|
1433
1192
|
...estimateGasError != null && estimateGasError !== "" ? { estimateGasError } : {}
|
|
1434
1193
|
},
|
|
1435
1194
|
swap: {
|
|
@@ -1471,92 +1230,98 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
|
|
|
1471
1230
|
note: "Dispatcher pulls via ERC20.transferFrom(user, universalRouter, amount); allowance must be on swap.to (see cast trace TRANSFER_FROM_FAILED when only the hub is approved)."
|
|
1472
1231
|
}
|
|
1473
1232
|
}
|
|
1474
|
-
};
|
|
1475
|
-
|
|
1476
|
-
{
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1233
|
+
});
|
|
1234
|
+
return buildEvmMultisignBatch({
|
|
1235
|
+
context: {
|
|
1236
|
+
chainCategory: "evm",
|
|
1237
|
+
keyGen: args.keyGen,
|
|
1238
|
+
purposeText: args.purposeText,
|
|
1239
|
+
chainId: args.chainId,
|
|
1240
|
+
rpcUrl: args.rpcUrl,
|
|
1241
|
+
executorAddress: args.executorAddress,
|
|
1242
|
+
chainDetail: args.chainDetail,
|
|
1243
|
+
useCustomGas: args.useCustomGas,
|
|
1244
|
+
customGasChainDetails: args.customGasChainDetails
|
|
1245
|
+
},
|
|
1246
|
+
steps,
|
|
1247
|
+
purposeSuffix,
|
|
1248
|
+
firstMsgRawNo0x: approveMsgRawNo0x,
|
|
1249
|
+
destinationAddress: tokenIn,
|
|
1250
|
+
payableValueWei: valueWei > 0n ? valueWei : void 0,
|
|
1251
|
+
estimateGasForStep: async ({ step, index, publicClient, executor }) => {
|
|
1252
|
+
if (index !== swapStepIndex) {
|
|
1253
|
+
return publicClient.estimateGas({
|
|
1254
|
+
to: step.to,
|
|
1255
|
+
data: step.data,
|
|
1256
|
+
value: step.value,
|
|
1257
|
+
account: executor
|
|
1258
|
+
});
|
|
1491
1259
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1260
|
+
const r = await estimateUniswapRouterSwapGas({
|
|
1261
|
+
publicClient,
|
|
1262
|
+
executor,
|
|
1263
|
+
to: toRouter,
|
|
1264
|
+
data: dataHex,
|
|
1265
|
+
value: valueWei,
|
|
1266
|
+
swapRecord,
|
|
1267
|
+
useCustomGas: args.useCustomGas,
|
|
1268
|
+
chainDetail: args.chainDetail
|
|
1269
|
+
});
|
|
1270
|
+
gasBuildSource = r.source;
|
|
1271
|
+
estimateGasError = r.estimateGasError;
|
|
1272
|
+
swapBaseGasUnits = r.baseGasUnits;
|
|
1273
|
+
return r.baseGasUnits;
|
|
1274
|
+
},
|
|
1275
|
+
buildBatchMeta: ({ index }) => {
|
|
1276
|
+
if (index === 0) {
|
|
1277
|
+
return {
|
|
1278
|
+
signatureText: JSON.stringify({
|
|
1279
|
+
kind: "UniswapV4",
|
|
1280
|
+
name: "ERC20.approve",
|
|
1281
|
+
to: usePermit2Triple ? "allowance hub" : "dispatcher(swap.to)",
|
|
1282
|
+
function: "approve(address spender, uint256 amount)",
|
|
1283
|
+
spender: erc20ApproveSpender,
|
|
1284
|
+
amountWei: approveAmountWei.toString()
|
|
1285
|
+
}),
|
|
1286
|
+
evm: {
|
|
1287
|
+
type: usePermit2Triple ? "uniswap_v4_skip_permit2_approve" : "uniswap_v4_skip_permit2_dispatcher_approve",
|
|
1288
|
+
version: 1,
|
|
1289
|
+
chainId: String(args.chainId),
|
|
1290
|
+
...usePermit2Triple ? { permit2: PERMIT2_ADDRESS } : { dispatcher: toRouter }
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1512
1293
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1294
|
+
if (usePermit2Triple && index === 1) {
|
|
1295
|
+
return {
|
|
1296
|
+
signatureText: JSON.stringify({
|
|
1297
|
+
kind: "UniswapV4",
|
|
1298
|
+
name: "AllowanceHub.approve",
|
|
1299
|
+
function: "approve(address token, address spender, uint160 amount, uint48 expiration)",
|
|
1300
|
+
token: tokenIn,
|
|
1301
|
+
spender: permit2Spender,
|
|
1302
|
+
amountWei: approveAmountWei.toString(),
|
|
1303
|
+
expiration: expiration48.toString()
|
|
1304
|
+
}),
|
|
1305
|
+
evm: {
|
|
1306
|
+
type: "uniswap_v4_skip_permit2_permit2_approve",
|
|
1307
|
+
version: 1,
|
|
1308
|
+
chainId: String(args.chainId),
|
|
1309
|
+
permit2: PERMIT2_ADDRESS,
|
|
1310
|
+
permit2Spender
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
return {
|
|
1315
|
+
signatureText: JSON.stringify({
|
|
1316
|
+
kind: "UniswapV4",
|
|
1317
|
+
name: "UniversalRouter.execute",
|
|
1318
|
+
note: `Calldata from Trade API POST /swap; signed tx hash in messageHashes[${swapMsgIndex}].`
|
|
1319
|
+
}),
|
|
1320
|
+
evm: { type: "uniswap_v4_swap_tx", version: 1, chainId: String(args.chainId) },
|
|
1321
|
+
uniswapV4: buildSwapAudit()
|
|
1322
|
+
};
|
|
1530
1323
|
}
|
|
1531
|
-
}
|
|
1532
|
-
const extraJSON = JSON.stringify(extraPayload);
|
|
1533
|
-
const firstSigText = batchMeta[0].signatureText;
|
|
1534
|
-
const bodyForSign = {
|
|
1535
|
-
keyList,
|
|
1536
|
-
pubKey: ph,
|
|
1537
|
-
msgHash: messageHashes[0],
|
|
1538
|
-
msgRaw: approveMsgRawNo0x,
|
|
1539
|
-
messageHashes,
|
|
1540
|
-
messageRawBatch,
|
|
1541
|
-
destinationChainID: String(args.chainId),
|
|
1542
|
-
destinationAddress: tokenIn,
|
|
1543
|
-
extraJSON,
|
|
1544
|
-
signatureText: firstSigText,
|
|
1545
|
-
purpose: (() => {
|
|
1546
|
-
const t = args.purposeText.trim();
|
|
1547
|
-
const batchDesc = usePermit2Triple ? "Uniswap V4: 3-tx batch (classic allowance) \u2014 (1) ERC-20 approve allowance hub, (2) hub approve(Universal Router), (3) swap (Trade /swap)." : "Uniswap V4: 2-tx batch (classic allowance, dispatcher) \u2014 (1) ERC-20 approve swap.to (dispatcher pulls tokens), (2) swap (Trade /swap).";
|
|
1548
|
-
return (t ? `${t}
|
|
1549
|
-
|
|
1550
|
-
` : "") + batchDesc;
|
|
1551
|
-
})(),
|
|
1552
|
-
...firstTxFeePayload,
|
|
1553
|
-
proposalTxParams: proposalTxParamsBatch
|
|
1554
|
-
};
|
|
1555
|
-
if (valueWei > 0n) {
|
|
1556
|
-
bodyForSign.value = valueWei.toString();
|
|
1557
|
-
}
|
|
1558
|
-
if (clientId) bodyForSign.clientId = clientId;
|
|
1559
|
-
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1324
|
+
});
|
|
1560
1325
|
}
|
|
1561
1326
|
|
|
1562
1327
|
// src/protocols/evm/uniswap-v4/support.ts
|