@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.
Files changed (83) hide show
  1. package/dist/agent/catalog.cjs +712 -44
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +1630 -244
  4. package/dist/agent/catalog.js +678 -45
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/aave-v4/SKILL.md +19 -4
  7. package/dist/agent/skills/aerodrome/SKILL.md +2 -2
  8. package/dist/agent/skills/compound-v3/SKILL.md +2 -2
  9. package/dist/agent/skills/curve-dao/SKILL.md +14 -0
  10. package/dist/agent/skills/ethena/SKILL.md +3 -1
  11. package/dist/agent/skills/euler-v2/SKILL.md +1 -1
  12. package/dist/agent/skills/gmx/SKILL.md +1 -1
  13. package/dist/agent/skills/hyperliquid/SKILL.md +7 -5
  14. package/dist/agent/skills/lido/SKILL.md +3 -0
  15. package/dist/agent/skills/maple-syrup/SKILL.md +35 -3
  16. package/dist/agent/skills/morpho/SKILL.md +1 -1
  17. package/dist/agent/skills/sky/SKILL.md +2 -0
  18. package/dist/agent/skills/yield-compare/SKILL.md +78 -0
  19. package/dist/index.cjs +129 -29
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.js +129 -27
  22. package/dist/index.js.map +1 -1
  23. package/dist/protocols/evm/aave-v4/index.cjs +170 -0
  24. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  25. package/dist/protocols/evm/aave-v4/index.d.ts +59 -1
  26. package/dist/protocols/evm/aave-v4/index.js +164 -1
  27. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  28. package/dist/protocols/evm/aerodrome/index.cjs +1308 -901
  29. package/dist/protocols/evm/aerodrome/index.cjs.map +1 -1
  30. package/dist/protocols/evm/aerodrome/index.d.ts +69 -2
  31. package/dist/protocols/evm/aerodrome/index.js +1217 -807
  32. package/dist/protocols/evm/aerodrome/index.js.map +1 -1
  33. package/dist/protocols/evm/compound-v3/index.cjs +82 -6
  34. package/dist/protocols/evm/compound-v3/index.cjs.map +1 -1
  35. package/dist/protocols/evm/compound-v3/index.d.ts +89 -1
  36. package/dist/protocols/evm/compound-v3/index.js +80 -7
  37. package/dist/protocols/evm/compound-v3/index.js.map +1 -1
  38. package/dist/protocols/evm/curve-dao/index.cjs +447 -18
  39. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
  40. package/dist/protocols/evm/curve-dao/index.d.ts +108 -1
  41. package/dist/protocols/evm/curve-dao/index.js +440 -19
  42. package/dist/protocols/evm/curve-dao/index.js.map +1 -1
  43. package/dist/protocols/evm/ethena/index.cjs +89 -0
  44. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  45. package/dist/protocols/evm/ethena/index.d.ts +17 -1
  46. package/dist/protocols/evm/ethena/index.js +89 -2
  47. package/dist/protocols/evm/ethena/index.js.map +1 -1
  48. package/dist/protocols/evm/euler-v2/index.cjs +29 -0
  49. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  50. package/dist/protocols/evm/euler-v2/index.d.ts +21 -1
  51. package/dist/protocols/evm/euler-v2/index.js +29 -1
  52. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  53. package/dist/protocols/evm/gmx/index.cjs +83 -1
  54. package/dist/protocols/evm/gmx/index.cjs.map +1 -1
  55. package/dist/protocols/evm/gmx/index.d.ts +22 -2
  56. package/dist/protocols/evm/gmx/index.js +80 -2
  57. package/dist/protocols/evm/gmx/index.js.map +1 -1
  58. package/dist/protocols/evm/hyperliquid/index.cjs +67 -11
  59. package/dist/protocols/evm/hyperliquid/index.cjs.map +1 -1
  60. package/dist/protocols/evm/hyperliquid/index.d.ts +39 -2
  61. package/dist/protocols/evm/hyperliquid/index.js +64 -12
  62. package/dist/protocols/evm/hyperliquid/index.js.map +1 -1
  63. package/dist/protocols/evm/lido/index.cjs +13 -0
  64. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  65. package/dist/protocols/evm/lido/index.d.ts +9 -1
  66. package/dist/protocols/evm/lido/index.js +13 -1
  67. package/dist/protocols/evm/lido/index.js.map +1 -1
  68. package/dist/protocols/evm/maple/index.cjs +129 -1
  69. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  70. package/dist/protocols/evm/maple/index.d.ts +45 -1
  71. package/dist/protocols/evm/maple/index.js +124 -3
  72. package/dist/protocols/evm/maple/index.js.map +1 -1
  73. package/dist/protocols/evm/morpho/index.cjs +34 -7
  74. package/dist/protocols/evm/morpho/index.cjs.map +1 -1
  75. package/dist/protocols/evm/morpho/index.d.ts +23 -1
  76. package/dist/protocols/evm/morpho/index.js +34 -8
  77. package/dist/protocols/evm/morpho/index.js.map +1 -1
  78. package/dist/protocols/evm/sky/index.cjs +52 -0
  79. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  80. package/dist/protocols/evm/sky/index.d.ts +16 -1
  81. package/dist/protocols/evm/sky/index.js +50 -1
  82. package/dist/protocols/evm/sky/index.js.map +1 -1
  83. 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 publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
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 publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
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 publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
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 publicClient.estimateGas({
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 publicClient.getGasPrice();
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 publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
664
- const erc20AllowanceAbi = viem.parseAbi([
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 erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
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 publicClient.readContract({
671
+ const decimalsN = await publicClient2.readContract({
672
672
  address: tokenIn,
673
- abi: erc20AllowanceAbi,
673
+ abi: erc20AllowanceAbi2,
674
674
  functionName: "decimals"
675
675
  });
676
676
  const amountWei = viem.parseUnits(args.amountHuman, Number(decimalsN));
677
- const currentAllowance = await publicClient.readContract({
677
+ const currentAllowance = await publicClient2.readContract({
678
678
  address: tokenIn,
679
- abi: erc20AllowanceAbi,
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: erc20ApproveAbi,
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: erc20ApproveAbi,
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 publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
804
- const decimalsN = await publicClient.readContract({
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;