@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.
- package/dist/agent/catalog.cjs +62 -11
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +27 -1
- package/dist/agent/catalog.js +61 -12
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/curve-dao/SKILL.md +2 -1
- package/dist/chains/evm/index.cjs +54 -0
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.ts +13 -1
- package/dist/chains/evm/index.js +52 -2
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/core/index.cjs +64 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +20 -1
- package/dist/core/index.js +56 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +131 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +120 -2
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs +766 -6
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +417 -1
- package/dist/protocols/evm/aave-v4/index.js +741 -8
- package/dist/protocols/evm/aave-v4/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.cjs +233 -7
- package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +66 -1
- package/dist/protocols/evm/curve-dao/index.js +227 -9
- package/dist/protocols/evm/curve-dao/index.js.map +1 -1
- package/dist/protocols/evm/ethena/index.cjs +104 -0
- package/dist/protocols/evm/ethena/index.cjs.map +1 -1
- package/dist/protocols/evm/ethena/index.d.ts +46 -1
- package/dist/protocols/evm/ethena/index.js +99 -1
- package/dist/protocols/evm/ethena/index.js.map +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +2334 -37
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +464 -1
- package/dist/protocols/evm/euler-v2/index.js +2286 -39
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/lido/index.cjs +110 -0
- package/dist/protocols/evm/lido/index.cjs.map +1 -1
- package/dist/protocols/evm/lido/index.d.ts +33 -2
- package/dist/protocols/evm/lido/index.js +107 -2
- package/dist/protocols/evm/lido/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs +83 -0
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.d.ts +22 -1
- package/dist/protocols/evm/maple/index.js +82 -1
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/sky/index.cjs +217 -0
- package/dist/protocols/evm/sky/index.cjs.map +1 -1
- package/dist/protocols/evm/sky/index.d.ts +56 -1
- package/dist/protocols/evm/sky/index.js +210 -2
- package/dist/protocols/evm/sky/index.js.map +1 -1
- 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
|
-
|
|
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;
|