@continuumdao/ctm-mpc-defi 0.1.3

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 (65) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +111 -0
  3. package/dist/agent/catalog.cjs +484 -0
  4. package/dist/agent/catalog.cjs.map +1 -0
  5. package/dist/agent/catalog.d.cts +117 -0
  6. package/dist/agent/catalog.d.ts +117 -0
  7. package/dist/agent/catalog.js +474 -0
  8. package/dist/agent/catalog.js.map +1 -0
  9. package/dist/chains/evm/index.cjs +474 -0
  10. package/dist/chains/evm/index.cjs.map +1 -0
  11. package/dist/chains/evm/index.d.cts +62 -0
  12. package/dist/chains/evm/index.d.ts +62 -0
  13. package/dist/chains/evm/index.js +459 -0
  14. package/dist/chains/evm/index.js.map +1 -0
  15. package/dist/chains/near/index.cjs +25 -0
  16. package/dist/chains/near/index.cjs.map +1 -0
  17. package/dist/chains/near/index.d.cts +37 -0
  18. package/dist/chains/near/index.d.ts +37 -0
  19. package/dist/chains/near/index.js +20 -0
  20. package/dist/chains/near/index.js.map +1 -0
  21. package/dist/chains/solana/index.cjs +25 -0
  22. package/dist/chains/solana/index.cjs.map +1 -0
  23. package/dist/chains/solana/index.d.cts +40 -0
  24. package/dist/chains/solana/index.d.ts +40 -0
  25. package/dist/chains/solana/index.js +20 -0
  26. package/dist/chains/solana/index.js.map +1 -0
  27. package/dist/core/index.cjs +128 -0
  28. package/dist/core/index.cjs.map +1 -0
  29. package/dist/core/index.d.cts +10 -0
  30. package/dist/core/index.d.ts +10 -0
  31. package/dist/core/index.js +116 -0
  32. package/dist/core/index.js.map +1 -0
  33. package/dist/envelope-CcE5Cz_q.d.ts +35 -0
  34. package/dist/envelope-DYDPnrHZ.d.cts +35 -0
  35. package/dist/index.cjs +2481 -0
  36. package/dist/index.cjs.map +1 -0
  37. package/dist/index.d.cts +15 -0
  38. package/dist/index.d.ts +15 -0
  39. package/dist/index.js +2446 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/keygen-CfNp8yKJ.d.cts +9 -0
  42. package/dist/keygen-DsINazx8.d.ts +9 -0
  43. package/dist/nodeRead-BnmSaMGO.d.cts +8 -0
  44. package/dist/nodeRead-BnmSaMGO.d.ts +8 -0
  45. package/dist/protocols/evm/curve-dao/index.cjs +869 -0
  46. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -0
  47. package/dist/protocols/evm/curve-dao/index.d.cts +147 -0
  48. package/dist/protocols/evm/curve-dao/index.d.ts +147 -0
  49. package/dist/protocols/evm/curve-dao/index.js +846 -0
  50. package/dist/protocols/evm/curve-dao/index.js.map +1 -0
  51. package/dist/protocols/evm/uniswap-v4/index.cjs +1700 -0
  52. package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -0
  53. package/dist/protocols/evm/uniswap-v4/index.d.cts +323 -0
  54. package/dist/protocols/evm/uniswap-v4/index.d.ts +323 -0
  55. package/dist/protocols/evm/uniswap-v4/index.js +1659 -0
  56. package/dist/protocols/evm/uniswap-v4/index.js.map +1 -0
  57. package/dist/registry-BwZoE668.d.cts +8 -0
  58. package/dist/registry-oMKlO_5z.d.ts +8 -0
  59. package/dist/txParams-BC7ogvdR.d.cts +19 -0
  60. package/dist/txParams-BC7ogvdR.d.ts +19 -0
  61. package/dist/types-5u863Fd9.d.ts +34 -0
  62. package/dist/types-B8idm_gu.d.cts +34 -0
  63. package/dist/types-Ce2qNHai.d.cts +57 -0
  64. package/dist/types-Ce2qNHai.d.ts +57 -0
  65. package/package.json +94 -0
@@ -0,0 +1,869 @@
1
+ 'use strict';
2
+
3
+ var viem = require('viem');
4
+
5
+ // src/core/registry.ts
6
+ var modules = [];
7
+ function registerProtocolModule(mod) {
8
+ const existing = modules.findIndex((m) => m.id === mod.id);
9
+ if (existing >= 0) {
10
+ modules[existing] = mod;
11
+ } else {
12
+ modules.push(mod);
13
+ }
14
+ }
15
+
16
+ // src/chains/evm/chainIdParse.ts
17
+ function parseEvmChainIdToNumber(chainId) {
18
+ if (chainId == null) return Number.NaN;
19
+ if (typeof chainId === "bigint") {
20
+ const n = Number(chainId);
21
+ return Number.isSafeInteger(n) && n >= 0 ? n : Number.NaN;
22
+ }
23
+ if (typeof chainId === "number") {
24
+ return Number.isInteger(chainId) && chainId >= 0 ? chainId : Number.NaN;
25
+ }
26
+ const t = String(chainId).trim();
27
+ if (!t) return Number.NaN;
28
+ const low = t.toLowerCase();
29
+ if (low.startsWith("eip155:")) {
30
+ const rest = t.slice("eip155:".length).trim();
31
+ const n = Number.parseInt(rest, 10);
32
+ return Number.isNaN(n) || n < 0 ? Number.NaN : n;
33
+ }
34
+ if (low.startsWith("0x")) {
35
+ return Number.parseInt(t, 16);
36
+ }
37
+ return Number.parseInt(t, 10);
38
+ }
39
+
40
+ // src/protocols/evm/curve-dao/support.ts
41
+ var CURVE_FULL_NETWORK_CONSTANTS_CHAIN_IDS = /* @__PURE__ */ new Set([
42
+ 1,
43
+ 10,
44
+ 56,
45
+ 100,
46
+ 137,
47
+ 146,
48
+ 196,
49
+ 250,
50
+ 252,
51
+ 324,
52
+ 999,
53
+ 1284,
54
+ 2222,
55
+ 5e3,
56
+ 8453,
57
+ 42161,
58
+ 42220,
59
+ 43114,
60
+ 1313161554
61
+ ]);
62
+ function isCurveApiChainSupported(chainId) {
63
+ const n = parseEvmChainIdToNumber(chainId);
64
+ if (Number.isNaN(n) || n < 0) return false;
65
+ return CURVE_FULL_NETWORK_CONSTANTS_CHAIN_IDS.has(n);
66
+ }
67
+ var CURVE_NATIVE_PLACEHOLDER = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
68
+ var ETH_PLACEHOLDER = CURVE_NATIVE_PLACEHOLDER;
69
+ function normalizeAddr(a) {
70
+ return a.toLowerCase();
71
+ }
72
+ function toCurveTokenKey(tokenInAddress, wrappedNative) {
73
+ if (!viem.isAddress(tokenInAddress)) return normalizeAddr(tokenInAddress);
74
+ try {
75
+ const a = viem.getAddress(tokenInAddress);
76
+ if (wrappedNative && a.toLowerCase() === wrappedNative.toLowerCase()) {
77
+ return ETH_PLACEHOLDER;
78
+ }
79
+ return a.toLowerCase();
80
+ } catch {
81
+ return normalizeAddr(tokenInAddress);
82
+ }
83
+ }
84
+ function toCurveRouterTokenId(tokenAddress, wrappedNative) {
85
+ const t = (tokenAddress ?? "").trim().toLowerCase();
86
+ if (t === ETH_PLACEHOLDER) return ETH_PLACEHOLDER;
87
+ if (!viem.isAddress((tokenAddress ?? "").trim())) return normalizeAddr((tokenAddress ?? "").trim());
88
+ try {
89
+ const a = viem.getAddress((tokenAddress ?? "").trim());
90
+ if (wrappedNative && a.toLowerCase() === wrappedNative.toLowerCase()) {
91
+ return ETH_PLACEHOLDER;
92
+ }
93
+ return a.toLowerCase();
94
+ } catch {
95
+ return normalizeAddr((tokenAddress ?? "").trim());
96
+ }
97
+ }
98
+ function normalizeCurveRouterAmountString(raw, tokenInDecimals) {
99
+ const t = raw.trim().replace(/,/g, "");
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);
106
+ }
107
+ function addEdge(adj, a, b) {
108
+ if (a === b) return;
109
+ if (!adj.has(a)) adj.set(a, /* @__PURE__ */ new Set());
110
+ if (!adj.has(b)) adj.set(b, /* @__PURE__ */ new Set());
111
+ adj.get(a).add(b);
112
+ adj.get(b).add(a);
113
+ }
114
+ function buildCurveLiquidityGraphFromApi(curve) {
115
+ const adj = /* @__PURE__ */ new Map();
116
+ for (const id of curve.getPoolList()) {
117
+ try {
118
+ const pool = curve.getPool(id);
119
+ const w = (pool.wrappedCoinAddresses ?? []).map(normalizeAddr);
120
+ const u = (pool.underlyingCoinAddresses ?? []).map(normalizeAddr);
121
+ const all = [.../* @__PURE__ */ new Set([...w, ...u])];
122
+ for (let i = 0; i < all.length; i++) {
123
+ for (let j = i + 1; j < all.length; j++) {
124
+ addEdge(adj, all[i], all[j]);
125
+ }
126
+ }
127
+ } catch {
128
+ }
129
+ }
130
+ return adj;
131
+ }
132
+ function addNativeWethBridge(adj, wrappedNative) {
133
+ if (!wrappedNative) return;
134
+ const w = wrappedNative.toLowerCase();
135
+ const a = ETH_PLACEHOLDER;
136
+ const b = w;
137
+ if (a === b) return;
138
+ if (!adj.has(a)) adj.set(a, /* @__PURE__ */ new Set());
139
+ if (!adj.has(b)) adj.set(b, /* @__PURE__ */ new Set());
140
+ adj.get(a).add(b);
141
+ adj.get(b).add(a);
142
+ }
143
+ function isCurveTokenKeySwappable(swappableNodeKeys, graphTokenKey) {
144
+ return swappableNodeKeys.has(graphTokenKey);
145
+ }
146
+ function swappableCurveGraphNodeKeys(adj) {
147
+ const out = /* @__PURE__ */ new Set();
148
+ for (const [k, neigh] of adj) {
149
+ if (neigh.size > 0) out.add(k);
150
+ }
151
+ return out;
152
+ }
153
+ function bfsCurveSwapDestinations(adj, start) {
154
+ const s = normalizeAddr(start);
155
+ const visited = /* @__PURE__ */ new Set([s]);
156
+ const q = [s];
157
+ while (q.length) {
158
+ const n = q.shift();
159
+ for (const nxt of adj.get(n) ?? []) {
160
+ if (!visited.has(nxt)) {
161
+ visited.add(nxt);
162
+ q.push(nxt);
163
+ }
164
+ }
165
+ }
166
+ visited.delete(s);
167
+ return visited;
168
+ }
169
+
170
+ // src/protocols/evm/curve-dao/apiSession.ts
171
+ async function fetchAllCurvePools(curve) {
172
+ const run = (p) => p.catch(() => void 0);
173
+ await Promise.all([
174
+ run(curve.factory.fetchPools()),
175
+ run(curve.crvUSDFactory.fetchPools()),
176
+ run(curve.EYWAFactory.fetchPools()),
177
+ run(curve.cryptoFactory.fetchPools()),
178
+ run(curve.twocryptoFactory.fetchPools()),
179
+ run(curve.tricryptoFactory.fetchPools()),
180
+ run(curve.stableNgFactory.fetchPools())
181
+ ]);
182
+ }
183
+ async function loadFullCurveSessionForRpc(rpcUrl) {
184
+ const url = (rpcUrl ?? "").trim();
185
+ if (!url) return null;
186
+ try {
187
+ const { default: curve } = await import('@curvefi/api');
188
+ await curve.init("JsonRpc", { url }, {});
189
+ const wrapped = curve.getNetworkConstants().NATIVE_COIN?.wrappedAddress;
190
+ if (!curve.hasRouter || !curve.hasRouter()) {
191
+ return {
192
+ curve,
193
+ adj: /* @__PURE__ */ new Map(),
194
+ swappableNodeKeys: /* @__PURE__ */ new Set(),
195
+ wrappedNative: wrapped
196
+ };
197
+ }
198
+ await fetchAllCurvePools(curve);
199
+ const adj = buildCurveLiquidityGraphFromApi(curve);
200
+ addNativeWethBridge(adj, wrapped);
201
+ return {
202
+ curve,
203
+ adj,
204
+ swappableNodeKeys: swappableCurveGraphNodeKeys(adj),
205
+ wrappedNative: wrapped
206
+ };
207
+ } catch {
208
+ return null;
209
+ }
210
+ }
211
+
212
+ // src/protocols/evm/curve-dao/purpose.ts
213
+ function buildCurveDaoPurposePrefill(args) {
214
+ const symIn = (args.tokenInSymbol || "").trim() || "token in";
215
+ const symOut = (args.tokenOutSymbol || "").trim() || "token out";
216
+ const rawS = String(args.slippageInput || "0.5").trim().replace(/,/g, "");
217
+ const parsedSlip = Number.parseFloat(rawS);
218
+ const slip = Number.isFinite(parsedSlip) ? parsedSlip : 0.5;
219
+ const slipText = (Math.round(slip * 100) / 100).toString();
220
+ const cname = (args.chainName || "").trim() || "chain";
221
+ const amt = (args.amountIn || "").trim();
222
+ const qo = (args.quoteOutAmount || "").trim();
223
+ let mid;
224
+ if (amt && qo) {
225
+ mid = `${amt} ${symIn} for an estimated ${qo} ${symOut}`;
226
+ } else if (amt) {
227
+ mid = `${amt} ${symIn} for an estimated output in ${symOut}`;
228
+ } else {
229
+ mid = `\u2026 ${symIn} \u2192 ${symOut}`;
230
+ }
231
+ return `Curve: swap ${mid} (max slippage ${slipText}%) on ${cname} (chainId ${args.chainId})`;
232
+ }
233
+
234
+ // src/protocols/evm/curve-dao/executeHelpers.ts
235
+ function parseOptionalGasLimitString(raw) {
236
+ if (raw == null) return null;
237
+ const s = String(raw).trim();
238
+ if (!s) return null;
239
+ try {
240
+ if (/^0x[0-9a-fA-F]+$/.test(s)) return BigInt(s);
241
+ return BigInt(s);
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+ function parseExtraJsonObject(detail) {
247
+ if (!detail) return null;
248
+ const raw = detail.ExtraJSON ?? detail.extraJSON;
249
+ if (raw == null) return null;
250
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
251
+ if (typeof raw === "string" && raw.trim()) {
252
+ try {
253
+ const p = JSON.parse(raw);
254
+ if (p && typeof p === "object" && !Array.isArray(p)) return p;
255
+ } catch {
256
+ return null;
257
+ }
258
+ }
259
+ return null;
260
+ }
261
+ var CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS = 1200000n;
262
+ function isCurveDaoSwapEvmSignRequest(detail, batchIndex) {
263
+ const ex = parseExtraJsonObject(detail);
264
+ if (batchIndex != null && batchIndex >= 0 && ex) {
265
+ const bm = ex.batchMeta;
266
+ if (Array.isArray(bm) && bm[batchIndex] && typeof bm[batchIndex] === "object" && !Array.isArray(bm[batchIndex])) {
267
+ const evm0 = bm[batchIndex].evm;
268
+ if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
269
+ if (String(evm0.type ?? "") === "curve_dao_router_exchange") return true;
270
+ }
271
+ }
272
+ }
273
+ const evm = ex?.evm;
274
+ if (!evm || typeof evm !== "object" || Array.isArray(evm)) return false;
275
+ return String(evm.type ?? "") === "curve_dao_router_exchange";
276
+ }
277
+ function isCurveDaoErc20ApproveEvmSignRequest(detail, batchIndex) {
278
+ const ex = parseExtraJsonObject(detail);
279
+ const index = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
280
+ const bm = ex?.batchMeta;
281
+ if (Array.isArray(bm) && bm[index] && typeof bm[index] === "object" && !Array.isArray(bm[index])) {
282
+ const evm0 = bm[index].evm;
283
+ if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
284
+ const t = String(evm0.type ?? "");
285
+ return t === "curve_dao_erc20_approve";
286
+ }
287
+ }
288
+ return false;
289
+ }
290
+ function resolveCurveDaoRouterGasUnitsFromSignRequest(detail, batchIndex) {
291
+ if (!detail) return null;
292
+ const propBatch = detail.proposal_tx_params ?? detail.proposalTxParams ?? detail.ProposalTxParams;
293
+ const index = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
294
+ if (Array.isArray(propBatch) && propBatch.length > index) {
295
+ const row = propBatch[index];
296
+ if (row && typeof row === "object" && !Array.isArray(row)) {
297
+ const r = row;
298
+ const gl = parseOptionalGasLimitString(
299
+ r.gas_limit ?? r.gasLimit ?? r.GasLimit
300
+ );
301
+ if (gl != null && gl > 0n) return gl;
302
+ }
303
+ }
304
+ const ex = parseExtraJsonObject(detail);
305
+ const bm = ex?.batchMeta;
306
+ if (Array.isArray(bm)) {
307
+ for (const item of bm) {
308
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
309
+ const cd = item.curveDao;
310
+ const gb = cd?.gasBuildExchange;
311
+ const base = parseOptionalGasLimitString(gb?.baseGasUnits);
312
+ if (base != null && base > 0n) return base;
313
+ }
314
+ }
315
+ return null;
316
+ }
317
+
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
+ // src/core/purpose.ts
329
+ function mergePurposeText(purposeText, purposeSuffix) {
330
+ const t = purposeText.trim();
331
+ const suffix = (purposeSuffix ?? "").trim();
332
+ if (!suffix) return t;
333
+ return t ? `${t}
334
+
335
+ ${suffix}` : suffix;
336
+ }
337
+
338
+ // src/core/envelope.ts
339
+ function finalizeMultisign(input) {
340
+ const { keyGen, destinationChainID, legs } = input;
341
+ if (legs.length === 0) {
342
+ throw new Error("finalizeMultisign requires at least one leg");
343
+ }
344
+ const ph = (keyGen.pubkeyhex ?? "").trim();
345
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
346
+ const keyList = keyGen.keylist ?? [];
347
+ const clientId = firstClientIdFromKeyGen(keyGen);
348
+ const first = legs[0];
349
+ const messageHashes = legs.map((l) => l.msgHash);
350
+ const messageRawBatch = legs.map((l) => l.msgRaw);
351
+ const batchMeta = legs.map((l) => ({
352
+ destinationAddress: l.destinationAddress,
353
+ signatureText: l.signatureText,
354
+ ...l.audit
355
+ }));
356
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
357
+ const extraPayload = {
358
+ batchMeta,
359
+ ...input.extraJSON ?? {}
360
+ };
361
+ const extraJSON = JSON.stringify(extraPayload);
362
+ const bodyForSign = {
363
+ keyList,
364
+ pubKey: ph,
365
+ msgHash: messageHashes[0],
366
+ msgRaw: first.msgRaw,
367
+ destinationChainID,
368
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
369
+ extraJSON,
370
+ signatureText: first.signatureText,
371
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
372
+ ...first.feeSnapshot
373
+ };
374
+ if (legs.length > 1) {
375
+ bodyForSign.messageHashes = messageHashes;
376
+ bodyForSign.messageRawBatch = messageRawBatch;
377
+ }
378
+ if (proposalTxParams.length > 0) {
379
+ bodyForSign.proposalTxParams = proposalTxParams;
380
+ }
381
+ const valueWei = first.valueWei;
382
+ if (valueWei != null && valueWei > 0n) {
383
+ bodyForSign.value = valueWei.toString();
384
+ }
385
+ if (clientId) bodyForSign.clientId = clientId;
386
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
387
+ }
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
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
469
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
470
+ return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
471
+ }
472
+ return (estimatedGas * 12n + 9n) / 10n;
473
+ }
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
+
490
+ // src/chains/evm/buildBatch.ts
491
+ async function buildEvmMultisignBatch(args) {
492
+ const { context, steps } = args;
493
+ const {
494
+ chainId,
495
+ rpcUrl,
496
+ executorAddress,
497
+ chainDetail,
498
+ useCustomGas,
499
+ customGasChainDetails,
500
+ keyGen,
501
+ purposeText
502
+ } = context;
503
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
504
+ const ch = viem.defineChain({
505
+ id: chainId,
506
+ name: "Destination",
507
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
508
+ rpcUrls: { default: { http: [rpcUrl] } }
509
+ });
510
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
511
+ const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
512
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
513
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
514
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
515
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
516
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
517
+ const executor = viem.getAddress(executorAddress);
518
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
519
+ const legs = [];
520
+ for (let i = 0; i < steps.length; i++) {
521
+ const step = steps[i];
522
+ const currentNonce = baseNonce + i;
523
+ let estimatedGas;
524
+ try {
525
+ estimatedGas = await publicClient.estimateGas({
526
+ to: step.to,
527
+ data: step.data,
528
+ value: step.value,
529
+ account: executor
530
+ });
531
+ } catch {
532
+ estimatedGas = step.fallbackGas ?? 100000n;
533
+ }
534
+ let gasLimitI;
535
+ if (args.resolveGasLimit) {
536
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
537
+ } else if (step.routerSwap) {
538
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
539
+ } else {
540
+ gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
541
+ }
542
+ let proposalTxParams;
543
+ let feeSnapshot;
544
+ let serialized;
545
+ if (legacy) {
546
+ let gasPriceWei = await publicClient.getGasPrice();
547
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
548
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
549
+ }
550
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
551
+ const configured = viem.parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
552
+ if (configured > gasPriceWei) gasPriceWei = configured;
553
+ }
554
+ serialized = viem.serializeTransaction({
555
+ type: "legacy",
556
+ to: step.to,
557
+ data: step.data,
558
+ value: step.value,
559
+ gas: gasLimitI,
560
+ gasPrice: gasPriceWei,
561
+ nonce: currentNonce,
562
+ chainId
563
+ });
564
+ proposalTxParams = {
565
+ nonce: currentNonce,
566
+ gasLimit: gasLimitI.toString(),
567
+ txType: "legacy",
568
+ gasPrice: gasPriceWei.toString()
569
+ };
570
+ feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
571
+ } else {
572
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
573
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
574
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
575
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
576
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
577
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
578
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
579
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
580
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
581
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
582
+ let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
583
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
584
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
585
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
586
+ }
587
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
588
+ maxFeePerGas,
589
+ maxPriorityFeePerGas,
590
+ latestBaseFeeWei
591
+ ));
592
+ serialized = viem.serializeTransaction({
593
+ type: "eip1559",
594
+ to: step.to,
595
+ data: step.data,
596
+ value: step.value,
597
+ gas: gasLimitI,
598
+ maxFeePerGas,
599
+ maxPriorityFeePerGas,
600
+ nonce: currentNonce,
601
+ chainId
602
+ });
603
+ proposalTxParams = {
604
+ nonce: currentNonce,
605
+ gasLimit: gasLimitI.toString(),
606
+ txType: "eip1559",
607
+ maxFeePerGas: maxFeePerGas.toString(),
608
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
609
+ };
610
+ feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
611
+ }
612
+ const h = viem.keccak256(serialized);
613
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
614
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
615
+ legs.push({
616
+ msgHash,
617
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
618
+ destinationAddress: step.to,
619
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
620
+ audit: batchMetaExtra,
621
+ feeSnapshot: i === 0 ? feeSnapshot : {},
622
+ proposalTxParams,
623
+ valueWei: i === 0 ? step.value : void 0
624
+ });
625
+ if (i === 0 && args.firstMsgRawNo0x != null) {
626
+ legs[0].msgRaw = args.firstMsgRawNo0x;
627
+ }
628
+ }
629
+ const extraJSON = {};
630
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
631
+ extraJSON.customGasChainDetails = customGasChainDetails;
632
+ }
633
+ const result = finalizeMultisign({
634
+ keyGen,
635
+ purposeText,
636
+ purposeSuffix: args.purposeSuffix,
637
+ destinationChainID: String(chainId),
638
+ destinationAddress: args.destinationAddress ?? steps[0].to,
639
+ legs,
640
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
641
+ });
642
+ const pv = args.payableValueWei;
643
+ if (pv != null && pv > 0n) {
644
+ result.bodyForSign.value = pv.toString();
645
+ }
646
+ return result;
647
+ }
648
+
649
+ // src/protocols/evm/curve-dao/multisign.ts
650
+ var ETH_PLACEHOLDER2 = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
651
+ function txToViemStep(tx) {
652
+ if (!tx.to) throw new Error("Transaction missing `to`");
653
+ const to = viem.getAddress((tx.to ?? "").trim());
654
+ const raw = (tx.data ?? "0x").toString();
655
+ const data = raw.startsWith("0x") ? raw : `0x${raw}`;
656
+ let value = 0n;
657
+ if (tx.value != null) {
658
+ if (typeof tx.value === "bigint") value = tx.value;
659
+ else value = BigInt(String(tx.value));
660
+ }
661
+ return { to, data, value };
662
+ }
663
+ async function buildEvmMultisignBodyCurveDaoBatch(args) {
664
+ const tokenIn = viem.getAddress(args.tokenIn);
665
+ const executor = viem.getAddress(args.executorAddress);
666
+ const session = await loadFullCurveSessionForRpc(args.rpcUrl);
667
+ if (!session?.curve?.router?.populateSwap) {
668
+ throw new Error("Curve router is not available (load session failed or chain has no router).");
669
+ }
670
+ const { curve, wrappedNative } = session;
671
+ const wn = wrappedNative;
672
+ const tokenInId = toCurveRouterTokenId(tokenIn, wn);
673
+ const outTrim = (args.tokenOut ?? "").trim();
674
+ const tokenOutId = toCurveRouterTokenId(
675
+ outTrim.toLowerCase() === ETH_PLACEHOLDER2 ? ETH_PLACEHOLDER2 : viem.getAddress(outTrim),
676
+ wn
677
+ );
678
+ const isNativeIn = tokenInId.toLowerCase() === ETH_PLACEHOLDER2;
679
+ const slip = args.slippagePercent;
680
+ if (!Number.isFinite(slip) || slip <= 0 || slip >= 100) {
681
+ throw new Error("Slippage must be between 0 and 100 (exclusive).");
682
+ }
683
+ const swapPop = await curve.router.populateSwap(tokenInId, tokenOutId, args.amountHuman, slip);
684
+ const swapBase = txToViemStep(swapPop);
685
+ const rawTo = swapPop?.to;
686
+ if (rawTo == null || String(rawTo).trim() === "") {
687
+ throw new Error("Curve populateSwap did not return a destination address (router).");
688
+ }
689
+ const routerAddr = viem.getAddress(String(rawTo).trim());
690
+ const ch = viem.defineChain({
691
+ id: args.chainId,
692
+ name: "Destination",
693
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
694
+ rpcUrls: { default: { http: [args.rpcUrl] } }
695
+ });
696
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
697
+ const erc20AllowanceAbi = viem.parseAbi([
698
+ "function allowance(address owner, address spender) view returns (uint256)",
699
+ "function decimals() view returns (uint8)"
700
+ ]);
701
+ const erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
702
+ const steps = [];
703
+ if (!isNativeIn) {
704
+ const decimalsN = await publicClient.readContract({
705
+ address: tokenIn,
706
+ abi: erc20AllowanceAbi,
707
+ functionName: "decimals"
708
+ });
709
+ const amountWei = viem.parseUnits(args.amountHuman, Number(decimalsN));
710
+ const currentAllowance = await publicClient.readContract({
711
+ address: tokenIn,
712
+ abi: erc20AllowanceAbi,
713
+ functionName: "allowance",
714
+ args: [executor, routerAddr]
715
+ });
716
+ if (currentAllowance < amountWei) {
717
+ if (currentAllowance > 0n) {
718
+ const dataReset = viem.encodeFunctionData({
719
+ abi: erc20ApproveAbi,
720
+ functionName: "approve",
721
+ args: [routerAddr, 0n]
722
+ });
723
+ steps.push({ to: tokenIn, data: dataReset, value: 0n, kind: "approve", fallbackGas: 80000n });
724
+ }
725
+ const dataApprove = viem.encodeFunctionData({
726
+ abi: erc20ApproveAbi,
727
+ functionName: "approve",
728
+ args: [routerAddr, amountWei]
729
+ });
730
+ steps.push({ to: tokenIn, data: dataApprove, value: 0n, kind: "approve", fallbackGas: 80000n });
731
+ }
732
+ }
733
+ steps.push({
734
+ ...swapBase,
735
+ kind: "exchange",
736
+ routerSwap: true,
737
+ fallbackGas: CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS
738
+ });
739
+ const chainGasLimitRouter = args.chainDetail?.gasLimit != null && Number.isFinite(Number(args.chainDetail.gasLimit)) && Number(args.chainDetail.gasLimit) > 0 ? Number(args.chainDetail.gasLimit) : void 0;
740
+ const gasLimitByIndex = /* @__PURE__ */ new Map();
741
+ const n = steps.length;
742
+ const purposeSuffix = n === 1 ? "Curve (DAO): 1-tx \u2014 CurveRouterNG.exchange (native in or allowance already set)." : `Curve (DAO): ${n}-tx batch \u2014 ERC-20 approve Curve router, then CurveRouterNG.exchange.`;
743
+ const firstDataNo0x = steps[0].data.startsWith("0x") ? steps[0].data.slice(2) : steps[0].data;
744
+ const payableValueWei = steps.filter((s) => s.kind === "exchange").reduce((a, s) => a + s.value, 0n);
745
+ return buildEvmMultisignBatch({
746
+ context: {
747
+ chainCategory: "evm",
748
+ keyGen: args.keyGen,
749
+ purposeText: args.purposeText,
750
+ chainId: args.chainId,
751
+ rpcUrl: args.rpcUrl,
752
+ executorAddress: executor,
753
+ chainDetail: args.chainDetail,
754
+ useCustomGas: args.useCustomGas,
755
+ customGasChainDetails: args.customGasChainDetails
756
+ },
757
+ steps,
758
+ purposeSuffix,
759
+ firstMsgRawNo0x: firstDataNo0x,
760
+ destinationAddress: steps[0].to,
761
+ payableValueWei: payableValueWei > 0n ? payableValueWei : void 0,
762
+ resolveGasLimit: async ({ step, index, estimatedGas }) => {
763
+ const s = step;
764
+ let gasLimitI;
765
+ if (s.kind === "exchange") {
766
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
767
+ } else {
768
+ gasLimitI = estimatedGas;
769
+ }
770
+ gasLimitByIndex.set(index, gasLimitI);
771
+ return gasLimitI;
772
+ },
773
+ buildBatchMeta: ({ step, index, gasLimit }) => {
774
+ const s = step;
775
+ const gl = gasLimitByIndex.get(index) ?? gasLimit;
776
+ if (s.kind === "approve") {
777
+ return {
778
+ signatureText: JSON.stringify({
779
+ kind: "CurveDao",
780
+ name: "ERC20.approve",
781
+ to: "Curve router",
782
+ function: "approve(address spender, uint256 amount)",
783
+ spender: routerAddr,
784
+ amountHuman: args.amountHuman,
785
+ note: "Allowance approved for this swap amount only (not unlimited)."
786
+ }),
787
+ evm: { type: "curve_dao_erc20_approve", version: 1, chainId: String(args.chainId) }
788
+ };
789
+ }
790
+ return {
791
+ signatureText: JSON.stringify({
792
+ kind: "CurveDao",
793
+ name: "CurveRouterNG.exchange",
794
+ note: "Calldata from @curvefi/api `router.populateSwap` (same on-chain as router `exchange`)."
795
+ }),
796
+ evm: { type: "curve_dao_router_exchange", version: 1, chainId: String(args.chainId) },
797
+ curveDao: {
798
+ slippagePercent: slip,
799
+ amountHuman: args.amountHuman,
800
+ tokenIn: tokenInId,
801
+ tokenOut: tokenOutId,
802
+ isNativeIn,
803
+ gasBuildExchange: { baseGasUnits: gl.toString() }
804
+ }
805
+ };
806
+ }
807
+ });
808
+ }
809
+
810
+ // src/protocols/evm/curve-dao/index.ts
811
+ var CURVE_DAO_PROTOCOL_ID = "curve-dao";
812
+ var curveDaoProtocolModule = {
813
+ id: CURVE_DAO_PROTOCOL_ID,
814
+ chainCategory: "evm",
815
+ isChainSupported(ctx) {
816
+ if (ctx.chainCategory !== "evm") return false;
817
+ return isCurveApiChainSupported(ctx.chainId);
818
+ },
819
+ isTokenSupported(token) {
820
+ if (token.category !== "evm") return false;
821
+ return token.kind === "native" || token.kind === "erc20";
822
+ },
823
+ actions: [
824
+ {
825
+ id: "curve-dao.swap",
826
+ protocolId: CURVE_DAO_PROTOCOL_ID,
827
+ chainCategory: "evm",
828
+ description: "Swap via Curve Router NG (optional ERC-20 approve batch)",
829
+ commonParams: ["keyGen", "purposeText", "useCustomGas"],
830
+ params: {
831
+ tokenIn: { type: "address", required: true, description: "ERC-20 token in (native uses WETH path in UI)" },
832
+ tokenOut: { type: "address", required: true, description: "Output token or 0xeeee\u2026 native placeholder" },
833
+ amountHuman: { type: "string", required: true, description: "Human-readable input amount" },
834
+ slippagePercent: { type: "number", required: true, description: "Slippage percent (0\u2013100 exclusive)" }
835
+ }
836
+ }
837
+ ]
838
+ };
839
+ registerProtocolModule(curveDaoProtocolModule);
840
+ var curveDao = {
841
+ buildSwapMultisignBody: buildEvmMultisignBodyCurveDaoBatch,
842
+ isChainSupported: isCurveApiChainSupported,
843
+ loadSession: loadFullCurveSessionForRpc
844
+ };
845
+
846
+ exports.CURVE_DAO_PROTOCOL_ID = CURVE_DAO_PROTOCOL_ID;
847
+ exports.CURVE_NATIVE_PLACEHOLDER = CURVE_NATIVE_PLACEHOLDER;
848
+ exports.CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS = CURVE_ROUTER_EXCHANGE_DEFAULT_GAS_UNITS;
849
+ exports.addNativeWethBridge = addNativeWethBridge;
850
+ exports.bfsCurveSwapDestinations = bfsCurveSwapDestinations;
851
+ exports.buildCurveDaoPurposePrefill = buildCurveDaoPurposePrefill;
852
+ exports.buildCurveLiquidityGraphFromApi = buildCurveLiquidityGraphFromApi;
853
+ exports.buildEvmMultisignBodyCurveDaoBatch = buildEvmMultisignBodyCurveDaoBatch;
854
+ exports.curveDao = curveDao;
855
+ exports.curveDaoProtocolModule = curveDaoProtocolModule;
856
+ exports.fetchAllCurvePools = fetchAllCurvePools;
857
+ exports.isCurveApiChainSupported = isCurveApiChainSupported;
858
+ exports.isCurveDaoChainSupported = isCurveApiChainSupported;
859
+ exports.isCurveDaoErc20ApproveEvmSignRequest = isCurveDaoErc20ApproveEvmSignRequest;
860
+ exports.isCurveDaoSwapEvmSignRequest = isCurveDaoSwapEvmSignRequest;
861
+ exports.isCurveTokenKeySwappable = isCurveTokenKeySwappable;
862
+ exports.loadFullCurveSessionForRpc = loadFullCurveSessionForRpc;
863
+ exports.normalizeCurveRouterAmountString = normalizeCurveRouterAmountString;
864
+ exports.resolveCurveDaoRouterGasUnitsFromSignRequest = resolveCurveDaoRouterGasUnitsFromSignRequest;
865
+ exports.swappableCurveGraphNodeKeys = swappableCurveGraphNodeKeys;
866
+ exports.toCurveRouterTokenId = toCurveRouterTokenId;
867
+ exports.toCurveTokenKey = toCurveTokenKey;
868
+ //# sourceMappingURL=index.cjs.map
869
+ //# sourceMappingURL=index.cjs.map