@hypurrquant/defi-cli 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -15,693 +15,7 @@ import { encodeFunctionData as encodeFunctionData2, decodeFunctionResult, parseA
15
15
  import { readFileSync, readdirSync } from "fs";
16
16
  import { resolve } from "path";
17
17
  import { fileURLToPath } from "url";
18
-
19
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/error.js
20
- function getLineColFromPtr(string, ptr) {
21
- let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
22
- return [lines.length, lines.pop().length + 1];
23
- }
24
- function makeCodeBlock(string, line, column) {
25
- let lines = string.split(/\r\n|\n|\r/g);
26
- let codeblock = "";
27
- let numberLen = (Math.log10(line + 1) | 0) + 1;
28
- for (let i = line - 1; i <= line + 1; i++) {
29
- let l = lines[i - 1];
30
- if (!l)
31
- continue;
32
- codeblock += i.toString().padEnd(numberLen, " ");
33
- codeblock += ": ";
34
- codeblock += l;
35
- codeblock += "\n";
36
- if (i === line) {
37
- codeblock += " ".repeat(numberLen + column + 2);
38
- codeblock += "^\n";
39
- }
40
- }
41
- return codeblock;
42
- }
43
- var TomlError = class extends Error {
44
- line;
45
- column;
46
- codeblock;
47
- constructor(message, options) {
48
- const [line, column] = getLineColFromPtr(options.toml, options.ptr);
49
- const codeblock = makeCodeBlock(options.toml, line, column);
50
- super(`Invalid TOML document: ${message}
51
-
52
- ${codeblock}`, options);
53
- this.line = line;
54
- this.column = column;
55
- this.codeblock = codeblock;
56
- }
57
- };
58
-
59
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/util.js
60
- function isEscaped(str, ptr) {
61
- let i = 0;
62
- while (str[ptr - ++i] === "\\")
63
- ;
64
- return --i && i % 2;
65
- }
66
- function indexOfNewline(str, start = 0, end = str.length) {
67
- let idx = str.indexOf("\n", start);
68
- if (str[idx - 1] === "\r")
69
- idx--;
70
- return idx <= end ? idx : -1;
71
- }
72
- function skipComment(str, ptr) {
73
- for (let i = ptr; i < str.length; i++) {
74
- let c = str[i];
75
- if (c === "\n")
76
- return i;
77
- if (c === "\r" && str[i + 1] === "\n")
78
- return i + 1;
79
- if (c < " " && c !== " " || c === "\x7F") {
80
- throw new TomlError("control characters are not allowed in comments", {
81
- toml: str,
82
- ptr
83
- });
84
- }
85
- }
86
- return str.length;
87
- }
88
- function skipVoid(str, ptr, banNewLines, banComments) {
89
- let c;
90
- while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
91
- ptr++;
92
- return banComments || c !== "#" ? ptr : skipVoid(str, skipComment(str, ptr), banNewLines);
93
- }
94
- function skipUntil(str, ptr, sep, end, banNewLines = false) {
95
- if (!end) {
96
- ptr = indexOfNewline(str, ptr);
97
- return ptr < 0 ? str.length : ptr;
98
- }
99
- for (let i = ptr; i < str.length; i++) {
100
- let c = str[i];
101
- if (c === "#") {
102
- i = indexOfNewline(str, i);
103
- } else if (c === sep) {
104
- return i + 1;
105
- } else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
106
- return i;
107
- }
108
- }
109
- throw new TomlError("cannot find end of structure", {
110
- toml: str,
111
- ptr
112
- });
113
- }
114
- function getStringEnd(str, seek) {
115
- let first = str[seek];
116
- let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2] ? str.slice(seek, seek + 3) : first;
117
- seek += target.length - 1;
118
- do
119
- seek = str.indexOf(target, ++seek);
120
- while (seek > -1 && first !== "'" && isEscaped(str, seek));
121
- if (seek > -1) {
122
- seek += target.length;
123
- if (target.length > 1) {
124
- if (str[seek] === first)
125
- seek++;
126
- if (str[seek] === first)
127
- seek++;
128
- }
129
- }
130
- return seek;
131
- }
132
-
133
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/date.js
134
- var DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
135
- var TomlDate = class _TomlDate extends Date {
136
- #hasDate = false;
137
- #hasTime = false;
138
- #offset = null;
139
- constructor(date) {
140
- let hasDate = true;
141
- let hasTime = true;
142
- let offset = "Z";
143
- if (typeof date === "string") {
144
- let match = date.match(DATE_TIME_RE);
145
- if (match) {
146
- if (!match[1]) {
147
- hasDate = false;
148
- date = `0000-01-01T${date}`;
149
- }
150
- hasTime = !!match[2];
151
- hasTime && date[10] === " " && (date = date.replace(" ", "T"));
152
- if (match[2] && +match[2] > 23) {
153
- date = "";
154
- } else {
155
- offset = match[3] || null;
156
- date = date.toUpperCase();
157
- if (!offset && hasTime)
158
- date += "Z";
159
- }
160
- } else {
161
- date = "";
162
- }
163
- }
164
- super(date);
165
- if (!isNaN(this.getTime())) {
166
- this.#hasDate = hasDate;
167
- this.#hasTime = hasTime;
168
- this.#offset = offset;
169
- }
170
- }
171
- isDateTime() {
172
- return this.#hasDate && this.#hasTime;
173
- }
174
- isLocal() {
175
- return !this.#hasDate || !this.#hasTime || !this.#offset;
176
- }
177
- isDate() {
178
- return this.#hasDate && !this.#hasTime;
179
- }
180
- isTime() {
181
- return this.#hasTime && !this.#hasDate;
182
- }
183
- isValid() {
184
- return this.#hasDate || this.#hasTime;
185
- }
186
- toISOString() {
187
- let iso = super.toISOString();
188
- if (this.isDate())
189
- return iso.slice(0, 10);
190
- if (this.isTime())
191
- return iso.slice(11, 23);
192
- if (this.#offset === null)
193
- return iso.slice(0, -1);
194
- if (this.#offset === "Z")
195
- return iso;
196
- let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
197
- offset = this.#offset[0] === "-" ? offset : -offset;
198
- let offsetDate = new Date(this.getTime() - offset * 6e4);
199
- return offsetDate.toISOString().slice(0, -1) + this.#offset;
200
- }
201
- static wrapAsOffsetDateTime(jsDate, offset = "Z") {
202
- let date = new _TomlDate(jsDate);
203
- date.#offset = offset;
204
- return date;
205
- }
206
- static wrapAsLocalDateTime(jsDate) {
207
- let date = new _TomlDate(jsDate);
208
- date.#offset = null;
209
- return date;
210
- }
211
- static wrapAsLocalDate(jsDate) {
212
- let date = new _TomlDate(jsDate);
213
- date.#hasTime = false;
214
- date.#offset = null;
215
- return date;
216
- }
217
- static wrapAsLocalTime(jsDate) {
218
- let date = new _TomlDate(jsDate);
219
- date.#hasDate = false;
220
- date.#offset = null;
221
- return date;
222
- }
223
- };
224
-
225
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/primitive.js
226
- var INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
227
- var FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
228
- var LEADING_ZERO = /^[+-]?0[0-9_]/;
229
- var ESCAPE_REGEX = /^[0-9a-f]{2,8}$/i;
230
- var ESC_MAP = {
231
- b: "\b",
232
- t: " ",
233
- n: "\n",
234
- f: "\f",
235
- r: "\r",
236
- e: "\x1B",
237
- '"': '"',
238
- "\\": "\\"
239
- };
240
- function parseString(str, ptr = 0, endPtr = str.length) {
241
- let isLiteral = str[ptr] === "'";
242
- let isMultiline = str[ptr++] === str[ptr] && str[ptr] === str[ptr + 1];
243
- if (isMultiline) {
244
- endPtr -= 2;
245
- if (str[ptr += 2] === "\r")
246
- ptr++;
247
- if (str[ptr] === "\n")
248
- ptr++;
249
- }
250
- let tmp = 0;
251
- let isEscape;
252
- let parsed = "";
253
- let sliceStart = ptr;
254
- while (ptr < endPtr - 1) {
255
- let c = str[ptr++];
256
- if (c === "\n" || c === "\r" && str[ptr] === "\n") {
257
- if (!isMultiline) {
258
- throw new TomlError("newlines are not allowed in strings", {
259
- toml: str,
260
- ptr: ptr - 1
261
- });
262
- }
263
- } else if (c < " " && c !== " " || c === "\x7F") {
264
- throw new TomlError("control characters are not allowed in strings", {
265
- toml: str,
266
- ptr: ptr - 1
267
- });
268
- }
269
- if (isEscape) {
270
- isEscape = false;
271
- if (c === "x" || c === "u" || c === "U") {
272
- let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
273
- if (!ESCAPE_REGEX.test(code)) {
274
- throw new TomlError("invalid unicode escape", {
275
- toml: str,
276
- ptr: tmp
277
- });
278
- }
279
- try {
280
- parsed += String.fromCodePoint(parseInt(code, 16));
281
- } catch {
282
- throw new TomlError("invalid unicode escape", {
283
- toml: str,
284
- ptr: tmp
285
- });
286
- }
287
- } else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) {
288
- ptr = skipVoid(str, ptr - 1, true);
289
- if (str[ptr] !== "\n" && str[ptr] !== "\r") {
290
- throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
291
- toml: str,
292
- ptr: tmp
293
- });
294
- }
295
- ptr = skipVoid(str, ptr);
296
- } else if (c in ESC_MAP) {
297
- parsed += ESC_MAP[c];
298
- } else {
299
- throw new TomlError("unrecognized escape sequence", {
300
- toml: str,
301
- ptr: tmp
302
- });
303
- }
304
- sliceStart = ptr;
305
- } else if (!isLiteral && c === "\\") {
306
- tmp = ptr - 1;
307
- isEscape = true;
308
- parsed += str.slice(sliceStart, tmp);
309
- }
310
- }
311
- return parsed + str.slice(sliceStart, endPtr - 1);
312
- }
313
- function parseValue(value, toml, ptr, integersAsBigInt) {
314
- if (value === "true")
315
- return true;
316
- if (value === "false")
317
- return false;
318
- if (value === "-inf")
319
- return -Infinity;
320
- if (value === "inf" || value === "+inf")
321
- return Infinity;
322
- if (value === "nan" || value === "+nan" || value === "-nan")
323
- return NaN;
324
- if (value === "-0")
325
- return integersAsBigInt ? 0n : 0;
326
- let isInt = INT_REGEX.test(value);
327
- if (isInt || FLOAT_REGEX.test(value)) {
328
- if (LEADING_ZERO.test(value)) {
329
- throw new TomlError("leading zeroes are not allowed", {
330
- toml,
331
- ptr
332
- });
333
- }
334
- value = value.replace(/_/g, "");
335
- let numeric = +value;
336
- if (isNaN(numeric)) {
337
- throw new TomlError("invalid number", {
338
- toml,
339
- ptr
340
- });
341
- }
342
- if (isInt) {
343
- if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
344
- throw new TomlError("integer value cannot be represented losslessly", {
345
- toml,
346
- ptr
347
- });
348
- }
349
- if (isInt || integersAsBigInt === true)
350
- numeric = BigInt(value);
351
- }
352
- return numeric;
353
- }
354
- const date = new TomlDate(value);
355
- if (!date.isValid()) {
356
- throw new TomlError("invalid value", {
357
- toml,
358
- ptr
359
- });
360
- }
361
- return date;
362
- }
363
-
364
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/extract.js
365
- function sliceAndTrimEndOf(str, startPtr, endPtr) {
366
- let value = str.slice(startPtr, endPtr);
367
- let commentIdx = value.indexOf("#");
368
- if (commentIdx > -1) {
369
- skipComment(str, commentIdx);
370
- value = value.slice(0, commentIdx);
371
- }
372
- return [value.trimEnd(), commentIdx];
373
- }
374
- function extractValue(str, ptr, end, depth, integersAsBigInt) {
375
- if (depth === 0) {
376
- throw new TomlError("document contains excessively nested structures. aborting.", {
377
- toml: str,
378
- ptr
379
- });
380
- }
381
- let c = str[ptr];
382
- if (c === "[" || c === "{") {
383
- let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
384
- if (end) {
385
- endPtr2 = skipVoid(str, endPtr2);
386
- if (str[endPtr2] === ",")
387
- endPtr2++;
388
- else if (str[endPtr2] !== end) {
389
- throw new TomlError("expected comma or end of structure", {
390
- toml: str,
391
- ptr: endPtr2
392
- });
393
- }
394
- }
395
- return [value, endPtr2];
396
- }
397
- let endPtr;
398
- if (c === '"' || c === "'") {
399
- endPtr = getStringEnd(str, ptr);
400
- let parsed = parseString(str, ptr, endPtr);
401
- if (end) {
402
- endPtr = skipVoid(str, endPtr);
403
- if (str[endPtr] && str[endPtr] !== "," && str[endPtr] !== end && str[endPtr] !== "\n" && str[endPtr] !== "\r") {
404
- throw new TomlError("unexpected character encountered", {
405
- toml: str,
406
- ptr: endPtr
407
- });
408
- }
409
- endPtr += +(str[endPtr] === ",");
410
- }
411
- return [parsed, endPtr];
412
- }
413
- endPtr = skipUntil(str, ptr, ",", end);
414
- let slice = sliceAndTrimEndOf(str, ptr, endPtr - +(str[endPtr - 1] === ","));
415
- if (!slice[0]) {
416
- throw new TomlError("incomplete key-value declaration: no value specified", {
417
- toml: str,
418
- ptr
419
- });
420
- }
421
- if (end && slice[1] > -1) {
422
- endPtr = skipVoid(str, ptr + slice[1]);
423
- endPtr += +(str[endPtr] === ",");
424
- }
425
- return [
426
- parseValue(slice[0], str, ptr, integersAsBigInt),
427
- endPtr
428
- ];
429
- }
430
-
431
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/struct.js
432
- var KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
433
- function parseKey(str, ptr, end = "=") {
434
- let dot = ptr - 1;
435
- let parsed = [];
436
- let endPtr = str.indexOf(end, ptr);
437
- if (endPtr < 0) {
438
- throw new TomlError("incomplete key-value: cannot find end of key", {
439
- toml: str,
440
- ptr
441
- });
442
- }
443
- do {
444
- let c = str[ptr = ++dot];
445
- if (c !== " " && c !== " ") {
446
- if (c === '"' || c === "'") {
447
- if (c === str[ptr + 1] && c === str[ptr + 2]) {
448
- throw new TomlError("multiline strings are not allowed in keys", {
449
- toml: str,
450
- ptr
451
- });
452
- }
453
- let eos = getStringEnd(str, ptr);
454
- if (eos < 0) {
455
- throw new TomlError("unfinished string encountered", {
456
- toml: str,
457
- ptr
458
- });
459
- }
460
- dot = str.indexOf(".", eos);
461
- let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
462
- let newLine = indexOfNewline(strEnd);
463
- if (newLine > -1) {
464
- throw new TomlError("newlines are not allowed in keys", {
465
- toml: str,
466
- ptr: ptr + dot + newLine
467
- });
468
- }
469
- if (strEnd.trimStart()) {
470
- throw new TomlError("found extra tokens after the string part", {
471
- toml: str,
472
- ptr: eos
473
- });
474
- }
475
- if (endPtr < eos) {
476
- endPtr = str.indexOf(end, eos);
477
- if (endPtr < 0) {
478
- throw new TomlError("incomplete key-value: cannot find end of key", {
479
- toml: str,
480
- ptr
481
- });
482
- }
483
- }
484
- parsed.push(parseString(str, ptr, eos));
485
- } else {
486
- dot = str.indexOf(".", ptr);
487
- let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
488
- if (!KEY_PART_RE.test(part)) {
489
- throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
490
- toml: str,
491
- ptr
492
- });
493
- }
494
- parsed.push(part.trimEnd());
495
- }
496
- }
497
- } while (dot + 1 && dot < endPtr);
498
- return [parsed, skipVoid(str, endPtr + 1, true, true)];
499
- }
500
- function parseInlineTable(str, ptr, depth, integersAsBigInt) {
501
- let res = {};
502
- let seen = /* @__PURE__ */ new Set();
503
- let c;
504
- ptr++;
505
- while ((c = str[ptr++]) !== "}" && c) {
506
- if (c === ",") {
507
- throw new TomlError("expected value, found comma", {
508
- toml: str,
509
- ptr: ptr - 1
510
- });
511
- } else if (c === "#")
512
- ptr = skipComment(str, ptr);
513
- else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
514
- let k;
515
- let t = res;
516
- let hasOwn = false;
517
- let [key, keyEndPtr] = parseKey(str, ptr - 1);
518
- for (let i = 0; i < key.length; i++) {
519
- if (i)
520
- t = hasOwn ? t[k] : t[k] = {};
521
- k = key[i];
522
- if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
523
- throw new TomlError("trying to redefine an already defined value", {
524
- toml: str,
525
- ptr
526
- });
527
- }
528
- if (!hasOwn && k === "__proto__") {
529
- Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
530
- }
531
- }
532
- if (hasOwn) {
533
- throw new TomlError("trying to redefine an already defined value", {
534
- toml: str,
535
- ptr
536
- });
537
- }
538
- let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
539
- seen.add(value);
540
- t[k] = value;
541
- ptr = valueEndPtr;
542
- }
543
- }
544
- if (!c) {
545
- throw new TomlError("unfinished table encountered", {
546
- toml: str,
547
- ptr
548
- });
549
- }
550
- return [res, ptr];
551
- }
552
- function parseArray(str, ptr, depth, integersAsBigInt) {
553
- let res = [];
554
- let c;
555
- ptr++;
556
- while ((c = str[ptr++]) !== "]" && c) {
557
- if (c === ",") {
558
- throw new TomlError("expected value, found comma", {
559
- toml: str,
560
- ptr: ptr - 1
561
- });
562
- } else if (c === "#")
563
- ptr = skipComment(str, ptr);
564
- else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
565
- let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
566
- res.push(e[0]);
567
- ptr = e[1];
568
- }
569
- }
570
- if (!c) {
571
- throw new TomlError("unfinished array encountered", {
572
- toml: str,
573
- ptr
574
- });
575
- }
576
- return [res, ptr];
577
- }
578
-
579
- // ../../node_modules/.pnpm/smol-toml@1.6.0/node_modules/smol-toml/dist/parse.js
580
- function peekTable(key, table, meta, type) {
581
- let t = table;
582
- let m = meta;
583
- let k;
584
- let hasOwn = false;
585
- let state;
586
- for (let i = 0; i < key.length; i++) {
587
- if (i) {
588
- t = hasOwn ? t[k] : t[k] = {};
589
- m = (state = m[k]).c;
590
- if (type === 0 && (state.t === 1 || state.t === 2)) {
591
- return null;
592
- }
593
- if (state.t === 2) {
594
- let l = t.length - 1;
595
- t = t[l];
596
- m = m[l].c;
597
- }
598
- }
599
- k = key[i];
600
- if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
601
- return null;
602
- }
603
- if (!hasOwn) {
604
- if (k === "__proto__") {
605
- Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
606
- Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
607
- }
608
- m[k] = {
609
- t: i < key.length - 1 && type === 2 ? 3 : type,
610
- d: false,
611
- i: 0,
612
- c: {}
613
- };
614
- }
615
- }
616
- state = m[k];
617
- if (state.t !== type && !(type === 1 && state.t === 3)) {
618
- return null;
619
- }
620
- if (type === 2) {
621
- if (!state.d) {
622
- state.d = true;
623
- t[k] = [];
624
- }
625
- t[k].push(t = {});
626
- state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
627
- }
628
- if (state.d) {
629
- return null;
630
- }
631
- state.d = true;
632
- if (type === 1) {
633
- t = hasOwn ? t[k] : t[k] = {};
634
- } else if (type === 0 && hasOwn) {
635
- return null;
636
- }
637
- return [k, t, state.c];
638
- }
639
- function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
640
- let res = {};
641
- let meta = {};
642
- let tbl = res;
643
- let m = meta;
644
- for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) {
645
- if (toml[ptr] === "[") {
646
- let isTableArray = toml[++ptr] === "[";
647
- let k = parseKey(toml, ptr += +isTableArray, "]");
648
- if (isTableArray) {
649
- if (toml[k[1] - 1] !== "]") {
650
- throw new TomlError("expected end of table declaration", {
651
- toml,
652
- ptr: k[1] - 1
653
- });
654
- }
655
- k[1]++;
656
- }
657
- let p = peekTable(
658
- k[0],
659
- res,
660
- meta,
661
- isTableArray ? 2 : 1
662
- /* Type.EXPLICIT */
663
- );
664
- if (!p) {
665
- throw new TomlError("trying to redefine an already defined table or value", {
666
- toml,
667
- ptr
668
- });
669
- }
670
- m = p[2];
671
- tbl = p[1];
672
- ptr = k[1];
673
- } else {
674
- let k = parseKey(toml, ptr);
675
- let p = peekTable(
676
- k[0],
677
- tbl,
678
- m,
679
- 0
680
- /* Type.DOTTED */
681
- );
682
- if (!p) {
683
- throw new TomlError("trying to redefine an already defined table or value", {
684
- toml,
685
- ptr
686
- });
687
- }
688
- let v = extractValue(toml, k[1], void 0, maxDepth, integersAsBigInt);
689
- p[1][p[0]] = v[0];
690
- ptr = v[1];
691
- }
692
- ptr = skipVoid(toml, ptr, true);
693
- if (toml[ptr] && toml[ptr] !== "\n" && toml[ptr] !== "\r") {
694
- throw new TomlError("each key-value declaration must be followed by an end-of-line", {
695
- toml,
696
- ptr
697
- });
698
- }
699
- ptr = skipVoid(toml, ptr);
700
- }
701
- return res;
702
- }
703
-
704
- // ../defi-core/dist/index.js
18
+ import { parse } from "smol-toml";
705
19
  import { existsSync } from "fs";
706
20
  var TxStatus = /* @__PURE__ */ ((TxStatus2) => {
707
21
  TxStatus2["DryRun"] = "dry_run";
@@ -6388,155 +5702,6 @@ var HINT_HELPERS_ABI = parseAbi21([
6388
5702
  var SORTED_TROVES_ABI = parseAbi21([
6389
5703
  "function findInsertPosition(uint256 _annualInterestRate, uint256 _prevId, uint256 _nextId) external view returns (uint256 prevId, uint256 nextId)"
6390
5704
  ]);
6391
- var FelixCdpAdapter = class {
6392
- protocolName;
6393
- borrowerOperations;
6394
- troveManager;
6395
- hintHelpers;
6396
- sortedTroves;
6397
- rpcUrl;
6398
- constructor(entry, rpcUrl) {
6399
- this.protocolName = entry.name;
6400
- this.rpcUrl = rpcUrl;
6401
- const contracts = entry.contracts ?? {};
6402
- const bo = contracts["borrower_operations"];
6403
- if (!bo) throw DefiError.contractError("Missing 'borrower_operations' contract");
6404
- this.borrowerOperations = bo;
6405
- this.troveManager = contracts["trove_manager"];
6406
- this.hintHelpers = contracts["hint_helpers"];
6407
- this.sortedTroves = contracts["sorted_troves"];
6408
- }
6409
- name() {
6410
- return this.protocolName;
6411
- }
6412
- async getHints(interestRate) {
6413
- if (!this.hintHelpers || !this.sortedTroves || !this.rpcUrl) {
6414
- return [0n, 0n];
6415
- }
6416
- const client = createPublicClient16({ transport: http16(this.rpcUrl) });
6417
- const approxResult = await client.readContract({
6418
- address: this.hintHelpers,
6419
- abi: HINT_HELPERS_ABI,
6420
- functionName: "getApproxHint",
6421
- args: [0n, interestRate, 15n, 42n]
6422
- }).catch(() => null);
6423
- if (!approxResult) return [0n, 0n];
6424
- const [hintId] = approxResult;
6425
- const insertResult = await client.readContract({
6426
- address: this.sortedTroves,
6427
- abi: SORTED_TROVES_ABI,
6428
- functionName: "findInsertPosition",
6429
- args: [interestRate, hintId, hintId]
6430
- }).catch(() => null);
6431
- if (!insertResult) return [0n, 0n];
6432
- const [prevId, nextId] = insertResult;
6433
- return [prevId, nextId];
6434
- }
6435
- async buildOpen(params) {
6436
- const interestRate = 50000000000000000n;
6437
- const [upperHint, lowerHint] = await this.getHints(interestRate);
6438
- const hasHints = upperHint !== 0n || lowerHint !== 0n;
6439
- const data = encodeFunctionData20({
6440
- abi: BORROWER_OPS_ABI,
6441
- functionName: "openTrove",
6442
- args: [
6443
- params.recipient,
6444
- 0n,
6445
- params.collateral_amount,
6446
- params.debt_amount,
6447
- upperHint,
6448
- lowerHint,
6449
- interestRate,
6450
- BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
6451
- // U256::MAX
6452
- params.recipient,
6453
- params.recipient,
6454
- params.recipient
6455
- ]
6456
- });
6457
- return {
6458
- description: `[${this.protocolName}] Open trove: collateral=${params.collateral_amount}, debt=${params.debt_amount} (hints=${hasHints ? "optimized" : "none"})`,
6459
- to: this.borrowerOperations,
6460
- data,
6461
- value: 0n,
6462
- gas_estimate: hasHints ? 5e5 : 5e6
6463
- };
6464
- }
6465
- async buildAdjust(params) {
6466
- const collChange = params.collateral_delta ?? 0n;
6467
- const debtChange = params.debt_delta ?? 0n;
6468
- const data = encodeFunctionData20({
6469
- abi: BORROWER_OPS_ABI,
6470
- functionName: "adjustTrove",
6471
- args: [
6472
- params.cdp_id,
6473
- collChange,
6474
- params.add_collateral,
6475
- debtChange,
6476
- params.add_debt,
6477
- 0n,
6478
- 0n,
6479
- BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
6480
- ]
6481
- });
6482
- return {
6483
- description: `[${this.protocolName}] Adjust trove ${params.cdp_id}`,
6484
- to: this.borrowerOperations,
6485
- data,
6486
- value: 0n,
6487
- gas_estimate: 4e5
6488
- };
6489
- }
6490
- async buildClose(params) {
6491
- const data = encodeFunctionData20({
6492
- abi: BORROWER_OPS_ABI,
6493
- functionName: "closeTrove",
6494
- args: [params.cdp_id]
6495
- });
6496
- return {
6497
- description: `[${this.protocolName}] Close trove ${params.cdp_id}`,
6498
- to: this.borrowerOperations,
6499
- data,
6500
- value: 0n,
6501
- gas_estimate: 35e4
6502
- };
6503
- }
6504
- async getCdpInfo(cdpId) {
6505
- if (!this.rpcUrl) throw DefiError.rpcError(`[${this.protocolName}] getCdpInfo requires RPC \u2014 set HYPEREVM_RPC_URL`);
6506
- if (!this.troveManager) throw DefiError.contractError(`[${this.protocolName}] trove_manager contract not configured`);
6507
- const client = createPublicClient16({ transport: http16(this.rpcUrl) });
6508
- const data = await client.readContract({
6509
- address: this.troveManager,
6510
- abi: TROVE_MANAGER_ABI,
6511
- functionName: "getLatestTroveData",
6512
- args: [cdpId]
6513
- }).catch((e) => {
6514
- throw DefiError.invalidParam(`[${this.protocolName}] Trove ${cdpId} not found: ${e}`);
6515
- });
6516
- const [entireDebt, entireColl] = data;
6517
- if (entireDebt === 0n && entireColl === 0n) {
6518
- throw DefiError.invalidParam(`[${this.protocolName}] Trove ${cdpId} does not exist`);
6519
- }
6520
- const collRatio = entireDebt > 0n ? Number(entireColl) / Number(entireDebt) : 0;
6521
- return {
6522
- protocol: this.protocolName,
6523
- cdp_id: cdpId,
6524
- collateral: {
6525
- token: zeroAddress11,
6526
- symbol: "WHYPE",
6527
- amount: entireColl,
6528
- decimals: 18
6529
- },
6530
- debt: {
6531
- token: zeroAddress11,
6532
- symbol: "feUSD",
6533
- amount: entireDebt,
6534
- decimals: 18
6535
- },
6536
- collateral_ratio: collRatio
6537
- };
6538
- }
6539
- };
6540
5705
  var PRICE_FEED_ABI = parseAbi222([
6541
5706
  "function fetchPrice() external view returns (uint256 price, bool isNewOracleFailureDetected)",
6542
5707
  "function lastGoodPrice() external view returns (uint256)"
@@ -6792,14 +5957,6 @@ function createLending(entry, rpcUrl) {
6792
5957
  throw DefiError.unsupported(`Lending interface '${entry.interface}' not yet implemented`);
6793
5958
  }
6794
5959
  }
6795
- function createCdp(entry, rpcUrl) {
6796
- switch (entry.interface) {
6797
- case "liquity_v2":
6798
- return new FelixCdpAdapter(entry, rpcUrl);
6799
- default:
6800
- throw DefiError.unsupported(`CDP interface '${entry.interface}' not yet implemented`);
6801
- }
6802
- }
6803
5960
  function createVault(entry, rpcUrl) {
6804
5961
  switch (entry.interface) {
6805
5962
  case "erc4626":
@@ -6895,6 +6052,21 @@ var DexSpotPrice = class {
6895
6052
  }
6896
6053
  };
6897
6054
 
6055
+ // src/whitelist.ts
6056
+ import { readFileSync as readFileSync2 } from "fs";
6057
+ import { resolve as resolve2 } from "path";
6058
+ import { parse as parse2 } from "smol-toml";
6059
+ function loadWhitelist() {
6060
+ const path = resolve2(process.env["HOME"] ?? "~", ".defi", "pools.toml");
6061
+ try {
6062
+ const raw = readFileSync2(path, "utf-8");
6063
+ const parsed = parse2(raw);
6064
+ return parsed.whitelist ?? [];
6065
+ } catch {
6066
+ return [];
6067
+ }
6068
+ }
6069
+
6898
6070
  // src/commands/lp.ts
6899
6071
  function resolveAccount(optOwner) {
6900
6072
  if (optOwner) return optOwner;
@@ -6962,7 +6134,11 @@ function registerLP(parent, getOpts, makeExecutor2) {
6962
6134
  pair: `${p.symbolX}/${p.symbolY}`,
6963
6135
  type: "EMISSION",
6964
6136
  source: "lb_hooks",
6965
- stopped: p.stopped
6137
+ stopped: p.stopped,
6138
+ moePerDay: p.moePerDay,
6139
+ aprPercent: p.aprPercent,
6140
+ rangeTvlUsd: p.rangeTvlUsd,
6141
+ isTopPool: p.isTopPool
6966
6142
  });
6967
6143
  }
6968
6144
  }
@@ -7224,35 +6400,269 @@ function registerLP(parent, getOpts, makeExecutor2) {
7224
6400
  );
7225
6401
  printOutput(results, getOpts());
7226
6402
  });
7227
- }
7228
-
7229
- // src/commands/lending.ts
7230
- function registerLending(parent, getOpts, makeExecutor2) {
7231
- const lending = parent.command("lending").description("Lending operations: supply, borrow, repay, withdraw, rates, position");
7232
- lending.command("rates").description("Show current lending rates").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").action(async (opts) => {
7233
- const chainName = parent.opts().chain ?? "hyperevm";
7234
- const registry = Registry.loadEmbedded();
7235
- const chain = registry.getChain(chainName);
7236
- const protocol = registry.getProtocol(opts.protocol);
7237
- const adapter = createLending(protocol, chain.effectiveRpcUrl());
7238
- const asset = opts.asset.startsWith("0x") ? opts.asset : registry.resolveToken(chainName, opts.asset).address;
7239
- const rates = await adapter.getRates(asset);
7240
- printOutput(rates, getOpts());
7241
- });
7242
- lending.command("position").description("Show current lending position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--address <address>", "Wallet address to query").action(async (opts) => {
7243
- const chainName = parent.opts().chain ?? "hyperevm";
7244
- const registry = Registry.loadEmbedded();
7245
- const chain = registry.getChain(chainName);
7246
- const protocol = registry.getProtocol(opts.protocol);
7247
- const adapter = createLending(protocol, chain.effectiveRpcUrl());
7248
- const position = await adapter.getUserPosition(opts.address);
7249
- printOutput(position, getOpts());
7250
- });
7251
- lending.command("supply").description("Supply an asset to a lending protocol").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount to supply in wei").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
7252
- const executor = makeExecutor2();
7253
- const chainName = parent.opts().chain ?? "hyperevm";
6403
+ lp.command("autopilot").description("Auto-allocate budget across whitelisted pools (reads ~/.defi/pools.toml)").requiredOption("--budget <usd>", "Total budget in USD").option("--chain <chain>", "Filter whitelist to a specific chain").option("--dry-run", "Show plan only (default)", true).option("--broadcast", "Execute the plan (lending supply supported; LP types show a warning)").action(async (opts) => {
6404
+ const budgetUsd = parseFloat(opts.budget);
6405
+ if (isNaN(budgetUsd) || budgetUsd <= 0) {
6406
+ printOutput({ error: `Invalid budget: ${opts.budget}` }, getOpts());
6407
+ process.exit(1);
6408
+ return;
6409
+ }
6410
+ let whitelist = loadWhitelist();
6411
+ if (whitelist.length === 0) {
6412
+ printOutput(
6413
+ { error: "No pools whitelisted. Create ~/.defi/pools.toml (see config/pools.example.toml)" },
6414
+ getOpts()
6415
+ );
6416
+ process.exit(1);
6417
+ return;
6418
+ }
6419
+ const chainFilter = opts.chain?.toLowerCase();
6420
+ if (chainFilter) {
6421
+ whitelist = whitelist.filter((e) => e.chain.toLowerCase() === chainFilter);
6422
+ if (whitelist.length === 0) {
6423
+ printOutput(
6424
+ { error: `No whitelisted pools found for chain '${chainFilter}'` },
6425
+ getOpts()
6426
+ );
6427
+ process.exit(1);
6428
+ return;
6429
+ }
6430
+ }
7254
6431
  const registry = Registry.loadEmbedded();
7255
- const chain = registry.getChain(chainName);
6432
+ const scanned = await Promise.all(
6433
+ whitelist.map(async (entry) => {
6434
+ try {
6435
+ const chainName = entry.chain.toLowerCase();
6436
+ let chain;
6437
+ try {
6438
+ chain = registry.getChain(chainName);
6439
+ } catch {
6440
+ return { entry, scan_error: `Unknown chain '${chainName}'` };
6441
+ }
6442
+ const rpcUrl = chain.effectiveRpcUrl();
6443
+ if (entry.type === "lending" && entry.asset) {
6444
+ const protos = registry.getProtocolsForChain(chainName).filter(
6445
+ (p) => p.category === ProtocolCategory.Lending && p.slug === entry.protocol
6446
+ );
6447
+ if (protos.length === 0) {
6448
+ return { entry, scan_error: `Protocol not found: ${entry.protocol}` };
6449
+ }
6450
+ const proto = protos[0];
6451
+ const assetAddr = registry.resolveToken(chainName, entry.asset).address;
6452
+ const adapter = createLending(proto, rpcUrl);
6453
+ const rates = await adapter.getRates(assetAddr);
6454
+ return { entry, apy: rates.supply_apy };
6455
+ }
6456
+ if (entry.type === "lb" && entry.pool) {
6457
+ const protos = registry.getProtocolsForChain(chainName).filter((p) => p.slug === entry.protocol);
6458
+ if (protos.length === 0) {
6459
+ return { entry, scan_error: `Protocol not found: ${entry.protocol}` };
6460
+ }
6461
+ const proto = protos[0];
6462
+ if (proto.interface === "uniswap_v2" && proto.contracts?.["lb_factory"]) {
6463
+ const adapter = createMerchantMoeLB(proto, rpcUrl);
6464
+ const pools = await adapter.discoverRewardedPools();
6465
+ const match = pools.find(
6466
+ (p) => p.pool.toLowerCase() === entry.pool.toLowerCase() || `${p.symbolX}/${p.symbolY}`.toLowerCase() === entry.pool.toLowerCase() || `${p.symbolY}/${p.symbolX}`.toLowerCase() === entry.pool.toLowerCase()
6467
+ );
6468
+ if (match) {
6469
+ return { entry, apr: match.aprPercent, active: !match.stopped };
6470
+ }
6471
+ }
6472
+ return { entry, scan_error: "Pool not found in LB discovery" };
6473
+ }
6474
+ if (entry.type === "farming" && entry.pool) {
6475
+ const protos = registry.getProtocolsForChain(chainName).filter((p) => p.slug === entry.protocol);
6476
+ if (protos.length === 0) {
6477
+ return { entry, scan_error: `Protocol not found: ${entry.protocol}` };
6478
+ }
6479
+ const proto = protos[0];
6480
+ if (proto.interface === "algebra_v3" && proto.contracts?.["farming_center"]) {
6481
+ const adapter = createKittenSwapFarming(proto, rpcUrl);
6482
+ const pools = await adapter.discoverFarmingPools();
6483
+ const match = pools.find(
6484
+ (p) => p.pool.toLowerCase() === entry.pool.toLowerCase()
6485
+ );
6486
+ if (match) {
6487
+ return { entry, active: match.active };
6488
+ }
6489
+ }
6490
+ return { entry, scan_error: "Pool not found in farming discovery" };
6491
+ }
6492
+ if (entry.type === "gauge" && entry.pool) {
6493
+ const protos = registry.getProtocolsForChain(chainName).filter((p) => p.slug === entry.protocol);
6494
+ if (protos.length === 0) {
6495
+ return { entry, scan_error: `Protocol not found: ${entry.protocol}` };
6496
+ }
6497
+ const proto = protos[0];
6498
+ if (["solidly_v2", "solidly_cl", "algebra_v3", "hybra"].includes(proto.interface)) {
6499
+ const adapter = createGauge(proto, rpcUrl);
6500
+ if (adapter.discoverGaugedPools) {
6501
+ const pools = await adapter.discoverGaugedPools();
6502
+ const poolAddr = entry.pool.startsWith("0x") ? entry.pool.toLowerCase() : void 0;
6503
+ const match = pools.find(
6504
+ (p) => poolAddr && p.pool.toLowerCase() === poolAddr || `${p.token0}/${p.token1}`.toLowerCase() === entry.pool.toLowerCase() || `${p.token1}/${p.token0}`.toLowerCase() === entry.pool.toLowerCase()
6505
+ );
6506
+ return { entry, active: !!match };
6507
+ }
6508
+ }
6509
+ return { entry, scan_error: "Gauge discovery not supported for this protocol" };
6510
+ }
6511
+ return { entry, scan_error: "Unsupported entry type or missing pool/asset field" };
6512
+ } catch (err) {
6513
+ return { entry, scan_error: String(err) };
6514
+ }
6515
+ })
6516
+ );
6517
+ const RESERVE_PCT = 0.2;
6518
+ const deployableBudget = budgetUsd * (1 - RESERVE_PCT);
6519
+ const reserveUsd = budgetUsd * RESERVE_PCT;
6520
+ const ranked = [...scanned].sort((a, b) => {
6521
+ const scoreA = a.apy ?? a.apr ?? (a.active ? 1 : 0);
6522
+ const scoreB = b.apy ?? b.apr ?? (b.active ? 1 : 0);
6523
+ return scoreB - scoreA;
6524
+ });
6525
+ const allocations = [];
6526
+ let remainingBudget = deployableBudget;
6527
+ for (const s of ranked) {
6528
+ if (remainingBudget <= 0) break;
6529
+ const maxAlloc = budgetUsd * (s.entry.max_allocation_pct / 100);
6530
+ const alloc = Math.min(maxAlloc, remainingBudget);
6531
+ if (alloc <= 0) continue;
6532
+ const item = {
6533
+ protocol: s.entry.protocol,
6534
+ chain: s.entry.chain,
6535
+ type: s.entry.type,
6536
+ amount_usd: Math.round(alloc * 100) / 100
6537
+ };
6538
+ if (s.entry.pool) item["pool"] = s.entry.pool;
6539
+ if (s.entry.asset) item["asset"] = s.entry.asset;
6540
+ if (s.apy !== void 0) item["apy"] = s.apy;
6541
+ if (s.apr !== void 0) item["apr"] = s.apr;
6542
+ if (s.active !== void 0) item["active"] = s.active;
6543
+ if (s.scan_error) item["scan_error"] = s.scan_error;
6544
+ allocations.push(item);
6545
+ remainingBudget -= alloc;
6546
+ }
6547
+ const totalReserved = reserveUsd + remainingBudget;
6548
+ allocations.push({
6549
+ reserve: true,
6550
+ amount_usd: Math.round(totalReserved * 100) / 100,
6551
+ note: "20% safety margin (hardcoded) + unallocated remainder"
6552
+ });
6553
+ let estimatedAnnualYieldUsd = 0;
6554
+ for (const alloc of allocations) {
6555
+ if (alloc["reserve"]) continue;
6556
+ const amt = alloc["amount_usd"];
6557
+ const rate = alloc["apy"] ?? alloc["apr"];
6558
+ if (rate !== void 0 && rate > 0) {
6559
+ estimatedAnnualYieldUsd += amt * rate;
6560
+ }
6561
+ }
6562
+ const estimatedDailyYieldUsd = estimatedAnnualYieldUsd / 365;
6563
+ const isBroadcast = !!opts.broadcast;
6564
+ const plan = {
6565
+ budget_usd: budgetUsd,
6566
+ deployable_usd: Math.round(deployableBudget * 100) / 100,
6567
+ reserve_pct: RESERVE_PCT * 100,
6568
+ allocations,
6569
+ estimated_daily_yield_usd: Math.round(estimatedDailyYieldUsd * 100) / 100,
6570
+ estimated_annual_yield_usd: Math.round(estimatedAnnualYieldUsd * 100) / 100,
6571
+ execution: isBroadcast ? "broadcast" : "dry_run"
6572
+ };
6573
+ printOutput(plan, getOpts());
6574
+ if (!isBroadcast) return;
6575
+ process.stderr.write("\nExecuting autopilot plan...\n");
6576
+ const executor = makeExecutor2();
6577
+ const execResults = [];
6578
+ let allocIndex = 0;
6579
+ const actionAllocs = allocations.filter((a) => !a["reserve"]);
6580
+ for (const alloc of actionAllocs) {
6581
+ allocIndex++;
6582
+ const chainName = alloc["chain"].toLowerCase();
6583
+ let chain;
6584
+ try {
6585
+ chain = registry.getChain(chainName);
6586
+ } catch {
6587
+ process.stderr.write(`
6588
+ --- ${allocIndex}/${actionAllocs.length}: ${alloc["protocol"]} \u2014 unknown chain '${chainName}', skipping ---
6589
+ `);
6590
+ execResults.push({ ...alloc, exec_status: "skipped", exec_error: `Unknown chain '${chainName}'` });
6591
+ continue;
6592
+ }
6593
+ const rpc = chain.effectiveRpcUrl();
6594
+ process.stderr.write(`
6595
+ --- ${allocIndex}/${actionAllocs.length}: ${alloc["protocol"]} (${alloc["type"]}) $${alloc["amount_usd"]} ---
6596
+ `);
6597
+ if (alloc["type"] === "lending" && alloc["asset"]) {
6598
+ try {
6599
+ const protocol = registry.getProtocol(alloc["protocol"]);
6600
+ const adapter = createLending(protocol, rpc);
6601
+ const tokenInfo = registry.resolveToken(chainName, alloc["asset"]);
6602
+ const assetAddr = tokenInfo.address;
6603
+ const decimals = tokenInfo.decimals ?? 18;
6604
+ const amountWei = BigInt(Math.floor(alloc["amount_usd"] * 10 ** decimals));
6605
+ const wallet = resolveAccount();
6606
+ const tx = await adapter.buildSupply({
6607
+ protocol: alloc["protocol"],
6608
+ asset: assetAddr,
6609
+ amount: amountWei,
6610
+ on_behalf_of: wallet
6611
+ });
6612
+ process.stderr.write(` Supplying ${amountWei} wei of ${alloc["asset"]} to ${alloc["protocol"]}...
6613
+ `);
6614
+ const result = await executor.execute(tx);
6615
+ process.stderr.write(` Status: ${result.status}
6616
+ `);
6617
+ const explorerUrl = result.details?.["explorer_url"];
6618
+ if (explorerUrl) process.stderr.write(` Explorer: ${explorerUrl}
6619
+ `);
6620
+ execResults.push({ ...alloc, exec_status: result.status, tx_hash: result.tx_hash });
6621
+ } catch (err) {
6622
+ const msg = err instanceof Error ? err.message : String(err);
6623
+ process.stderr.write(` Error: ${msg}
6624
+ `);
6625
+ execResults.push({ ...alloc, exec_status: "error", exec_error: msg });
6626
+ }
6627
+ continue;
6628
+ }
6629
+ const lpMsg = `LP execution for type '${alloc["type"]}' pool '${alloc["pool"] ?? ""}' \u2014 requires manual token preparation (swap + addLiquidity not yet automated)`;
6630
+ process.stderr.write(` Warning: ${lpMsg}
6631
+ `);
6632
+ execResults.push({ ...alloc, exec_status: "skipped", exec_note: lpMsg });
6633
+ }
6634
+ process.stderr.write("\nAutopilot execution complete.\n");
6635
+ printOutput({ execution_results: execResults }, getOpts());
6636
+ });
6637
+ }
6638
+
6639
+ // src/commands/lending.ts
6640
+ function registerLending(parent, getOpts, makeExecutor2) {
6641
+ const lending = parent.command("lending").description("Lending operations: supply, borrow, repay, withdraw, rates, position");
6642
+ lending.command("rates").description("Show current lending rates").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").action(async (opts) => {
6643
+ const chainName = parent.opts().chain ?? "hyperevm";
6644
+ const registry = Registry.loadEmbedded();
6645
+ const chain = registry.getChain(chainName);
6646
+ const protocol = registry.getProtocol(opts.protocol);
6647
+ const adapter = createLending(protocol, chain.effectiveRpcUrl());
6648
+ const asset = opts.asset.startsWith("0x") ? opts.asset : registry.resolveToken(chainName, opts.asset).address;
6649
+ const rates = await adapter.getRates(asset);
6650
+ printOutput(rates, getOpts());
6651
+ });
6652
+ lending.command("position").description("Show current lending position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--address <address>", "Wallet address to query").action(async (opts) => {
6653
+ const chainName = parent.opts().chain ?? "hyperevm";
6654
+ const registry = Registry.loadEmbedded();
6655
+ const chain = registry.getChain(chainName);
6656
+ const protocol = registry.getProtocol(opts.protocol);
6657
+ const adapter = createLending(protocol, chain.effectiveRpcUrl());
6658
+ const position = await adapter.getUserPosition(opts.address);
6659
+ printOutput(position, getOpts());
6660
+ });
6661
+ lending.command("supply").description("Supply an asset to a lending protocol").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--asset <token>", "Token symbol or address").requiredOption("--amount <amount>", "Amount to supply in wei").option("--on-behalf-of <address>", "On behalf of address").action(async (opts) => {
6662
+ const executor = makeExecutor2();
6663
+ const chainName = parent.opts().chain ?? "hyperevm";
6664
+ const registry = Registry.loadEmbedded();
6665
+ const chain = registry.getChain(chainName);
7256
6666
  const protocol = registry.getProtocol(opts.protocol);
7257
6667
  const adapter = createLending(protocol, chain.effectiveRpcUrl());
7258
6668
  const asset = opts.asset.startsWith("0x") ? opts.asset : registry.resolveToken(chainName, opts.asset).address;
@@ -7314,115 +6724,6 @@ function registerLending(parent, getOpts, makeExecutor2) {
7314
6724
  });
7315
6725
  }
7316
6726
 
7317
- // src/commands/cdp.ts
7318
- function registerCdp(parent, getOpts, makeExecutor2) {
7319
- const cdp = parent.command("cdp").description("CDP operations: open, adjust, close, info");
7320
- cdp.command("open").description("Open a new CDP position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--collateral <token>", "Collateral token address").requiredOption("--amount <amount>", "Collateral amount in wei").requiredOption("--mint <amount>", "Stablecoin to mint in wei").option("--recipient <address>", "Recipient address").action(async (opts) => {
7321
- const executor = makeExecutor2();
7322
- const chainName = parent.opts().chain ?? "hyperevm";
7323
- const registry = Registry.loadEmbedded();
7324
- const chain = registry.getChain(chainName);
7325
- const protocol = registry.getProtocol(opts.protocol);
7326
- const adapter = createCdp(protocol, chain.effectiveRpcUrl());
7327
- const recipient = opts.recipient ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7328
- const tx = await adapter.buildOpen({
7329
- protocol: protocol.name,
7330
- collateral: opts.collateral,
7331
- collateral_amount: BigInt(opts.amount),
7332
- debt_amount: BigInt(opts.mint),
7333
- recipient
7334
- });
7335
- const result = await executor.execute(tx);
7336
- printOutput(result, getOpts());
7337
- });
7338
- cdp.command("info").description("Show CDP position info, or protocol overview if --position is omitted").requiredOption("--protocol <protocol>", "Protocol slug").option("--position <id>", "CDP/trove ID (omit for protocol overview)").action(async (opts) => {
7339
- const chainName = parent.opts().chain ?? "hyperevm";
7340
- const registry = Registry.loadEmbedded();
7341
- const chain = registry.getChain(chainName);
7342
- const protocol = registry.getProtocol(opts.protocol);
7343
- if (opts.position === void 0) {
7344
- printOutput({
7345
- name: protocol.name,
7346
- slug: protocol.slug,
7347
- chain: chainName,
7348
- contracts: protocol.contracts ?? {}
7349
- }, getOpts());
7350
- return;
7351
- }
7352
- const adapter = createCdp(protocol, chain.effectiveRpcUrl());
7353
- const info = await adapter.getCdpInfo(BigInt(opts.position));
7354
- printOutput(info, getOpts());
7355
- });
7356
- cdp.command("adjust").description("Adjust an existing CDP position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--position <id>", "CDP/trove ID").option("--add-collateral <amount>", "Add collateral in wei").option("--withdraw-collateral <amount>", "Withdraw collateral in wei").option("--mint <amount>", "Mint additional stablecoin").option("--repay <amount>", "Repay stablecoin").action(async (opts) => {
7357
- const executor = makeExecutor2();
7358
- const chainName = parent.opts().chain ?? "hyperevm";
7359
- const registry = Registry.loadEmbedded();
7360
- const chain = registry.getChain(chainName);
7361
- const protocol = registry.getProtocol(opts.protocol);
7362
- const adapter = createCdp(protocol, chain.effectiveRpcUrl());
7363
- const tx = await adapter.buildAdjust({
7364
- protocol: protocol.name,
7365
- cdp_id: BigInt(opts.position),
7366
- collateral_delta: opts.addCollateral ? BigInt(opts.addCollateral) : opts.withdrawCollateral ? BigInt(opts.withdrawCollateral) : void 0,
7367
- debt_delta: opts.mint ? BigInt(opts.mint) : opts.repay ? BigInt(opts.repay) : void 0,
7368
- add_collateral: !!opts.addCollateral,
7369
- add_debt: !!opts.mint
7370
- });
7371
- const result = await executor.execute(tx);
7372
- printOutput(result, getOpts());
7373
- });
7374
- cdp.command("close").description("Close a CDP position").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--position <id>", "CDP/trove ID").action(async (opts) => {
7375
- const executor = makeExecutor2();
7376
- const chainName = parent.opts().chain ?? "hyperevm";
7377
- const registry = Registry.loadEmbedded();
7378
- const chain = registry.getChain(chainName);
7379
- const protocol = registry.getProtocol(opts.protocol);
7380
- const adapter = createCdp(protocol, chain.effectiveRpcUrl());
7381
- const tx = await adapter.buildClose({ protocol: protocol.name, cdp_id: BigInt(opts.position) });
7382
- const result = await executor.execute(tx);
7383
- printOutput(result, getOpts());
7384
- });
7385
- }
7386
-
7387
- // src/commands/vault.ts
7388
- function registerVault(parent, getOpts, makeExecutor2) {
7389
- const vault = parent.command("vault").description("Vault operations: deposit, withdraw, info");
7390
- vault.command("deposit").description("Deposit assets into a vault").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei").option("--receiver <address>", "Receiver address for vault shares").action(async (opts) => {
7391
- const executor = makeExecutor2();
7392
- const chainName = parent.opts().chain ?? "hyperevm";
7393
- const registry = Registry.loadEmbedded();
7394
- const chain = registry.getChain(chainName);
7395
- const protocol = registry.getProtocol(opts.protocol);
7396
- const adapter = createVault(protocol, chain.effectiveRpcUrl());
7397
- const receiver = opts.receiver ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7398
- const tx = await adapter.buildDeposit(BigInt(opts.amount), receiver);
7399
- const result = await executor.execute(tx);
7400
- printOutput(result, getOpts());
7401
- });
7402
- vault.command("withdraw").description("Withdraw assets from a vault").requiredOption("--protocol <protocol>", "Protocol slug").requiredOption("--amount <amount>", "Amount in wei (shares)").option("--receiver <address>", "Receiver address").option("--owner <address>", "Owner address").action(async (opts) => {
7403
- const executor = makeExecutor2();
7404
- const chainName = parent.opts().chain ?? "hyperevm";
7405
- const registry = Registry.loadEmbedded();
7406
- const chain = registry.getChain(chainName);
7407
- const protocol = registry.getProtocol(opts.protocol);
7408
- const adapter = createVault(protocol, chain.effectiveRpcUrl());
7409
- const receiver = opts.receiver ?? process.env.DEFI_WALLET_ADDRESS ?? "0x0000000000000000000000000000000000000001";
7410
- const owner = opts.owner ?? receiver;
7411
- const tx = await adapter.buildWithdraw(BigInt(opts.amount), receiver, owner);
7412
- const result = await executor.execute(tx);
7413
- printOutput(result, getOpts());
7414
- });
7415
- vault.command("info").description("Show vault info (TVL, APY, shares)").requiredOption("--protocol <protocol>", "Protocol slug").action(async (opts) => {
7416
- const chainName = parent.opts().chain ?? "hyperevm";
7417
- const registry = Registry.loadEmbedded();
7418
- const chain = registry.getChain(chainName);
7419
- const protocol = registry.getProtocol(opts.protocol);
7420
- const adapter = createVault(protocol, chain.effectiveRpcUrl());
7421
- const info = await adapter.getVaultInfo();
7422
- printOutput(info, getOpts());
7423
- });
7424
- }
7425
-
7426
6727
  // src/commands/yield.ts
7427
6728
  function resolveAsset(registry, chain, asset) {
7428
6729
  if (/^0x[0-9a-fA-F]{40}$/.test(asset)) {
@@ -8036,9 +7337,9 @@ function registerYield(parent, getOpts, makeExecutor2) {
8036
7337
  import { encodeFunctionData as encodeFunctionData28, parseAbi as parseAbi31 } from "viem";
8037
7338
 
8038
7339
  // src/portfolio-tracker.ts
8039
- import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
7340
+ import { mkdirSync, writeFileSync, readdirSync as readdirSync2, readFileSync as readFileSync3, existsSync as existsSync2 } from "fs";
8040
7341
  import { homedir } from "os";
8041
- import { resolve as resolve2 } from "path";
7342
+ import { resolve as resolve3 } from "path";
8042
7343
  import { encodeFunctionData as encodeFunctionData27, parseAbi as parseAbi30 } from "viem";
8043
7344
  var ERC20_ABI4 = parseAbi30([
8044
7345
  "function balanceOf(address owner) external view returns (uint256)"
@@ -8055,7 +7356,7 @@ function decodeU256Word(data, wordOffset = 0) {
8055
7356
  return BigInt("0x" + hex);
8056
7357
  }
8057
7358
  function snapshotDir() {
8058
- return resolve2(homedir(), ".defi-cli", "snapshots");
7359
+ return resolve3(homedir(), ".defi-cli", "snapshots");
8059
7360
  }
8060
7361
  async function takeSnapshot(chainName, wallet, registry) {
8061
7362
  const chain = registry.getChain(chainName);
@@ -8169,7 +7470,7 @@ function saveSnapshot(snapshot) {
8169
7470
  const dir = snapshotDir();
8170
7471
  mkdirSync(dir, { recursive: true });
8171
7472
  const filename = `${snapshot.chain}_${snapshot.wallet}_${snapshot.timestamp}.json`;
8172
- const filepath = resolve2(dir, filename);
7473
+ const filepath = resolve3(dir, filename);
8173
7474
  writeFileSync(filepath, JSON.stringify(snapshot, (_k, v) => typeof v === "bigint" ? v.toString() : v, 2));
8174
7475
  return filepath;
8175
7476
  }
@@ -8179,7 +7480,7 @@ function loadSnapshots(chain, wallet, limit = 10) {
8179
7480
  const prefix = `${chain}_${wallet}_`;
8180
7481
  const files = readdirSync2(dir).filter((f) => f.startsWith(prefix) && f.endsWith(".json")).sort().reverse().slice(0, limit);
8181
7482
  return files.map((f) => {
8182
- const raw = JSON.parse(readFileSync2(resolve2(dir, f), "utf-8"));
7483
+ const raw = JSON.parse(readFileSync3(resolve3(dir, f), "utf-8"));
8183
7484
  if (Array.isArray(raw.tokens)) {
8184
7485
  for (const t of raw.tokens) {
8185
7486
  if (typeof t.balance === "string") t.balance = BigInt(t.balance);
@@ -8483,908 +7784,58 @@ function registerPortfolio(parent, getOpts) {
8483
7784
  });
8484
7785
  }
8485
7786
 
8486
- // src/commands/monitor.ts
8487
- async function checkChainLendingPositions(chainKey, registry, address, threshold) {
8488
- let chain;
8489
- try {
8490
- chain = registry.getChain(chainKey);
8491
- } catch {
8492
- return [];
8493
- }
8494
- const rpc = chain.effectiveRpcUrl();
8495
- const chainName = chain.name;
8496
- const protocols = registry.getProtocolsForChain(chainKey).filter(
8497
- (p) => p.category === ProtocolCategory.Lending
8498
- );
8499
- const results = await Promise.all(
8500
- protocols.map(async (proto) => {
8501
- try {
8502
- const adapter = createLending(proto, rpc);
8503
- const position = await adapter.getUserPosition(address);
8504
- const hf = position.health_factor ?? Infinity;
8505
- const totalBorrow = position.borrows?.reduce(
8506
- (sum, b) => sum + (b.value_usd ?? 0),
8507
- 0
8508
- ) ?? 0;
8509
- if (totalBorrow === 0) return null;
8510
- const totalSupply = position.supplies?.reduce(
8511
- (sum, s) => sum + (s.value_usd ?? 0),
8512
- 0
8513
- ) ?? 0;
8514
- return {
8515
- chain: chainName,
8516
- protocol: proto.name,
8517
- health_factor: hf === Infinity ? 999999 : Math.round(hf * 100) / 100,
8518
- total_supply_usd: Math.round(totalSupply * 100) / 100,
8519
- total_borrow_usd: Math.round(totalBorrow * 100) / 100,
8520
- alert: hf < threshold
8521
- };
8522
- } catch {
8523
- return null;
8524
- }
8525
- })
8526
- );
8527
- return results.filter((r) => r !== null);
8528
- }
8529
- function registerMonitor(parent, getOpts) {
8530
- parent.command("monitor").description("Monitor health factor with alerts").option("--protocol <protocol>", "Protocol slug (required unless --all-chains)").requiredOption("--address <address>", "Wallet address to monitor").option("--threshold <hf>", "Health factor alert threshold", "1.5").option("--interval <secs>", "Polling interval in seconds", "60").option("--once", "Run once instead of continuously").option("--all-chains", "Scan all chains for lending positions").action(async (opts) => {
8531
- const threshold = parseFloat(opts.threshold);
8532
- const address = opts.address;
8533
- if (opts.allChains) {
8534
- const registry = Registry.loadEmbedded();
8535
- const chainKeys = Array.from(registry.chains.keys());
8536
- const poll = async () => {
8537
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
8538
- const chainResults = await Promise.all(
8539
- chainKeys.map(
8540
- (ck) => checkChainLendingPositions(ck, registry, address, threshold)
8541
- )
8542
- );
8543
- const positions = chainResults.flat();
8544
- const alertsCount = positions.filter((p) => p.alert).length;
8545
- const output = {
8546
- timestamp,
8547
- address,
8548
- threshold,
8549
- positions,
8550
- alerts_count: alertsCount
8551
- };
8552
- for (const pos of positions) {
8553
- if (pos.alert) {
8554
- process.stderr.write(
8555
- `ALERT: ${pos.chain}/${pos.protocol} HF=${pos.health_factor} < ${threshold}
8556
- `
8557
- );
8558
- }
8559
- }
8560
- printOutput(output, getOpts());
8561
- };
8562
- await poll();
8563
- if (!opts.once) {
8564
- const intervalMs = parseInt(opts.interval) * 1e3;
8565
- const timer = setInterval(poll, intervalMs);
8566
- process.on("SIGINT", () => {
8567
- clearInterval(timer);
8568
- process.exit(0);
8569
- });
8570
- }
8571
- } else {
8572
- if (!opts.protocol) {
8573
- printOutput({ error: "Either --protocol or --all-chains is required" }, getOpts());
8574
- process.exit(1);
8575
- }
8576
- const chainName = parent.opts().chain ?? "hyperevm";
8577
- const registry = Registry.loadEmbedded();
8578
- const chain = registry.getChain(chainName);
8579
- const protocol = registry.getProtocol(opts.protocol);
8580
- const adapter = createLending(protocol, chain.effectiveRpcUrl());
8581
- const poll = async () => {
8582
- try {
8583
- const position = await adapter.getUserPosition(address);
8584
- const hf = position.health_factor ?? Infinity;
8585
- const alert = hf < threshold;
8586
- printOutput({
8587
- protocol: protocol.name,
8588
- user: opts.address,
8589
- health_factor: hf,
8590
- threshold,
8591
- alert,
8592
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8593
- supplies: position.supplies,
8594
- borrows: position.borrows
8595
- }, getOpts());
8596
- } catch (e) {
8597
- printOutput({
8598
- error: e instanceof Error ? e.message : String(e),
8599
- protocol: protocol.name,
8600
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
8601
- }, getOpts());
8602
- }
8603
- };
8604
- await poll();
8605
- if (!opts.once) {
8606
- const intervalMs = parseInt(opts.interval) * 1e3;
8607
- const timer = setInterval(poll, intervalMs);
8608
- process.on("SIGINT", () => {
8609
- clearInterval(timer);
8610
- process.exit(0);
8611
- });
8612
- }
8613
- }
8614
- });
8615
- }
8616
-
8617
- // src/commands/alert.ts
8618
- function registerAlert(parent, getOpts) {
8619
- parent.command("alert").description("Alert on DEX vs Oracle price deviation").option("--threshold <pct>", "Deviation threshold in percent", "5.0").option("--once", "Run once instead of continuously").option("--interval <secs>", "Polling interval in seconds", "60").action(async (opts) => {
8620
- const chainName = parent.opts().chain ?? "hyperevm";
8621
- const registry = Registry.loadEmbedded();
8622
- const chain = registry.getChain(chainName);
8623
- const rpcUrl = chain.effectiveRpcUrl();
8624
- const threshold = parseFloat(opts.threshold);
8625
- const dexProtocols = registry.getProtocolsByCategory("dex").filter((p) => p.chain === chainName);
8626
- const lendingProtocols = registry.getProtocolsByCategory("lending").filter((p) => p.chain === chainName);
8627
- const poll = async () => {
8628
- const alerts = [];
8629
- for (const p of dexProtocols) {
8630
- try {
8631
- const dex = createDex(p, rpcUrl);
8632
- alerts.push({
8633
- protocol: p.name,
8634
- type: "info",
8635
- message: `DEX ${dex.name()} active on ${chainName}`
8636
- });
8637
- } catch {
8638
- }
8639
- }
8640
- printOutput({
8641
- chain: chainName,
8642
- threshold_pct: threshold,
8643
- alerts,
8644
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
8645
- }, getOpts());
8646
- };
8647
- await poll();
8648
- if (!opts.once) {
8649
- const intervalMs = parseInt(opts.interval) * 1e3;
8650
- const timer = setInterval(poll, intervalMs);
8651
- process.on("SIGINT", () => {
8652
- clearInterval(timer);
8653
- process.exit(0);
8654
- });
8655
- }
8656
- });
8657
- }
8658
-
8659
- // src/commands/scan.ts
8660
- import { encodeFunctionData as encodeFunctionData29, parseAbi as parseAbi33 } from "viem";
8661
- var AAVE_ORACLE_ABI = parseAbi33([
8662
- "function getAssetPrice(address asset) external view returns (uint256)"
8663
- ]);
8664
- var UNIV2_ROUTER_ABI = parseAbi33([
8665
- "function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory)"
8666
- ]);
8667
- var VTOKEN_ABI = parseAbi33([
8668
- "function exchangeRateStored() external view returns (uint256)"
8669
- ]);
8670
- var STABLECOINS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI", "USDT0"]);
7787
+ // src/commands/price.ts
8671
7788
  function round2(x) {
8672
7789
  return Math.round(x * 100) / 100;
8673
7790
  }
8674
- function round4(x) {
8675
- return Math.round(x * 1e4) / 1e4;
8676
- }
8677
- function round6(x) {
8678
- return Math.round(x * 1e6) / 1e6;
8679
- }
8680
- function parseU256F64(data, decimals) {
8681
- if (!data || data.length < 66) return 0;
8682
- const raw = BigInt(data.slice(0, 66));
8683
- return Number(raw) / 10 ** decimals;
8684
- }
8685
- function parseAmountsOutLast(data, outDecimals) {
8686
- if (!data) return 0;
8687
- const hex = data.startsWith("0x") ? data.slice(2) : data;
8688
- if (hex.length < 128) return 0;
8689
- const num = parseInt(hex.slice(64, 128), 16);
8690
- if (num === 0) return 0;
8691
- const byteOff = 64 + (num - 1) * 32;
8692
- const hexOff = byteOff * 2;
8693
- if (hex.length < hexOff + 64) return 0;
8694
- const val = BigInt("0x" + hex.slice(hexOff, hexOff + 64));
8695
- return Number(val) / 10 ** outDecimals;
7791
+ function resolveAsset2(registry, chain, asset) {
7792
+ if (/^0x[0-9a-fA-F]{40}$/.test(asset)) {
7793
+ return { address: asset, symbol: asset, decimals: 18 };
7794
+ }
7795
+ const token = registry.resolveToken(chain, asset);
7796
+ return { address: token.address, symbol: token.symbol, decimals: token.decimals };
8696
7797
  }
8697
- function registerScan(parent, getOpts) {
8698
- parent.command("scan").description("Multi-pattern exploit detection scanner").option("--chain <chain>", "Chain to scan", "hyperevm").option("--patterns <patterns>", "Comma-separated patterns: oracle,stable,exchange_rate", "oracle,stable,exchange_rate").option("--oracle-threshold <pct>", "Oracle divergence threshold (percent)", "5.0").option("--stable-threshold <price>", "Stablecoin depeg threshold (min price)", "0.98").option("--rate-threshold <pct>", "Exchange rate change threshold (percent)", "5.0").option("--interval <secs>", "Polling interval in seconds", "30").option("--once", "Single check then exit").option("--all-chains", "Scan all chains in parallel").action(async (opts) => {
7798
+ var WHYPE_ADDRESS = "0x5555555555555555555555555555555555555555";
7799
+ function registerPrice(parent, getOpts) {
7800
+ parent.command("price").description("Query asset prices from oracles and DEXes").requiredOption("--asset <token>", "Token symbol or address").option("--source <source>", "Price source: oracle, dex, or all", "all").action(async (opts) => {
7801
+ const mode = getOpts();
7802
+ const registry = Registry.loadEmbedded();
7803
+ const chainName = (parent.opts().chain ?? "hyperevm").toLowerCase();
7804
+ let chain;
8699
7805
  try {
8700
- const registry = Registry.loadEmbedded();
8701
- const oracleThreshold = parseFloat(opts.oracleThreshold ?? "5.0");
8702
- const stableThreshold = parseFloat(opts.stableThreshold ?? "0.98");
8703
- const rateThreshold = parseFloat(opts.rateThreshold ?? "5.0");
8704
- const interval = parseInt(opts.interval ?? "30", 10);
8705
- const patterns = opts.patterns ?? "oracle,stable,exchange_rate";
8706
- const once = !!opts.once;
8707
- if (opts.allChains) {
8708
- const result = await runAllChains(registry, patterns, oracleThreshold, stableThreshold, rateThreshold);
8709
- printOutput(result, getOpts());
8710
- return;
8711
- }
8712
- const chainName = (opts.chain ?? "hyperevm").toLowerCase();
8713
- const chain = registry.getChain(chainName);
8714
- const rpc = chain.effectiveRpcUrl();
8715
- const pats = patterns.split(",").map((s) => s.trim());
8716
- const doOracle = pats.includes("oracle");
8717
- const doStable = pats.includes("stable");
8718
- const doRate = pats.includes("exchange_rate");
8719
- const allTokens = registry.tokens.get(chainName) ?? [];
8720
- const wrappedNative = chain.wrapped_native;
8721
- const quoteStable = (() => {
8722
- for (const sym of ["USDT", "USDC", "USDT0"]) {
7806
+ chain = registry.getChain(chainName);
7807
+ } catch (e) {
7808
+ printOutput({ error: `Chain not found: ${chainName}` }, mode);
7809
+ return;
7810
+ }
7811
+ const rpcUrl = chain.effectiveRpcUrl();
7812
+ let assetAddr;
7813
+ let assetSymbol;
7814
+ let assetDecimals;
7815
+ try {
7816
+ const resolved = resolveAsset2(registry, chainName, opts.asset);
7817
+ assetAddr = resolved.address;
7818
+ assetSymbol = resolved.symbol;
7819
+ assetDecimals = resolved.decimals;
7820
+ } catch (e) {
7821
+ printOutput({ error: `Could not resolve asset: ${opts.asset}` }, mode);
7822
+ return;
7823
+ }
7824
+ const fetchOracle = opts.source === "all" || opts.source === "oracle";
7825
+ const fetchDex = opts.source === "all" || opts.source === "dex";
7826
+ const allPrices = [];
7827
+ if (fetchOracle) {
7828
+ const lendingProtocols = registry.getProtocolsByCategory(ProtocolCategory.Lending).filter((p) => p.chain.toLowerCase() === chainName);
7829
+ await Promise.all(
7830
+ lendingProtocols.map(async (entry) => {
8723
7831
  try {
8724
- return registry.resolveToken(chainName, sym);
8725
- } catch {
8726
- }
8727
- }
8728
- return null;
8729
- })();
8730
- if (!quoteStable) {
8731
- printOutput({ error: `No stablecoin found on chain ${chainName}` }, getOpts());
8732
- return;
8733
- }
8734
- const scanTokens = allTokens.filter(
8735
- (t) => t.address !== "0x0000000000000000000000000000000000000000" && !STABLECOINS.has(t.symbol)
8736
- );
8737
- const oracles = registry.getProtocolsForChain(chainName).filter(
8738
- (p) => p.category === ProtocolCategory.Lending && (p.interface === "aave_v3" || p.interface === "aave_v2" || p.interface === "aave_v3_isolated")
8739
- ).flatMap((p) => {
8740
- const oracleAddr = p.contracts?.["oracle"];
8741
- if (!oracleAddr) return [];
8742
- const decimals = p.interface === "aave_v2" ? 18 : 8;
8743
- return [{ name: p.name, addr: oracleAddr, decimals }];
8744
- });
8745
- const dexProto = registry.getProtocolsForChain(chainName).find((p) => p.category === ProtocolCategory.Dex && p.interface === "uniswap_v2");
8746
- const dexRouter = dexProto?.contracts?.["router"];
8747
- const compoundForks = registry.getProtocolsForChain(chainName).filter((p) => p.category === ProtocolCategory.Lending && p.interface === "compound_v2").map((p) => ({
8748
- name: p.name,
8749
- vtokens: Object.entries(p.contracts ?? {}).filter(([k]) => k.startsWith("v")).map(([k, a]) => ({ key: k, addr: a }))
8750
- }));
8751
- const usdc = (() => {
8752
- try {
8753
- return registry.resolveToken(chainName, "USDC");
8754
- } catch {
8755
- return null;
8756
- }
8757
- })();
8758
- const usdt = (() => {
8759
- try {
8760
- return registry.resolveToken(chainName, "USDT");
8761
- } catch {
8762
- return null;
8763
- }
8764
- })();
8765
- const prevRates = /* @__PURE__ */ new Map();
8766
- const runOnce = async () => {
8767
- const timestamp = Math.floor(Date.now() / 1e3);
8768
- const t0 = Date.now();
8769
- const calls = [];
8770
- const callTypes = [];
8771
- if (doOracle) {
8772
- for (const oracle of oracles) {
8773
- for (const token of scanTokens) {
8774
- callTypes.push({ kind: "oracle", oracle: oracle.name, token: token.symbol, oracleDecimals: oracle.decimals });
8775
- calls.push([
8776
- oracle.addr,
8777
- encodeFunctionData29({ abi: AAVE_ORACLE_ABI, functionName: "getAssetPrice", args: [token.address] })
8778
- ]);
8779
- }
8780
- }
8781
- if (dexRouter) {
8782
- for (const token of scanTokens) {
8783
- const amountIn = BigInt(10) ** BigInt(token.decimals);
8784
- const path = wrappedNative && token.address.toLowerCase() === wrappedNative.toLowerCase() ? [token.address, quoteStable.address] : wrappedNative ? [token.address, wrappedNative, quoteStable.address] : [token.address, quoteStable.address];
8785
- callTypes.push({ kind: "dex", token: token.symbol, outDecimals: quoteStable.decimals });
8786
- calls.push([
8787
- dexRouter,
8788
- encodeFunctionData29({ abi: UNIV2_ROUTER_ABI, functionName: "getAmountsOut", args: [amountIn, path] })
8789
- ]);
8790
- }
8791
- }
8792
- }
8793
- if (doStable && usdc && usdt && dexRouter) {
8794
- callTypes.push({ kind: "stable", from: "USDC", to: "USDT", outDecimals: usdt.decimals });
8795
- calls.push([
8796
- dexRouter,
8797
- encodeFunctionData29({
8798
- abi: UNIV2_ROUTER_ABI,
8799
- functionName: "getAmountsOut",
8800
- args: [BigInt(10) ** BigInt(usdc.decimals), [usdc.address, usdt.address]]
8801
- })
8802
- ]);
8803
- callTypes.push({ kind: "stable", from: "USDT", to: "USDC", outDecimals: usdc.decimals });
8804
- calls.push([
8805
- dexRouter,
8806
- encodeFunctionData29({
8807
- abi: UNIV2_ROUTER_ABI,
8808
- functionName: "getAmountsOut",
8809
- args: [BigInt(10) ** BigInt(usdt.decimals), [usdt.address, usdc.address]]
8810
- })
8811
- ]);
8812
- }
8813
- if (doRate) {
8814
- for (const fork of compoundForks) {
8815
- for (const { key, addr } of fork.vtokens) {
8816
- callTypes.push({ kind: "exchangeRate", protocol: fork.name, vtoken: key });
8817
- calls.push([addr, encodeFunctionData29({ abi: VTOKEN_ABI, functionName: "exchangeRateStored", args: [] })]);
8818
- }
8819
- }
8820
- }
8821
- if (calls.length === 0) {
8822
- printOutput({ error: `No scannable resources found on ${chainName}` }, getOpts());
8823
- return;
8824
- }
8825
- const results = await multicallRead(rpc, calls);
8826
- const scanMs = Date.now() - t0;
8827
- const alerts = [];
8828
- const oracleByToken = /* @__PURE__ */ new Map();
8829
- const dexByToken = /* @__PURE__ */ new Map();
8830
- const oracleData = {};
8831
- const dexData = {};
8832
- const stableData = {};
8833
- const stablePrices = [];
8834
- const rateData = {};
8835
- for (let i = 0; i < callTypes.length; i++) {
8836
- const ct = callTypes[i];
8837
- const raw = results[i] ?? null;
8838
- if (ct.kind === "oracle") {
8839
- const price = parseU256F64(raw, ct.oracleDecimals);
8840
- if (price > 0) {
8841
- const existing = oracleByToken.get(ct.token) ?? [];
8842
- existing.push({ oracle: ct.oracle, price });
8843
- oracleByToken.set(ct.token, existing);
8844
- oracleData[`${ct.oracle}/${ct.token}`] = round4(price);
8845
- }
8846
- } else if (ct.kind === "dex") {
8847
- const price = parseAmountsOutLast(raw, ct.outDecimals);
8848
- if (price > 0) {
8849
- dexByToken.set(ct.token, price);
8850
- dexData[ct.token] = round4(price);
8851
- }
8852
- } else if (ct.kind === "stable") {
8853
- const price = parseAmountsOutLast(raw, ct.outDecimals);
8854
- if (price <= 0) continue;
8855
- const pair = `${ct.from}/${ct.to}`;
8856
- stableData[pair] = round4(price);
8857
- stablePrices.push({ asset: ct.from, pair, price });
8858
- } else if (ct.kind === "exchangeRate") {
8859
- const rate = parseU256F64(raw, 18);
8860
- const key = `${ct.protocol}/${ct.vtoken}`;
8861
- rateData[key] = round6(rate);
8862
- if (rate > 0) {
8863
- const prev = prevRates.get(key);
8864
- if (prev !== void 0) {
8865
- const change = Math.abs((rate - prev) / prev * 100);
8866
- if (change > rateThreshold) {
8867
- const severity = change > 50 ? "critical" : change > 20 ? "high" : "medium";
8868
- alerts.push({
8869
- pattern: "exchange_rate_anomaly",
8870
- severity,
8871
- protocol: ct.protocol,
8872
- vtoken: ct.vtoken,
8873
- prev_rate: round6(prev),
8874
- curr_rate: round6(rate),
8875
- change_pct: round2(change),
8876
- action: `possible donation attack on ${ct.protocol} ${ct.vtoken}`
8877
- });
8878
- }
8879
- }
8880
- prevRates.set(key, rate);
8881
- }
8882
- }
8883
- }
8884
- if (stablePrices.length >= 2) {
8885
- const allBelow = stablePrices.every((s) => s.price < stableThreshold);
8886
- if (!allBelow) {
8887
- for (const { asset, pair, price } of stablePrices) {
8888
- if (price < stableThreshold) {
8889
- const severity = price < 0.95 ? "critical" : "high";
8890
- alerts.push({
8891
- pattern: "stablecoin_depeg",
8892
- severity,
8893
- asset,
8894
- pair,
8895
- price: round4(price),
8896
- threshold: stableThreshold,
8897
- action: `buy ${asset} at $${round4(price)}, wait for repeg`
8898
- });
8899
- }
8900
- }
8901
- }
8902
- } else {
8903
- for (const { asset, pair, price } of stablePrices) {
8904
- if (price < stableThreshold) {
8905
- const severity = price < 0.95 ? "critical" : "high";
8906
- alerts.push({
8907
- pattern: "stablecoin_depeg",
8908
- severity,
8909
- asset,
8910
- pair,
8911
- price: round4(price),
8912
- threshold: stableThreshold,
8913
- action: `buy ${asset} at $${round4(price)}, wait for repeg`
8914
- });
8915
- }
8916
- }
8917
- }
8918
- if (doOracle) {
8919
- for (const [token, oracleEntries] of oracleByToken) {
8920
- const dexPrice = dexByToken.get(token);
8921
- if (dexPrice === void 0) continue;
8922
- for (const { oracle, price: oraclePrice } of oracleEntries) {
8923
- if (dexPrice < oraclePrice && dexPrice < oraclePrice * 0.1) continue;
8924
- const deviation = Math.abs(dexPrice - oraclePrice) / oraclePrice * 100;
8925
- if (deviation > oracleThreshold) {
8926
- const severity = deviation > 100 ? "critical" : deviation > 20 ? "high" : "medium";
8927
- const action = dexPrice > oraclePrice ? `borrow ${token} from ${oracle}, sell on DEX` : `buy ${token} on DEX, use as collateral on ${oracle}`;
8928
- alerts.push({
8929
- pattern: "oracle_divergence",
8930
- severity,
8931
- asset: token,
8932
- oracle,
8933
- oracle_price: round4(oraclePrice),
8934
- dex_price: round4(dexPrice),
8935
- deviation_pct: round2(deviation),
8936
- action
8937
- });
8938
- }
8939
- }
8940
- }
8941
- }
8942
- const data = {};
8943
- if (Object.keys(oracleData).length > 0) data["oracle_prices"] = oracleData;
8944
- if (Object.keys(dexData).length > 0) data["dex_prices"] = dexData;
8945
- if (Object.keys(stableData).length > 0) data["stablecoin_pegs"] = stableData;
8946
- if (Object.keys(rateData).length > 0) data["exchange_rates"] = rateData;
8947
- const output = {
8948
- timestamp,
8949
- chain: chain.name,
8950
- scan_duration_ms: scanMs,
8951
- patterns,
8952
- alert_count: alerts.length,
8953
- alerts,
8954
- data
8955
- };
8956
- for (const alert of alerts) {
8957
- process.stderr.write(
8958
- `ALERT [${alert["severity"]}]: ${alert["pattern"]} \u2014 ${alert["action"]}
8959
- `
8960
- );
8961
- }
8962
- printOutput(output, getOpts());
8963
- };
8964
- await runOnce();
8965
- if (!once) {
8966
- const intervalMs = interval * 1e3;
8967
- const loop = async () => {
8968
- await new Promise((r) => setTimeout(r, intervalMs));
8969
- await runOnce();
8970
- void loop();
8971
- };
8972
- await loop();
8973
- }
8974
- } catch (err) {
8975
- printOutput({ error: String(err) }, getOpts());
8976
- process.exit(1);
8977
- }
8978
- });
8979
- }
8980
- async function runAllChains(registry, patterns, oracleThreshold, stableThreshold, _rateThreshold) {
8981
- const t0 = Date.now();
8982
- const chainKeys = Array.from(registry.chains.keys());
8983
- const tasks = chainKeys.map(async (ck) => {
8984
- try {
8985
- const chain = registry.getChain(ck);
8986
- const rpc = chain.effectiveRpcUrl();
8987
- const chainName = chain.name.toLowerCase();
8988
- const allTokens = registry.tokens.get(chainName) ?? [];
8989
- const wrappedNative = chain.wrapped_native;
8990
- const quoteStable = (() => {
8991
- for (const sym of ["USDT", "USDC", "USDT0"]) {
8992
- try {
8993
- return registry.resolveToken(chainName, sym);
8994
- } catch {
8995
- }
8996
- }
8997
- return null;
8998
- })();
8999
- if (!quoteStable) return null;
9000
- const scanTokens = allTokens.filter(
9001
- (t) => t.address !== "0x0000000000000000000000000000000000000000" && !STABLECOINS.has(t.symbol)
9002
- );
9003
- const pats = patterns.split(",").map((s) => s.trim());
9004
- const doOracle = pats.includes("oracle");
9005
- const doStable = pats.includes("stable");
9006
- const oracles = registry.getProtocolsForChain(chainName).filter(
9007
- (p) => p.category === ProtocolCategory.Lending && (p.interface === "aave_v3" || p.interface === "aave_v2" || p.interface === "aave_v3_isolated")
9008
- ).flatMap((p) => {
9009
- const oracleAddr = p.contracts?.["oracle"];
9010
- if (!oracleAddr) return [];
9011
- return [{ name: p.name, addr: oracleAddr, decimals: p.interface === "aave_v2" ? 18 : 8 }];
9012
- });
9013
- const dexProto = registry.getProtocolsForChain(chainName).find((p) => p.category === ProtocolCategory.Dex && p.interface === "uniswap_v2");
9014
- const dexRouter = dexProto?.contracts?.["router"];
9015
- const usdc = (() => {
9016
- try {
9017
- return registry.resolveToken(chainName, "USDC");
9018
- } catch {
9019
- return null;
9020
- }
9021
- })();
9022
- const usdt = (() => {
9023
- try {
9024
- return registry.resolveToken(chainName, "USDT");
9025
- } catch {
9026
- return null;
9027
- }
9028
- })();
9029
- const calls = [];
9030
- const cts = [];
9031
- if (doOracle) {
9032
- for (const oracle of oracles) {
9033
- for (const token of scanTokens) {
9034
- cts.push({ kind: "oracle", oracle: oracle.name, token: token.symbol, dec: oracle.decimals });
9035
- calls.push([oracle.addr, encodeFunctionData29({ abi: AAVE_ORACLE_ABI, functionName: "getAssetPrice", args: [token.address] })]);
9036
- }
9037
- }
9038
- if (dexRouter) {
9039
- for (const token of scanTokens) {
9040
- const path = wrappedNative && token.address.toLowerCase() === wrappedNative.toLowerCase() ? [token.address, quoteStable.address] : wrappedNative ? [token.address, wrappedNative, quoteStable.address] : [token.address, quoteStable.address];
9041
- cts.push({ kind: "dex", token: token.symbol, dec: quoteStable.decimals });
9042
- calls.push([dexRouter, encodeFunctionData29({ abi: UNIV2_ROUTER_ABI, functionName: "getAmountsOut", args: [BigInt(10) ** BigInt(token.decimals), path] })]);
9043
- }
9044
- }
9045
- }
9046
- if (doStable && usdc && usdt && dexRouter) {
9047
- cts.push({ kind: "stable", from: "USDC", to: "USDT", dec: usdt.decimals });
9048
- calls.push([dexRouter, encodeFunctionData29({ abi: UNIV2_ROUTER_ABI, functionName: "getAmountsOut", args: [BigInt(10) ** BigInt(usdc.decimals), [usdc.address, usdt.address]] })]);
9049
- cts.push({ kind: "stable", from: "USDT", to: "USDC", dec: usdc.decimals });
9050
- calls.push([dexRouter, encodeFunctionData29({ abi: UNIV2_ROUTER_ABI, functionName: "getAmountsOut", args: [BigInt(10) ** BigInt(usdt.decimals), [usdt.address, usdc.address]] })]);
9051
- }
9052
- if (calls.length === 0) return null;
9053
- const ct0 = Date.now();
9054
- const results = await multicallRead(rpc, calls);
9055
- const scanMs = Date.now() - ct0;
9056
- const alerts = [];
9057
- const oracleByToken = /* @__PURE__ */ new Map();
9058
- const dexByToken = /* @__PURE__ */ new Map();
9059
- const stablePrices = [];
9060
- for (let i = 0; i < cts.length; i++) {
9061
- const ct = cts[i];
9062
- const raw = results[i] ?? null;
9063
- if (ct.kind === "oracle") {
9064
- const price = parseU256F64(raw, ct.dec);
9065
- if (price > 0) {
9066
- const existing = oracleByToken.get(ct.token) ?? [];
9067
- existing.push({ oracle: ct.oracle, price });
9068
- oracleByToken.set(ct.token, existing);
9069
- }
9070
- } else if (ct.kind === "dex") {
9071
- const price = parseAmountsOutLast(raw, ct.dec);
9072
- if (price > 0) dexByToken.set(ct.token, price);
9073
- } else if (ct.kind === "stable") {
9074
- const price = parseAmountsOutLast(raw, ct.dec);
9075
- if (price > 0) stablePrices.push({ asset: ct.from, pair: `${ct.from}/${ct.to}`, price });
9076
- }
9077
- }
9078
- if (stablePrices.length >= 2) {
9079
- const allBelow = stablePrices.every((s) => s.price < stableThreshold);
9080
- if (!allBelow) {
9081
- for (const { asset, pair, price } of stablePrices) {
9082
- if (price < stableThreshold) {
9083
- alerts.push({ pattern: "stablecoin_depeg", severity: price < 0.95 ? "critical" : "high", asset, pair, price: round4(price) });
9084
- }
9085
- }
9086
- }
9087
- }
9088
- for (const [token, oEntries] of oracleByToken) {
9089
- const dp = dexByToken.get(token);
9090
- if (dp === void 0) continue;
9091
- for (const { oracle, price: op } of oEntries) {
9092
- if (dp < op && dp < op * 0.1) continue;
9093
- const dev = Math.abs(dp - op) / op * 100;
9094
- if (dev > oracleThreshold) {
9095
- const sev = dev > 100 ? "critical" : dev > 20 ? "high" : "medium";
9096
- alerts.push({
9097
- pattern: "oracle_divergence",
9098
- severity: sev,
9099
- asset: token,
9100
- oracle,
9101
- oracle_price: round4(op),
9102
- dex_price: round4(dp),
9103
- deviation_pct: round2(dev),
9104
- action: dp > op ? `borrow ${token} from ${oracle}, sell on DEX` : `buy ${token} on DEX, collateral on ${oracle}`
9105
- });
9106
- }
9107
- }
9108
- }
9109
- return { chain: chain.name, scan_duration_ms: scanMs, alert_count: alerts.length, alerts };
9110
- } catch {
9111
- return null;
9112
- }
9113
- });
9114
- const chainResults = (await Promise.all(tasks)).filter(Boolean);
9115
- chainResults.sort((a, b) => {
9116
- const ac = a["alert_count"] ?? 0;
9117
- const bc = b["alert_count"] ?? 0;
9118
- return bc - ac;
9119
- });
9120
- const totalAlerts = chainResults.reduce((sum, r) => sum + (r["alert_count"] ?? 0), 0);
9121
- return {
9122
- mode: "all_chains",
9123
- chains_scanned: chainKeys.length,
9124
- scan_duration_ms: Date.now() - t0,
9125
- total_alerts: totalAlerts,
9126
- chains: chainResults
9127
- };
9128
- }
9129
-
9130
- // src/commands/positions.ts
9131
- import { encodeFunctionData as encodeFunctionData30, parseAbi as parseAbi34 } from "viem";
9132
- var ERC20_ABI6 = parseAbi34([
9133
- "function balanceOf(address owner) external view returns (uint256)"
9134
- ]);
9135
- var POOL_ABI5 = parseAbi34([
9136
- "function getUserAccountData(address user) external view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)"
9137
- ]);
9138
- var ORACLE_ABI6 = parseAbi34([
9139
- "function getAssetPrice(address asset) external view returns (uint256)"
9140
- ]);
9141
- function round22(x) {
9142
- return Math.round(x * 100) / 100;
9143
- }
9144
- function round42(x) {
9145
- return Math.round(x * 1e4) / 1e4;
9146
- }
9147
- function estimateTokenValue(symbol, balance, nativePrice) {
9148
- const s = symbol.toUpperCase();
9149
- if (s.includes("USD") || s.includes("DAI")) return balance;
9150
- if (s.includes("BTC") || s.includes("FBTC")) return balance * 75e3;
9151
- if (["WETH", "ETH", "METH", "CBETH", "WSTETH"].includes(s)) return balance * 2350;
9152
- return balance * nativePrice;
9153
- }
9154
- function decodeU2563(data, offset = 0) {
9155
- if (!data || data.length < 2 + (offset + 32) * 2) return 0n;
9156
- const hex = data.slice(2 + offset * 64, 2 + offset * 64 + 64);
9157
- return BigInt("0x" + hex);
9158
- }
9159
- async function scanSingleChain(chainName, rpc, user, tokens, lendingPools, oracleAddr, wrappedNative) {
9160
- const calls = [];
9161
- const callTypes = [];
9162
- for (const token of tokens) {
9163
- if (token.address !== "0x0000000000000000000000000000000000000000") {
9164
- callTypes.push({ kind: "token", symbol: token.symbol, decimals: token.decimals });
9165
- calls.push([
9166
- token.address,
9167
- encodeFunctionData30({ abi: ERC20_ABI6, functionName: "balanceOf", args: [user] })
9168
- ]);
9169
- }
9170
- }
9171
- for (const { name, pool, iface } of lendingPools) {
9172
- callTypes.push({ kind: "lending", protocol: name, iface });
9173
- calls.push([
9174
- pool,
9175
- encodeFunctionData30({ abi: POOL_ABI5, functionName: "getUserAccountData", args: [user] })
9176
- ]);
9177
- }
9178
- if (oracleAddr) {
9179
- callTypes.push({ kind: "native_price" });
9180
- calls.push([
9181
- oracleAddr,
9182
- encodeFunctionData30({ abi: ORACLE_ABI6, functionName: "getAssetPrice", args: [wrappedNative] })
9183
- ]);
9184
- }
9185
- if (calls.length === 0) return null;
9186
- let results;
9187
- try {
9188
- results = await multicallRead(rpc, calls);
9189
- } catch {
9190
- return null;
9191
- }
9192
- const nativePrice = oracleAddr ? Number(decodeU2563(results[results.length - 1])) / 1e8 : 0;
9193
- const tokenBalances = [];
9194
- const lendingPositions = [];
9195
- let chainValue = 0;
9196
- let totalColl = 0;
9197
- let totalDebt = 0;
9198
- for (let i = 0; i < callTypes.length; i++) {
9199
- const ct = callTypes[i];
9200
- const data = results[i] ?? null;
9201
- if (ct.kind === "token") {
9202
- const balance = decodeU2563(data);
9203
- if (balance > 0n) {
9204
- const balF64 = Number(balance) / 10 ** ct.decimals;
9205
- const valueUsd = estimateTokenValue(ct.symbol, balF64, nativePrice);
9206
- if (valueUsd > 0.01) {
9207
- chainValue += valueUsd;
9208
- tokenBalances.push({
9209
- symbol: ct.symbol,
9210
- balance: round42(balF64),
9211
- value_usd: round22(valueUsd)
9212
- });
9213
- }
9214
- }
9215
- } else if (ct.kind === "lending") {
9216
- if (data && data.length >= 2 + 192 * 2) {
9217
- const priceDecimals = ct.iface === "aave_v2" ? 18 : 8;
9218
- const divisor = 10 ** priceDecimals;
9219
- const collateral = Number(decodeU2563(data, 0)) / divisor;
9220
- const debt = Number(decodeU2563(data, 1)) / divisor;
9221
- const hfRaw = decodeU2563(data, 5);
9222
- let hf = null;
9223
- if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
9224
- const v = Number(hfRaw) / 1e18;
9225
- hf = v > 1e10 ? null : round22(v);
9226
- }
9227
- if (collateral > 0.01 || debt > 0.01) {
9228
- const net = collateral - debt;
9229
- chainValue += net;
9230
- totalColl += collateral;
9231
- totalDebt += debt;
9232
- lendingPositions.push({
9233
- protocol: ct.protocol,
9234
- collateral_usd: round22(collateral),
9235
- debt_usd: round22(debt),
9236
- net_usd: round22(net),
9237
- health_factor: hf
9238
- });
9239
- }
9240
- }
9241
- }
9242
- }
9243
- if (tokenBalances.length === 0 && lendingPositions.length === 0) return null;
9244
- return {
9245
- chain_name: chainName,
9246
- native_price: nativePrice,
9247
- chain_value: chainValue,
9248
- collateral: totalColl,
9249
- debt: totalDebt,
9250
- token_balances: tokenBalances,
9251
- lending_positions: lendingPositions
9252
- };
9253
- }
9254
- function registerPositions(parent, getOpts) {
9255
- parent.command("positions").description("Cross-chain position scanner: find all your positions everywhere").requiredOption("--address <address>", "Wallet address to scan").option("--chains <chains>", "Comma-separated chain names (omit for all)").action(async (opts) => {
9256
- const mode = getOpts();
9257
- const registry = Registry.loadEmbedded();
9258
- const user = opts.address;
9259
- if (!/^0x[0-9a-fA-F]{40}$/.test(user)) {
9260
- printOutput({ error: `Invalid address: ${opts.address}` }, mode);
9261
- return;
9262
- }
9263
- const chainFilter = opts.chains ? opts.chains.split(",").map((s) => s.trim().toLowerCase()) : null;
9264
- const chainKeys = chainFilter ?? Array.from(registry.chains.keys());
9265
- const start = Date.now();
9266
- const scanParams = [];
9267
- for (const chainKey of chainKeys) {
9268
- let chain;
9269
- try {
9270
- chain = registry.getChain(chainKey);
9271
- } catch {
9272
- continue;
9273
- }
9274
- const rpc = chain.effectiveRpcUrl();
9275
- const rawTokens = registry.tokens.get(chainKey) ?? [];
9276
- const tokens = rawTokens.map((t) => ({
9277
- address: t.address,
9278
- symbol: t.symbol,
9279
- decimals: t.decimals
9280
- }));
9281
- const chainProtocols = registry.getProtocolsForChain(chainKey);
9282
- const lendingPools = chainProtocols.filter(
9283
- (p) => p.category === ProtocolCategory.Lending && (p.interface === "aave_v3" || p.interface === "aave_v2")
9284
- ).filter((p) => p.contracts?.["pool"]).map((p) => ({
9285
- name: p.name,
9286
- pool: p.contracts["pool"],
9287
- iface: p.interface
9288
- }));
9289
- const oracleEntry = chainProtocols.find(
9290
- (p) => p.interface === "aave_v3" && p.contracts?.["oracle"]
9291
- );
9292
- const oracleAddr = oracleEntry?.contracts?.["oracle"];
9293
- const wrappedNative = chain.wrapped_native ?? "0x5555555555555555555555555555555555555555";
9294
- scanParams.push({ chainName: chain.name, rpc, tokens, lendingPools, oracleAddr, wrappedNative });
9295
- }
9296
- const chainResultsRaw = await Promise.all(
9297
- scanParams.map(
9298
- (p) => scanSingleChain(p.chainName, p.rpc, user, p.tokens, p.lendingPools, p.oracleAddr, p.wrappedNative)
9299
- )
9300
- );
9301
- let grandTotalUsd = 0;
9302
- let totalCollateralUsd = 0;
9303
- let totalDebtUsd = 0;
9304
- const chainResults = chainResultsRaw.filter((r) => r !== null).map((r) => {
9305
- grandTotalUsd += r.chain_value;
9306
- totalCollateralUsd += r.collateral;
9307
- totalDebtUsd += r.debt;
9308
- return {
9309
- chain: r.chain_name,
9310
- native_price_usd: round22(r.native_price),
9311
- chain_total_usd: round22(r.chain_value),
9312
- token_balances: r.token_balances,
9313
- lending_positions: r.lending_positions
9314
- };
9315
- }).sort((a, b) => b.chain_total_usd - a.chain_total_usd);
9316
- const scanMs = Date.now() - start;
9317
- printOutput(
9318
- {
9319
- address: user,
9320
- scan_duration_ms: scanMs,
9321
- chains_scanned: chainKeys.length,
9322
- chains_with_positions: chainResults.length,
9323
- summary: {
9324
- total_value_usd: round22(grandTotalUsd),
9325
- total_collateral_usd: round22(totalCollateralUsd),
9326
- total_debt_usd: round22(totalDebtUsd),
9327
- net_lending_usd: round22(totalCollateralUsd - totalDebtUsd)
9328
- },
9329
- chains: chainResults
9330
- },
9331
- mode
9332
- );
9333
- });
9334
- }
9335
-
9336
- // src/commands/price.ts
9337
- function round23(x) {
9338
- return Math.round(x * 100) / 100;
9339
- }
9340
- function resolveAsset2(registry, chain, asset) {
9341
- if (/^0x[0-9a-fA-F]{40}$/.test(asset)) {
9342
- return { address: asset, symbol: asset, decimals: 18 };
9343
- }
9344
- const token = registry.resolveToken(chain, asset);
9345
- return { address: token.address, symbol: token.symbol, decimals: token.decimals };
9346
- }
9347
- var WHYPE_ADDRESS = "0x5555555555555555555555555555555555555555";
9348
- function registerPrice(parent, getOpts) {
9349
- parent.command("price").description("Query asset prices from oracles and DEXes").requiredOption("--asset <token>", "Token symbol or address").option("--source <source>", "Price source: oracle, dex, or all", "all").action(async (opts) => {
9350
- const mode = getOpts();
9351
- const registry = Registry.loadEmbedded();
9352
- const chainName = (parent.opts().chain ?? "hyperevm").toLowerCase();
9353
- let chain;
9354
- try {
9355
- chain = registry.getChain(chainName);
9356
- } catch (e) {
9357
- printOutput({ error: `Chain not found: ${chainName}` }, mode);
9358
- return;
9359
- }
9360
- const rpcUrl = chain.effectiveRpcUrl();
9361
- let assetAddr;
9362
- let assetSymbol;
9363
- let assetDecimals;
9364
- try {
9365
- const resolved = resolveAsset2(registry, chainName, opts.asset);
9366
- assetAddr = resolved.address;
9367
- assetSymbol = resolved.symbol;
9368
- assetDecimals = resolved.decimals;
9369
- } catch (e) {
9370
- printOutput({ error: `Could not resolve asset: ${opts.asset}` }, mode);
9371
- return;
9372
- }
9373
- const fetchOracle = opts.source === "all" || opts.source === "oracle";
9374
- const fetchDex = opts.source === "all" || opts.source === "dex";
9375
- const allPrices = [];
9376
- if (fetchOracle) {
9377
- const lendingProtocols = registry.getProtocolsByCategory(ProtocolCategory.Lending).filter((p) => p.chain.toLowerCase() === chainName);
9378
- await Promise.all(
9379
- lendingProtocols.map(async (entry) => {
9380
- try {
9381
- const oracle = createOracleFromLending(entry, rpcUrl);
9382
- const price = await oracle.getPrice(assetAddr);
9383
- allPrices.push({
9384
- source: price.source,
9385
- source_type: price.source_type,
9386
- price_f64: price.price_f64
9387
- });
7832
+ const oracle = createOracleFromLending(entry, rpcUrl);
7833
+ const price = await oracle.getPrice(assetAddr);
7834
+ allPrices.push({
7835
+ source: price.source,
7836
+ source_type: price.source_type,
7837
+ price_f64: price.price_f64
7838
+ });
9388
7839
  } catch {
9389
7840
  }
9390
7841
  })
@@ -9462,10 +7913,10 @@ function registerPrice(parent, getOpts) {
9462
7913
  prices: allPrices.map((p) => ({
9463
7914
  source: p.source,
9464
7915
  source_type: p.source_type,
9465
- price: round23(p.price_f64)
7916
+ price: round2(p.price_f64)
9466
7917
  })),
9467
- max_spread_pct: round23(maxSpreadPct),
9468
- oracle_vs_dex_spread_pct: round23(oracleVsDexSpreadPct)
7918
+ max_spread_pct: round2(maxSpreadPct),
7919
+ oracle_vs_dex_spread_pct: round2(oracleVsDexSpreadPct)
9469
7920
  };
9470
7921
  printOutput(report, mode);
9471
7922
  });
@@ -9553,192 +8004,6 @@ function registerToken(parent, getOpts, makeExecutor2) {
9553
8004
  });
9554
8005
  }
9555
8006
 
9556
- // src/commands/whales.ts
9557
- import { encodeFunctionData as encodeFunctionData31, parseAbi as parseAbi35 } from "viem";
9558
- var POOL_ABI6 = parseAbi35([
9559
- "function getUserAccountData(address user) external view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)"
9560
- ]);
9561
- function round24(x) {
9562
- return Math.round(x * 100) / 100;
9563
- }
9564
- function round43(x) {
9565
- return Math.round(x * 1e4) / 1e4;
9566
- }
9567
- function decodeU2564(data, wordOffset = 0) {
9568
- if (!data || data.length < 2 + (wordOffset + 1) * 64) return 0n;
9569
- const hex = data.slice(2 + wordOffset * 64, 2 + wordOffset * 64 + 64);
9570
- return BigInt("0x" + hex);
9571
- }
9572
- function getExplorerApi(chainId, explorerUrl) {
9573
- const routescanChains = [1, 43114, 10, 5e3];
9574
- if (routescanChains.includes(chainId)) {
9575
- return {
9576
- base: `https://api.routescan.io/v2/network/mainnet/evm/${chainId}/etherscan/api`
9577
- };
9578
- }
9579
- const apiKey = process.env["ETHERSCAN_API_KEY"];
9580
- if (apiKey) {
9581
- return {
9582
- base: `https://api.etherscan.io/v2/api?chainid=${chainId}`,
9583
- apiKey
9584
- };
9585
- }
9586
- return null;
9587
- }
9588
- function registerWhales(parent, getOpts) {
9589
- parent.command("whales").description("Find top token holders (whales) and their positions").requiredOption("--token <token>", "Token symbol or address").option("--top <n>", "Number of top holders to show", "10").option("--positions", "Also scan each whale's lending positions").action(async (opts) => {
9590
- const mode = getOpts();
9591
- const registry = Registry.loadEmbedded();
9592
- const chainName = (parent.opts().chain ?? "hyperevm").toLowerCase();
9593
- let chain;
9594
- try {
9595
- chain = registry.getChain(chainName);
9596
- } catch {
9597
- printOutput({ error: `Chain not found: ${chainName}` }, mode);
9598
- return;
9599
- }
9600
- const rpc = chain.effectiveRpcUrl();
9601
- const top = parseInt(opts.top, 10) || 10;
9602
- let token;
9603
- try {
9604
- token = registry.resolveToken(chainName, opts.token);
9605
- } catch {
9606
- printOutput({ error: `Token not found: ${opts.token}` }, mode);
9607
- return;
9608
- }
9609
- const explorerApi = getExplorerApi(chain.chain_id, chain.explorer_url);
9610
- if (!explorerApi) {
9611
- printOutput(
9612
- {
9613
- error: `No explorer API available for ${chain.name} (chain_id: ${chain.chain_id}). Set ETHERSCAN_API_KEY to enable.`
9614
- },
9615
- mode
9616
- );
9617
- return;
9618
- }
9619
- const tokenAddr = token.address;
9620
- let url = `${explorerApi.base}?module=token&action=tokenholderlist&contractaddress=${tokenAddr}&page=1&offset=${top}`;
9621
- if (explorerApi.apiKey) {
9622
- url += `&apikey=${explorerApi.apiKey}`;
9623
- }
9624
- let body;
9625
- try {
9626
- const resp = await fetch(url);
9627
- body = await resp.json();
9628
- } catch (e) {
9629
- printOutput({ error: `Explorer API request failed: ${e instanceof Error ? e.message : String(e)}` }, mode);
9630
- return;
9631
- }
9632
- if (body.status !== "1") {
9633
- const msg = typeof body.result === "string" ? body.result : "Unknown error";
9634
- if (msg.includes("API Key") || msg.includes("apikey")) {
9635
- printOutput(
9636
- { error: "Explorer API requires API key. Set ETHERSCAN_API_KEY environment variable." },
9637
- mode
9638
- );
9639
- return;
9640
- }
9641
- printOutput({ error: `Explorer API error: ${msg}` }, mode);
9642
- return;
9643
- }
9644
- const holders = Array.isArray(body.result) ? body.result : [];
9645
- const whaleList = [];
9646
- for (const h of holders) {
9647
- const addrStr = h["TokenHolderAddress"] ?? "";
9648
- const qtyStr = h["TokenHolderQuantity"] ?? "0";
9649
- if (/^0x[0-9a-fA-F]{40}$/.test(addrStr)) {
9650
- const raw = BigInt(qtyStr || "0");
9651
- const balance = Number(raw) / 10 ** token.decimals;
9652
- whaleList.push({ address: addrStr, balance });
9653
- }
9654
- }
9655
- const whaleData = [];
9656
- if (opts.positions && whaleList.length > 0) {
9657
- const lendingPools = registry.getProtocolsForChain(chainName).filter(
9658
- (p) => p.category === ProtocolCategory.Lending && (p.interface === "aave_v3" || p.interface === "aave_v2")
9659
- ).filter((p) => p.contracts?.["pool"]).map((p) => ({
9660
- name: p.name,
9661
- pool: p.contracts["pool"],
9662
- iface: p.interface
9663
- }));
9664
- const calls = [];
9665
- for (const whale of whaleList) {
9666
- for (const { pool } of lendingPools) {
9667
- calls.push([
9668
- pool,
9669
- encodeFunctionData31({ abi: POOL_ABI6, functionName: "getUserAccountData", args: [whale.address] })
9670
- ]);
9671
- }
9672
- }
9673
- let results = [];
9674
- if (calls.length > 0) {
9675
- try {
9676
- results = await multicallRead(rpc, calls);
9677
- } catch {
9678
- results = [];
9679
- }
9680
- }
9681
- const poolsPerWhale = lendingPools.length;
9682
- for (let wi = 0; wi < whaleList.length; wi++) {
9683
- const whale = whaleList[wi];
9684
- const positions = [];
9685
- for (let pi = 0; pi < lendingPools.length; pi++) {
9686
- const { name: protoName, iface } = lendingPools[pi];
9687
- const idx = wi * poolsPerWhale + pi;
9688
- const data = results[idx] ?? null;
9689
- if (data && data.length >= 2 + 192 * 2) {
9690
- const dec = iface === "aave_v2" ? 18 : 8;
9691
- const divisor = 10 ** dec;
9692
- const collateral = Number(decodeU2564(data, 0)) / divisor;
9693
- const debt = Number(decodeU2564(data, 1)) / divisor;
9694
- const hfRaw = decodeU2564(data, 5);
9695
- let hf = null;
9696
- if (hfRaw <= BigInt("0xffffffffffffffffffffffffffffffff")) {
9697
- const v = Number(hfRaw) / 1e18;
9698
- hf = v > 1e10 ? null : round24(v);
9699
- }
9700
- if (collateral > 0.01 || debt > 0.01) {
9701
- positions.push({
9702
- protocol: protoName,
9703
- collateral_usd: round24(collateral),
9704
- debt_usd: round24(debt),
9705
- health_factor: hf
9706
- });
9707
- }
9708
- }
9709
- }
9710
- whaleData.push({
9711
- rank: wi + 1,
9712
- address: whale.address,
9713
- balance: round43(whale.balance),
9714
- positions
9715
- });
9716
- }
9717
- } else {
9718
- for (let wi = 0; wi < whaleList.length; wi++) {
9719
- const whale = whaleList[wi];
9720
- whaleData.push({
9721
- rank: wi + 1,
9722
- address: whale.address,
9723
- balance: round43(whale.balance)
9724
- });
9725
- }
9726
- }
9727
- printOutput(
9728
- {
9729
- chain: chain.name,
9730
- token: opts.token,
9731
- token_address: tokenAddr,
9732
- decimals: token.decimals,
9733
- top,
9734
- holders: whaleData,
9735
- explorer: chain.explorer_url ?? ""
9736
- },
9737
- mode
9738
- );
9739
- });
9740
- }
9741
-
9742
8007
  // src/commands/bridge.ts
9743
8008
  var LIFI_API = "https://li.quest/v1";
9744
8009
  var DLN_API = "https://dln.debridge.finance/v1.0/dln/order";
@@ -9893,11 +8158,11 @@ function registerBridge(parent, getOpts) {
9893
8158
  const amountUsdc = Number(BigInt(opts.amount)) / 1e6;
9894
8159
  const { fee, maxFeeSubunits } = await getCctpFeeEstimate(srcDomain, dstDomain, amountUsdc);
9895
8160
  const recipientPadded = `0x${"0".repeat(24)}${recipient.replace("0x", "").toLowerCase()}`;
9896
- const { encodeFunctionData: encodeFunctionData33, parseAbi: parseAbi36 } = await import("viem");
9897
- const tokenMessengerAbi = parseAbi36([
8161
+ const { encodeFunctionData: encodeFunctionData29, parseAbi: parseAbi33 } = await import("viem");
8162
+ const tokenMessengerAbi = parseAbi33([
9898
8163
  "function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold) external returns (uint64 nonce)"
9899
8164
  ]);
9900
- const data = encodeFunctionData33({
8165
+ const data = encodeFunctionData29({
9901
8166
  abi: tokenMessengerAbi,
9902
8167
  functionName: "depositForBurn",
9903
8168
  args: [
@@ -9964,19 +8229,222 @@ function registerBridge(parent, getOpts) {
9964
8229
  });
9965
8230
  }
9966
8231
 
8232
+ // src/commands/swap.ts
8233
+ var CHAIN_NAMES = {
8234
+ hyperevm: { kyber: "hyperevm", openocean: "hyperevm" },
8235
+ mantle: { openocean: "mantle" }
8236
+ };
8237
+ var KYBER_API = "https://aggregator-api.kyberswap.com";
8238
+ async function kyberGetQuote(chain, tokenIn, tokenOut, amountIn) {
8239
+ const params = new URLSearchParams({ tokenIn, tokenOut, amountIn });
8240
+ const url = `${KYBER_API}/${chain}/api/v1/routes?${params}`;
8241
+ const res = await fetch(url, { headers: { "x-client-id": "defi-cli" } });
8242
+ if (!res.ok) throw new Error(`KyberSwap quote failed: ${res.status} ${await res.text()}`);
8243
+ const json = await res.json();
8244
+ const data = json.data;
8245
+ if (!data?.routeSummary) throw new Error(`KyberSwap: no route found`);
8246
+ return data;
8247
+ }
8248
+ async function kyberBuildTx(chain, routeSummary, sender, recipient, slippageTolerance) {
8249
+ const url = `${KYBER_API}/${chain}/api/v1/route/build`;
8250
+ const res = await fetch(url, {
8251
+ method: "POST",
8252
+ headers: { "Content-Type": "application/json", "x-client-id": "defi-cli" },
8253
+ body: JSON.stringify({ routeSummary, sender, recipient, slippageTolerance })
8254
+ });
8255
+ if (!res.ok) throw new Error(`KyberSwap build failed: ${res.status} ${await res.text()}`);
8256
+ const json = await res.json();
8257
+ const data = json.data;
8258
+ if (!data) throw new Error("KyberSwap: no build data");
8259
+ return {
8260
+ to: String(data.routerAddress),
8261
+ data: String(data.data),
8262
+ value: String(data.value ?? "0x0")
8263
+ };
8264
+ }
8265
+ var OPENOCEAN_API = "https://open-api.openocean.finance/v4";
8266
+ async function openoceanSwap(chain, inTokenAddress, outTokenAddress, amountIn, slippagePct, account) {
8267
+ const params = new URLSearchParams({
8268
+ inTokenAddress,
8269
+ outTokenAddress,
8270
+ amount: amountIn,
8271
+ gasPrice: "0.1",
8272
+ slippage: slippagePct,
8273
+ account
8274
+ });
8275
+ const url = `${OPENOCEAN_API}/${chain}/swap?${params}`;
8276
+ const res = await fetch(url);
8277
+ if (!res.ok) throw new Error(`OpenOcean swap failed: ${res.status} ${await res.text()}`);
8278
+ const json = await res.json();
8279
+ const data = json.data;
8280
+ if (!data) throw new Error("OpenOcean: no swap data");
8281
+ return {
8282
+ to: String(data.to),
8283
+ data: String(data.data),
8284
+ value: String(data.value ?? "0x0"),
8285
+ outAmount: String(data.outAmount ?? "0")
8286
+ };
8287
+ }
8288
+ var LIQD_API = "https://api.liqd.ag/v2";
8289
+ var LIQD_ROUTER = "0x744489ee3d540777a66f2cf297479745e0852f7a";
8290
+ async function liquidSwapRoute(tokenIn, tokenOut, amountIn, slippagePct) {
8291
+ const params = new URLSearchParams({ tokenIn, tokenOut, amountIn, slippage: slippagePct });
8292
+ const url = `${LIQD_API}/route?${params}`;
8293
+ const res = await fetch(url);
8294
+ if (!res.ok) throw new Error(`LiquidSwap route failed: ${res.status} ${await res.text()}`);
8295
+ const json = await res.json();
8296
+ const execution = json.execution;
8297
+ if (!execution) throw new Error("LiquidSwap: no execution data in response");
8298
+ const details = json.details;
8299
+ return {
8300
+ to: String(execution.to ?? LIQD_ROUTER),
8301
+ data: String(execution.calldata),
8302
+ value: String(execution.value ?? "0x0"),
8303
+ outAmount: String(details?.amountOut ?? json.amountOut ?? "0")
8304
+ };
8305
+ }
8306
+ function registerSwap(parent, getOpts, makeExecutor2) {
8307
+ parent.command("swap").description("Swap tokens via DEX aggregator (KyberSwap, OpenOcean, LiquidSwap)").requiredOption("--from <token>", "Input token symbol or address").requiredOption("--to <token>", "Output token symbol or address").requiredOption("--amount <amount>", "Amount of input token in wei").option("--provider <name>", "Aggregator: kyber, openocean, liquid", "kyber").option("--slippage <bps>", "Slippage tolerance in bps", "50").action(async (opts) => {
8308
+ const executor = makeExecutor2();
8309
+ const chainName = parent.opts().chain ?? "hyperevm";
8310
+ const registry = Registry.loadEmbedded();
8311
+ const provider = opts.provider.toLowerCase();
8312
+ const slippageBps = parseInt(opts.slippage, 10);
8313
+ const fromAddr = opts.from.startsWith("0x") ? opts.from : registry.resolveToken(chainName, opts.from).address;
8314
+ const toAddr = opts.to.startsWith("0x") ? opts.to : registry.resolveToken(chainName, opts.to).address;
8315
+ const wallet = process.env["DEFI_WALLET_ADDRESS"] ?? "0x0000000000000000000000000000000000000001";
8316
+ if (provider === "kyber") {
8317
+ const chainNames = CHAIN_NAMES[chainName];
8318
+ if (!chainNames?.kyber) {
8319
+ printOutput({ error: `KyberSwap: unsupported chain '${chainName}'. Supported: hyperevm` }, getOpts());
8320
+ return;
8321
+ }
8322
+ const kyberChain = chainNames.kyber;
8323
+ try {
8324
+ const quoteData = await kyberGetQuote(kyberChain, fromAddr, toAddr, opts.amount);
8325
+ const routeSummary = quoteData.routeSummary;
8326
+ const amountOut = String(routeSummary.amountOut ?? "0");
8327
+ const txData = await kyberBuildTx(
8328
+ kyberChain,
8329
+ routeSummary,
8330
+ wallet,
8331
+ wallet,
8332
+ slippageBps
8333
+ );
8334
+ const tx = {
8335
+ description: `KyberSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
8336
+ to: txData.to,
8337
+ data: txData.data,
8338
+ value: txData.value.startsWith("0x") ? BigInt(txData.value) : BigInt(txData.value || 0),
8339
+ approvals: [{ token: fromAddr, spender: txData.to, amount: BigInt(opts.amount) }]
8340
+ };
8341
+ const result = await executor.execute(tx);
8342
+ printOutput({
8343
+ provider: "kyber",
8344
+ chain: kyberChain,
8345
+ from_token: fromAddr,
8346
+ to_token: toAddr,
8347
+ amount_in: opts.amount,
8348
+ amount_out: amountOut,
8349
+ router: txData.to,
8350
+ ...result
8351
+ }, getOpts());
8352
+ } catch (e) {
8353
+ printOutput({ error: `KyberSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
8354
+ }
8355
+ return;
8356
+ }
8357
+ if (provider === "openocean") {
8358
+ const chainNames = CHAIN_NAMES[chainName];
8359
+ if (!chainNames) {
8360
+ printOutput({ error: `OpenOcean: unsupported chain '${chainName}'. Supported: ${Object.keys(CHAIN_NAMES).join(", ")}` }, getOpts());
8361
+ return;
8362
+ }
8363
+ const ooChain = chainNames.openocean;
8364
+ const fromToken = opts.from.startsWith("0x") ? registry.tokens.get(chainName)?.find((t) => t.address.toLowerCase() === opts.from.toLowerCase()) : registry.tokens.get(chainName)?.find((t) => t.symbol.toLowerCase() === opts.from.toLowerCase());
8365
+ const fromDecimals = fromToken?.decimals ?? 18;
8366
+ const humanAmount = (Number(opts.amount) / 10 ** fromDecimals).toString();
8367
+ const slippagePct = (slippageBps / 100).toFixed(2);
8368
+ try {
8369
+ const swap = await openoceanSwap(
8370
+ ooChain,
8371
+ fromAddr,
8372
+ toAddr,
8373
+ humanAmount,
8374
+ slippagePct,
8375
+ wallet
8376
+ );
8377
+ const tx = {
8378
+ description: `OpenOcean: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
8379
+ to: swap.to,
8380
+ data: swap.data,
8381
+ value: swap.value.startsWith("0x") ? BigInt(swap.value) : BigInt(swap.value || 0),
8382
+ approvals: [{ token: fromAddr, spender: swap.to, amount: BigInt(opts.amount) }]
8383
+ };
8384
+ const result = await executor.execute(tx);
8385
+ printOutput({
8386
+ provider: "openocean",
8387
+ chain: ooChain,
8388
+ from_token: fromAddr,
8389
+ to_token: toAddr,
8390
+ amount_in: opts.amount,
8391
+ amount_out: swap.outAmount,
8392
+ router: swap.to,
8393
+ ...result
8394
+ }, getOpts());
8395
+ } catch (e) {
8396
+ printOutput({ error: `OpenOcean error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
8397
+ }
8398
+ return;
8399
+ }
8400
+ if (provider === "liquid") {
8401
+ if (chainName !== "hyperevm") {
8402
+ printOutput({ error: `LiquidSwap only supports hyperevm, got '${chainName}'` }, getOpts());
8403
+ return;
8404
+ }
8405
+ const slippagePct = (slippageBps / 100).toFixed(2);
8406
+ try {
8407
+ const route = await liquidSwapRoute(fromAddr, toAddr, opts.amount, slippagePct);
8408
+ const tx = {
8409
+ description: `LiquidSwap: swap ${opts.amount} of ${fromAddr} -> ${toAddr}`,
8410
+ to: route.to,
8411
+ data: route.data,
8412
+ value: route.value.startsWith("0x") ? BigInt(route.value) : BigInt(route.value || 0),
8413
+ approvals: [{ token: fromAddr, spender: route.to, amount: BigInt(opts.amount) }]
8414
+ };
8415
+ const result = await executor.execute(tx);
8416
+ printOutput({
8417
+ provider: "liquid",
8418
+ chain: chainName,
8419
+ from_token: fromAddr,
8420
+ to_token: toAddr,
8421
+ amount_in: opts.amount,
8422
+ amount_out: route.outAmount,
8423
+ router: route.to,
8424
+ ...result
8425
+ }, getOpts());
8426
+ } catch (e) {
8427
+ printOutput({ error: `LiquidSwap error: ${e instanceof Error ? e.message : String(e)}` }, getOpts());
8428
+ }
8429
+ return;
8430
+ }
8431
+ printOutput({ error: `Unknown provider '${opts.provider}'. Choose: kyber, openocean, liquid` }, getOpts());
8432
+ });
8433
+ }
8434
+
9967
8435
  // src/commands/setup.ts
9968
8436
  import pc2 from "picocolors";
9969
8437
  import { createInterface } from "readline";
9970
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
9971
- import { resolve as resolve3 } from "path";
9972
- var DEFI_DIR = resolve3(process.env.HOME || "~", ".defi");
9973
- var ENV_FILE = resolve3(DEFI_DIR, ".env");
8438
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
8439
+ import { resolve as resolve4 } from "path";
8440
+ var DEFI_DIR = resolve4(process.env.HOME || "~", ".defi");
8441
+ var ENV_FILE = resolve4(DEFI_DIR, ".env");
9974
8442
  function ensureDefiDir() {
9975
8443
  if (!existsSync3(DEFI_DIR)) mkdirSync2(DEFI_DIR, { recursive: true, mode: 448 });
9976
8444
  }
9977
8445
  function loadEnvFile() {
9978
8446
  if (!existsSync3(ENV_FILE)) return {};
9979
- const lines = readFileSync3(ENV_FILE, "utf-8").split("\n");
8447
+ const lines = readFileSync4(ENV_FILE, "utf-8").split("\n");
9980
8448
  const env = {};
9981
8449
  for (const line of lines) {
9982
8450
  const trimmed = line.trim();
@@ -10121,8 +8589,8 @@ var BANNER = `
10121
8589
 
10122
8590
  2 chains \xB7 21 protocols \xB7 by HypurrQuant
10123
8591
 
10124
- Lending, LP provision, farming, gauges, vaults,
10125
- yield comparison \u2014 all from your terminal.
8592
+ Lending, LP farming, DEX swap, yield comparison
8593
+ \u2014 all from your terminal.
10126
8594
  `;
10127
8595
  var program = new Command().name("defi").description("DeFi CLI \u2014 Multi-chain DeFi toolkit").version(_pkg.version).addHelpText("before", BANNER).option("--json", "Output as JSON").option("--ndjson", "Output as newline-delimited JSON").option("--fields <fields>", "Select specific output fields (comma-separated)").option("--chain <chain>", "Target chain", "hyperevm").option("--dry-run", "Dry-run mode (default, no broadcast)", true).option("--broadcast", "Actually broadcast the transaction");
10128
8596
  function getOutputMode() {
@@ -10139,60 +8607,15 @@ registerStatus(program, getOutputMode);
10139
8607
  registerSchema(program, getOutputMode);
10140
8608
  registerLP(program, getOutputMode, makeExecutor);
10141
8609
  registerLending(program, getOutputMode, makeExecutor);
10142
- registerCdp(program, getOutputMode, makeExecutor);
10143
- registerVault(program, getOutputMode, makeExecutor);
10144
8610
  registerYield(program, getOutputMode, makeExecutor);
10145
8611
  registerPortfolio(program, getOutputMode);
10146
- registerMonitor(program, getOutputMode);
10147
- registerAlert(program, getOutputMode);
10148
- registerScan(program, getOutputMode);
10149
- registerPositions(program, getOutputMode);
10150
8612
  registerPrice(program, getOutputMode);
10151
8613
  registerWallet(program, getOutputMode);
10152
8614
  registerToken(program, getOutputMode, makeExecutor);
10153
- registerWhales(program, getOutputMode);
10154
8615
  registerBridge(program, getOutputMode);
8616
+ registerSwap(program, getOutputMode, makeExecutor);
10155
8617
  registerSetup(program);
10156
8618
  export {
10157
8619
  program
10158
8620
  };
10159
- /*! Bundled license information:
10160
-
10161
- smol-toml/dist/error.js:
10162
- smol-toml/dist/util.js:
10163
- smol-toml/dist/date.js:
10164
- smol-toml/dist/primitive.js:
10165
- smol-toml/dist/extract.js:
10166
- smol-toml/dist/struct.js:
10167
- smol-toml/dist/parse.js:
10168
- smol-toml/dist/stringify.js:
10169
- smol-toml/dist/index.js:
10170
- (*!
10171
- * Copyright (c) Squirrel Chat et al., All rights reserved.
10172
- * SPDX-License-Identifier: BSD-3-Clause
10173
- *
10174
- * Redistribution and use in source and binary forms, with or without
10175
- * modification, are permitted provided that the following conditions are met:
10176
- *
10177
- * 1. Redistributions of source code must retain the above copyright notice, this
10178
- * list of conditions and the following disclaimer.
10179
- * 2. Redistributions in binary form must reproduce the above copyright notice,
10180
- * this list of conditions and the following disclaimer in the
10181
- * documentation and/or other materials provided with the distribution.
10182
- * 3. Neither the name of the copyright holder nor the names of its contributors
10183
- * may be used to endorse or promote products derived from this software without
10184
- * specific prior written permission.
10185
- *
10186
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
10187
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10188
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
10189
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
10190
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
10191
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
10192
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
10193
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
10194
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
10195
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
10196
- *)
10197
- */
10198
8621
  //# sourceMappingURL=index.js.map