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