@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 = [];
@@ -64,6 +65,17 @@ function isCurveApiChainSupported(chainId) {
64
65
  if (Number.isNaN(n) || n < 0) return false;
65
66
  return CURVE_FULL_NETWORK_CONSTANTS_CHAIN_IDS.has(n);
66
67
  }
68
+ function normalizeHumanDecimalAmount(raw, tokenDecimals) {
69
+ const t = raw.trim().replace(/,/g, "");
70
+ if (!t) return "";
71
+ if (!Number.isInteger(tokenDecimals) || tokenDecimals < 0 || tokenDecimals > 18) {
72
+ throw new Error("Invalid token decimals for amount normalization.");
73
+ }
74
+ const wei = viem.parseUnits(t, tokenDecimals);
75
+ return viem.formatUnits(wei, tokenDecimals);
76
+ }
77
+
78
+ // src/protocols/evm/curve-dao/swapDestinations.ts
67
79
  var CURVE_NATIVE_PLACEHOLDER = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
68
80
  var ETH_PLACEHOLDER = CURVE_NATIVE_PLACEHOLDER;
69
81
  function normalizeAddr(a) {
@@ -96,13 +108,7 @@ function toCurveRouterTokenId(tokenAddress, wrappedNative) {
96
108
  }
97
109
  }
98
110
  function normalizeCurveRouterAmountString(raw, tokenInDecimals) {
99
- const t = raw.trim().replace(/,/g, "");
100
- if (!t) return "";
101
- if (!Number.isInteger(tokenInDecimals) || tokenInDecimals < 0 || tokenInDecimals > 18) {
102
- throw new Error("Invalid token-in decimals for amount normalization.");
103
- }
104
- const wei = viem.parseUnits(t, tokenInDecimals);
105
- return viem.formatUnits(wei, tokenInDecimals);
111
+ return normalizeHumanDecimalAmount(raw, tokenInDecimals);
106
112
  }
107
113
  function addEdge(adj, a, b) {
108
114
  if (a === b) return;
@@ -208,6 +214,36 @@ async function loadFullCurveSessionForRpc(rpcUrl) {
208
214
  return null;
209
215
  }
210
216
  }
217
+ async function loadCurveSessionSnapshotForRpc(rpcUrl) {
218
+ const session = await loadFullCurveSessionForRpc(rpcUrl);
219
+ if (!session) return null;
220
+ const adj = {};
221
+ for (const [key, neighbors] of session.adj) {
222
+ adj[key] = [...neighbors];
223
+ }
224
+ return {
225
+ wrappedNative: session.wrappedNative,
226
+ swappableNodeKeys: [...session.swappableNodeKeys],
227
+ routerReady: Boolean(session.curve?.hasRouter?.() && session.curve.hasRouter()),
228
+ adj
229
+ };
230
+ }
231
+ async function fetchCurveCoinsDataForRpc(rpcUrl, addresses) {
232
+ const session = await loadFullCurveSessionForRpc(rpcUrl);
233
+ if (!session?.curve?.getCoinsData) return [];
234
+ const coins = await session.curve.getCoinsData(addresses);
235
+ return addresses.map((addr, i) => {
236
+ const c = coins[i];
237
+ const dec = c?.decimals;
238
+ const decimals = typeof dec === "number" && Number.isInteger(dec) && dec >= 0 && dec <= 18 ? dec : null;
239
+ return {
240
+ address: addr,
241
+ name: c?.name && String(c.name).trim() || "\u2014",
242
+ symbol: c?.symbol && String(c.symbol).trim() || addr.slice(0, 6),
243
+ decimals
244
+ };
245
+ });
246
+ }
211
247
 
212
248
  // src/protocols/evm/curve-dao/purpose.ts
213
249
  function buildCurveDaoPurposePrefill(args) {
@@ -315,16 +351,6 @@ function resolveCurveDaoRouterGasUnitsFromSignRequest(detail, batchIndex) {
315
351
  return null;
316
352
  }
317
353
 
318
- // src/core/keygen.ts
319
- function firstClientIdFromKeyGen(data) {
320
- const map = data?.ClientKeys;
321
- if (!map || typeof map !== "object") return null;
322
- for (const v of Object.values(map)) {
323
- if (typeof v === "string" && v.trim()) return v.trim();
324
- }
325
- return null;
326
- }
327
-
328
354
  // src/core/purpose.ts
329
355
  function mergePurposeText(purposeText, purposeSuffix) {
330
356
  const t = purposeText.trim();
@@ -344,7 +370,7 @@ function finalizeMultisign(input) {
344
370
  const ph = (keyGen.pubkeyhex ?? "").trim();
345
371
  if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
346
372
  const keyList = keyGen.keylist ?? [];
347
- const clientId = firstClientIdFromKeyGen(keyGen);
373
+ const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
348
374
  const first = legs[0];
349
375
  const messageHashes = legs.map((l) => l.msgHash);
350
376
  const messageRawBatch = legs.map((l) => l.msgRaw);
@@ -385,107 +411,12 @@ function finalizeMultisign(input) {
385
411
  if (clientId) bodyForSign.clientId = clientId;
386
412
  return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
387
413
  }
388
- async function fetchChainFeeParams(rpcUrl, chainId) {
389
- const url = rpcUrl.trim();
390
- if (!url) return { isEip1559: false };
391
- const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
392
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
393
- const chain = viem.defineChain({
394
- id: chainIdNum,
395
- name: "Discovery",
396
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
397
- rpcUrls: { default: { http: [url] } }
398
- });
399
- const publicClient = viem.createPublicClient({
400
- chain,
401
- transport: viem.http(url)
402
- });
403
- const getGasPriceGwei = async () => {
404
- const gasPriceWei = await publicClient.getGasPrice();
405
- return parseFloat(viem.formatUnits(gasPriceWei, 9));
406
- };
407
- try {
408
- const block = await publicClient.getBlock({ blockTag: "latest" });
409
- const baseFeePerGas = block?.baseFeePerGas;
410
- if (baseFeePerGas == null || baseFeePerGas === void 0) {
411
- const gasPriceGwei2 = await getGasPriceGwei();
412
- return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
413
- }
414
- const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
415
- let priorityFeeGwei;
416
- try {
417
- const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
418
- priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
419
- } catch {
420
- }
421
- const gasPriceGwei = await getGasPriceGwei();
422
- return {
423
- isEip1559: true,
424
- baseFeeGwei,
425
- priorityFeeGwei,
426
- gasPriceGwei
427
- };
428
- } catch {
429
- try {
430
- const gasPriceWei = await publicClient.getGasPrice();
431
- const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
432
- return { isEip1559: false, gasPriceGwei };
433
- } catch {
434
- return { isEip1559: false };
435
- }
436
- }
437
- }
438
- function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
439
- let maxP = maxPriorityFeePerGas;
440
- let maxF = maxFeePerGas;
441
- if (baseWei > 0n && maxF < baseWei + maxP) {
442
- maxF = baseWei + maxP + viem.parseGwei("0.001");
443
- }
444
- if (maxF < maxP) {
445
- maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
446
- }
447
- return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
448
- }
449
- function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
450
- return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
451
- }
452
- function gweiToDecimalString(n) {
453
- if (!Number.isFinite(n)) return "0";
454
- if (n === 0) return "0";
455
- const s = String(n);
456
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
457
- return s;
458
- }
459
-
460
- // src/chains/evm/txParams.ts
461
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
462
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
463
- return estimatedGas;
464
- }
465
- const cfg = BigInt(Math.floor(chainGasLimit));
466
- return cfg > estimatedGas ? cfg : estimatedGas;
467
- }
468
414
  function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
469
415
  if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
470
- return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
416
+ return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
471
417
  }
472
418
  return (estimatedGas * 12n + 9n) / 10n;
473
419
  }
474
- function proposalTxParamsToFeeSnapshot(params) {
475
- if (params.txType === "legacy") {
476
- return {
477
- txNonce: params.nonce,
478
- txGasLimit: params.gasLimit,
479
- txGasPrice: params.gasPrice ?? "0"
480
- };
481
- }
482
- return {
483
- txNonce: params.nonce,
484
- txGasLimit: params.gasLimit,
485
- txMaxFeePerGas: params.maxFeePerGas ?? "",
486
- txMaxPriorityFeePerGas: params.maxPriorityFeePerGas ?? ""
487
- };
488
- }
489
420
 
490
421
  // src/chains/evm/buildBatch.ts
491
422
  async function buildEvmMultisignBatch(args) {
@@ -508,7 +439,7 @@ async function buildEvmMultisignBatch(args) {
508
439
  rpcUrls: { default: { http: [rpcUrl] } }
509
440
  });
510
441
  const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
511
- const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
442
+ const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
512
443
  const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
513
444
  const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
514
445
  const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
@@ -521,15 +452,19 @@ async function buildEvmMultisignBatch(args) {
521
452
  const step = steps[i];
522
453
  const currentNonce = baseNonce + i;
523
454
  let estimatedGas;
524
- try {
525
- estimatedGas = await publicClient.estimateGas({
526
- to: step.to,
527
- data: step.data,
528
- value: step.value,
529
- account: executor
530
- });
531
- } catch {
532
- estimatedGas = step.fallbackGas ?? 100000n;
455
+ if (args.estimateGasForStep) {
456
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
457
+ } else {
458
+ try {
459
+ estimatedGas = await publicClient.estimateGas({
460
+ to: step.to,
461
+ data: step.data,
462
+ value: step.value,
463
+ account: executor
464
+ });
465
+ } catch {
466
+ estimatedGas = step.fallbackGas ?? 100000n;
467
+ }
533
468
  }
534
469
  let gasLimitI;
535
470
  if (args.resolveGasLimit) {
@@ -537,7 +472,7 @@ async function buildEvmMultisignBatch(args) {
537
472
  } else if (step.routerSwap) {
538
473
  gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
539
474
  } else {
540
- gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
475
+ gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
541
476
  }
542
477
  let proposalTxParams;
543
478
  let feeSnapshot;
@@ -548,7 +483,7 @@ async function buildEvmMultisignBatch(args) {
548
483
  gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
549
484
  }
550
485
  if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
551
- const configured = viem.parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
486
+ const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
552
487
  if (configured > gasPriceWei) gasPriceWei = configured;
553
488
  }
554
489
  serialized = viem.serializeTransaction({
@@ -567,7 +502,7 @@ async function buildEvmMultisignBatch(args) {
567
502
  txType: "legacy",
568
503
  gasPrice: gasPriceWei.toString()
569
504
  };
570
- feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
505
+ feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
571
506
  } else {
572
507
  const fetchedBase = feeParams.baseFeeGwei ?? 0;
573
508
  const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
@@ -578,13 +513,13 @@ async function buildEvmMultisignBatch(args) {
578
513
  const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
579
514
  const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
580
515
  const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
581
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
582
- let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
516
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
517
+ let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
583
518
  if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
584
519
  maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
585
520
  maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
586
521
  }
587
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
522
+ ({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
588
523
  maxFeePerGas,
589
524
  maxPriorityFeePerGas,
590
525
  latestBaseFeeWei
@@ -607,7 +542,7 @@ async function buildEvmMultisignBatch(args) {
607
542
  maxFeePerGas: maxFeePerGas.toString(),
608
543
  maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
609
544
  };
610
- feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
545
+ feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
611
546
  }
612
547
  const h = viem.keccak256(serialized);
613
548
  const msgHash = h.startsWith("0x") ? h.slice(2) : h;
@@ -806,6 +741,176 @@ async function buildEvmMultisignBodyCurveDaoBatch(args) {
806
741
  }
807
742
  });
808
743
  }
744
+ var erc20DecimalsAbi = viem.parseAbi(["function decimals() view returns (uint8)"]);
745
+ function normalizeNativeTokenIn(tokenIn) {
746
+ const trimmed = tokenIn.trim();
747
+ if (!trimmed) return tokenIn;
748
+ const lower = trimmed.toLowerCase();
749
+ if (lower === "eth" || lower === "native" || lower === "native_eth" || lower === viem.zeroAddress.toLowerCase() || lower === CURVE_NATIVE_PLACEHOLDER.toLowerCase()) {
750
+ return CURVE_NATIVE_PLACEHOLDER;
751
+ }
752
+ return trimmed;
753
+ }
754
+ async function resolveTokenInDecimals(args) {
755
+ if (args.tokenInDecimals != null && Number.isInteger(args.tokenInDecimals) && args.tokenInDecimals >= 0 && args.tokenInDecimals <= 18) {
756
+ return args.tokenInDecimals;
757
+ }
758
+ const normalized = normalizeNativeTokenIn(args.tokenIn);
759
+ if (normalized.toLowerCase() === CURVE_NATIVE_PLACEHOLDER.toLowerCase()) {
760
+ return 18;
761
+ }
762
+ const addr = viem.getAddress(
763
+ normalized.startsWith("0x") ? normalized : `0x${normalized}`
764
+ );
765
+ const ch = viem.defineChain({
766
+ id: args.chainId,
767
+ name: "CurveQuote",
768
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
769
+ rpcUrls: { default: { http: [args.rpcUrl] } }
770
+ });
771
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
772
+ const decimalsN = await publicClient.readContract({
773
+ address: addr,
774
+ abi: erc20DecimalsAbi,
775
+ functionName: "decimals"
776
+ });
777
+ return Number(decimalsN);
778
+ }
779
+ async function curveDaoQuote(args) {
780
+ const rpcUrl = (args.rpcUrl ?? "").trim();
781
+ if (!rpcUrl) {
782
+ throw new Error("rpcUrl is required.");
783
+ }
784
+ if (!Number.isFinite(args.chainId) || args.chainId <= 0) {
785
+ throw new Error("chainId must be a positive integer.");
786
+ }
787
+ if (!isCurveApiChainSupported(args.chainId)) {
788
+ throw new Error(`Chain ${args.chainId} is not supported by Curve Router NG.`);
789
+ }
790
+ const tokenInDecimals = await resolveTokenInDecimals({
791
+ rpcUrl,
792
+ chainId: args.chainId,
793
+ tokenIn: args.tokenIn,
794
+ tokenInDecimals: args.tokenInDecimals
795
+ });
796
+ const amountForRouter = normalizeCurveRouterAmountString(
797
+ args.amountHuman,
798
+ tokenInDecimals
799
+ );
800
+ if (!amountForRouter) {
801
+ throw new Error("amountHuman must be a positive decimal string.");
802
+ }
803
+ const session = await loadFullCurveSessionForRpc(rpcUrl);
804
+ if (!session?.curve?.router?.getBestRouteAndOutput) {
805
+ throw new Error("Curve router is not available (session load failed or chain has no router).");
806
+ }
807
+ const { curve, wrappedNative: wn } = session;
808
+ const tokenInRouterId = toCurveRouterTokenId(normalizeNativeTokenIn(args.tokenIn), wn);
809
+ const tokenOutTrim = (args.tokenOut ?? "").trim();
810
+ const tokenOutRouterId = toCurveRouterTokenId(
811
+ tokenOutTrim.toLowerCase() === CURVE_NATIVE_PLACEHOLDER.toLowerCase() ? CURVE_NATIVE_PLACEHOLDER : viem.getAddress(
812
+ tokenOutTrim.startsWith("0x") ? tokenOutTrim : `0x${tokenOutTrim}`
813
+ ),
814
+ wn
815
+ );
816
+ const res = await curve.router.getBestRouteAndOutput(
817
+ tokenInRouterId,
818
+ tokenOutRouterId,
819
+ amountForRouter
820
+ );
821
+ const route = res?.route;
822
+ if (!route || Array.isArray(route) && route.length === 0) {
823
+ throw new Error(
824
+ "Curve router could not find a pool route for this pair and size."
825
+ );
826
+ }
827
+ const outStr = String(res?.output ?? "0");
828
+ let priceImpactPercent = null;
829
+ try {
830
+ const pi = await curve.router.swapPriceImpact(
831
+ tokenInRouterId,
832
+ tokenOutRouterId,
833
+ amountForRouter
834
+ );
835
+ if (typeof pi === "number" && Number.isFinite(pi)) {
836
+ priceImpactPercent = pi;
837
+ }
838
+ } catch {
839
+ }
840
+ return {
841
+ inputAmount: amountForRouter,
842
+ output: outStr,
843
+ route,
844
+ priceImpactPercent,
845
+ tokenInRouterId,
846
+ tokenOutRouterId
847
+ };
848
+ }
849
+ function curveProbeInAmountString(fullNormalizedHuman, tokenInDecimals) {
850
+ const t = (fullNormalizedHuman ?? "").trim();
851
+ if (!t) return null;
852
+ if (!Number.isInteger(tokenInDecimals) || tokenInDecimals < 0 || tokenInDecimals > 18) return null;
853
+ let wei;
854
+ try {
855
+ wei = viem.parseUnits(t, tokenInDecimals);
856
+ } catch {
857
+ return null;
858
+ }
859
+ if (wei <= 1n) return null;
860
+ let p = wei / 10000n;
861
+ if (p < 1n) p = 1n;
862
+ if (p >= wei) p = wei - 1n;
863
+ return viem.formatUnits(p, tokenInDecimals);
864
+ }
865
+ function computeCurveExecutionSlippagePercent(args) {
866
+ const aIn = parseFloat((args.fullInHuman ?? "").replace(/,/g, "").trim());
867
+ const aOut = parseFloat((args.fullOutHuman ?? "").replace(/,/g, "").trim());
868
+ const pIn = parseFloat((args.probeInHuman ?? "").replace(/,/g, "").trim());
869
+ const pOut = parseFloat((args.probeOutHuman ?? "").replace(/,/g, "").trim());
870
+ if (!(aIn > 0) || !(aOut >= 0) || !(pIn > 0) || !Number.isFinite(pOut) || pOut < 0) return null;
871
+ const marginal = pOut / pIn;
872
+ const avg = aOut / aIn;
873
+ if (!Number.isFinite(marginal) || marginal <= 0 || !Number.isFinite(avg)) return null;
874
+ const pct = (1 - avg / marginal) * 100;
875
+ if (!Number.isFinite(pct)) return null;
876
+ return Math.max(0, pct);
877
+ }
878
+ function formatCurveExchangeLine(amountIn, amountOut, tokenInSymbol, tokenOutSymbol) {
879
+ const a = parseFloat((amountIn ?? "").replace(/,/g, "").trim());
880
+ const b = parseFloat((amountOut ?? "").replace(/,/g, "").trim());
881
+ const sIn = (tokenInSymbol || "").trim() || "in";
882
+ const sOut = (tokenOutSymbol || "").trim() || "out";
883
+ if (!(a > 0) || !Number.isFinite(b)) return "\u2014";
884
+ const r = b / a;
885
+ let rate;
886
+ if (r >= 1e-4) {
887
+ rate = r.toFixed(8).replace(/0+$/, "").replace(/\.$/, "");
888
+ } else if (r > 0) {
889
+ rate = r.toPrecision(6);
890
+ } else {
891
+ rate = "0";
892
+ }
893
+ if (!rate) rate = "0";
894
+ return `1 ${sIn} \u2248 ${rate} ${sOut}`;
895
+ }
896
+ function formatCurvePriceImpact(percent0to100) {
897
+ if (percent0to100 == null || !Number.isFinite(percent0to100) || percent0to100 < 0) return "\u2014";
898
+ if (percent0to100 === 0) return "0%";
899
+ if (percent0to100 < 1e-4) return "<0.0001%";
900
+ const t = percent0to100 < 0.01 ? percent0to100.toFixed(4) : percent0to100 < 1 ? percent0to100.toFixed(3) : percent0to100 < 10 ? percent0to100.toFixed(2) : percent0to100.toFixed(1);
901
+ return `${t.replace(/(\.\d*?[1-9])0+$/g, "$1").replace(/\.0$/, "")}%`;
902
+ }
903
+ function formatMinOutAtUserSlippage(quoteOutDecimal, userSlippagePercentInput, tokenOutSymbol) {
904
+ const q = parseFloat((quoteOutDecimal ?? "").replace(/,/g, "").trim());
905
+ const rawS = (userSlippagePercentInput ?? "0.5").trim().replace(/,/g, "");
906
+ const slip = Number.parseFloat(rawS);
907
+ const s = Number.isFinite(slip) && slip >= 0 && slip < 100 ? slip : 0.5;
908
+ if (!Number.isFinite(q) || q < 0) return "\u2014";
909
+ const min = q * ((100 - s) / 100);
910
+ const sOut = (tokenOutSymbol || "").trim() || "out";
911
+ const t = min.toFixed(8).replace(/\.?0+$/, "").replace(/(\.\d*?[1-9])0+$/, "$1");
912
+ return `${t} ${sOut}`.trim();
913
+ }
809
914
 
810
915
  // src/protocols/evm/curve-dao/index.ts
811
916
  var CURVE_DAO_PROTOCOL_ID = "curve-dao";
@@ -821,6 +926,19 @@ var curveDaoProtocolModule = {
821
926
  return token.kind === "native" || token.kind === "erc20";
822
927
  },
823
928
  actions: [
929
+ {
930
+ id: "curve-dao.quote",
931
+ protocolId: CURVE_DAO_PROTOCOL_ID,
932
+ chainCategory: "evm",
933
+ description: "Quote swap via Curve Router NG (getBestRouteAndOutput)",
934
+ commonParams: [],
935
+ params: {
936
+ chainId: { type: "number", required: true, description: "EVM chain id" },
937
+ tokenIn: { type: "address", required: true, description: "Token in or native placeholder" },
938
+ tokenOut: { type: "address", required: true, description: "Token out or native placeholder" },
939
+ amountHuman: { type: "string", required: true, description: "Human-readable input amount" }
940
+ }
941
+ },
824
942
  {
825
943
  id: "curve-dao.swap",
826
944
  protocolId: CURVE_DAO_PROTOCOL_ID,
@@ -851,14 +969,22 @@ exports.bfsCurveSwapDestinations = bfsCurveSwapDestinations;
851
969
  exports.buildCurveDaoPurposePrefill = buildCurveDaoPurposePrefill;
852
970
  exports.buildCurveLiquidityGraphFromApi = buildCurveLiquidityGraphFromApi;
853
971
  exports.buildEvmMultisignBodyCurveDaoBatch = buildEvmMultisignBodyCurveDaoBatch;
972
+ exports.computeCurveExecutionSlippagePercent = computeCurveExecutionSlippagePercent;
854
973
  exports.curveDao = curveDao;
855
974
  exports.curveDaoProtocolModule = curveDaoProtocolModule;
975
+ exports.curveDaoQuote = curveDaoQuote;
976
+ exports.curveProbeInAmountString = curveProbeInAmountString;
856
977
  exports.fetchAllCurvePools = fetchAllCurvePools;
978
+ exports.fetchCurveCoinsDataForRpc = fetchCurveCoinsDataForRpc;
979
+ exports.formatCurveExchangeLine = formatCurveExchangeLine;
980
+ exports.formatCurvePriceImpact = formatCurvePriceImpact;
981
+ exports.formatMinOutAtUserSlippage = formatMinOutAtUserSlippage;
857
982
  exports.isCurveApiChainSupported = isCurveApiChainSupported;
858
983
  exports.isCurveDaoChainSupported = isCurveApiChainSupported;
859
984
  exports.isCurveDaoErc20ApproveEvmSignRequest = isCurveDaoErc20ApproveEvmSignRequest;
860
985
  exports.isCurveDaoSwapEvmSignRequest = isCurveDaoSwapEvmSignRequest;
861
986
  exports.isCurveTokenKeySwappable = isCurveTokenKeySwappable;
987
+ exports.loadCurveSessionSnapshotForRpc = loadCurveSessionSnapshotForRpc;
862
988
  exports.loadFullCurveSessionForRpc = loadFullCurveSessionForRpc;
863
989
  exports.normalizeCurveRouterAmountString = normalizeCurveRouterAmountString;
864
990
  exports.resolveCurveDaoRouterGasUnitsFromSignRequest = resolveCurveDaoRouterGasUnitsFromSignRequest;