@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.
- package/dist/cli.js +552 -58
- package/dist/index.js +560 -59
- 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
|
|
7
|
+
import { Cli as Cli5 } from "incur";
|
|
8
8
|
|
|
9
|
-
// src/commands/
|
|
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/
|
|
150
|
-
var
|
|
151
|
-
description: "
|
|
210
|
+
// src/commands/fees.ts
|
|
211
|
+
var feesCli = Cli.create("fees", {
|
|
212
|
+
description: "Protocol fees and revenue queries."
|
|
152
213
|
});
|
|
153
|
-
var
|
|
154
|
-
|
|
155
|
-
description: "List protocols ranked by
|
|
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
|
|
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(
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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:
|
|
215
|
-
limit:
|
|
565
|
+
options: z4.object({
|
|
566
|
+
limit: z4.coerce.number().default(20).describe("Max chains to display")
|
|
216
567
|
}),
|
|
217
|
-
output:
|
|
218
|
-
chains:
|
|
219
|
-
|
|
220
|
-
name:
|
|
221
|
-
tvl:
|
|
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:
|
|
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:
|
|
246
|
-
slug:
|
|
596
|
+
args: z4.object({
|
|
597
|
+
slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
|
|
247
598
|
}),
|
|
248
|
-
output:
|
|
249
|
-
name:
|
|
250
|
-
description:
|
|
251
|
-
category:
|
|
252
|
-
url:
|
|
253
|
-
symbol:
|
|
254
|
-
tvl:
|
|
255
|
-
chains:
|
|
256
|
-
|
|
257
|
-
chain:
|
|
258
|
-
tvl:
|
|
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:
|
|
302
|
-
slug:
|
|
652
|
+
args: z4.object({
|
|
653
|
+
slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
|
|
303
654
|
}),
|
|
304
|
-
options:
|
|
305
|
-
days:
|
|
306
|
-
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:
|
|
309
|
-
protocol:
|
|
310
|
-
chain:
|
|
311
|
-
days:
|
|
312
|
-
history:
|
|
313
|
-
|
|
314
|
-
date:
|
|
315
|
-
tvl:
|
|
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 =
|
|
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
|
};
|