@continuumdao/ctm-mpc-defi 0.2.1 → 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 (57) hide show
  1. package/dist/agent/catalog.cjs +62 -11
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +27 -1
  4. package/dist/agent/catalog.js +61 -12
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/curve-dao/SKILL.md +2 -1
  7. package/dist/chains/evm/index.cjs +54 -0
  8. package/dist/chains/evm/index.cjs.map +1 -1
  9. package/dist/chains/evm/index.d.ts +13 -1
  10. package/dist/chains/evm/index.js +52 -2
  11. package/dist/chains/evm/index.js.map +1 -1
  12. package/dist/core/index.cjs +64 -0
  13. package/dist/core/index.cjs.map +1 -1
  14. package/dist/core/index.d.ts +20 -1
  15. package/dist/core/index.js +56 -1
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/index.cjs +131 -0
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +120 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/protocols/evm/aave-v4/index.cjs +766 -6
  23. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  24. package/dist/protocols/evm/aave-v4/index.d.ts +417 -1
  25. package/dist/protocols/evm/aave-v4/index.js +741 -8
  26. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  27. package/dist/protocols/evm/curve-dao/index.cjs +233 -7
  28. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
  29. package/dist/protocols/evm/curve-dao/index.d.ts +66 -1
  30. package/dist/protocols/evm/curve-dao/index.js +227 -9
  31. package/dist/protocols/evm/curve-dao/index.js.map +1 -1
  32. package/dist/protocols/evm/ethena/index.cjs +104 -0
  33. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  34. package/dist/protocols/evm/ethena/index.d.ts +46 -1
  35. package/dist/protocols/evm/ethena/index.js +99 -1
  36. package/dist/protocols/evm/ethena/index.js.map +1 -1
  37. package/dist/protocols/evm/euler-v2/index.cjs +2334 -37
  38. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  39. package/dist/protocols/evm/euler-v2/index.d.ts +464 -1
  40. package/dist/protocols/evm/euler-v2/index.js +2286 -39
  41. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  42. package/dist/protocols/evm/lido/index.cjs +110 -0
  43. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  44. package/dist/protocols/evm/lido/index.d.ts +33 -2
  45. package/dist/protocols/evm/lido/index.js +107 -2
  46. package/dist/protocols/evm/lido/index.js.map +1 -1
  47. package/dist/protocols/evm/maple/index.cjs +83 -0
  48. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  49. package/dist/protocols/evm/maple/index.d.ts +22 -1
  50. package/dist/protocols/evm/maple/index.js +82 -1
  51. package/dist/protocols/evm/maple/index.js.map +1 -1
  52. package/dist/protocols/evm/sky/index.cjs +217 -0
  53. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  54. package/dist/protocols/evm/sky/index.d.ts +56 -1
  55. package/dist/protocols/evm/sky/index.js +210 -2
  56. package/dist/protocols/evm/sky/index.js.map +1 -1
  57. package/package.json +1 -1
@@ -65,6 +65,17 @@ function isCurveApiChainSupported(chainId) {
65
65
  if (Number.isNaN(n) || n < 0) return false;
66
66
  return CURVE_FULL_NETWORK_CONSTANTS_CHAIN_IDS.has(n);
67
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
68
79
  var CURVE_NATIVE_PLACEHOLDER = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
69
80
  var ETH_PLACEHOLDER = CURVE_NATIVE_PLACEHOLDER;
70
81
  function normalizeAddr(a) {
@@ -97,13 +108,7 @@ function toCurveRouterTokenId(tokenAddress, wrappedNative) {
97
108
  }
98
109
  }
99
110
  function normalizeCurveRouterAmountString(raw, tokenInDecimals) {
100
- const t = raw.trim().replace(/,/g, "");
101
- if (!t) return "";
102
- if (!Number.isInteger(tokenInDecimals) || tokenInDecimals < 0 || tokenInDecimals > 18) {
103
- throw new Error("Invalid token-in decimals for amount normalization.");
104
- }
105
- const wei = viem.parseUnits(t, tokenInDecimals);
106
- return viem.formatUnits(wei, tokenInDecimals);
111
+ return normalizeHumanDecimalAmount(raw, tokenInDecimals);
107
112
  }
108
113
  function addEdge(adj, a, b) {
109
114
  if (a === b) return;
@@ -209,6 +214,36 @@ async function loadFullCurveSessionForRpc(rpcUrl) {
209
214
  return null;
210
215
  }
211
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
+ }
212
247
 
213
248
  // src/protocols/evm/curve-dao/purpose.ts
214
249
  function buildCurveDaoPurposePrefill(args) {
@@ -706,6 +741,176 @@ async function buildEvmMultisignBodyCurveDaoBatch(args) {
706
741
  }
707
742
  });
708
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
+ }
709
914
 
710
915
  // src/protocols/evm/curve-dao/index.ts
711
916
  var CURVE_DAO_PROTOCOL_ID = "curve-dao";
@@ -721,6 +926,19 @@ var curveDaoProtocolModule = {
721
926
  return token.kind === "native" || token.kind === "erc20";
722
927
  },
723
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
+ },
724
942
  {
725
943
  id: "curve-dao.swap",
726
944
  protocolId: CURVE_DAO_PROTOCOL_ID,
@@ -751,14 +969,22 @@ exports.bfsCurveSwapDestinations = bfsCurveSwapDestinations;
751
969
  exports.buildCurveDaoPurposePrefill = buildCurveDaoPurposePrefill;
752
970
  exports.buildCurveLiquidityGraphFromApi = buildCurveLiquidityGraphFromApi;
753
971
  exports.buildEvmMultisignBodyCurveDaoBatch = buildEvmMultisignBodyCurveDaoBatch;
972
+ exports.computeCurveExecutionSlippagePercent = computeCurveExecutionSlippagePercent;
754
973
  exports.curveDao = curveDao;
755
974
  exports.curveDaoProtocolModule = curveDaoProtocolModule;
975
+ exports.curveDaoQuote = curveDaoQuote;
976
+ exports.curveProbeInAmountString = curveProbeInAmountString;
756
977
  exports.fetchAllCurvePools = fetchAllCurvePools;
978
+ exports.fetchCurveCoinsDataForRpc = fetchCurveCoinsDataForRpc;
979
+ exports.formatCurveExchangeLine = formatCurveExchangeLine;
980
+ exports.formatCurvePriceImpact = formatCurvePriceImpact;
981
+ exports.formatMinOutAtUserSlippage = formatMinOutAtUserSlippage;
757
982
  exports.isCurveApiChainSupported = isCurveApiChainSupported;
758
983
  exports.isCurveDaoChainSupported = isCurveApiChainSupported;
759
984
  exports.isCurveDaoErc20ApproveEvmSignRequest = isCurveDaoErc20ApproveEvmSignRequest;
760
985
  exports.isCurveDaoSwapEvmSignRequest = isCurveDaoSwapEvmSignRequest;
761
986
  exports.isCurveTokenKeySwappable = isCurveTokenKeySwappable;
987
+ exports.loadCurveSessionSnapshotForRpc = loadCurveSessionSnapshotForRpc;
762
988
  exports.loadFullCurveSessionForRpc = loadFullCurveSessionForRpc;
763
989
  exports.normalizeCurveRouterAmountString = normalizeCurveRouterAmountString;
764
990
  exports.resolveCurveDaoRouterGasUnitsFromSignRequest = resolveCurveDaoRouterGasUnitsFromSignRequest;