@spectratools/defillama-cli 0.2.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +101 -0
  2. package/dist/cli.js +575 -60
  3. package/dist/index.js +581 -60
  4. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -4,9 +4,9 @@
4
4
  import { readFileSync, realpathSync } from "fs";
5
5
  import { dirname, resolve } from "path";
6
6
  import { fileURLToPath } from "url";
7
- import { Cli as Cli2 } from "incur";
7
+ import { Cli as Cli5 } from "incur";
8
8
 
9
- // src/commands/tvl.ts
9
+ // src/commands/fees.ts
10
10
  import { Cli, z as z2 } from "incur";
11
11
 
12
12
  // src/api.ts
@@ -118,48 +118,403 @@ var protocolDetailSchema = z.object({
118
118
  var volumeEntrySchema = z.object({
119
119
  name: z.string(),
120
120
  displayName: z.string().optional(),
121
+ slug: z.string().optional(),
122
+ category: z.string().nullable().optional(),
123
+ chains: z.array(z.string()).optional(),
121
124
  total24h: z.number().nullable().optional(),
122
125
  total7d: z.number().nullable().optional(),
123
126
  total30d: z.number().nullable().optional(),
127
+ totalAllTime: z.number().nullable().optional(),
124
128
  change_1d: z.number().nullable().optional(),
125
129
  change_7d: z.number().nullable().optional(),
126
130
  change_1m: z.number().nullable().optional()
127
131
  });
132
+ var volumeOverviewResponseSchema = z.object({
133
+ protocols: z.array(volumeEntrySchema),
134
+ allChains: z.array(z.string()).optional(),
135
+ chain: z.string().nullable().optional(),
136
+ total24h: z.number().nullable().optional(),
137
+ total7d: z.number().nullable().optional(),
138
+ total30d: z.number().nullable().optional(),
139
+ change_1d: z.number().nullable().optional()
140
+ });
128
141
  var feeEntrySchema = z.object({
129
142
  name: z.string(),
130
143
  displayName: z.string().optional(),
144
+ slug: z.string().optional(),
145
+ category: z.string().nullable().optional(),
146
+ chains: z.array(z.string()).optional(),
131
147
  total24h: z.number().nullable().optional(),
132
148
  total7d: z.number().nullable().optional(),
133
149
  total30d: z.number().nullable().optional(),
134
150
  totalAllTime: z.number().nullable().optional(),
151
+ change_1d: z.number().nullable().optional(),
152
+ change_7d: z.number().nullable().optional(),
153
+ change_1m: z.number().nullable().optional()
154
+ });
155
+ var feeOverviewResponseSchema = z.object({
156
+ protocols: z.array(feeEntrySchema),
157
+ allChains: z.array(z.string()).optional(),
158
+ chain: z.string().nullable().optional(),
159
+ total24h: z.number().nullable().optional(),
160
+ total7d: z.number().nullable().optional(),
161
+ total30d: z.number().nullable().optional(),
135
162
  change_1d: z.number().nullable().optional()
136
163
  });
164
+ var summaryDetailSchema = z.object({
165
+ name: z.string(),
166
+ displayName: z.string().optional(),
167
+ slug: z.string().optional(),
168
+ category: z.string().nullable().optional(),
169
+ chains: z.array(z.string()).optional(),
170
+ total24h: z.number().nullable().optional(),
171
+ total48hto24h: z.number().nullable().optional(),
172
+ total7d: z.number().nullable().optional(),
173
+ total30d: z.number().nullable().optional(),
174
+ totalAllTime: z.number().nullable().optional(),
175
+ change_1d: z.number().nullable().optional(),
176
+ change_7d: z.number().nullable().optional(),
177
+ change_1m: z.number().nullable().optional()
178
+ });
179
+ var coinPriceSchema = z.object({
180
+ price: z.number(),
181
+ decimals: z.number().optional(),
182
+ symbol: z.string(),
183
+ timestamp: z.number(),
184
+ confidence: z.number().optional()
185
+ });
186
+ var pricesResponseSchema = z.object({
187
+ coins: z.record(z.string(), coinPriceSchema)
188
+ });
189
+ var coinChartSchema = z.object({
190
+ decimals: z.number().optional(),
191
+ symbol: z.string(),
192
+ prices: z.array(z.object({ timestamp: z.number(), price: z.number() })),
193
+ confidence: z.number().optional()
194
+ });
195
+ var chartResponseSchema = z.object({
196
+ coins: z.record(z.string(), coinChartSchema)
197
+ });
137
198
 
138
- // src/commands/tvl.ts
139
- var tvlCli = Cli.create("tvl", {
140
- description: "Total value locked queries."
199
+ // src/commands/fees.ts
200
+ var feesCli = Cli.create("fees", {
201
+ description: "Protocol fees and revenue queries."
141
202
  });
142
- var protocolSortFields = ["tvl", "change_1d", "change_7d"];
143
- tvlCli.command("protocols", {
144
- description: "List protocols ranked by TVL.",
203
+ var feesSortFields = ["total24h", "total7d", "change_1d"];
204
+ feesCli.command("overview", {
205
+ description: "List protocols ranked by fees and revenue.",
145
206
  options: z2.object({
146
- chain: z2.string().optional().describe("Filter protocols by chain name"),
207
+ chain: z2.string().optional().describe("Filter by chain name"),
147
208
  limit: z2.coerce.number().default(20).describe("Max protocols to display"),
148
- sort: z2.enum(protocolSortFields).default("tvl").describe("Sort field: tvl, change_1d, or change_7d")
209
+ sort: z2.enum(feesSortFields).default("total24h").describe("Sort field: total24h, total7d, or change_1d"),
210
+ category: z2.string().optional().describe("Filter by category (e.g. Dexs, Lending, Bridge)")
149
211
  }),
150
212
  output: z2.object({
151
213
  protocols: z2.array(
152
214
  z2.object({
153
215
  name: z2.string(),
154
- tvl: z2.string(),
155
- change_1d: z2.string(),
156
- change_7d: z2.string(),
157
- category: z2.string()
216
+ fees_24h: z2.string(),
217
+ fees_7d: z2.string(),
218
+ change_1d: z2.string()
158
219
  })
159
220
  ),
160
221
  chain: z2.string().optional(),
222
+ category: z2.string().optional(),
161
223
  total: z2.number()
162
224
  }),
225
+ examples: [
226
+ { options: { limit: 10 }, description: "Top 10 protocols by 24h fees" },
227
+ { options: { chain: "abstract", limit: 10 }, description: "Top protocols on Abstract by fees" },
228
+ { options: { sort: "change_1d", limit: 5 }, description: "Top 5 by 1-day fee change" },
229
+ {
230
+ options: { category: "Dexs", limit: 10 },
231
+ description: "Top 10 DEXes by fees"
232
+ }
233
+ ],
234
+ async run(c) {
235
+ const client = createDefiLlamaClient();
236
+ const path = c.options.chain ? `/overview/fees/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/fees?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
237
+ const raw = await client.get("api", path);
238
+ const data = feeOverviewResponseSchema.parse(raw);
239
+ let protocols = data.protocols;
240
+ if (c.options.category) {
241
+ const catLower = c.options.category.toLowerCase();
242
+ protocols = protocols.filter(
243
+ (p) => p.category != null && p.category.toLowerCase() === catLower
244
+ );
245
+ }
246
+ protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
247
+ const sortKey = c.options.sort;
248
+ protocols.sort((a, b) => {
249
+ const aVal = a[sortKey] ?? 0;
250
+ const bVal = b[sortKey] ?? 0;
251
+ return bVal - aVal;
252
+ });
253
+ const limited = protocols.slice(0, c.options.limit);
254
+ const rows = limited.map((p) => ({
255
+ name: p.displayName ?? p.name,
256
+ fees_24h: formatUsd(p.total24h ?? 0),
257
+ fees_7d: formatUsd(p.total7d ?? 0),
258
+ change_1d: formatPct(p.change_1d)
259
+ }));
260
+ return c.ok({
261
+ protocols: rows,
262
+ chain: c.options.chain,
263
+ category: c.options.category,
264
+ total: protocols.length
265
+ });
266
+ }
267
+ });
268
+ feesCli.command("protocol", {
269
+ description: "Get detailed fee and revenue data for a specific protocol.",
270
+ args: z2.object({
271
+ slug: z2.string().describe("Protocol slug (e.g. aave, lido)")
272
+ }),
273
+ output: z2.object({
274
+ name: z2.string(),
275
+ fees_24h: z2.string(),
276
+ fees_7d: z2.string(),
277
+ fees_30d: z2.string(),
278
+ fees_all_time: z2.string(),
279
+ change_1d: z2.string(),
280
+ change_7d: z2.string(),
281
+ change_1m: z2.string(),
282
+ chains: z2.array(z2.string())
283
+ }),
284
+ examples: [{ args: { slug: "aave" }, description: "Aave fee details" }],
285
+ async run(c) {
286
+ const client = createDefiLlamaClient();
287
+ const raw = await client.get("api", `/summary/fees/${c.args.slug}`);
288
+ const detail = summaryDetailSchema.parse(raw);
289
+ return c.ok({
290
+ name: detail.displayName ?? detail.name,
291
+ fees_24h: formatUsd(detail.total24h ?? 0),
292
+ fees_7d: formatUsd(detail.total7d ?? 0),
293
+ fees_30d: formatUsd(detail.total30d ?? 0),
294
+ fees_all_time: formatUsd(detail.totalAllTime ?? 0),
295
+ change_1d: formatPct(detail.change_1d),
296
+ change_7d: formatPct(detail.change_7d),
297
+ change_1m: formatPct(detail.change_1m),
298
+ chains: detail.chains ?? []
299
+ });
300
+ }
301
+ });
302
+
303
+ // src/commands/prices.ts
304
+ import { Cli as Cli2, z as z3 } from "incur";
305
+ var pricesCli = Cli2.create("prices", {
306
+ description: "Token price queries via coins.llama.fi."
307
+ });
308
+ var COIN_ID_RE = /^[a-zA-Z0-9_-]+:0x[a-fA-F0-9]{1,}$/;
309
+ function normalizeCoinArgs(raw) {
310
+ return Array.isArray(raw) ? raw : [raw];
311
+ }
312
+ function parseCoins(raw) {
313
+ const coins = [];
314
+ for (const arg of normalizeCoinArgs(raw)) {
315
+ for (const part of arg.split(",")) {
316
+ const trimmed = part.trim();
317
+ if (trimmed.length === 0) continue;
318
+ if (!COIN_ID_RE.test(trimmed)) {
319
+ throw new Error(
320
+ `Invalid coin identifier "${trimmed}". Expected format: chainName:0xAddress (e.g. ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7)`
321
+ );
322
+ }
323
+ coins.push(trimmed);
324
+ }
325
+ }
326
+ if (coins.length === 0) {
327
+ throw new Error("At least one coin identifier is required.");
328
+ }
329
+ return [...new Set(coins)].join(",");
330
+ }
331
+ function parseTimestamp(value) {
332
+ if (/^\d+$/.test(value)) {
333
+ return Number(value);
334
+ }
335
+ const ms = Date.parse(value);
336
+ if (Number.isNaN(ms)) {
337
+ throw new Error(
338
+ `Invalid timestamp "${value}". Provide a Unix timestamp in seconds or an ISO date string (e.g. 2025-01-01 or 2025-01-01T00:00:00Z).`
339
+ );
340
+ }
341
+ return Math.floor(ms / 1e3);
342
+ }
343
+ var coinsArgsSchema = z3.string().describe("Coin identifiers (chainName:0xAddress)");
344
+ pricesCli.command("current", {
345
+ description: "Get current prices for one or more tokens.",
346
+ args: z3.object({
347
+ coins: coinsArgsSchema
348
+ }),
349
+ options: z3.object({
350
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
351
+ }),
352
+ output: z3.object({
353
+ prices: z3.array(
354
+ z3.object({
355
+ coin: z3.string(),
356
+ symbol: z3.string(),
357
+ price: z3.string(),
358
+ confidence: z3.number().optional(),
359
+ timestamp: z3.string()
360
+ })
361
+ )
362
+ }),
363
+ examples: [
364
+ {
365
+ args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
366
+ description: "Current price of USDT on Ethereum"
367
+ }
368
+ ],
369
+ async run(c) {
370
+ const coinsPath = parseCoins(c.args.coins);
371
+ const client = createDefiLlamaClient();
372
+ const searchWidth = c.options["search-width"];
373
+ const path = `/prices/current/${coinsPath}?searchWidth=${searchWidth}`;
374
+ const raw = await client.get("coins", path);
375
+ const data = pricesResponseSchema.parse(raw);
376
+ const prices = Object.entries(data.coins).map(([coin, info]) => ({
377
+ coin,
378
+ symbol: info.symbol,
379
+ price: formatUsd(info.price),
380
+ confidence: info.confidence,
381
+ timestamp: new Date(info.timestamp * 1e3).toISOString()
382
+ }));
383
+ return c.ok({ prices });
384
+ }
385
+ });
386
+ pricesCli.command("historical", {
387
+ description: "Get token prices at a specific point in time.",
388
+ args: z3.object({
389
+ coins: coinsArgsSchema
390
+ }),
391
+ options: z3.object({
392
+ timestamp: z3.string().optional().describe("Unix timestamp in seconds"),
393
+ date: z3.string().optional().describe("ISO date string (e.g. 2025-01-01)"),
394
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
395
+ }),
396
+ output: z3.object({
397
+ prices: z3.array(
398
+ z3.object({
399
+ coin: z3.string(),
400
+ symbol: z3.string(),
401
+ price: z3.string(),
402
+ timestamp: z3.string()
403
+ })
404
+ )
405
+ }),
406
+ examples: [
407
+ {
408
+ args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
409
+ options: { date: "2025-01-01" },
410
+ description: "USDT price on 2025-01-01"
411
+ }
412
+ ],
413
+ async run(c) {
414
+ const tsRaw = c.options.timestamp ?? c.options.date;
415
+ if (!tsRaw) {
416
+ throw new Error("Either --timestamp or --date is required for historical prices.");
417
+ }
418
+ const ts = parseTimestamp(tsRaw);
419
+ const coinsPath = parseCoins(c.args.coins);
420
+ const client = createDefiLlamaClient();
421
+ const searchWidth = c.options["search-width"];
422
+ const path = `/prices/historical/${ts}/${coinsPath}?searchWidth=${searchWidth}`;
423
+ const raw = await client.get("coins", path);
424
+ const data = pricesResponseSchema.parse(raw);
425
+ const prices = Object.entries(data.coins).map(([coin, info]) => ({
426
+ coin,
427
+ symbol: info.symbol,
428
+ price: formatUsd(info.price),
429
+ timestamp: new Date(info.timestamp * 1e3).toISOString()
430
+ }));
431
+ return c.ok({ prices });
432
+ }
433
+ });
434
+ pricesCli.command("chart", {
435
+ description: "Get a price chart over a time range.",
436
+ args: z3.object({
437
+ coins: coinsArgsSchema
438
+ }),
439
+ options: z3.object({
440
+ start: z3.string().optional().describe("Start timestamp or ISO date"),
441
+ end: z3.string().optional().describe("End timestamp or ISO date (default: now)"),
442
+ span: z3.coerce.number().optional().describe("Number of data points"),
443
+ period: z3.string().optional().describe("Data point period (e.g. 1d, 1h, 4h)"),
444
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 600)")
445
+ }),
446
+ output: z3.object({
447
+ charts: z3.array(
448
+ z3.object({
449
+ coin: z3.string(),
450
+ symbol: z3.string(),
451
+ prices: z3.array(
452
+ z3.object({
453
+ date: z3.string(),
454
+ price: z3.string()
455
+ })
456
+ )
457
+ })
458
+ )
459
+ }),
460
+ examples: [
461
+ {
462
+ args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
463
+ options: { start: "2025-01-01", period: "1d" },
464
+ description: "USDT daily price chart since 2025-01-01"
465
+ }
466
+ ],
467
+ async run(c) {
468
+ const coinsPath = parseCoins(c.args.coins);
469
+ const client = createDefiLlamaClient();
470
+ const params = new URLSearchParams();
471
+ if (c.options.start) params.set("start", String(parseTimestamp(c.options.start)));
472
+ if (c.options.end) params.set("end", String(parseTimestamp(c.options.end)));
473
+ if (c.options.span) params.set("span", String(c.options.span));
474
+ if (c.options.period) params.set("period", c.options.period);
475
+ params.set("searchWidth", c.options["search-width"]);
476
+ const qs = params.toString();
477
+ const path = `/chart/${coinsPath}${qs ? `?${qs}` : ""}`;
478
+ const raw = await client.get("coins", path);
479
+ const data = chartResponseSchema.parse(raw);
480
+ const charts = Object.entries(data.coins).map(([coin, info]) => ({
481
+ coin,
482
+ symbol: info.symbol,
483
+ prices: info.prices.map((p) => ({
484
+ date: new Date(p.timestamp * 1e3).toISOString(),
485
+ price: formatUsd(p.price)
486
+ }))
487
+ }));
488
+ return c.ok({ charts });
489
+ }
490
+ });
491
+
492
+ // src/commands/tvl.ts
493
+ import { Cli as Cli3, z as z4 } from "incur";
494
+ var tvlCli = Cli3.create("tvl", {
495
+ description: "Total value locked queries."
496
+ });
497
+ var protocolSortFields = ["tvl", "change_1d", "change_7d"];
498
+ tvlCli.command("protocols", {
499
+ description: "List protocols ranked by TVL.",
500
+ options: z4.object({
501
+ chain: z4.string().optional().describe("Filter protocols by chain name"),
502
+ limit: z4.coerce.number().default(20).describe("Max protocols to display"),
503
+ sort: z4.enum(protocolSortFields).default("tvl").describe("Sort field: tvl, change_1d, or change_7d")
504
+ }),
505
+ output: z4.object({
506
+ protocols: z4.array(
507
+ z4.object({
508
+ name: z4.string(),
509
+ tvl: z4.string(),
510
+ change_1d: z4.string(),
511
+ change_7d: z4.string(),
512
+ category: z4.string()
513
+ })
514
+ ),
515
+ chain: z4.string().optional(),
516
+ total: z4.number()
517
+ }),
163
518
  examples: [
164
519
  { options: { limit: 10 }, description: "Top 10 protocols by TVL" },
165
520
  { options: { chain: "abstract", limit: 10 }, description: "Top protocols on Abstract" },
@@ -200,17 +555,17 @@ tvlCli.command("protocols", {
200
555
  });
201
556
  tvlCli.command("chains", {
202
557
  description: "List chains ranked by TVL.",
203
- options: z2.object({
204
- limit: z2.coerce.number().default(20).describe("Max chains to display")
558
+ options: z4.object({
559
+ limit: z4.coerce.number().default(20).describe("Max chains to display")
205
560
  }),
206
- output: z2.object({
207
- chains: z2.array(
208
- z2.object({
209
- name: z2.string(),
210
- tvl: z2.string()
561
+ output: z4.object({
562
+ chains: z4.array(
563
+ z4.object({
564
+ name: z4.string(),
565
+ tvl: z4.string()
211
566
  })
212
567
  ),
213
- total: z2.number()
568
+ total: z4.number()
214
569
  }),
215
570
  examples: [{ options: { limit: 10 }, description: "Top 10 chains by TVL" }],
216
571
  async run(c) {
@@ -231,20 +586,20 @@ tvlCli.command("chains", {
231
586
  });
232
587
  tvlCli.command("protocol", {
233
588
  description: "Get detailed protocol info with TVL breakdown by chain.",
234
- args: z2.object({
235
- slug: z2.string().describe("Protocol slug (e.g. aave, uniswap)")
589
+ args: z4.object({
590
+ slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
236
591
  }),
237
- output: z2.object({
238
- name: z2.string(),
239
- description: z2.string(),
240
- category: z2.string(),
241
- url: z2.string(),
242
- symbol: z2.string(),
243
- tvl: z2.string(),
244
- chains: z2.array(
245
- z2.object({
246
- chain: z2.string(),
247
- tvl: z2.string()
592
+ output: z4.object({
593
+ name: z4.string(),
594
+ description: z4.string(),
595
+ category: z4.string(),
596
+ url: z4.string(),
597
+ symbol: z4.string(),
598
+ tvl: z4.string(),
599
+ chains: z4.array(
600
+ z4.object({
601
+ chain: z4.string(),
602
+ tvl: z4.string()
248
603
  })
249
604
  )
250
605
  }),
@@ -287,21 +642,21 @@ tvlCli.command("protocol", {
287
642
  });
288
643
  tvlCli.command("history", {
289
644
  description: "Show historical TVL for a protocol.",
290
- args: z2.object({
291
- slug: z2.string().describe("Protocol slug (e.g. aave, uniswap)")
645
+ args: z4.object({
646
+ slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
292
647
  }),
293
- options: z2.object({
294
- days: z2.coerce.number().default(30).describe("Number of days of history to display"),
295
- chain: z2.string().optional().describe("Filter to a specific chain")
648
+ options: z4.object({
649
+ days: z4.coerce.number().default(30).describe("Number of days of history to display"),
650
+ chain: z4.string().optional().describe("Filter to a specific chain")
296
651
  }),
297
- output: z2.object({
298
- protocol: z2.string(),
299
- chain: z2.string().optional(),
300
- days: z2.number(),
301
- history: z2.array(
302
- z2.object({
303
- date: z2.string(),
304
- tvl: z2.string()
652
+ output: z4.object({
653
+ protocol: z4.string(),
654
+ chain: z4.string().optional(),
655
+ days: z4.number(),
656
+ history: z4.array(
657
+ z4.object({
658
+ date: z4.string(),
659
+ tvl: z4.string()
305
660
  })
306
661
  )
307
662
  }),
@@ -347,26 +702,185 @@ tvlCli.command("history", {
347
702
  }
348
703
  });
349
704
 
705
+ // src/commands/volume.ts
706
+ import { Cli as Cli4, z as z5 } from "incur";
707
+ var volumeCli = Cli4.create("volume", {
708
+ description: "DEX volume queries."
709
+ });
710
+ var dexsSortFields = ["total24h", "total7d", "change_1d"];
711
+ volumeCli.command("dexs", {
712
+ description: "List DEXes ranked by trading volume.",
713
+ options: z5.object({
714
+ chain: z5.string().optional().describe("Filter by chain name"),
715
+ limit: z5.coerce.number().default(20).describe("Max protocols to display"),
716
+ sort: z5.enum(dexsSortFields).default("total24h").describe("Sort field: total24h, total7d, or change_1d"),
717
+ category: z5.string().optional().describe("Filter by category (e.g. Dexs, Prediction)")
718
+ }),
719
+ output: z5.object({
720
+ protocols: z5.array(
721
+ z5.object({
722
+ name: z5.string(),
723
+ volume_24h: z5.string(),
724
+ volume_7d: z5.string(),
725
+ change_1d: z5.string()
726
+ })
727
+ ),
728
+ chain: z5.string().optional(),
729
+ category: z5.string().optional(),
730
+ total: z5.number()
731
+ }),
732
+ examples: [
733
+ { options: { limit: 10 }, description: "Top 10 DEXes by 24h volume" },
734
+ { options: { chain: "abstract", limit: 10 }, description: "Top DEXes on Abstract" },
735
+ { options: { sort: "change_1d", limit: 5 }, description: "Top 5 by 1-day volume change" },
736
+ {
737
+ options: { category: "Dexs", limit: 10 },
738
+ description: "Top 10 DEXes excluding prediction markets"
739
+ }
740
+ ],
741
+ async run(c) {
742
+ const client = createDefiLlamaClient();
743
+ const path = c.options.chain ? `/overview/dexs/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
744
+ const raw = await client.get("api", path);
745
+ const data = volumeOverviewResponseSchema.parse(raw);
746
+ let protocols = data.protocols;
747
+ if (c.options.category) {
748
+ const catLower = c.options.category.toLowerCase();
749
+ protocols = protocols.filter(
750
+ (p) => p.category != null && p.category.toLowerCase() === catLower
751
+ );
752
+ }
753
+ protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
754
+ const sortKey = c.options.sort;
755
+ protocols.sort((a, b) => {
756
+ const aVal = a[sortKey] ?? 0;
757
+ const bVal = b[sortKey] ?? 0;
758
+ return bVal - aVal;
759
+ });
760
+ const limited = protocols.slice(0, c.options.limit);
761
+ const rows = limited.map((p) => ({
762
+ name: p.displayName ?? p.name,
763
+ volume_24h: formatUsd(p.total24h ?? 0),
764
+ volume_7d: formatUsd(p.total7d ?? 0),
765
+ change_1d: formatPct(p.change_1d)
766
+ }));
767
+ return c.ok({
768
+ protocols: rows,
769
+ chain: c.options.chain,
770
+ category: c.options.category,
771
+ total: protocols.length
772
+ });
773
+ }
774
+ });
775
+ volumeCli.command("protocol", {
776
+ description: "Get detailed volume data for a specific protocol.",
777
+ args: z5.object({
778
+ slug: z5.string().describe("Protocol slug (e.g. uniswap, curve-dex)")
779
+ }),
780
+ output: z5.object({
781
+ name: z5.string(),
782
+ volume_24h: z5.string(),
783
+ volume_7d: z5.string(),
784
+ volume_30d: z5.string(),
785
+ volume_all_time: z5.string(),
786
+ change_1d: z5.string(),
787
+ change_7d: z5.string(),
788
+ change_1m: z5.string(),
789
+ chains: z5.array(z5.string())
790
+ }),
791
+ examples: [{ args: { slug: "uniswap" }, description: "Uniswap volume details" }],
792
+ async run(c) {
793
+ const client = createDefiLlamaClient();
794
+ const raw = await client.get("api", `/summary/dexs/${c.args.slug}`);
795
+ const detail = summaryDetailSchema.parse(raw);
796
+ return c.ok({
797
+ name: detail.displayName ?? detail.name,
798
+ volume_24h: formatUsd(detail.total24h ?? 0),
799
+ volume_7d: formatUsd(detail.total7d ?? 0),
800
+ volume_30d: formatUsd(detail.total30d ?? 0),
801
+ volume_all_time: formatUsd(detail.totalAllTime ?? 0),
802
+ change_1d: formatPct(detail.change_1d),
803
+ change_7d: formatPct(detail.change_7d),
804
+ change_1m: formatPct(detail.change_1m),
805
+ chains: detail.chains ?? []
806
+ });
807
+ }
808
+ });
809
+ volumeCli.command("aggregators", {
810
+ description: "List DEX aggregators ranked by trading volume.",
811
+ options: z5.object({
812
+ chain: z5.string().optional().describe("Filter by chain name"),
813
+ limit: z5.coerce.number().default(20).describe("Max aggregators to display")
814
+ }),
815
+ output: z5.object({
816
+ protocols: z5.array(
817
+ z5.object({
818
+ name: z5.string(),
819
+ volume_24h: z5.string(),
820
+ volume_7d: z5.string(),
821
+ change_1d: z5.string()
822
+ })
823
+ ),
824
+ chain: z5.string().optional(),
825
+ total: z5.number()
826
+ }),
827
+ examples: [
828
+ { options: { limit: 10 }, description: "Top 10 DEX aggregators by volume" },
829
+ {
830
+ options: { chain: "ethereum", limit: 5 },
831
+ description: "Top 5 aggregators on Ethereum"
832
+ }
833
+ ],
834
+ async run(c) {
835
+ const client = createDefiLlamaClient();
836
+ const path = c.options.chain ? `/overview/aggregators/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/aggregators?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
837
+ const raw = await client.get("api", path);
838
+ const data = volumeOverviewResponseSchema.parse(raw);
839
+ let protocols = data.protocols;
840
+ protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
841
+ protocols.sort((a, b) => (b.total24h ?? 0) - (a.total24h ?? 0));
842
+ const limited = protocols.slice(0, c.options.limit);
843
+ const rows = limited.map((p) => ({
844
+ name: p.displayName ?? p.name,
845
+ volume_24h: formatUsd(p.total24h ?? 0),
846
+ volume_7d: formatUsd(p.total7d ?? 0),
847
+ change_1d: formatPct(p.change_1d)
848
+ }));
849
+ return c.ok({
850
+ protocols: rows,
851
+ chain: c.options.chain,
852
+ total: protocols.length
853
+ });
854
+ }
855
+ });
856
+
350
857
  // src/cli.ts
351
858
  var __dirname = dirname(fileURLToPath(import.meta.url));
352
859
  var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
353
- var cli = Cli2.create("defillama", {
860
+ var cli = Cli5.create("defillama", {
354
861
  version: pkg.version,
355
862
  description: "Query DefiLlama API data from the command line."
356
863
  });
357
864
  cli.command(tvlCli);
358
- var volumeCli = Cli2.create("volume", {
359
- description: "DEX volume queries."
360
- });
361
865
  cli.command(volumeCli);
362
- var feesCli = Cli2.create("fees", {
363
- description: "Protocol fees and revenue queries."
364
- });
365
866
  cli.command(feesCli);
366
- var pricesCli = Cli2.create("prices", {
367
- description: "Token price queries."
368
- });
369
867
  cli.command(pricesCli);
868
+ var PRICE_SUBCOMMANDS = /* @__PURE__ */ new Set(["current", "historical", "chart"]);
869
+ function normalizePricesArgv(argv) {
870
+ if (argv[0] !== "prices") return argv;
871
+ const subcommand = argv[1];
872
+ if (!subcommand || !PRICE_SUBCOMMANDS.has(subcommand)) return argv;
873
+ let i = 2;
874
+ const coins = [];
875
+ while (i < argv.length) {
876
+ const token = argv[i];
877
+ if (!token || token.startsWith("-")) break;
878
+ coins.push(token);
879
+ i += 1;
880
+ }
881
+ if (coins.length <= 1) return argv;
882
+ return [...argv.slice(0, 2), coins.join(","), ...argv.slice(i)];
883
+ }
370
884
  var isMain = (() => {
371
885
  const entrypoint = process.argv[1];
372
886
  if (!entrypoint) {
@@ -379,8 +893,9 @@ var isMain = (() => {
379
893
  }
380
894
  })();
381
895
  if (isMain) {
382
- cli.serve();
896
+ cli.serve(normalizePricesArgv(process.argv.slice(2)));
383
897
  }
384
898
  export {
385
- cli
899
+ cli,
900
+ normalizePricesArgv
386
901
  };