@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.
Files changed (100) hide show
  1. package/README.md +20 -78
  2. package/dist/agent/catalog.cjs +563 -5
  3. package/dist/agent/catalog.cjs.map +1 -1
  4. package/dist/agent/catalog.d.ts +166 -20
  5. package/dist/agent/catalog.js +551 -7
  6. package/dist/agent/catalog.js.map +1 -1
  7. package/dist/agent/skills/aave-v4/SKILL.md +43 -0
  8. package/dist/agent/skills/curve-dao/SKILL.md +13 -0
  9. package/dist/agent/skills/ethena/SKILL.md +10 -0
  10. package/dist/agent/skills/euler-v2/SKILL.md +10 -0
  11. package/dist/agent/skills/lido/SKILL.md +22 -0
  12. package/dist/agent/skills/maple-syrup/SKILL.md +10 -0
  13. package/dist/agent/skills/sky/SKILL.md +10 -0
  14. package/dist/agent/skills/uniswap-v4/SKILL.md +22 -0
  15. package/dist/chains/evm/index.cjs +79 -224
  16. package/dist/chains/evm/index.cjs.map +1 -1
  17. package/dist/chains/evm/index.d.ts +26 -26
  18. package/dist/chains/evm/index.js +69 -209
  19. package/dist/chains/evm/index.js.map +1 -1
  20. package/dist/chains/near/index.d.ts +1 -1
  21. package/dist/chains/solana/index.d.ts +1 -1
  22. package/dist/core/index.cjs +68 -106
  23. package/dist/core/index.cjs.map +1 -1
  24. package/dist/core/index.d.ts +21 -36
  25. package/dist/core/index.js +57 -96
  26. package/dist/core/index.js.map +1 -1
  27. package/dist/{envelope-CcE5Cz_q.d.ts → envelope-CpBUh9eP.d.ts} +1 -1
  28. package/dist/index.cjs +356 -1855
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +7 -11
  31. package/dist/index.js +332 -1826
  32. package/dist/index.js.map +1 -1
  33. package/dist/protocols/evm/aave-v4/index.cjs +1152 -669
  34. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  35. package/dist/protocols/evm/aave-v4/index.d.ts +418 -3
  36. package/dist/protocols/evm/aave-v4/index.js +1126 -670
  37. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  38. package/dist/protocols/evm/curve-dao/index.cjs +257 -131
  39. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
  40. package/dist/protocols/evm/curve-dao/index.d.ts +69 -5
  41. package/dist/protocols/evm/curve-dao/index.js +242 -124
  42. package/dist/protocols/evm/curve-dao/index.js.map +1 -1
  43. package/dist/protocols/evm/ethena/index.cjs +394 -402
  44. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  45. package/dist/protocols/evm/ethena/index.d.ts +47 -3
  46. package/dist/protocols/evm/ethena/index.js +390 -404
  47. package/dist/protocols/evm/ethena/index.js.map +1 -1
  48. package/dist/protocols/evm/euler-v2/index.cjs +2810 -1191
  49. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  50. package/dist/protocols/evm/euler-v2/index.d.ts +465 -3
  51. package/dist/protocols/evm/euler-v2/index.js +2761 -1192
  52. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  53. package/dist/protocols/evm/lido/index.cjs +351 -236
  54. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  55. package/dist/protocols/evm/lido/index.d.ts +34 -4
  56. package/dist/protocols/evm/lido/index.js +348 -238
  57. package/dist/protocols/evm/lido/index.js.map +1 -1
  58. package/dist/protocols/evm/maple/index.cjs +390 -395
  59. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  60. package/dist/protocols/evm/maple/index.d.ts +23 -3
  61. package/dist/protocols/evm/maple/index.js +390 -397
  62. package/dist/protocols/evm/maple/index.js.map +1 -1
  63. package/dist/protocols/evm/sky/index.cjs +454 -232
  64. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  65. package/dist/protocols/evm/sky/index.d.ts +57 -3
  66. package/dist/protocols/evm/sky/index.js +444 -231
  67. package/dist/protocols/evm/sky/index.js.map +1 -1
  68. package/dist/protocols/evm/uniswap-v4/index.cjs +423 -658
  69. package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
  70. package/dist/protocols/evm/uniswap-v4/index.d.ts +3 -4
  71. package/dist/protocols/evm/uniswap-v4/index.js +422 -657
  72. package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
  73. package/dist/{registry-oMKlO_5z.d.ts → registry-Bv5o37_w.d.ts} +1 -1
  74. package/dist/{types-Ce2qNHai.d.cts → types-BfjWdw1j.d.ts} +3 -1
  75. package/dist/{types-5u863Fd9.d.ts → types-DUeNJLr9.d.ts} +1 -1
  76. package/package.json +7 -6
  77. package/dist/agent/catalog.d.cts +0 -939
  78. package/dist/chains/evm/index.d.cts +0 -64
  79. package/dist/chains/near/index.d.cts +0 -37
  80. package/dist/chains/solana/index.d.cts +0 -40
  81. package/dist/core/index.d.cts +0 -43
  82. package/dist/envelope-DYDPnrHZ.d.cts +0 -35
  83. package/dist/index.d.cts +0 -16
  84. package/dist/keygen-CfNp8yKJ.d.cts +0 -9
  85. package/dist/keygen-DsINazx8.d.ts +0 -9
  86. package/dist/nodeRead-BnmSaMGO.d.cts +0 -8
  87. package/dist/nodeRead-BnmSaMGO.d.ts +0 -8
  88. package/dist/protocols/evm/aave-v4/index.d.cts +0 -500
  89. package/dist/protocols/evm/curve-dao/index.d.cts +0 -147
  90. package/dist/protocols/evm/ethena/index.d.cts +0 -161
  91. package/dist/protocols/evm/euler-v2/index.d.cts +0 -317
  92. package/dist/protocols/evm/lido/index.d.cts +0 -120
  93. package/dist/protocols/evm/maple/index.d.cts +0 -109
  94. package/dist/protocols/evm/sky/index.d.cts +0 -218
  95. package/dist/protocols/evm/uniswap-v4/index.d.cts +0 -324
  96. package/dist/registry-BwZoE668.d.cts +0 -8
  97. package/dist/txParams-BC7ogvdR.d.cts +0 -19
  98. package/dist/txParams-BC7ogvdR.d.ts +0 -19
  99. package/dist/types-B8idm_gu.d.cts +0 -34
  100. 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/keygen.ts
461
- function firstClientIdFromKeyGen(data) {
462
- const map = data?.ClientKeys;
463
- if (!map || typeof map !== "object") return null;
464
- for (const v of Object.values(map)) {
465
- if (typeof v === "string" && v.trim()) return v.trim();
466
- }
467
- return null;
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/chains/evm/txParams.ts
471
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
472
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
473
- return estimatedGas;
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
- const cfg = BigInt(Math.floor(chainGasLimit));
476
- return cfg > estimatedGas ? cfg : estimatedGas;
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
- async function fetchChainFeeParams(rpcUrl, chainId) {
485
- const url = rpcUrl.trim();
486
- if (!url) return { isEip1559: false };
487
- const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
488
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
489
- const chain = viem.defineChain({
490
- id: chainIdNum,
491
- name: "Discovery",
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: [url] } }
534
+ rpcUrls: { default: { http: [rpcUrl] } }
494
535
  });
495
- const publicClient = viem.createPublicClient({
496
- chain,
497
- transport: viem.http(url)
498
- });
499
- const getGasPriceGwei = async () => {
500
- const gasPriceWei = await publicClient.getGasPrice();
501
- return parseFloat(viem.formatUnits(gasPriceWei, 9));
502
- };
503
- try {
504
- const block = await publicClient.getBlock({ blockTag: "latest" });
505
- const baseFeePerGas = block?.baseFeePerGas;
506
- if (baseFeePerGas == null || baseFeePerGas === void 0) {
507
- const gasPriceGwei2 = await getGasPriceGwei();
508
- return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
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
- const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
511
- let priorityFeeGwei;
512
- try {
513
- const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
514
- priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
515
- } catch {
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
- const gasPriceGwei = await getGasPriceGwei();
518
- return {
519
- isEip1559: true,
520
- baseFeeGwei,
521
- priorityFeeGwei,
522
- gasPriceGwei
523
- };
524
- } catch {
525
- try {
526
- const gasPriceWei = await publicClient.getGasPrice();
527
- const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
528
- return { isEip1559: false, gasPriceGwei };
529
- } catch {
530
- return { isEip1559: false };
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
- function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
535
- let maxP = maxPriorityFeePerGas;
536
- let maxF = maxFeePerGas;
537
- if (baseWei > 0n && maxF < baseWei + maxP) {
538
- maxF = baseWei + maxP + viem.parseGwei("0.001");
539
- }
540
- if (maxF < maxP) {
541
- maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
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 { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
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 baseGasUnits1;
885
- if (fromTradeApi != null && fromTradeApi > 0n) {
886
- baseGasUnits1 = fromTradeApi;
887
- gasBuildSource = "tradeApi";
888
- } else {
889
- try {
890
- baseGasUnits1 = await publicClient.estimateGas({
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
- account: args.executorAddress
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
- fullQuoteFromPermitSnapshot: args.fullQuoteSnapshot,
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: audit
1028
- }
1029
- ];
1030
- const extraPayload = { batchMeta };
1031
- if (useCustomGas) {
1032
- const snap = args.customGasChainDetails;
1033
- if (snap && typeof snap === "object" && !Array.isArray(snap) && Object.keys(snap).length > 0) {
1034
- extraPayload.customGasChainDetails = snap;
1035
- }
1036
- }
1037
- const extraJSON = JSON.stringify(extraPayload);
1038
- const firstSigText = batchMeta[0].signatureText;
1039
- const bodyForSign = {
1040
- keyList,
1041
- pubKey: ph,
1042
- msgHash: messageHashes[0],
1043
- msgRaw: dataNo0x,
1044
- messageHashes,
1045
- messageRawBatch,
1046
- destinationChainID: String(args.chainId),
1047
- destinationAddress: toRouter,
1048
- extraJSON,
1049
- signatureText: firstSigText,
1050
- purpose: (() => {
1051
- const t = args.purposeText.trim();
1052
- const batchDesc = "Uniswap V4: 1-tx batch \u2014 native gas token in (no ERC-20 approve) \u2014 single payable swap (Trade /swap).";
1053
- return (t ? `${t}
1054
-
1055
- ` : "") + batchDesc;
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 ch = viem.defineChain({
1126
- id: args.chainId,
1127
- name: "Destination",
1128
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1129
- rpcUrls: { default: { http: [args.rpcUrl] } }
1130
- });
1131
- const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1132
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
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
- const approveP2Gas = await publicClient.estimateGas({
1238
- to: PERMIT2_ADDRESS,
1239
- data: permit2ApproveData,
1240
- value: 0n,
1241
- account: args.executorAddress
1242
- });
1243
- const gasLimitP2 = useCustomGas ? gasLimitFromEstimateAndChainConfig(approveP2Gas, gasLimitConfig) : approveP2Gas;
1244
- const currentNonceP2 = nonce + 1;
1245
- if (legacy) {
1246
- let gasPriceWeiP2 = await publicClient.getGasPrice();
1247
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1248
- gasPriceWeiP2 = gasPriceWeiP2 * BigInt(100 + gasFeeMultiplier) / 100n;
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: baseGasUnits1.toString(),
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
- const batchMeta = [
1476
- {
1477
- destinationAddress: tokenIn,
1478
- signatureText: JSON.stringify({
1479
- kind: "UniswapV4",
1480
- name: "ERC20.approve",
1481
- to: usePermit2Triple ? "allowance hub" : "dispatcher(swap.to)",
1482
- function: "approve(address spender, uint256 amount)",
1483
- spender: erc20ApproveSpender,
1484
- amountWei: approveAmountWei.toString()
1485
- }),
1486
- evm: {
1487
- type: usePermit2Triple ? "uniswap_v4_skip_permit2_approve" : "uniswap_v4_skip_permit2_dispatcher_approve",
1488
- version: 1,
1489
- chainId: String(args.chainId),
1490
- ...usePermit2Triple ? { permit2: PERMIT2_ADDRESS } : { dispatcher: toRouter }
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
- if (usePermit2Triple) {
1495
- batchMeta.push({
1496
- destinationAddress: PERMIT2_ADDRESS,
1497
- signatureText: JSON.stringify({
1498
- kind: "UniswapV4",
1499
- name: "AllowanceHub.approve",
1500
- function: "approve(address token, address spender, uint160 amount, uint48 expiration)",
1501
- token: tokenIn,
1502
- spender: permit2Spender,
1503
- amountWei: approveAmountWei.toString(),
1504
- expiration: expiration48.toString()
1505
- }),
1506
- evm: {
1507
- type: "uniswap_v4_skip_permit2_permit2_approve",
1508
- version: 1,
1509
- chainId: String(args.chainId),
1510
- permit2: PERMIT2_ADDRESS,
1511
- permit2Spender
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
- batchMeta.push({
1516
- destinationAddress: toRouter,
1517
- signatureText: JSON.stringify({
1518
- kind: "UniswapV4",
1519
- name: "UniversalRouter.execute",
1520
- note: `Calldata from Trade API POST /swap; signed tx hash in messageHashes[${swapMsgIndex}].`
1521
- }),
1522
- evm: { type: "uniswap_v4_swap_tx", version: 1, chainId: String(args.chainId) },
1523
- uniswapV4: audit
1524
- });
1525
- const extraPayload = { batchMeta };
1526
- if (useCustomGas) {
1527
- const snap = args.customGasChainDetails;
1528
- if (snap && typeof snap === "object" && !Array.isArray(snap) && Object.keys(snap).length > 0) {
1529
- extraPayload.customGasChainDetails = snap;
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