@spectratools/defillama-cli 0.2.0 → 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.
Files changed (3) hide show
  1. package/dist/cli.js +552 -58
  2. package/dist/index.js +560 -59
  3. 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,399 @@ 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 parseCoins(raw) {
310
+ const coins = [];
311
+ for (const arg of raw) {
312
+ for (const part of arg.split(",")) {
313
+ const trimmed = part.trim();
314
+ if (trimmed.length === 0) continue;
315
+ if (!COIN_ID_RE.test(trimmed)) {
316
+ throw new Error(
317
+ `Invalid coin identifier "${trimmed}". Expected format: chainName:0xAddress (e.g. ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7)`
318
+ );
319
+ }
320
+ coins.push(trimmed);
321
+ }
322
+ }
323
+ if (coins.length === 0) {
324
+ throw new Error("At least one coin identifier is required.");
325
+ }
326
+ return [...new Set(coins)].join(",");
327
+ }
328
+ function parseTimestamp(value) {
329
+ if (/^\d+$/.test(value)) {
330
+ return Number(value);
331
+ }
332
+ const ms = Date.parse(value);
333
+ if (Number.isNaN(ms)) {
334
+ throw new Error(
335
+ `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).`
336
+ );
337
+ }
338
+ return Math.floor(ms / 1e3);
339
+ }
340
+ pricesCli.command("current", {
341
+ description: "Get current prices for one or more tokens.",
342
+ args: z3.object({
343
+ coins: z3.array(z3.string()).describe("Coin identifiers (chainName:0xAddress)")
344
+ }),
345
+ options: z3.object({
346
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
347
+ }),
348
+ output: z3.object({
349
+ prices: z3.array(
350
+ z3.object({
351
+ coin: z3.string(),
352
+ symbol: z3.string(),
353
+ price: z3.string(),
354
+ confidence: z3.number().optional(),
355
+ timestamp: z3.string()
356
+ })
357
+ )
358
+ }),
359
+ examples: [
360
+ {
361
+ args: { coins: ["ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7"] },
362
+ description: "Current price of USDT on Ethereum"
363
+ }
364
+ ],
365
+ async run(c) {
366
+ const coinsPath = parseCoins(c.args.coins);
367
+ const client = createDefiLlamaClient();
368
+ const searchWidth = c.options["search-width"];
369
+ const path = `/prices/current/${coinsPath}?searchWidth=${searchWidth}`;
370
+ const raw = await client.get("coins", path);
371
+ const data = pricesResponseSchema.parse(raw);
372
+ const prices = Object.entries(data.coins).map(([coin, info]) => ({
373
+ coin,
374
+ symbol: info.symbol,
375
+ price: formatUsd(info.price),
376
+ confidence: info.confidence,
377
+ timestamp: new Date(info.timestamp * 1e3).toISOString()
378
+ }));
379
+ return c.ok({ prices });
380
+ }
381
+ });
382
+ pricesCli.command("historical", {
383
+ description: "Get token prices at a specific point in time.",
384
+ args: z3.object({
385
+ coins: z3.array(z3.string()).describe("Coin identifiers (chainName:0xAddress)")
386
+ }),
387
+ options: z3.object({
388
+ timestamp: z3.string().optional().describe("Unix timestamp in seconds"),
389
+ date: z3.string().optional().describe("ISO date string (e.g. 2025-01-01)"),
390
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
391
+ }),
392
+ output: z3.object({
393
+ prices: z3.array(
394
+ z3.object({
395
+ coin: z3.string(),
396
+ symbol: z3.string(),
397
+ price: z3.string(),
398
+ timestamp: z3.string()
399
+ })
400
+ )
401
+ }),
402
+ examples: [
403
+ {
404
+ args: { coins: ["ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7"] },
405
+ options: { date: "2025-01-01" },
406
+ description: "USDT price on 2025-01-01"
407
+ }
408
+ ],
409
+ async run(c) {
410
+ const tsRaw = c.options.timestamp ?? c.options.date;
411
+ if (!tsRaw) {
412
+ throw new Error("Either --timestamp or --date is required for historical prices.");
413
+ }
414
+ const ts = parseTimestamp(tsRaw);
415
+ const coinsPath = parseCoins(c.args.coins);
416
+ const client = createDefiLlamaClient();
417
+ const searchWidth = c.options["search-width"];
418
+ const path = `/prices/historical/${ts}/${coinsPath}?searchWidth=${searchWidth}`;
419
+ const raw = await client.get("coins", path);
420
+ const data = pricesResponseSchema.parse(raw);
421
+ const prices = Object.entries(data.coins).map(([coin, info]) => ({
422
+ coin,
423
+ symbol: info.symbol,
424
+ price: formatUsd(info.price),
425
+ timestamp: new Date(info.timestamp * 1e3).toISOString()
426
+ }));
427
+ return c.ok({ prices });
428
+ }
429
+ });
430
+ pricesCli.command("chart", {
431
+ description: "Get a price chart over a time range.",
432
+ args: z3.object({
433
+ coins: z3.array(z3.string()).describe("Coin identifiers (chainName:0xAddress)")
434
+ }),
435
+ options: z3.object({
436
+ start: z3.string().optional().describe("Start timestamp or ISO date"),
437
+ end: z3.string().optional().describe("End timestamp or ISO date (default: now)"),
438
+ span: z3.coerce.number().optional().describe("Number of data points"),
439
+ period: z3.string().optional().describe("Data point period (e.g. 1d, 1h, 4h)"),
440
+ "search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 600)")
441
+ }),
442
+ output: z3.object({
443
+ charts: z3.array(
444
+ z3.object({
445
+ coin: z3.string(),
446
+ symbol: z3.string(),
447
+ prices: z3.array(
448
+ z3.object({
449
+ date: z3.string(),
450
+ price: z3.string()
451
+ })
452
+ )
453
+ })
454
+ )
455
+ }),
456
+ examples: [
457
+ {
458
+ args: { coins: ["ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7"] },
459
+ options: { start: "2025-01-01", period: "1d" },
460
+ description: "USDT daily price chart since 2025-01-01"
461
+ }
462
+ ],
463
+ async run(c) {
464
+ const coinsPath = parseCoins(c.args.coins);
465
+ const client = createDefiLlamaClient();
466
+ const params = new URLSearchParams();
467
+ if (c.options.start) params.set("start", String(parseTimestamp(c.options.start)));
468
+ if (c.options.end) params.set("end", String(parseTimestamp(c.options.end)));
469
+ if (c.options.span) params.set("span", String(c.options.span));
470
+ if (c.options.period) params.set("period", c.options.period);
471
+ params.set("searchWidth", c.options["search-width"]);
472
+ const qs = params.toString();
473
+ const path = `/chart/${coinsPath}${qs ? `?${qs}` : ""}`;
474
+ const raw = await client.get("coins", path);
475
+ const data = chartResponseSchema.parse(raw);
476
+ const charts = Object.entries(data.coins).map(([coin, info]) => ({
477
+ coin,
478
+ symbol: info.symbol,
479
+ prices: info.prices.map((p) => ({
480
+ date: new Date(p.timestamp * 1e3).toISOString(),
481
+ price: formatUsd(p.price)
482
+ }))
483
+ }));
484
+ return c.ok({ charts });
485
+ }
486
+ });
487
+
488
+ // src/commands/tvl.ts
489
+ import { Cli as Cli3, z as z4 } from "incur";
490
+ var tvlCli = Cli3.create("tvl", {
491
+ description: "Total value locked queries."
492
+ });
493
+ var protocolSortFields = ["tvl", "change_1d", "change_7d"];
494
+ tvlCli.command("protocols", {
495
+ description: "List protocols ranked by TVL.",
496
+ options: z4.object({
497
+ chain: z4.string().optional().describe("Filter protocols by chain name"),
498
+ limit: z4.coerce.number().default(20).describe("Max protocols to display"),
499
+ sort: z4.enum(protocolSortFields).default("tvl").describe("Sort field: tvl, change_1d, or change_7d")
500
+ }),
501
+ output: z4.object({
502
+ protocols: z4.array(
503
+ z4.object({
504
+ name: z4.string(),
505
+ tvl: z4.string(),
506
+ change_1d: z4.string(),
507
+ change_7d: z4.string(),
508
+ category: z4.string()
509
+ })
510
+ ),
511
+ chain: z4.string().optional(),
512
+ total: z4.number()
513
+ }),
163
514
  examples: [
164
515
  { options: { limit: 10 }, description: "Top 10 protocols by TVL" },
165
516
  { options: { chain: "abstract", limit: 10 }, description: "Top protocols on Abstract" },
@@ -200,17 +551,17 @@ tvlCli.command("protocols", {
200
551
  });
201
552
  tvlCli.command("chains", {
202
553
  description: "List chains ranked by TVL.",
203
- options: z2.object({
204
- limit: z2.coerce.number().default(20).describe("Max chains to display")
554
+ options: z4.object({
555
+ limit: z4.coerce.number().default(20).describe("Max chains to display")
205
556
  }),
206
- output: z2.object({
207
- chains: z2.array(
208
- z2.object({
209
- name: z2.string(),
210
- tvl: z2.string()
557
+ output: z4.object({
558
+ chains: z4.array(
559
+ z4.object({
560
+ name: z4.string(),
561
+ tvl: z4.string()
211
562
  })
212
563
  ),
213
- total: z2.number()
564
+ total: z4.number()
214
565
  }),
215
566
  examples: [{ options: { limit: 10 }, description: "Top 10 chains by TVL" }],
216
567
  async run(c) {
@@ -231,20 +582,20 @@ tvlCli.command("chains", {
231
582
  });
232
583
  tvlCli.command("protocol", {
233
584
  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)")
585
+ args: z4.object({
586
+ slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
236
587
  }),
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()
588
+ output: z4.object({
589
+ name: z4.string(),
590
+ description: z4.string(),
591
+ category: z4.string(),
592
+ url: z4.string(),
593
+ symbol: z4.string(),
594
+ tvl: z4.string(),
595
+ chains: z4.array(
596
+ z4.object({
597
+ chain: z4.string(),
598
+ tvl: z4.string()
248
599
  })
249
600
  )
250
601
  }),
@@ -287,21 +638,21 @@ tvlCli.command("protocol", {
287
638
  });
288
639
  tvlCli.command("history", {
289
640
  description: "Show historical TVL for a protocol.",
290
- args: z2.object({
291
- slug: z2.string().describe("Protocol slug (e.g. aave, uniswap)")
641
+ args: z4.object({
642
+ slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
292
643
  }),
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")
644
+ options: z4.object({
645
+ days: z4.coerce.number().default(30).describe("Number of days of history to display"),
646
+ chain: z4.string().optional().describe("Filter to a specific chain")
296
647
  }),
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()
648
+ output: z4.object({
649
+ protocol: z4.string(),
650
+ chain: z4.string().optional(),
651
+ days: z4.number(),
652
+ history: z4.array(
653
+ z4.object({
654
+ date: z4.string(),
655
+ tvl: z4.string()
305
656
  })
306
657
  )
307
658
  }),
@@ -347,25 +698,168 @@ tvlCli.command("history", {
347
698
  }
348
699
  });
349
700
 
701
+ // src/commands/volume.ts
702
+ import { Cli as Cli4, z as z5 } from "incur";
703
+ var volumeCli = Cli4.create("volume", {
704
+ description: "DEX volume queries."
705
+ });
706
+ var dexsSortFields = ["total24h", "total7d", "change_1d"];
707
+ volumeCli.command("dexs", {
708
+ description: "List DEXes ranked by trading volume.",
709
+ options: z5.object({
710
+ chain: z5.string().optional().describe("Filter by chain name"),
711
+ limit: z5.coerce.number().default(20).describe("Max protocols to display"),
712
+ sort: z5.enum(dexsSortFields).default("total24h").describe("Sort field: total24h, total7d, or change_1d"),
713
+ category: z5.string().optional().describe("Filter by category (e.g. Dexs, Prediction)")
714
+ }),
715
+ output: z5.object({
716
+ protocols: z5.array(
717
+ z5.object({
718
+ name: z5.string(),
719
+ volume_24h: z5.string(),
720
+ volume_7d: z5.string(),
721
+ change_1d: z5.string()
722
+ })
723
+ ),
724
+ chain: z5.string().optional(),
725
+ category: z5.string().optional(),
726
+ total: z5.number()
727
+ }),
728
+ examples: [
729
+ { options: { limit: 10 }, description: "Top 10 DEXes by 24h volume" },
730
+ { options: { chain: "abstract", limit: 10 }, description: "Top DEXes on Abstract" },
731
+ { options: { sort: "change_1d", limit: 5 }, description: "Top 5 by 1-day volume change" },
732
+ {
733
+ options: { category: "Dexs", limit: 10 },
734
+ description: "Top 10 DEXes excluding prediction markets"
735
+ }
736
+ ],
737
+ async run(c) {
738
+ const client = createDefiLlamaClient();
739
+ const path = c.options.chain ? `/overview/dexs/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
740
+ const raw = await client.get("api", path);
741
+ const data = volumeOverviewResponseSchema.parse(raw);
742
+ let protocols = data.protocols;
743
+ if (c.options.category) {
744
+ const catLower = c.options.category.toLowerCase();
745
+ protocols = protocols.filter(
746
+ (p) => p.category != null && p.category.toLowerCase() === catLower
747
+ );
748
+ }
749
+ protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
750
+ const sortKey = c.options.sort;
751
+ protocols.sort((a, b) => {
752
+ const aVal = a[sortKey] ?? 0;
753
+ const bVal = b[sortKey] ?? 0;
754
+ return bVal - aVal;
755
+ });
756
+ const limited = protocols.slice(0, c.options.limit);
757
+ const rows = limited.map((p) => ({
758
+ name: p.displayName ?? p.name,
759
+ volume_24h: formatUsd(p.total24h ?? 0),
760
+ volume_7d: formatUsd(p.total7d ?? 0),
761
+ change_1d: formatPct(p.change_1d)
762
+ }));
763
+ return c.ok({
764
+ protocols: rows,
765
+ chain: c.options.chain,
766
+ category: c.options.category,
767
+ total: protocols.length
768
+ });
769
+ }
770
+ });
771
+ volumeCli.command("protocol", {
772
+ description: "Get detailed volume data for a specific protocol.",
773
+ args: z5.object({
774
+ slug: z5.string().describe("Protocol slug (e.g. uniswap, curve-dex)")
775
+ }),
776
+ output: z5.object({
777
+ name: z5.string(),
778
+ volume_24h: z5.string(),
779
+ volume_7d: z5.string(),
780
+ volume_30d: z5.string(),
781
+ volume_all_time: z5.string(),
782
+ change_1d: z5.string(),
783
+ change_7d: z5.string(),
784
+ change_1m: z5.string(),
785
+ chains: z5.array(z5.string())
786
+ }),
787
+ examples: [{ args: { slug: "uniswap" }, description: "Uniswap volume details" }],
788
+ async run(c) {
789
+ const client = createDefiLlamaClient();
790
+ const raw = await client.get("api", `/summary/dexs/${c.args.slug}`);
791
+ const detail = summaryDetailSchema.parse(raw);
792
+ return c.ok({
793
+ name: detail.displayName ?? detail.name,
794
+ volume_24h: formatUsd(detail.total24h ?? 0),
795
+ volume_7d: formatUsd(detail.total7d ?? 0),
796
+ volume_30d: formatUsd(detail.total30d ?? 0),
797
+ volume_all_time: formatUsd(detail.totalAllTime ?? 0),
798
+ change_1d: formatPct(detail.change_1d),
799
+ change_7d: formatPct(detail.change_7d),
800
+ change_1m: formatPct(detail.change_1m),
801
+ chains: detail.chains ?? []
802
+ });
803
+ }
804
+ });
805
+ volumeCli.command("aggregators", {
806
+ description: "List DEX aggregators ranked by trading volume.",
807
+ options: z5.object({
808
+ chain: z5.string().optional().describe("Filter by chain name"),
809
+ limit: z5.coerce.number().default(20).describe("Max aggregators to display")
810
+ }),
811
+ output: z5.object({
812
+ protocols: z5.array(
813
+ z5.object({
814
+ name: z5.string(),
815
+ volume_24h: z5.string(),
816
+ volume_7d: z5.string(),
817
+ change_1d: z5.string()
818
+ })
819
+ ),
820
+ chain: z5.string().optional(),
821
+ total: z5.number()
822
+ }),
823
+ examples: [
824
+ { options: { limit: 10 }, description: "Top 10 DEX aggregators by volume" },
825
+ {
826
+ options: { chain: "ethereum", limit: 5 },
827
+ description: "Top 5 aggregators on Ethereum"
828
+ }
829
+ ],
830
+ async run(c) {
831
+ const client = createDefiLlamaClient();
832
+ const path = c.options.chain ? `/overview/aggregators/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/aggregators?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
833
+ const raw = await client.get("api", path);
834
+ const data = volumeOverviewResponseSchema.parse(raw);
835
+ let protocols = data.protocols;
836
+ protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
837
+ protocols.sort((a, b) => (b.total24h ?? 0) - (a.total24h ?? 0));
838
+ const limited = protocols.slice(0, c.options.limit);
839
+ const rows = limited.map((p) => ({
840
+ name: p.displayName ?? p.name,
841
+ volume_24h: formatUsd(p.total24h ?? 0),
842
+ volume_7d: formatUsd(p.total7d ?? 0),
843
+ change_1d: formatPct(p.change_1d)
844
+ }));
845
+ return c.ok({
846
+ protocols: rows,
847
+ chain: c.options.chain,
848
+ total: protocols.length
849
+ });
850
+ }
851
+ });
852
+
350
853
  // src/cli.ts
351
854
  var __dirname = dirname(fileURLToPath(import.meta.url));
352
855
  var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
353
- var cli = Cli2.create("defillama", {
856
+ var cli = Cli5.create("defillama", {
354
857
  version: pkg.version,
355
858
  description: "Query DefiLlama API data from the command line."
356
859
  });
357
860
  cli.command(tvlCli);
358
- var volumeCli = Cli2.create("volume", {
359
- description: "DEX volume queries."
360
- });
361
861
  cli.command(volumeCli);
362
- var feesCli = Cli2.create("fees", {
363
- description: "Protocol fees and revenue queries."
364
- });
365
862
  cli.command(feesCli);
366
- var pricesCli = Cli2.create("prices", {
367
- description: "Token price queries."
368
- });
369
863
  cli.command(pricesCli);
370
864
  var isMain = (() => {
371
865
  const entrypoint = process.argv[1];