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