@continuumdao/ctm-mpc-defi 0.2.34 → 0.2.35
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 +712 -44
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +1630 -244
- package/dist/agent/catalog.js +678 -45
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/aave-v4/SKILL.md +19 -4
- package/dist/agent/skills/aerodrome/SKILL.md +2 -2
- package/dist/agent/skills/compound-v3/SKILL.md +2 -2
- package/dist/agent/skills/curve-dao/SKILL.md +14 -0
- package/dist/agent/skills/ethena/SKILL.md +3 -1
- package/dist/agent/skills/euler-v2/SKILL.md +1 -1
- package/dist/agent/skills/gmx/SKILL.md +1 -1
- package/dist/agent/skills/hyperliquid/SKILL.md +7 -5
- package/dist/agent/skills/lido/SKILL.md +3 -0
- package/dist/agent/skills/maple-syrup/SKILL.md +35 -3
- package/dist/agent/skills/morpho/SKILL.md +1 -1
- package/dist/agent/skills/sky/SKILL.md +2 -0
- package/dist/agent/skills/yield-compare/SKILL.md +78 -0
- package/dist/index.cjs +129 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +129 -27
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs +170 -0
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/aave-v4/index.d.ts +59 -1
- package/dist/protocols/evm/aave-v4/index.js +164 -1
- package/dist/protocols/evm/aave-v4/index.js.map +1 -1
- package/dist/protocols/evm/aerodrome/index.cjs +1308 -901
- package/dist/protocols/evm/aerodrome/index.cjs.map +1 -1
- package/dist/protocols/evm/aerodrome/index.d.ts +69 -2
- package/dist/protocols/evm/aerodrome/index.js +1217 -807
- package/dist/protocols/evm/aerodrome/index.js.map +1 -1
- package/dist/protocols/evm/compound-v3/index.cjs +82 -6
- package/dist/protocols/evm/compound-v3/index.cjs.map +1 -1
- package/dist/protocols/evm/compound-v3/index.d.ts +89 -1
- package/dist/protocols/evm/compound-v3/index.js +80 -7
- package/dist/protocols/evm/compound-v3/index.js.map +1 -1
- package/dist/protocols/evm/curve-dao/index.cjs +447 -18
- package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
- package/dist/protocols/evm/curve-dao/index.d.ts +108 -1
- package/dist/protocols/evm/curve-dao/index.js +440 -19
- package/dist/protocols/evm/curve-dao/index.js.map +1 -1
- package/dist/protocols/evm/ethena/index.cjs +89 -0
- package/dist/protocols/evm/ethena/index.cjs.map +1 -1
- package/dist/protocols/evm/ethena/index.d.ts +17 -1
- package/dist/protocols/evm/ethena/index.js +89 -2
- package/dist/protocols/evm/ethena/index.js.map +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs +29 -0
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.d.ts +21 -1
- package/dist/protocols/evm/euler-v2/index.js +29 -1
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/gmx/index.cjs +83 -1
- package/dist/protocols/evm/gmx/index.cjs.map +1 -1
- package/dist/protocols/evm/gmx/index.d.ts +22 -2
- package/dist/protocols/evm/gmx/index.js +80 -2
- package/dist/protocols/evm/gmx/index.js.map +1 -1
- package/dist/protocols/evm/hyperliquid/index.cjs +67 -11
- package/dist/protocols/evm/hyperliquid/index.cjs.map +1 -1
- package/dist/protocols/evm/hyperliquid/index.d.ts +39 -2
- package/dist/protocols/evm/hyperliquid/index.js +64 -12
- package/dist/protocols/evm/hyperliquid/index.js.map +1 -1
- package/dist/protocols/evm/lido/index.cjs +13 -0
- package/dist/protocols/evm/lido/index.cjs.map +1 -1
- package/dist/protocols/evm/lido/index.d.ts +9 -1
- package/dist/protocols/evm/lido/index.js +13 -1
- package/dist/protocols/evm/lido/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs +129 -1
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.d.ts +45 -1
- package/dist/protocols/evm/maple/index.js +124 -3
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/morpho/index.cjs +34 -7
- package/dist/protocols/evm/morpho/index.cjs.map +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +23 -1
- package/dist/protocols/evm/morpho/index.js +34 -8
- package/dist/protocols/evm/morpho/index.js.map +1 -1
- package/dist/protocols/evm/sky/index.cjs +52 -0
- package/dist/protocols/evm/sky/index.cjs.map +1 -1
- package/dist/protocols/evm/sky/index.d.ts +16 -1
- package/dist/protocols/evm/sky/index.js +50 -1
- package/dist/protocols/evm/sky/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -469,25 +469,25 @@ async function buildEvmMultisignBatch(args) {
|
|
|
469
469
|
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
470
470
|
rpcUrls: { default: { http: [rpcUrl] } }
|
|
471
471
|
});
|
|
472
|
-
const
|
|
472
|
+
const publicClient2 = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
|
|
473
473
|
const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
|
|
474
474
|
const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
|
|
475
|
-
const latestBaseFeeWei = !legacy ? (await
|
|
475
|
+
const latestBaseFeeWei = !legacy ? (await publicClient2.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
476
476
|
const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
|
|
477
477
|
const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
|
|
478
478
|
const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
|
|
479
479
|
const executor = viem.getAddress(executorAddress);
|
|
480
|
-
const baseNonce = await
|
|
480
|
+
const baseNonce = await publicClient2.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
481
481
|
const legs = [];
|
|
482
482
|
for (let i = 0; i < steps.length; i++) {
|
|
483
483
|
const step = steps[i];
|
|
484
484
|
const currentNonce = baseNonce + i;
|
|
485
485
|
let estimatedGas;
|
|
486
486
|
if (args.estimateGasForStep) {
|
|
487
|
-
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
|
|
487
|
+
estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient2, executor });
|
|
488
488
|
} else {
|
|
489
489
|
try {
|
|
490
|
-
estimatedGas = await
|
|
490
|
+
estimatedGas = await publicClient2.estimateGas({
|
|
491
491
|
to: step.to,
|
|
492
492
|
data: step.data,
|
|
493
493
|
value: step.value,
|
|
@@ -499,7 +499,7 @@ async function buildEvmMultisignBatch(args) {
|
|
|
499
499
|
}
|
|
500
500
|
let gasLimitI;
|
|
501
501
|
if (args.resolveGasLimit) {
|
|
502
|
-
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
|
|
502
|
+
gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient2 });
|
|
503
503
|
} else if (step.routerSwap) {
|
|
504
504
|
gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
|
|
505
505
|
} else {
|
|
@@ -509,7 +509,7 @@ async function buildEvmMultisignBatch(args) {
|
|
|
509
509
|
let feeSnapshot;
|
|
510
510
|
let serialized;
|
|
511
511
|
if (legacy) {
|
|
512
|
-
let gasPriceWei = await
|
|
512
|
+
let gasPriceWei = await publicClient2.getGasPrice();
|
|
513
513
|
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
514
514
|
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
515
515
|
}
|
|
@@ -660,37 +660,37 @@ async function buildEvmMultisignBodyCurveDaoBatch(args) {
|
|
|
660
660
|
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
661
661
|
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
662
662
|
});
|
|
663
|
-
const
|
|
664
|
-
const
|
|
663
|
+
const publicClient2 = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
664
|
+
const erc20AllowanceAbi2 = viem.parseAbi([
|
|
665
665
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
666
666
|
"function decimals() view returns (uint8)"
|
|
667
667
|
]);
|
|
668
|
-
const
|
|
668
|
+
const erc20ApproveAbi2 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
669
669
|
const steps = [];
|
|
670
670
|
if (!isNativeIn) {
|
|
671
|
-
const decimalsN = await
|
|
671
|
+
const decimalsN = await publicClient2.readContract({
|
|
672
672
|
address: tokenIn,
|
|
673
|
-
abi:
|
|
673
|
+
abi: erc20AllowanceAbi2,
|
|
674
674
|
functionName: "decimals"
|
|
675
675
|
});
|
|
676
676
|
const amountWei = viem.parseUnits(args.amountHuman, Number(decimalsN));
|
|
677
|
-
const currentAllowance = await
|
|
677
|
+
const currentAllowance = await publicClient2.readContract({
|
|
678
678
|
address: tokenIn,
|
|
679
|
-
abi:
|
|
679
|
+
abi: erc20AllowanceAbi2,
|
|
680
680
|
functionName: "allowance",
|
|
681
681
|
args: [executor, routerAddr]
|
|
682
682
|
});
|
|
683
683
|
if (currentAllowance < amountWei) {
|
|
684
684
|
if (currentAllowance > 0n) {
|
|
685
685
|
const dataReset = viem.encodeFunctionData({
|
|
686
|
-
abi:
|
|
686
|
+
abi: erc20ApproveAbi2,
|
|
687
687
|
functionName: "approve",
|
|
688
688
|
args: [routerAddr, 0n]
|
|
689
689
|
});
|
|
690
690
|
steps.push({ to: tokenIn, data: dataReset, value: 0n, kind: "approve", fallbackGas: 80000n });
|
|
691
691
|
}
|
|
692
692
|
const dataApprove = viem.encodeFunctionData({
|
|
693
|
-
abi:
|
|
693
|
+
abi: erc20ApproveAbi2,
|
|
694
694
|
functionName: "approve",
|
|
695
695
|
args: [routerAddr, amountWei]
|
|
696
696
|
});
|
|
@@ -800,8 +800,8 @@ async function resolveTokenInDecimals(args) {
|
|
|
800
800
|
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
801
801
|
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
802
802
|
});
|
|
803
|
-
const
|
|
804
|
-
const decimalsN = await
|
|
803
|
+
const publicClient2 = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
804
|
+
const decimalsN = await publicClient2.readContract({
|
|
805
805
|
address: addr,
|
|
806
806
|
abi: erc20DecimalsAbi,
|
|
807
807
|
functionName: "decimals"
|
|
@@ -950,6 +950,411 @@ function formatMinOutAtUserSlippage(quoteOutDecimal, userSlippagePercentInput, t
|
|
|
950
950
|
return `${t} ${sOut}`.trim();
|
|
951
951
|
}
|
|
952
952
|
|
|
953
|
+
// src/protocols/evm/curve-dao/importantPools.ts
|
|
954
|
+
var CURVE_IMPORTANT_POOL_CAP = 100;
|
|
955
|
+
var CURVE_API_BASE = "https://api.curve.finance/api/getPools";
|
|
956
|
+
var CHAIN_SLUG = {
|
|
957
|
+
1: "ethereum",
|
|
958
|
+
10: "optimism",
|
|
959
|
+
56: "bsc",
|
|
960
|
+
100: "xdai",
|
|
961
|
+
137: "polygon",
|
|
962
|
+
146: "sonic",
|
|
963
|
+
250: "fantom",
|
|
964
|
+
324: "zksync",
|
|
965
|
+
8453: "base",
|
|
966
|
+
42161: "arbitrum",
|
|
967
|
+
43114: "avalanche",
|
|
968
|
+
42220: "celo",
|
|
969
|
+
1313161554: "aurora"
|
|
970
|
+
};
|
|
971
|
+
var REGISTRIES = [
|
|
972
|
+
"main",
|
|
973
|
+
"crypto",
|
|
974
|
+
"factory",
|
|
975
|
+
"factory-crypto",
|
|
976
|
+
"factory-crvusd",
|
|
977
|
+
"factory-stable-ng",
|
|
978
|
+
"factory-twocrypto",
|
|
979
|
+
"factory-tricrypto"
|
|
980
|
+
];
|
|
981
|
+
function formatApy(n) {
|
|
982
|
+
if (typeof n === "number" && Number.isFinite(n) && n >= 0) return `${n.toFixed(2)}%`;
|
|
983
|
+
if (Array.isArray(n) && typeof n[0] === "number" && Number.isFinite(n[0])) return `${n[0].toFixed(2)}%`;
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
function pickApy(pool, keys) {
|
|
987
|
+
for (const k of keys) {
|
|
988
|
+
const label = formatApy(pool[k]);
|
|
989
|
+
if (label) return label;
|
|
990
|
+
}
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
function mapPool(raw) {
|
|
994
|
+
const address = String(raw.address ?? raw.id ?? "").trim();
|
|
995
|
+
if (!address) return null;
|
|
996
|
+
const tvl = Number(raw.usdTotal ?? raw.tvl ?? raw.usd_total ?? 0);
|
|
997
|
+
if (!Number.isFinite(tvl) || tvl <= 0) return null;
|
|
998
|
+
const coinsRaw = raw.coins ?? raw.underlyingCoins ?? [];
|
|
999
|
+
const coins = coinsRaw.map((c) => ({
|
|
1000
|
+
address: String(c.address ?? c.addr ?? "").trim(),
|
|
1001
|
+
symbol: String(c.symbol ?? c.coinsymbol ?? "").trim() || "TOKEN",
|
|
1002
|
+
decimals: Number(c.decimals ?? 18) || 18
|
|
1003
|
+
})).filter((c) => c.address);
|
|
1004
|
+
if (!coins.length) return null;
|
|
1005
|
+
const gauge = String(raw.gaugeAddress ?? raw.gauge ?? "").trim();
|
|
1006
|
+
return {
|
|
1007
|
+
id: String(raw.id ?? address),
|
|
1008
|
+
address,
|
|
1009
|
+
name: String(raw.name ?? raw.poolName ?? coins.map((c) => c.symbol).join("/")),
|
|
1010
|
+
coins,
|
|
1011
|
+
tvlUsd: tvl,
|
|
1012
|
+
baseApy: pickApy(raw, ["latestDailyApy", "latestWeeklyApy", "baseApy", "apy"]),
|
|
1013
|
+
gaugeCrvApy: pickApy(raw, ["gaugeCrvApy", "gauge_crv_apy", "crvApy"]),
|
|
1014
|
+
gaugeAddress: gauge || null
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
async function fetchRegistry(slug, registry) {
|
|
1018
|
+
try {
|
|
1019
|
+
const res = await fetch(`${CURVE_API_BASE}/${slug}/${registry}`, { cache: "no-store" });
|
|
1020
|
+
if (!res.ok) return [];
|
|
1021
|
+
const j = await res.json();
|
|
1022
|
+
return j.data?.poolData ?? j.poolData ?? [];
|
|
1023
|
+
} catch {
|
|
1024
|
+
return [];
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
async function curveDaoFetchImportantPools(args) {
|
|
1028
|
+
if (!isCurveApiChainSupported(args.chainId)) {
|
|
1029
|
+
return { chainId: args.chainId, pools: [], notes: `Curve is not supported on chain ${args.chainId}.` };
|
|
1030
|
+
}
|
|
1031
|
+
const slug = CHAIN_SLUG[args.chainId];
|
|
1032
|
+
if (!slug) {
|
|
1033
|
+
return { chainId: args.chainId, pools: [], notes: `No official Curve API slug for chain ${args.chainId}.` };
|
|
1034
|
+
}
|
|
1035
|
+
const pages = await Promise.all(REGISTRIES.map((r) => fetchRegistry(slug, r)));
|
|
1036
|
+
const byAddr = /* @__PURE__ */ new Map();
|
|
1037
|
+
for (const raw of pages.flat()) {
|
|
1038
|
+
const row = mapPool(raw);
|
|
1039
|
+
if (!row) continue;
|
|
1040
|
+
const key = row.address.toLowerCase();
|
|
1041
|
+
const prev = byAddr.get(key);
|
|
1042
|
+
if (!prev || row.tvlUsd > prev.tvlUsd) byAddr.set(key, row);
|
|
1043
|
+
}
|
|
1044
|
+
let pools = [...byAddr.values()].sort((a, b) => b.tvlUsd - a.tvlUsd);
|
|
1045
|
+
const q = (args.query ?? "").trim().toLowerCase();
|
|
1046
|
+
if (q) {
|
|
1047
|
+
pools = pools.filter(
|
|
1048
|
+
(p) => [p.name, p.address, p.id, ...p.coins.map((c) => c.symbol)].join(" ").toLowerCase().includes(q)
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
pools = pools.slice(0, CURVE_IMPORTANT_POOL_CAP);
|
|
1052
|
+
return {
|
|
1053
|
+
chainId: args.chainId,
|
|
1054
|
+
pools,
|
|
1055
|
+
notes: `Official api.curve.fi catalog, TVL-ranked, hard cap ${CURVE_IMPORTANT_POOL_CAP}. Dead/zero-TVL dropped. Do not call fetchAllCurvePools or invent a list-all-pools tool. gauge_crv_apy requires staking LP in the gauge.`
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
async function curveDaoFetchLpYields(args) {
|
|
1059
|
+
const { pools, notes } = await curveDaoFetchImportantPools({ chainId: args.chainId, query: args.query });
|
|
1060
|
+
const poolFilter = (args.pool ?? "").trim().toLowerCase();
|
|
1061
|
+
const filtered = poolFilter ? pools.filter((p) => p.address.toLowerCase() === poolFilter || p.id.toLowerCase() === poolFilter) : pools;
|
|
1062
|
+
return { chainId: args.chainId, pools: filtered, notes };
|
|
1063
|
+
}
|
|
1064
|
+
var erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
1065
|
+
var erc20AllowanceAbi = viem.parseAbi([
|
|
1066
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1067
|
+
"function decimals() view returns (uint8)"
|
|
1068
|
+
]);
|
|
1069
|
+
var gaugeAbi = viem.parseAbi(["function deposit(uint256 value)", "function withdraw(uint256 value)"]);
|
|
1070
|
+
function findCurvePool(curve, poolRef) {
|
|
1071
|
+
const want = poolRef.trim().toLowerCase();
|
|
1072
|
+
const ids = [];
|
|
1073
|
+
try {
|
|
1074
|
+
ids.push(...curve.getPoolList?.() ?? []);
|
|
1075
|
+
} catch {
|
|
1076
|
+
}
|
|
1077
|
+
for (const id of ids) {
|
|
1078
|
+
try {
|
|
1079
|
+
const p = curve.getPool(id);
|
|
1080
|
+
if (!p) continue;
|
|
1081
|
+
if (String(p.id ?? "").toLowerCase() === want) return p;
|
|
1082
|
+
if (String(p.address ?? "").toLowerCase() === want) return p;
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
throw new Error(
|
|
1087
|
+
`Curve pool ${poolRef} is not in the loaded session. Copy address/id from ctm_curve_dao_fetch_important_pools.`
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
function addLiquidityAbi(n, ng) {
|
|
1091
|
+
if (ng) return viem.parseAbi(["function add_liquidity(uint256[] amounts, uint256 min_mint_amount)"]);
|
|
1092
|
+
if (n === 2) return viem.parseAbi(["function add_liquidity(uint256[2] amounts, uint256 min_mint_amount)"]);
|
|
1093
|
+
if (n === 3) return viem.parseAbi(["function add_liquidity(uint256[3] amounts, uint256 min_mint_amount)"]);
|
|
1094
|
+
if (n === 4) return viem.parseAbi(["function add_liquidity(uint256[4] amounts, uint256 min_mint_amount)"]);
|
|
1095
|
+
return viem.parseAbi(["function add_liquidity(uint256[] amounts, uint256 min_mint_amount)"]);
|
|
1096
|
+
}
|
|
1097
|
+
function removeLiquidityAbi(n, ng) {
|
|
1098
|
+
if (ng) return viem.parseAbi(["function remove_liquidity(uint256 amount, uint256[] min_amounts)"]);
|
|
1099
|
+
if (n === 2) return viem.parseAbi(["function remove_liquidity(uint256 amount, uint256[2] min_amounts)"]);
|
|
1100
|
+
if (n === 3) return viem.parseAbi(["function remove_liquidity(uint256 amount, uint256[3] min_amounts)"]);
|
|
1101
|
+
if (n === 4) return viem.parseAbi(["function remove_liquidity(uint256 amount, uint256[4] min_amounts)"]);
|
|
1102
|
+
return viem.parseAbi(["function remove_liquidity(uint256 amount, uint256[] min_amounts)"]);
|
|
1103
|
+
}
|
|
1104
|
+
function applySlippage(expected, slipPercent) {
|
|
1105
|
+
const bps = BigInt(Math.round((1 - slipPercent / 100) * 1e4));
|
|
1106
|
+
return expected * bps / 10000n;
|
|
1107
|
+
}
|
|
1108
|
+
function publicClient(rpcUrl, chainId) {
|
|
1109
|
+
const ch = viem.defineChain({
|
|
1110
|
+
id: chainId,
|
|
1111
|
+
name: "CurveLp",
|
|
1112
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
1113
|
+
rpcUrls: { default: { http: [rpcUrl] } }
|
|
1114
|
+
});
|
|
1115
|
+
return viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
|
|
1116
|
+
}
|
|
1117
|
+
function isNativeCoin(addr) {
|
|
1118
|
+
return addr.trim().toLowerCase() === CURVE_NATIVE_PLACEHOLDER.toLowerCase();
|
|
1119
|
+
}
|
|
1120
|
+
async function curveDaoQuoteAddLiquidity(args) {
|
|
1121
|
+
const session = await loadFullCurveSessionForRpc(args.rpcUrl);
|
|
1122
|
+
const pool = findCurvePool(session.curve, args.pool);
|
|
1123
|
+
const coins = pool.underlyingCoins ?? pool.wrappedCoins ?? [];
|
|
1124
|
+
if (args.amountsHuman.length !== coins.length) {
|
|
1125
|
+
throw new Error(`Expected ${coins.length} amounts (coins: ${coins.join(", ")}).`);
|
|
1126
|
+
}
|
|
1127
|
+
const expected = await pool.depositExpected(args.amountsHuman);
|
|
1128
|
+
const catalog = await curveDaoFetchImportantPools({ chainId: args.chainId, query: args.pool });
|
|
1129
|
+
const meta = catalog.pools.find(
|
|
1130
|
+
(p) => p.address.toLowerCase() === String(pool.address).toLowerCase() || p.id.toLowerCase() === String(pool.id).toLowerCase()
|
|
1131
|
+
);
|
|
1132
|
+
const gauge = String(pool.gauge?.address ?? meta?.gaugeAddress ?? "");
|
|
1133
|
+
return {
|
|
1134
|
+
pool: viem.getAddress(pool.address),
|
|
1135
|
+
poolId: String(pool.id),
|
|
1136
|
+
name: String(pool.name ?? pool.fullName ?? ""),
|
|
1137
|
+
coins,
|
|
1138
|
+
amountsHuman: args.amountsHuman,
|
|
1139
|
+
expectedLp: expected,
|
|
1140
|
+
gauge: gauge || null,
|
|
1141
|
+
stakeInGauge: Boolean(args.stakeInGauge && gauge),
|
|
1142
|
+
baseApy: meta?.baseApy ?? null,
|
|
1143
|
+
gaugeCrvApy: meta?.gaugeCrvApy ?? null,
|
|
1144
|
+
notes: meta?.gaugeCrvApy ? "gauge_crv_apy requires staking the LP token in the gauge (stakeInGauge or ctm_curve_dao_build_gauge_deposit_multisign)." : "Fee APY only unless this pool advertises gauge_crv_apy."
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
async function curveDaoQuoteRemoveLiquidity(args) {
|
|
1148
|
+
const session = await loadFullCurveSessionForRpc(args.rpcUrl);
|
|
1149
|
+
const pool = findCurvePool(session.curve, args.pool);
|
|
1150
|
+
const coins = pool.underlyingCoins ?? pool.wrappedCoins ?? [];
|
|
1151
|
+
const expected = await pool.withdrawExpected(args.lpAmountHuman);
|
|
1152
|
+
return {
|
|
1153
|
+
pool: viem.getAddress(pool.address),
|
|
1154
|
+
poolId: String(pool.id),
|
|
1155
|
+
coins,
|
|
1156
|
+
lpAmountHuman: args.lpAmountHuman,
|
|
1157
|
+
expectedAmounts: expected,
|
|
1158
|
+
notes: "Balanced remove. Gauge must be unstaked first if LP is in the gauge."
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
async function pushApproves(args) {
|
|
1162
|
+
for (const { token, amount, spender } of args.tokens) {
|
|
1163
|
+
if (amount <= 0n || isNativeCoin(token)) continue;
|
|
1164
|
+
const current = await args.client.readContract({ address: token, abi: erc20AllowanceAbi, functionName: "allowance", args: [args.owner, spender] }).catch(() => 0n);
|
|
1165
|
+
if (current >= amount) continue;
|
|
1166
|
+
if (current > 0n) {
|
|
1167
|
+
args.steps.push({
|
|
1168
|
+
to: token,
|
|
1169
|
+
data: viem.encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [spender, 0n] }),
|
|
1170
|
+
value: 0n
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
args.steps.push({
|
|
1174
|
+
to: token,
|
|
1175
|
+
data: viem.encodeFunctionData({ abi: erc20ApproveAbi, functionName: "approve", args: [spender, amount] }),
|
|
1176
|
+
value: 0n
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
async function buildEvmMultisignBodyCurveAddLiquidity(args) {
|
|
1181
|
+
const slip = args.slippagePercent;
|
|
1182
|
+
if (!Number.isFinite(slip) || slip <= 0 || slip >= 100) {
|
|
1183
|
+
throw new Error("Slippage must be between 0 and 100 (exclusive).");
|
|
1184
|
+
}
|
|
1185
|
+
const session = await loadFullCurveSessionForRpc(args.rpcUrl);
|
|
1186
|
+
const pool = findCurvePool(session.curve, args.pool);
|
|
1187
|
+
const coins = pool.underlyingCoinAddresses ?? pool.wrappedCoinAddresses ?? [];
|
|
1188
|
+
const decimals = pool.underlyingDecimals ?? pool.wrappedDecimals ?? coins.map(() => 18);
|
|
1189
|
+
if (args.amountsHuman.length !== coins.length) {
|
|
1190
|
+
throw new Error(`Expected ${coins.length} amounts.`);
|
|
1191
|
+
}
|
|
1192
|
+
const amounts = args.amountsHuman.map((h, i) => viem.parseUnits(h.trim() || "0", decimals[i] ?? 18));
|
|
1193
|
+
const expectedHuman = await pool.depositExpected(args.amountsHuman);
|
|
1194
|
+
const expectedLp = viem.parseUnits(expectedHuman, 18);
|
|
1195
|
+
const minMint = applySlippage(expectedLp, slip);
|
|
1196
|
+
const ng = Boolean(pool.isNg);
|
|
1197
|
+
const abi = addLiquidityAbi(coins.length, ng);
|
|
1198
|
+
const amountsArg = ng ? amounts : amounts;
|
|
1199
|
+
const data = viem.encodeFunctionData({
|
|
1200
|
+
abi,
|
|
1201
|
+
functionName: "add_liquidity",
|
|
1202
|
+
args: [amountsArg, minMint]
|
|
1203
|
+
});
|
|
1204
|
+
const poolAddr = viem.getAddress(pool.address);
|
|
1205
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1206
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
1207
|
+
const steps = [];
|
|
1208
|
+
await pushApproves({
|
|
1209
|
+
client,
|
|
1210
|
+
steps,
|
|
1211
|
+
owner: executor,
|
|
1212
|
+
tokens: coins.map((c, i) => ({
|
|
1213
|
+
token: isNativeCoin(c) ? CURVE_NATIVE_PLACEHOLDER : viem.getAddress(c),
|
|
1214
|
+
amount: amounts[i],
|
|
1215
|
+
spender: poolAddr
|
|
1216
|
+
}))
|
|
1217
|
+
});
|
|
1218
|
+
const value = coins.reduce((acc, c, i) => isNativeCoin(c) ? acc + amounts[i] : acc, 0n);
|
|
1219
|
+
steps.push({ to: poolAddr, data, value });
|
|
1220
|
+
if (args.stakeInGauge) {
|
|
1221
|
+
const gaugeAddr = String(pool.gauge?.address ?? "").trim();
|
|
1222
|
+
if (!gaugeAddr || gaugeAddr.toLowerCase() === "0x0000000000000000000000000000000000000000") {
|
|
1223
|
+
throw new Error("This pool has no gauge. Fee APY only \u2014 omit stakeInGauge.");
|
|
1224
|
+
}
|
|
1225
|
+
const lp = viem.getAddress(pool.lpToken || pool.address);
|
|
1226
|
+
await pushApproves({
|
|
1227
|
+
client,
|
|
1228
|
+
steps,
|
|
1229
|
+
owner: executor,
|
|
1230
|
+
tokens: [{ token: lp, amount: expectedLp, spender: viem.getAddress(gaugeAddr) }]
|
|
1231
|
+
});
|
|
1232
|
+
steps.push({
|
|
1233
|
+
to: viem.getAddress(gaugeAddr),
|
|
1234
|
+
data: viem.encodeFunctionData({ abi: gaugeAbi, functionName: "deposit", args: [expectedLp] }),
|
|
1235
|
+
value: 0n
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
return finishCurveLpBatch({
|
|
1239
|
+
args,
|
|
1240
|
+
executor,
|
|
1241
|
+
steps,
|
|
1242
|
+
purposeSuffix: args.stakeInGauge ? "Curve LP: approve + add_liquidity + gauge.deposit" : "Curve LP: approve + add_liquidity",
|
|
1243
|
+
evmType: "curve_dao_add_liquidity"
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
async function buildEvmMultisignBodyCurveRemoveLiquidity(args) {
|
|
1247
|
+
const slip = args.slippagePercent;
|
|
1248
|
+
if (!Number.isFinite(slip) || slip <= 0 || slip >= 100) {
|
|
1249
|
+
throw new Error("Slippage must be between 0 and 100 (exclusive).");
|
|
1250
|
+
}
|
|
1251
|
+
const session = await loadFullCurveSessionForRpc(args.rpcUrl);
|
|
1252
|
+
const pool = findCurvePool(session.curve, args.pool);
|
|
1253
|
+
const coins = pool.underlyingCoinAddresses ?? pool.wrappedCoinAddresses ?? [];
|
|
1254
|
+
const expected = await pool.withdrawExpected(args.lpAmountHuman);
|
|
1255
|
+
const decimals = pool.underlyingDecimals ?? pool.wrappedDecimals ?? coins.map(() => 18);
|
|
1256
|
+
const minAmounts = expected.map((h, i) => applySlippage(viem.parseUnits(h || "0", decimals[i] ?? 18), slip));
|
|
1257
|
+
const lpAmount = viem.parseUnits(args.lpAmountHuman, 18);
|
|
1258
|
+
const ng = Boolean(pool.isNg);
|
|
1259
|
+
const abi = removeLiquidityAbi(coins.length, ng);
|
|
1260
|
+
const minArg = ng ? minAmounts : minAmounts;
|
|
1261
|
+
const data = viem.encodeFunctionData({
|
|
1262
|
+
abi,
|
|
1263
|
+
functionName: "remove_liquidity",
|
|
1264
|
+
args: [lpAmount, minArg]
|
|
1265
|
+
});
|
|
1266
|
+
const poolAddr = viem.getAddress(pool.address);
|
|
1267
|
+
const lp = viem.getAddress(pool.lpToken || pool.address);
|
|
1268
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1269
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
1270
|
+
const steps = [];
|
|
1271
|
+
await pushApproves({
|
|
1272
|
+
client,
|
|
1273
|
+
steps,
|
|
1274
|
+
owner: executor,
|
|
1275
|
+
tokens: [{ token: lp, amount: lpAmount, spender: poolAddr }]
|
|
1276
|
+
});
|
|
1277
|
+
steps.push({ to: poolAddr, data, value: 0n });
|
|
1278
|
+
return finishCurveLpBatch({
|
|
1279
|
+
args,
|
|
1280
|
+
executor,
|
|
1281
|
+
steps,
|
|
1282
|
+
purposeSuffix: "Curve LP: approve + remove_liquidity",
|
|
1283
|
+
evmType: "curve_dao_remove_liquidity"
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
async function buildEvmMultisignBodyCurveGaugeDeposit(args) {
|
|
1287
|
+
const session = await loadFullCurveSessionForRpc(args.rpcUrl);
|
|
1288
|
+
const pool = findCurvePool(session.curve, args.pool);
|
|
1289
|
+
const gaugeAddr = String(pool.gauge?.address ?? "").trim();
|
|
1290
|
+
if (!gaugeAddr || gaugeAddr.toLowerCase() === "0x0000000000000000000000000000000000000000") {
|
|
1291
|
+
throw new Error("This pool has no gauge.");
|
|
1292
|
+
}
|
|
1293
|
+
const lpAmount = viem.parseUnits(args.lpAmountHuman, 18);
|
|
1294
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1295
|
+
const lp = viem.getAddress(pool.lpToken || pool.address);
|
|
1296
|
+
const client = publicClient(args.rpcUrl, args.chainId);
|
|
1297
|
+
const steps = [];
|
|
1298
|
+
if (!args.withdraw) {
|
|
1299
|
+
await pushApproves({
|
|
1300
|
+
client,
|
|
1301
|
+
steps,
|
|
1302
|
+
owner: executor,
|
|
1303
|
+
tokens: [{ token: lp, amount: lpAmount, spender: viem.getAddress(gaugeAddr) }]
|
|
1304
|
+
});
|
|
1305
|
+
steps.push({
|
|
1306
|
+
to: viem.getAddress(gaugeAddr),
|
|
1307
|
+
data: viem.encodeFunctionData({ abi: gaugeAbi, functionName: "deposit", args: [lpAmount] }),
|
|
1308
|
+
value: 0n
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
steps.push({
|
|
1312
|
+
to: viem.getAddress(gaugeAddr),
|
|
1313
|
+
data: viem.encodeFunctionData({ abi: gaugeAbi, functionName: "withdraw", args: [lpAmount] }),
|
|
1314
|
+
value: 0n
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
return finishCurveLpBatch({
|
|
1318
|
+
args,
|
|
1319
|
+
executor,
|
|
1320
|
+
steps,
|
|
1321
|
+
purposeSuffix: args.withdraw ? "Curve LP: gauge.withdraw" : "Curve LP: approve + gauge.deposit",
|
|
1322
|
+
evmType: args.withdraw ? "curve_dao_gauge_withdraw" : "curve_dao_gauge_deposit"
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
function finishCurveLpBatch(opts) {
|
|
1326
|
+
const first = opts.steps[0];
|
|
1327
|
+
if (!first) throw new Error("No Curve LP transaction steps.");
|
|
1328
|
+
const firstDataNo0x = first.data.startsWith("0x") ? first.data.slice(2) : first.data;
|
|
1329
|
+
const payableValueWei = opts.steps.reduce((a, s) => a + (s.value ?? 0n), 0n);
|
|
1330
|
+
return buildEvmMultisignBatch({
|
|
1331
|
+
context: {
|
|
1332
|
+
chainCategory: "evm",
|
|
1333
|
+
keyGen: opts.args.keyGen,
|
|
1334
|
+
purposeText: opts.args.purposeText,
|
|
1335
|
+
chainId: opts.args.chainId,
|
|
1336
|
+
rpcUrl: opts.args.rpcUrl,
|
|
1337
|
+
executorAddress: opts.executor,
|
|
1338
|
+
chainDetail: opts.args.chainDetail,
|
|
1339
|
+
useCustomGas: opts.args.useCustomGas,
|
|
1340
|
+
customGasChainDetails: opts.args.customGasChainDetails
|
|
1341
|
+
},
|
|
1342
|
+
steps: opts.steps,
|
|
1343
|
+
purposeSuffix: opts.purposeSuffix,
|
|
1344
|
+
firstMsgRawNo0x: firstDataNo0x,
|
|
1345
|
+
destinationAddress: first.to,
|
|
1346
|
+
payableValueWei: payableValueWei > 0n ? payableValueWei : void 0,
|
|
1347
|
+
buildBatchMeta: ({ step }) => ({
|
|
1348
|
+
signatureText: JSON.stringify({
|
|
1349
|
+
kind: "CurveDao",
|
|
1350
|
+
name: opts.evmType,
|
|
1351
|
+
to: step.to
|
|
1352
|
+
}),
|
|
1353
|
+
evm: { type: opts.evmType, version: 1, chainId: String(opts.args.chainId) }
|
|
1354
|
+
})
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
|
|
953
1358
|
// src/protocols/evm/curve-dao/index.ts
|
|
954
1359
|
var CURVE_DAO_PROTOCOL_ID = "curve-dao";
|
|
955
1360
|
var curveDaoProtocolModule = {
|
|
@@ -989,6 +1394,22 @@ var curveDaoProtocolModule = {
|
|
|
989
1394
|
amountHuman: { type: "string", required: true, description: "Human-readable input amount" },
|
|
990
1395
|
slippagePercent: { type: "number", required: true, description: "Slippage percent (0\u2013100 exclusive)" }
|
|
991
1396
|
}
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
id: "curve-dao.add-liquidity",
|
|
1400
|
+
protocolId: CURVE_DAO_PROTOCOL_ID,
|
|
1401
|
+
chainCategory: "evm",
|
|
1402
|
+
description: "Add liquidity to an important Curve pool (optional gauge stake)",
|
|
1403
|
+
commonParams: ["keyGen", "purposeText", "useCustomGas"],
|
|
1404
|
+
params: {}
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
id: "curve-dao.remove-liquidity",
|
|
1408
|
+
protocolId: CURVE_DAO_PROTOCOL_ID,
|
|
1409
|
+
chainCategory: "evm",
|
|
1410
|
+
description: "Remove liquidity from an important Curve pool",
|
|
1411
|
+
commonParams: ["keyGen", "purposeText", "useCustomGas"],
|
|
1412
|
+
params: {}
|
|
992
1413
|
}
|
|
993
1414
|
]
|
|
994
1415
|
};
|
|
@@ -1000,17 +1421,25 @@ var curveDao = {
|
|
|
1000
1421
|
};
|
|
1001
1422
|
|
|
1002
1423
|
exports.CURVE_DAO_PROTOCOL_ID = CURVE_DAO_PROTOCOL_ID;
|
|
1424
|
+
exports.CURVE_IMPORTANT_POOL_CAP = CURVE_IMPORTANT_POOL_CAP;
|
|
1003
1425
|
exports.CURVE_NATIVE_PLACEHOLDER = CURVE_NATIVE_PLACEHOLDER;
|
|
1004
1426
|
exports.CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS = CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS;
|
|
1005
1427
|
exports.addNativeWethBridge = addNativeWethBridge;
|
|
1006
1428
|
exports.bfsCurveSwapDestinations = bfsCurveSwapDestinations;
|
|
1007
1429
|
exports.buildCurveDaoPurposePrefill = buildCurveDaoPurposePrefill;
|
|
1008
1430
|
exports.buildCurveLiquidityGraphFromApi = buildCurveLiquidityGraphFromApi;
|
|
1431
|
+
exports.buildEvmMultisignBodyCurveAddLiquidity = buildEvmMultisignBodyCurveAddLiquidity;
|
|
1009
1432
|
exports.buildEvmMultisignBodyCurveDaoBatch = buildEvmMultisignBodyCurveDaoBatch;
|
|
1433
|
+
exports.buildEvmMultisignBodyCurveGaugeDeposit = buildEvmMultisignBodyCurveGaugeDeposit;
|
|
1434
|
+
exports.buildEvmMultisignBodyCurveRemoveLiquidity = buildEvmMultisignBodyCurveRemoveLiquidity;
|
|
1010
1435
|
exports.computeCurveExecutionSlippagePercent = computeCurveExecutionSlippagePercent;
|
|
1011
1436
|
exports.curveDao = curveDao;
|
|
1437
|
+
exports.curveDaoFetchImportantPools = curveDaoFetchImportantPools;
|
|
1438
|
+
exports.curveDaoFetchLpYields = curveDaoFetchLpYields;
|
|
1012
1439
|
exports.curveDaoProtocolModule = curveDaoProtocolModule;
|
|
1013
1440
|
exports.curveDaoQuote = curveDaoQuote;
|
|
1441
|
+
exports.curveDaoQuoteAddLiquidity = curveDaoQuoteAddLiquidity;
|
|
1442
|
+
exports.curveDaoQuoteRemoveLiquidity = curveDaoQuoteRemoveLiquidity;
|
|
1014
1443
|
exports.curveProbeInAmountString = curveProbeInAmountString;
|
|
1015
1444
|
exports.fetchAllCurvePools = fetchAllCurvePools;
|
|
1016
1445
|
exports.fetchCurveCoinsDataForRpc = fetchCurveCoinsDataForRpc;
|