@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.
- package/README.md +101 -0
- package/dist/cli.js +575 -60
- package/dist/index.js +581 -60
- 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,403 @@ 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 normalizeCoinArgs(raw) {
|
|
321
|
+
return Array.isArray(raw) ? raw : [raw];
|
|
322
|
+
}
|
|
323
|
+
function parseCoins(raw) {
|
|
324
|
+
const coins = [];
|
|
325
|
+
for (const arg of normalizeCoinArgs(raw)) {
|
|
326
|
+
for (const part of arg.split(",")) {
|
|
327
|
+
const trimmed = part.trim();
|
|
328
|
+
if (trimmed.length === 0) continue;
|
|
329
|
+
if (!COIN_ID_RE.test(trimmed)) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
`Invalid coin identifier "${trimmed}". Expected format: chainName:0xAddress (e.g. ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7)`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
coins.push(trimmed);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (coins.length === 0) {
|
|
338
|
+
throw new Error("At least one coin identifier is required.");
|
|
339
|
+
}
|
|
340
|
+
return [...new Set(coins)].join(",");
|
|
341
|
+
}
|
|
342
|
+
function parseTimestamp(value) {
|
|
343
|
+
if (/^\d+$/.test(value)) {
|
|
344
|
+
return Number(value);
|
|
345
|
+
}
|
|
346
|
+
const ms = Date.parse(value);
|
|
347
|
+
if (Number.isNaN(ms)) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`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).`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
return Math.floor(ms / 1e3);
|
|
353
|
+
}
|
|
354
|
+
var coinsArgsSchema = z3.string().describe("Coin identifiers (chainName:0xAddress)");
|
|
355
|
+
pricesCli.command("current", {
|
|
356
|
+
description: "Get current prices for one or more tokens.",
|
|
357
|
+
args: z3.object({
|
|
358
|
+
coins: coinsArgsSchema
|
|
359
|
+
}),
|
|
360
|
+
options: z3.object({
|
|
361
|
+
"search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
|
|
362
|
+
}),
|
|
363
|
+
output: z3.object({
|
|
364
|
+
prices: z3.array(
|
|
365
|
+
z3.object({
|
|
366
|
+
coin: z3.string(),
|
|
367
|
+
symbol: z3.string(),
|
|
368
|
+
price: z3.string(),
|
|
369
|
+
confidence: z3.number().optional(),
|
|
370
|
+
timestamp: z3.string()
|
|
371
|
+
})
|
|
372
|
+
)
|
|
373
|
+
}),
|
|
374
|
+
examples: [
|
|
375
|
+
{
|
|
376
|
+
args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
|
|
377
|
+
description: "Current price of USDT on Ethereum"
|
|
378
|
+
}
|
|
379
|
+
],
|
|
380
|
+
async run(c) {
|
|
381
|
+
const coinsPath = parseCoins(c.args.coins);
|
|
382
|
+
const client = createDefiLlamaClient();
|
|
383
|
+
const searchWidth = c.options["search-width"];
|
|
384
|
+
const path = `/prices/current/${coinsPath}?searchWidth=${searchWidth}`;
|
|
385
|
+
const raw = await client.get("coins", path);
|
|
386
|
+
const data = pricesResponseSchema.parse(raw);
|
|
387
|
+
const prices = Object.entries(data.coins).map(([coin, info]) => ({
|
|
388
|
+
coin,
|
|
389
|
+
symbol: info.symbol,
|
|
390
|
+
price: formatUsd(info.price),
|
|
391
|
+
confidence: info.confidence,
|
|
392
|
+
timestamp: new Date(info.timestamp * 1e3).toISOString()
|
|
393
|
+
}));
|
|
394
|
+
return c.ok({ prices });
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
pricesCli.command("historical", {
|
|
398
|
+
description: "Get token prices at a specific point in time.",
|
|
399
|
+
args: z3.object({
|
|
400
|
+
coins: coinsArgsSchema
|
|
401
|
+
}),
|
|
402
|
+
options: z3.object({
|
|
403
|
+
timestamp: z3.string().optional().describe("Unix timestamp in seconds"),
|
|
404
|
+
date: z3.string().optional().describe("ISO date string (e.g. 2025-01-01)"),
|
|
405
|
+
"search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 6h)")
|
|
406
|
+
}),
|
|
407
|
+
output: z3.object({
|
|
408
|
+
prices: z3.array(
|
|
409
|
+
z3.object({
|
|
410
|
+
coin: z3.string(),
|
|
411
|
+
symbol: z3.string(),
|
|
412
|
+
price: z3.string(),
|
|
413
|
+
timestamp: z3.string()
|
|
414
|
+
})
|
|
415
|
+
)
|
|
416
|
+
}),
|
|
417
|
+
examples: [
|
|
418
|
+
{
|
|
419
|
+
args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
|
|
420
|
+
options: { date: "2025-01-01" },
|
|
421
|
+
description: "USDT price on 2025-01-01"
|
|
422
|
+
}
|
|
423
|
+
],
|
|
424
|
+
async run(c) {
|
|
425
|
+
const tsRaw = c.options.timestamp ?? c.options.date;
|
|
426
|
+
if (!tsRaw) {
|
|
427
|
+
throw new Error("Either --timestamp or --date is required for historical prices.");
|
|
428
|
+
}
|
|
429
|
+
const ts = parseTimestamp(tsRaw);
|
|
430
|
+
const coinsPath = parseCoins(c.args.coins);
|
|
431
|
+
const client = createDefiLlamaClient();
|
|
432
|
+
const searchWidth = c.options["search-width"];
|
|
433
|
+
const path = `/prices/historical/${ts}/${coinsPath}?searchWidth=${searchWidth}`;
|
|
434
|
+
const raw = await client.get("coins", path);
|
|
435
|
+
const data = pricesResponseSchema.parse(raw);
|
|
436
|
+
const prices = Object.entries(data.coins).map(([coin, info]) => ({
|
|
437
|
+
coin,
|
|
438
|
+
symbol: info.symbol,
|
|
439
|
+
price: formatUsd(info.price),
|
|
440
|
+
timestamp: new Date(info.timestamp * 1e3).toISOString()
|
|
441
|
+
}));
|
|
442
|
+
return c.ok({ prices });
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
pricesCli.command("chart", {
|
|
446
|
+
description: "Get a price chart over a time range.",
|
|
447
|
+
args: z3.object({
|
|
448
|
+
coins: coinsArgsSchema
|
|
449
|
+
}),
|
|
450
|
+
options: z3.object({
|
|
451
|
+
start: z3.string().optional().describe("Start timestamp or ISO date"),
|
|
452
|
+
end: z3.string().optional().describe("End timestamp or ISO date (default: now)"),
|
|
453
|
+
span: z3.coerce.number().optional().describe("Number of data points"),
|
|
454
|
+
period: z3.string().optional().describe("Data point period (e.g. 1d, 1h, 4h)"),
|
|
455
|
+
"search-width": z3.string().default("4h").describe("Timestamp search width (e.g. 4h, 600)")
|
|
456
|
+
}),
|
|
457
|
+
output: z3.object({
|
|
458
|
+
charts: z3.array(
|
|
459
|
+
z3.object({
|
|
460
|
+
coin: z3.string(),
|
|
461
|
+
symbol: z3.string(),
|
|
462
|
+
prices: z3.array(
|
|
463
|
+
z3.object({
|
|
464
|
+
date: z3.string(),
|
|
465
|
+
price: z3.string()
|
|
466
|
+
})
|
|
467
|
+
)
|
|
468
|
+
})
|
|
469
|
+
)
|
|
470
|
+
}),
|
|
471
|
+
examples: [
|
|
472
|
+
{
|
|
473
|
+
args: { coins: "ethereum:0xdAC17F958D2ee523a2206206994597C13D831ec7" },
|
|
474
|
+
options: { start: "2025-01-01", period: "1d" },
|
|
475
|
+
description: "USDT daily price chart since 2025-01-01"
|
|
476
|
+
}
|
|
477
|
+
],
|
|
478
|
+
async run(c) {
|
|
479
|
+
const coinsPath = parseCoins(c.args.coins);
|
|
480
|
+
const client = createDefiLlamaClient();
|
|
481
|
+
const params = new URLSearchParams();
|
|
482
|
+
if (c.options.start) params.set("start", String(parseTimestamp(c.options.start)));
|
|
483
|
+
if (c.options.end) params.set("end", String(parseTimestamp(c.options.end)));
|
|
484
|
+
if (c.options.span) params.set("span", String(c.options.span));
|
|
485
|
+
if (c.options.period) params.set("period", c.options.period);
|
|
486
|
+
params.set("searchWidth", c.options["search-width"]);
|
|
487
|
+
const qs = params.toString();
|
|
488
|
+
const path = `/chart/${coinsPath}${qs ? `?${qs}` : ""}`;
|
|
489
|
+
const raw = await client.get("coins", path);
|
|
490
|
+
const data = chartResponseSchema.parse(raw);
|
|
491
|
+
const charts = Object.entries(data.coins).map(([coin, info]) => ({
|
|
492
|
+
coin,
|
|
493
|
+
symbol: info.symbol,
|
|
494
|
+
prices: info.prices.map((p) => ({
|
|
495
|
+
date: new Date(p.timestamp * 1e3).toISOString(),
|
|
496
|
+
price: formatUsd(p.price)
|
|
497
|
+
}))
|
|
498
|
+
}));
|
|
499
|
+
return c.ok({ charts });
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// src/commands/tvl.ts
|
|
504
|
+
import { Cli as Cli3, z as z4 } from "incur";
|
|
505
|
+
var tvlCli = Cli3.create("tvl", {
|
|
506
|
+
description: "Total value locked queries."
|
|
507
|
+
});
|
|
508
|
+
var protocolSortFields = ["tvl", "change_1d", "change_7d"];
|
|
509
|
+
tvlCli.command("protocols", {
|
|
510
|
+
description: "List protocols ranked by TVL.",
|
|
511
|
+
options: z4.object({
|
|
512
|
+
chain: z4.string().optional().describe("Filter protocols by chain name"),
|
|
513
|
+
limit: z4.coerce.number().default(20).describe("Max protocols to display"),
|
|
514
|
+
sort: z4.enum(protocolSortFields).default("tvl").describe("Sort field: tvl, change_1d, or change_7d")
|
|
515
|
+
}),
|
|
516
|
+
output: z4.object({
|
|
517
|
+
protocols: z4.array(
|
|
518
|
+
z4.object({
|
|
519
|
+
name: z4.string(),
|
|
520
|
+
tvl: z4.string(),
|
|
521
|
+
change_1d: z4.string(),
|
|
522
|
+
change_7d: z4.string(),
|
|
523
|
+
category: z4.string()
|
|
524
|
+
})
|
|
525
|
+
),
|
|
526
|
+
chain: z4.string().optional(),
|
|
527
|
+
total: z4.number()
|
|
528
|
+
}),
|
|
174
529
|
examples: [
|
|
175
530
|
{ options: { limit: 10 }, description: "Top 10 protocols by TVL" },
|
|
176
531
|
{ options: { chain: "abstract", limit: 10 }, description: "Top protocols on Abstract" },
|
|
@@ -211,17 +566,17 @@ tvlCli.command("protocols", {
|
|
|
211
566
|
});
|
|
212
567
|
tvlCli.command("chains", {
|
|
213
568
|
description: "List chains ranked by TVL.",
|
|
214
|
-
options:
|
|
215
|
-
limit:
|
|
569
|
+
options: z4.object({
|
|
570
|
+
limit: z4.coerce.number().default(20).describe("Max chains to display")
|
|
216
571
|
}),
|
|
217
|
-
output:
|
|
218
|
-
chains:
|
|
219
|
-
|
|
220
|
-
name:
|
|
221
|
-
tvl:
|
|
572
|
+
output: z4.object({
|
|
573
|
+
chains: z4.array(
|
|
574
|
+
z4.object({
|
|
575
|
+
name: z4.string(),
|
|
576
|
+
tvl: z4.string()
|
|
222
577
|
})
|
|
223
578
|
),
|
|
224
|
-
total:
|
|
579
|
+
total: z4.number()
|
|
225
580
|
}),
|
|
226
581
|
examples: [{ options: { limit: 10 }, description: "Top 10 chains by TVL" }],
|
|
227
582
|
async run(c) {
|
|
@@ -242,20 +597,20 @@ tvlCli.command("chains", {
|
|
|
242
597
|
});
|
|
243
598
|
tvlCli.command("protocol", {
|
|
244
599
|
description: "Get detailed protocol info with TVL breakdown by chain.",
|
|
245
|
-
args:
|
|
246
|
-
slug:
|
|
600
|
+
args: z4.object({
|
|
601
|
+
slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
|
|
247
602
|
}),
|
|
248
|
-
output:
|
|
249
|
-
name:
|
|
250
|
-
description:
|
|
251
|
-
category:
|
|
252
|
-
url:
|
|
253
|
-
symbol:
|
|
254
|
-
tvl:
|
|
255
|
-
chains:
|
|
256
|
-
|
|
257
|
-
chain:
|
|
258
|
-
tvl:
|
|
603
|
+
output: z4.object({
|
|
604
|
+
name: z4.string(),
|
|
605
|
+
description: z4.string(),
|
|
606
|
+
category: z4.string(),
|
|
607
|
+
url: z4.string(),
|
|
608
|
+
symbol: z4.string(),
|
|
609
|
+
tvl: z4.string(),
|
|
610
|
+
chains: z4.array(
|
|
611
|
+
z4.object({
|
|
612
|
+
chain: z4.string(),
|
|
613
|
+
tvl: z4.string()
|
|
259
614
|
})
|
|
260
615
|
)
|
|
261
616
|
}),
|
|
@@ -298,21 +653,21 @@ tvlCli.command("protocol", {
|
|
|
298
653
|
});
|
|
299
654
|
tvlCli.command("history", {
|
|
300
655
|
description: "Show historical TVL for a protocol.",
|
|
301
|
-
args:
|
|
302
|
-
slug:
|
|
656
|
+
args: z4.object({
|
|
657
|
+
slug: z4.string().describe("Protocol slug (e.g. aave, uniswap)")
|
|
303
658
|
}),
|
|
304
|
-
options:
|
|
305
|
-
days:
|
|
306
|
-
chain:
|
|
659
|
+
options: z4.object({
|
|
660
|
+
days: z4.coerce.number().default(30).describe("Number of days of history to display"),
|
|
661
|
+
chain: z4.string().optional().describe("Filter to a specific chain")
|
|
307
662
|
}),
|
|
308
|
-
output:
|
|
309
|
-
protocol:
|
|
310
|
-
chain:
|
|
311
|
-
days:
|
|
312
|
-
history:
|
|
313
|
-
|
|
314
|
-
date:
|
|
315
|
-
tvl:
|
|
663
|
+
output: z4.object({
|
|
664
|
+
protocol: z4.string(),
|
|
665
|
+
chain: z4.string().optional(),
|
|
666
|
+
days: z4.number(),
|
|
667
|
+
history: z4.array(
|
|
668
|
+
z4.object({
|
|
669
|
+
date: z4.string(),
|
|
670
|
+
tvl: z4.string()
|
|
316
671
|
})
|
|
317
672
|
)
|
|
318
673
|
}),
|
|
@@ -358,26 +713,185 @@ tvlCli.command("history", {
|
|
|
358
713
|
}
|
|
359
714
|
});
|
|
360
715
|
|
|
716
|
+
// src/commands/volume.ts
|
|
717
|
+
import { Cli as Cli4, z as z5 } from "incur";
|
|
718
|
+
var volumeCli = Cli4.create("volume", {
|
|
719
|
+
description: "DEX volume queries."
|
|
720
|
+
});
|
|
721
|
+
var dexsSortFields = ["total24h", "total7d", "change_1d"];
|
|
722
|
+
volumeCli.command("dexs", {
|
|
723
|
+
description: "List DEXes ranked by trading volume.",
|
|
724
|
+
options: z5.object({
|
|
725
|
+
chain: z5.string().optional().describe("Filter by chain name"),
|
|
726
|
+
limit: z5.coerce.number().default(20).describe("Max protocols to display"),
|
|
727
|
+
sort: z5.enum(dexsSortFields).default("total24h").describe("Sort field: total24h, total7d, or change_1d"),
|
|
728
|
+
category: z5.string().optional().describe("Filter by category (e.g. Dexs, Prediction)")
|
|
729
|
+
}),
|
|
730
|
+
output: z5.object({
|
|
731
|
+
protocols: z5.array(
|
|
732
|
+
z5.object({
|
|
733
|
+
name: z5.string(),
|
|
734
|
+
volume_24h: z5.string(),
|
|
735
|
+
volume_7d: z5.string(),
|
|
736
|
+
change_1d: z5.string()
|
|
737
|
+
})
|
|
738
|
+
),
|
|
739
|
+
chain: z5.string().optional(),
|
|
740
|
+
category: z5.string().optional(),
|
|
741
|
+
total: z5.number()
|
|
742
|
+
}),
|
|
743
|
+
examples: [
|
|
744
|
+
{ options: { limit: 10 }, description: "Top 10 DEXes by 24h volume" },
|
|
745
|
+
{ options: { chain: "abstract", limit: 10 }, description: "Top DEXes on Abstract" },
|
|
746
|
+
{ options: { sort: "change_1d", limit: 5 }, description: "Top 5 by 1-day volume change" },
|
|
747
|
+
{
|
|
748
|
+
options: { category: "Dexs", limit: 10 },
|
|
749
|
+
description: "Top 10 DEXes excluding prediction markets"
|
|
750
|
+
}
|
|
751
|
+
],
|
|
752
|
+
async run(c) {
|
|
753
|
+
const client = createDefiLlamaClient();
|
|
754
|
+
const path = c.options.chain ? `/overview/dexs/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/dexs?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
|
|
755
|
+
const raw = await client.get("api", path);
|
|
756
|
+
const data = volumeOverviewResponseSchema.parse(raw);
|
|
757
|
+
let protocols = data.protocols;
|
|
758
|
+
if (c.options.category) {
|
|
759
|
+
const catLower = c.options.category.toLowerCase();
|
|
760
|
+
protocols = protocols.filter(
|
|
761
|
+
(p) => p.category != null && p.category.toLowerCase() === catLower
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
|
|
765
|
+
const sortKey = c.options.sort;
|
|
766
|
+
protocols.sort((a, b) => {
|
|
767
|
+
const aVal = a[sortKey] ?? 0;
|
|
768
|
+
const bVal = b[sortKey] ?? 0;
|
|
769
|
+
return bVal - aVal;
|
|
770
|
+
});
|
|
771
|
+
const limited = protocols.slice(0, c.options.limit);
|
|
772
|
+
const rows = limited.map((p) => ({
|
|
773
|
+
name: p.displayName ?? p.name,
|
|
774
|
+
volume_24h: formatUsd(p.total24h ?? 0),
|
|
775
|
+
volume_7d: formatUsd(p.total7d ?? 0),
|
|
776
|
+
change_1d: formatPct(p.change_1d)
|
|
777
|
+
}));
|
|
778
|
+
return c.ok({
|
|
779
|
+
protocols: rows,
|
|
780
|
+
chain: c.options.chain,
|
|
781
|
+
category: c.options.category,
|
|
782
|
+
total: protocols.length
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
volumeCli.command("protocol", {
|
|
787
|
+
description: "Get detailed volume data for a specific protocol.",
|
|
788
|
+
args: z5.object({
|
|
789
|
+
slug: z5.string().describe("Protocol slug (e.g. uniswap, curve-dex)")
|
|
790
|
+
}),
|
|
791
|
+
output: z5.object({
|
|
792
|
+
name: z5.string(),
|
|
793
|
+
volume_24h: z5.string(),
|
|
794
|
+
volume_7d: z5.string(),
|
|
795
|
+
volume_30d: z5.string(),
|
|
796
|
+
volume_all_time: z5.string(),
|
|
797
|
+
change_1d: z5.string(),
|
|
798
|
+
change_7d: z5.string(),
|
|
799
|
+
change_1m: z5.string(),
|
|
800
|
+
chains: z5.array(z5.string())
|
|
801
|
+
}),
|
|
802
|
+
examples: [{ args: { slug: "uniswap" }, description: "Uniswap volume details" }],
|
|
803
|
+
async run(c) {
|
|
804
|
+
const client = createDefiLlamaClient();
|
|
805
|
+
const raw = await client.get("api", `/summary/dexs/${c.args.slug}`);
|
|
806
|
+
const detail = summaryDetailSchema.parse(raw);
|
|
807
|
+
return c.ok({
|
|
808
|
+
name: detail.displayName ?? detail.name,
|
|
809
|
+
volume_24h: formatUsd(detail.total24h ?? 0),
|
|
810
|
+
volume_7d: formatUsd(detail.total7d ?? 0),
|
|
811
|
+
volume_30d: formatUsd(detail.total30d ?? 0),
|
|
812
|
+
volume_all_time: formatUsd(detail.totalAllTime ?? 0),
|
|
813
|
+
change_1d: formatPct(detail.change_1d),
|
|
814
|
+
change_7d: formatPct(detail.change_7d),
|
|
815
|
+
change_1m: formatPct(detail.change_1m),
|
|
816
|
+
chains: detail.chains ?? []
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
volumeCli.command("aggregators", {
|
|
821
|
+
description: "List DEX aggregators ranked by trading volume.",
|
|
822
|
+
options: z5.object({
|
|
823
|
+
chain: z5.string().optional().describe("Filter by chain name"),
|
|
824
|
+
limit: z5.coerce.number().default(20).describe("Max aggregators to display")
|
|
825
|
+
}),
|
|
826
|
+
output: z5.object({
|
|
827
|
+
protocols: z5.array(
|
|
828
|
+
z5.object({
|
|
829
|
+
name: z5.string(),
|
|
830
|
+
volume_24h: z5.string(),
|
|
831
|
+
volume_7d: z5.string(),
|
|
832
|
+
change_1d: z5.string()
|
|
833
|
+
})
|
|
834
|
+
),
|
|
835
|
+
chain: z5.string().optional(),
|
|
836
|
+
total: z5.number()
|
|
837
|
+
}),
|
|
838
|
+
examples: [
|
|
839
|
+
{ options: { limit: 10 }, description: "Top 10 DEX aggregators by volume" },
|
|
840
|
+
{
|
|
841
|
+
options: { chain: "ethereum", limit: 5 },
|
|
842
|
+
description: "Top 5 aggregators on Ethereum"
|
|
843
|
+
}
|
|
844
|
+
],
|
|
845
|
+
async run(c) {
|
|
846
|
+
const client = createDefiLlamaClient();
|
|
847
|
+
const path = c.options.chain ? `/overview/aggregators/${c.options.chain}?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true` : "/overview/aggregators?excludeTotalDataChart=true&excludeTotalDataChartBreakdown=true";
|
|
848
|
+
const raw = await client.get("api", path);
|
|
849
|
+
const data = volumeOverviewResponseSchema.parse(raw);
|
|
850
|
+
let protocols = data.protocols;
|
|
851
|
+
protocols = protocols.filter((p) => p.total24h != null && p.total24h > 0);
|
|
852
|
+
protocols.sort((a, b) => (b.total24h ?? 0) - (a.total24h ?? 0));
|
|
853
|
+
const limited = protocols.slice(0, c.options.limit);
|
|
854
|
+
const rows = limited.map((p) => ({
|
|
855
|
+
name: p.displayName ?? p.name,
|
|
856
|
+
volume_24h: formatUsd(p.total24h ?? 0),
|
|
857
|
+
volume_7d: formatUsd(p.total7d ?? 0),
|
|
858
|
+
change_1d: formatPct(p.change_1d)
|
|
859
|
+
}));
|
|
860
|
+
return c.ok({
|
|
861
|
+
protocols: rows,
|
|
862
|
+
chain: c.options.chain,
|
|
863
|
+
total: protocols.length
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
|
|
361
868
|
// src/cli.ts
|
|
362
869
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
363
870
|
var pkg = JSON.parse(readFileSync(resolve(__dirname, "../package.json"), "utf8"));
|
|
364
|
-
var cli =
|
|
871
|
+
var cli = Cli5.create("defillama", {
|
|
365
872
|
version: pkg.version,
|
|
366
873
|
description: "Query DefiLlama API data from the command line."
|
|
367
874
|
});
|
|
368
875
|
cli.command(tvlCli);
|
|
369
|
-
var volumeCli = Cli2.create("volume", {
|
|
370
|
-
description: "DEX volume queries."
|
|
371
|
-
});
|
|
372
876
|
cli.command(volumeCli);
|
|
373
|
-
var feesCli = Cli2.create("fees", {
|
|
374
|
-
description: "Protocol fees and revenue queries."
|
|
375
|
-
});
|
|
376
877
|
cli.command(feesCli);
|
|
377
|
-
var pricesCli = Cli2.create("prices", {
|
|
378
|
-
description: "Token price queries."
|
|
379
|
-
});
|
|
380
878
|
cli.command(pricesCli);
|
|
879
|
+
var PRICE_SUBCOMMANDS = /* @__PURE__ */ new Set(["current", "historical", "chart"]);
|
|
880
|
+
function normalizePricesArgv(argv) {
|
|
881
|
+
if (argv[0] !== "prices") return argv;
|
|
882
|
+
const subcommand = argv[1];
|
|
883
|
+
if (!subcommand || !PRICE_SUBCOMMANDS.has(subcommand)) return argv;
|
|
884
|
+
let i = 2;
|
|
885
|
+
const coins = [];
|
|
886
|
+
while (i < argv.length) {
|
|
887
|
+
const token = argv[i];
|
|
888
|
+
if (!token || token.startsWith("-")) break;
|
|
889
|
+
coins.push(token);
|
|
890
|
+
i += 1;
|
|
891
|
+
}
|
|
892
|
+
if (coins.length <= 1) return argv;
|
|
893
|
+
return [...argv.slice(0, 2), coins.join(","), ...argv.slice(i)];
|
|
894
|
+
}
|
|
381
895
|
var isMain = (() => {
|
|
382
896
|
const entrypoint = process.argv[1];
|
|
383
897
|
if (!entrypoint) {
|
|
@@ -390,20 +904,27 @@ var isMain = (() => {
|
|
|
390
904
|
}
|
|
391
905
|
})();
|
|
392
906
|
if (isMain) {
|
|
393
|
-
cli.serve();
|
|
907
|
+
cli.serve(normalizePricesArgv(process.argv.slice(2)));
|
|
394
908
|
}
|
|
395
909
|
export {
|
|
396
910
|
DEFILLAMA_HOSTS,
|
|
397
911
|
chainTvlSchema,
|
|
912
|
+
chartResponseSchema,
|
|
398
913
|
cli,
|
|
914
|
+
coinChartSchema,
|
|
915
|
+
coinPriceSchema,
|
|
399
916
|
createDefiLlamaClient,
|
|
400
917
|
feeEntrySchema,
|
|
918
|
+
feeOverviewResponseSchema,
|
|
401
919
|
formatDelta,
|
|
402
920
|
formatNumber,
|
|
403
921
|
formatPct,
|
|
404
922
|
formatUsd,
|
|
923
|
+
pricesResponseSchema,
|
|
405
924
|
protocolDetailSchema,
|
|
406
925
|
protocolSummarySchema,
|
|
926
|
+
summaryDetailSchema,
|
|
407
927
|
tvlHistoryPointSchema,
|
|
408
|
-
volumeEntrySchema
|
|
928
|
+
volumeEntrySchema,
|
|
929
|
+
volumeOverviewResponseSchema
|
|
409
930
|
};
|