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