crypull 1.0.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/README.md +209 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1005 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +980 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +227 -0
- package/dist/index.d.ts +227 -0
- package/dist/index.js +711 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +701 -0
- package/dist/index.mjs.map +1 -0
- package/examples/basic.ts +28 -0
- package/package.json +52 -0
- package/src/Crypull.ts +288 -0
- package/src/cli.ts +327 -0
- package/src/index.ts +14 -0
- package/src/providers/binance.ts +74 -0
- package/src/providers/coincap.ts +65 -0
- package/src/providers/coingecko.ts +92 -0
- package/src/providers/coinpaprika.ts +82 -0
- package/src/providers/cryptocompare.ts +56 -0
- package/src/providers/dexscreener.ts +82 -0
- package/src/providers/geckoterminal.ts +70 -0
- package/src/types.ts +130 -0
- package/tests/index.test.ts +15 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +11 -0
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import * as asciichart from 'asciichart';
|
|
5
|
+
|
|
6
|
+
// src/providers/coingecko.ts
|
|
7
|
+
var CoinGeckoProvider = class {
|
|
8
|
+
name = "CoinGecko";
|
|
9
|
+
baseUrl = "https://api.coingecko.com/api/v3";
|
|
10
|
+
async search(query) {
|
|
11
|
+
try {
|
|
12
|
+
const res = await fetch(`${this.baseUrl}/search?query=${query}`);
|
|
13
|
+
if (!res.ok) return [];
|
|
14
|
+
const data = await res.json();
|
|
15
|
+
return (data.coins || []).slice(0, 10).map((coin) => ({
|
|
16
|
+
id: coin.id,
|
|
17
|
+
name: coin.name,
|
|
18
|
+
symbol: coin.symbol.toUpperCase(),
|
|
19
|
+
source: this.name
|
|
20
|
+
}));
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async getPrice(symbolOrId) {
|
|
26
|
+
try {
|
|
27
|
+
let id = symbolOrId.toLowerCase();
|
|
28
|
+
const searchRes = await this.search(id);
|
|
29
|
+
const match = searchRes.find((s) => s.symbol.toLowerCase() === id || s.id === id);
|
|
30
|
+
if (match && match.id) {
|
|
31
|
+
id = match.id;
|
|
32
|
+
}
|
|
33
|
+
const res = await fetch(`${this.baseUrl}/simple/price?ids=${id}&vs_currencies=usd`);
|
|
34
|
+
if (!res.ok) return null;
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
if (!data[id] || typeof data[id].usd === "undefined") return null;
|
|
37
|
+
return {
|
|
38
|
+
symbol: match ? match.symbol : id.toUpperCase(),
|
|
39
|
+
priceUsd: data[id].usd,
|
|
40
|
+
source: this.name,
|
|
41
|
+
lastUpdated: Date.now()
|
|
42
|
+
};
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async getTokenInfo(id) {
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(`${this.baseUrl}/coins/${id.toLowerCase()}?localization=false&tickers=false&market_data=true&community_data=false&developer_data=false&sparkline=false`);
|
|
50
|
+
if (!res.ok) return null;
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
if (data.error) return null;
|
|
53
|
+
return {
|
|
54
|
+
name: data.name,
|
|
55
|
+
symbol: data.symbol.toUpperCase(),
|
|
56
|
+
description: data.description?.en,
|
|
57
|
+
priceUsd: data.market_data?.current_price?.usd || 0,
|
|
58
|
+
marketCap: data.market_data?.market_cap?.usd,
|
|
59
|
+
marketCapRank: data.market_cap_rank,
|
|
60
|
+
fdv: data.market_data?.fully_diluted_valuation?.usd,
|
|
61
|
+
circulatingSupply: data.market_data?.circulating_supply,
|
|
62
|
+
totalSupply: data.market_data?.total_supply,
|
|
63
|
+
maxSupply: data.market_data?.max_supply,
|
|
64
|
+
volume24h: data.market_data?.total_volume?.usd,
|
|
65
|
+
priceChange24h: data.market_data?.price_change_24h,
|
|
66
|
+
priceChangePercentage24h: data.market_data?.price_change_percentage_24h,
|
|
67
|
+
priceChangePercentage7d: data.market_data?.price_change_percentage_7d,
|
|
68
|
+
ath: data.market_data?.ath?.usd,
|
|
69
|
+
athDate: data.market_data?.ath_date?.usd,
|
|
70
|
+
atl: data.market_data?.atl?.usd,
|
|
71
|
+
atlDate: data.market_data?.atl_date?.usd,
|
|
72
|
+
links: {
|
|
73
|
+
website: data.links?.homepage?.[0],
|
|
74
|
+
twitter: data.links?.twitter_screen_name ? `https://twitter.com/${data.links.twitter_screen_name}` : void 0,
|
|
75
|
+
telegram: data.links?.telegram_channel_identifier ? `https://t.me/${data.links.telegram_channel_identifier}` : void 0,
|
|
76
|
+
discord: data.links?.chat_url?.find((url) => url.includes("discord")),
|
|
77
|
+
github: data.links?.repos_url?.github?.[0]
|
|
78
|
+
},
|
|
79
|
+
source: this.name,
|
|
80
|
+
lastUpdated: Date.now()
|
|
81
|
+
};
|
|
82
|
+
} catch (e) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/providers/dexscreener.ts
|
|
89
|
+
var DexScreenerProvider = class {
|
|
90
|
+
name = "DexScreener";
|
|
91
|
+
baseUrl = "https://api.dexscreener.com/latest/dex";
|
|
92
|
+
async search(query) {
|
|
93
|
+
try {
|
|
94
|
+
const res = await fetch(`${this.baseUrl}/search?q=${query}`);
|
|
95
|
+
if (!res.ok) return [];
|
|
96
|
+
const data = await res.json();
|
|
97
|
+
return (data.pairs || []).slice(0, 10).map((pair) => ({
|
|
98
|
+
id: pair.baseToken.address,
|
|
99
|
+
name: pair.baseToken.name,
|
|
100
|
+
symbol: pair.baseToken.symbol.toUpperCase(),
|
|
101
|
+
network: pair.chainId,
|
|
102
|
+
source: this.name
|
|
103
|
+
}));
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async getPrice(address) {
|
|
109
|
+
const info = await this.getTokenInfo(address);
|
|
110
|
+
if (!info) return null;
|
|
111
|
+
return {
|
|
112
|
+
symbol: info.symbol,
|
|
113
|
+
priceUsd: info.priceUsd,
|
|
114
|
+
source: this.name,
|
|
115
|
+
lastUpdated: info.lastUpdated
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
async getTokenInfo(address) {
|
|
119
|
+
try {
|
|
120
|
+
const res = await fetch(`${this.baseUrl}/tokens/${address}`);
|
|
121
|
+
if (!res.ok) return null;
|
|
122
|
+
const data = await res.json();
|
|
123
|
+
if (!data.pairs || data.pairs.length === 0) return null;
|
|
124
|
+
const pair = data.pairs[0];
|
|
125
|
+
const mappedPairs = data.pairs.slice(0, 10).map((p) => ({
|
|
126
|
+
dexId: p.dexId,
|
|
127
|
+
url: p.url,
|
|
128
|
+
pairAddress: p.pairAddress,
|
|
129
|
+
baseTokenSymbol: p.baseToken.symbol,
|
|
130
|
+
quoteTokenSymbol: p.quoteToken.symbol,
|
|
131
|
+
priceUsd: parseFloat(p.priceUsd) || 0,
|
|
132
|
+
volume24h: p.volume?.h24,
|
|
133
|
+
liquidityUsd: p.liquidity?.usd
|
|
134
|
+
}));
|
|
135
|
+
return {
|
|
136
|
+
name: pair.baseToken.name,
|
|
137
|
+
symbol: pair.baseToken.symbol.toUpperCase(),
|
|
138
|
+
address: pair.baseToken.address,
|
|
139
|
+
network: pair.chainId,
|
|
140
|
+
priceUsd: parseFloat(pair.priceUsd) || 0,
|
|
141
|
+
fdv: pair.fdv,
|
|
142
|
+
volume24h: pair.volume?.h24,
|
|
143
|
+
liquidityUsd: pair.liquidity?.usd,
|
|
144
|
+
priceChangePercentage24h: pair.priceChange?.h24,
|
|
145
|
+
pairs: mappedPairs,
|
|
146
|
+
links: {
|
|
147
|
+
website: pair.info?.websites?.[0]?.url,
|
|
148
|
+
twitter: pair.info?.socials?.find((s) => s.type === "twitter")?.url,
|
|
149
|
+
telegram: pair.info?.socials?.find((s) => s.type === "telegram")?.url,
|
|
150
|
+
discord: pair.info?.socials?.find((s) => s.type === "discord")?.url
|
|
151
|
+
},
|
|
152
|
+
source: this.name,
|
|
153
|
+
lastUpdated: Date.now()
|
|
154
|
+
};
|
|
155
|
+
} catch (e) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/providers/geckoterminal.ts
|
|
162
|
+
var GeckoTerminalProvider = class {
|
|
163
|
+
name = "GeckoTerminal";
|
|
164
|
+
baseUrl = "https://api.geckoterminal.com/api/v2";
|
|
165
|
+
async search(query) {
|
|
166
|
+
try {
|
|
167
|
+
const res = await fetch(`${this.baseUrl}/search/pools?query=${query}`);
|
|
168
|
+
if (!res.ok) return [];
|
|
169
|
+
const data = await res.json();
|
|
170
|
+
const pools = data.data || [];
|
|
171
|
+
return pools.slice(0, 10).map((pool) => {
|
|
172
|
+
const attributes = pool.attributes;
|
|
173
|
+
const baseToken = attributes.name.split(" / ")[0];
|
|
174
|
+
return {
|
|
175
|
+
id: pool.id,
|
|
176
|
+
// e.g. "eth_0x..."
|
|
177
|
+
name: attributes.name,
|
|
178
|
+
symbol: baseToken,
|
|
179
|
+
network: pool.relationships.network.data.id,
|
|
180
|
+
source: this.name
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async getPrice(address, network = "eth") {
|
|
188
|
+
const info = await this.getTokenInfo(address, network);
|
|
189
|
+
if (!info) return null;
|
|
190
|
+
return {
|
|
191
|
+
symbol: info.symbol,
|
|
192
|
+
priceUsd: info.priceUsd,
|
|
193
|
+
source: this.name,
|
|
194
|
+
lastUpdated: info.lastUpdated
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async getTokenInfo(address, network = "eth") {
|
|
198
|
+
try {
|
|
199
|
+
const res = await fetch(`${this.baseUrl}/networks/${network}/tokens/${address}`);
|
|
200
|
+
if (!res.ok) return null;
|
|
201
|
+
const json = await res.json();
|
|
202
|
+
if (!json.data || !json.data.attributes) return null;
|
|
203
|
+
const attrs = json.data.attributes;
|
|
204
|
+
return {
|
|
205
|
+
name: attrs.name,
|
|
206
|
+
symbol: attrs.symbol.toUpperCase(),
|
|
207
|
+
address: attrs.address,
|
|
208
|
+
network,
|
|
209
|
+
priceUsd: parseFloat(attrs.price_usd) || 0,
|
|
210
|
+
fdv: parseFloat(attrs.fdv_usd),
|
|
211
|
+
volume24h: parseFloat(attrs.volume_usd?.h24 || 0),
|
|
212
|
+
source: this.name,
|
|
213
|
+
lastUpdated: Date.now()
|
|
214
|
+
};
|
|
215
|
+
} catch (e) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/providers/binance.ts
|
|
222
|
+
var BinanceProvider = class {
|
|
223
|
+
name = "Binance";
|
|
224
|
+
baseUrl = "https://api.binance.com/api/v3";
|
|
225
|
+
async search(query) {
|
|
226
|
+
try {
|
|
227
|
+
const symbol = query.toUpperCase();
|
|
228
|
+
const res = await fetch(`${this.baseUrl}/ticker/price?symbol=${symbol}USDT`);
|
|
229
|
+
if (res.ok) {
|
|
230
|
+
return [{
|
|
231
|
+
id: `${symbol}USDT`,
|
|
232
|
+
name: symbol,
|
|
233
|
+
symbol,
|
|
234
|
+
source: this.name
|
|
235
|
+
}];
|
|
236
|
+
}
|
|
237
|
+
return [];
|
|
238
|
+
} catch (e) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async getPrice(symbol) {
|
|
243
|
+
try {
|
|
244
|
+
let querySymbol = symbol.toUpperCase();
|
|
245
|
+
if (!querySymbol.endsWith("USDT") && !querySymbol.endsWith("BUSD") && !querySymbol.endsWith("BTC")) {
|
|
246
|
+
querySymbol += "USDT";
|
|
247
|
+
}
|
|
248
|
+
const res = await fetch(`${this.baseUrl}/ticker/price?symbol=${querySymbol}`);
|
|
249
|
+
if (!res.ok) return null;
|
|
250
|
+
const data = await res.json();
|
|
251
|
+
return {
|
|
252
|
+
symbol: symbol.toUpperCase(),
|
|
253
|
+
priceUsd: parseFloat(data.price) || 0,
|
|
254
|
+
source: this.name,
|
|
255
|
+
lastUpdated: Date.now()
|
|
256
|
+
};
|
|
257
|
+
} catch (e) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async getTokenInfo(symbol) {
|
|
262
|
+
try {
|
|
263
|
+
let querySymbol = symbol.toUpperCase();
|
|
264
|
+
if (!querySymbol.endsWith("USDT") && !querySymbol.endsWith("BUSD") && !querySymbol.endsWith("BTC")) {
|
|
265
|
+
querySymbol += "USDT";
|
|
266
|
+
}
|
|
267
|
+
const res = await fetch(`${this.baseUrl}/ticker/24hr?symbol=${querySymbol}`);
|
|
268
|
+
if (!res.ok) return null;
|
|
269
|
+
const data = await res.json();
|
|
270
|
+
return {
|
|
271
|
+
name: symbol.toUpperCase(),
|
|
272
|
+
symbol: symbol.toUpperCase(),
|
|
273
|
+
priceUsd: parseFloat(data.lastPrice) || 0,
|
|
274
|
+
volume24h: parseFloat(data.quoteVolume) || 0,
|
|
275
|
+
// Quote volume in USDT roughly = USD volume
|
|
276
|
+
source: this.name,
|
|
277
|
+
lastUpdated: Date.now()
|
|
278
|
+
};
|
|
279
|
+
} catch (e) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// src/providers/coinpaprika.ts
|
|
286
|
+
var CoinpaprikaProvider = class {
|
|
287
|
+
name = "Coinpaprika";
|
|
288
|
+
baseUrl = "https://api.coinpaprika.com/v1";
|
|
289
|
+
async search(query) {
|
|
290
|
+
try {
|
|
291
|
+
const res = await fetch(`${this.baseUrl}/search?q=${query}&c=currencies&limit=10`);
|
|
292
|
+
if (!res.ok) return [];
|
|
293
|
+
const data = await res.json();
|
|
294
|
+
return (data.currencies || []).map((coin) => ({
|
|
295
|
+
id: coin.id,
|
|
296
|
+
name: coin.name,
|
|
297
|
+
symbol: coin.symbol.toUpperCase(),
|
|
298
|
+
source: this.name
|
|
299
|
+
}));
|
|
300
|
+
} catch (e) {
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async getPrice(symbolOrId) {
|
|
305
|
+
const info = await this.getTokenInfo(symbolOrId);
|
|
306
|
+
if (!info) return null;
|
|
307
|
+
return {
|
|
308
|
+
symbol: info.symbol,
|
|
309
|
+
priceUsd: info.priceUsd,
|
|
310
|
+
source: this.name,
|
|
311
|
+
lastUpdated: info.lastUpdated
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async getTokenInfo(symbolOrId) {
|
|
315
|
+
try {
|
|
316
|
+
let id = symbolOrId.toLowerCase();
|
|
317
|
+
if (!id.includes("-")) {
|
|
318
|
+
const searchRes = await this.search(id);
|
|
319
|
+
const match = searchRes.find((s) => s.symbol.toLowerCase() === id || s.id === id);
|
|
320
|
+
if (match && match.id) {
|
|
321
|
+
id = match.id;
|
|
322
|
+
} else if (searchRes.length > 0 && searchRes[0].id) {
|
|
323
|
+
id = searchRes[0].id;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const res = await fetch(`${this.baseUrl}/tickers/${id}`);
|
|
327
|
+
if (!res.ok) return null;
|
|
328
|
+
const data = await res.json();
|
|
329
|
+
return {
|
|
330
|
+
name: data.name,
|
|
331
|
+
symbol: data.symbol.toUpperCase(),
|
|
332
|
+
description: data.description,
|
|
333
|
+
priceUsd: data.quotes?.USD?.price || 0,
|
|
334
|
+
marketCap: data.quotes?.USD?.market_cap,
|
|
335
|
+
marketCapRank: data.rank,
|
|
336
|
+
circulatingSupply: data.circulating_supply,
|
|
337
|
+
totalSupply: data.total_supply,
|
|
338
|
+
maxSupply: data.max_supply,
|
|
339
|
+
volume24h: data.quotes?.USD?.volume_24h,
|
|
340
|
+
priceChangePercentage24h: data.quotes?.USD?.percent_change_24h,
|
|
341
|
+
priceChangePercentage7d: data.quotes?.USD?.percent_change_7d,
|
|
342
|
+
ath: data.quotes?.USD?.ath_price,
|
|
343
|
+
athDate: data.quotes?.USD?.ath_date,
|
|
344
|
+
links: {
|
|
345
|
+
website: data.links?.website?.[0],
|
|
346
|
+
twitter: data.links?.twitter?.[0],
|
|
347
|
+
discord: data.links?.discord?.[0],
|
|
348
|
+
github: data.links?.source_code?.[0]
|
|
349
|
+
},
|
|
350
|
+
source: this.name,
|
|
351
|
+
lastUpdated: Date.now()
|
|
352
|
+
};
|
|
353
|
+
} catch (e) {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// src/providers/coincap.ts
|
|
360
|
+
var CoinCapProvider = class {
|
|
361
|
+
name = "CoinCap";
|
|
362
|
+
baseUrl = "https://api.coincap.io/v2";
|
|
363
|
+
async search(query) {
|
|
364
|
+
try {
|
|
365
|
+
const res = await fetch(`${this.baseUrl}/assets?search=${query}&limit=10`);
|
|
366
|
+
if (!res.ok) return [];
|
|
367
|
+
const data = await res.json();
|
|
368
|
+
return (data.data || []).map((coin) => ({
|
|
369
|
+
id: coin.id,
|
|
370
|
+
name: coin.name,
|
|
371
|
+
symbol: coin.symbol.toUpperCase(),
|
|
372
|
+
source: this.name
|
|
373
|
+
}));
|
|
374
|
+
} catch (e) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async getPrice(symbolOrId) {
|
|
379
|
+
const info = await this.getTokenInfo(symbolOrId);
|
|
380
|
+
if (!info) return null;
|
|
381
|
+
return {
|
|
382
|
+
symbol: info.symbol,
|
|
383
|
+
priceUsd: info.priceUsd,
|
|
384
|
+
source: this.name,
|
|
385
|
+
lastUpdated: info.lastUpdated
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
async getTokenInfo(symbolOrId) {
|
|
389
|
+
try {
|
|
390
|
+
let id = symbolOrId.toLowerCase();
|
|
391
|
+
const searchRes = await this.search(id);
|
|
392
|
+
const match = searchRes.find((s) => s.symbol.toLowerCase() === id || s.id === id);
|
|
393
|
+
if (match && match.id) {
|
|
394
|
+
id = match.id;
|
|
395
|
+
}
|
|
396
|
+
const res = await fetch(`${this.baseUrl}/assets/${id}`);
|
|
397
|
+
if (!res.ok) return null;
|
|
398
|
+
const data = await res.json();
|
|
399
|
+
const asset = data.data;
|
|
400
|
+
if (!asset) return null;
|
|
401
|
+
return {
|
|
402
|
+
name: asset.name,
|
|
403
|
+
symbol: asset.symbol.toUpperCase(),
|
|
404
|
+
priceUsd: parseFloat(asset.priceUsd) || 0,
|
|
405
|
+
marketCap: parseFloat(asset.marketCapUsd),
|
|
406
|
+
volume24h: parseFloat(asset.volumeUsd24Hr),
|
|
407
|
+
source: this.name,
|
|
408
|
+
lastUpdated: Date.now()
|
|
409
|
+
};
|
|
410
|
+
} catch (e) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
// src/providers/cryptocompare.ts
|
|
417
|
+
var CryptoCompareProvider = class {
|
|
418
|
+
name = "CryptoCompare";
|
|
419
|
+
baseUrl = "https://min-api.cryptocompare.com/data";
|
|
420
|
+
async search(query) {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
async getPrice(symbol) {
|
|
424
|
+
try {
|
|
425
|
+
const sym = symbol.toUpperCase();
|
|
426
|
+
const res = await fetch(`${this.baseUrl}/price?fsym=${sym}&tsyms=USD`);
|
|
427
|
+
if (!res.ok) return null;
|
|
428
|
+
const data = await res.json();
|
|
429
|
+
if (data.Response === "Error" || !data.USD) return null;
|
|
430
|
+
return {
|
|
431
|
+
symbol: sym,
|
|
432
|
+
priceUsd: data.USD,
|
|
433
|
+
source: this.name,
|
|
434
|
+
lastUpdated: Date.now()
|
|
435
|
+
};
|
|
436
|
+
} catch (e) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async getTokenInfo(symbol) {
|
|
441
|
+
try {
|
|
442
|
+
const sym = symbol.toUpperCase();
|
|
443
|
+
const res = await fetch(`${this.baseUrl}/pricemultifull?fsyms=${sym}&tsyms=USD`);
|
|
444
|
+
if (!res.ok) return null;
|
|
445
|
+
const data = await res.json();
|
|
446
|
+
if (!data.RAW || !data.RAW[sym] || !data.RAW[sym].USD) return null;
|
|
447
|
+
const raw = data.RAW[sym].USD;
|
|
448
|
+
return {
|
|
449
|
+
name: sym,
|
|
450
|
+
// CryptoCompare doesn't give full name in this endpoint
|
|
451
|
+
symbol: sym,
|
|
452
|
+
priceUsd: raw.PRICE || 0,
|
|
453
|
+
marketCap: raw.MKTCAP,
|
|
454
|
+
volume24h: raw.TOTALVOLUME24HTO,
|
|
455
|
+
source: this.name,
|
|
456
|
+
lastUpdated: Date.now()
|
|
457
|
+
};
|
|
458
|
+
} catch (e) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/Crypull.ts
|
|
465
|
+
var Crypull = class {
|
|
466
|
+
providers;
|
|
467
|
+
// Standard free providers
|
|
468
|
+
coinGecko = new CoinGeckoProvider();
|
|
469
|
+
dexScreener = new DexScreenerProvider();
|
|
470
|
+
geckoTerminal = new GeckoTerminalProvider();
|
|
471
|
+
binance = new BinanceProvider();
|
|
472
|
+
coinpaprika = new CoinpaprikaProvider();
|
|
473
|
+
coincap = new CoinCapProvider();
|
|
474
|
+
cryptoCompare = new CryptoCompareProvider();
|
|
475
|
+
constructor(options) {
|
|
476
|
+
this.providers = options?.providers || [
|
|
477
|
+
this.binance,
|
|
478
|
+
this.coinGecko,
|
|
479
|
+
this.coinpaprika,
|
|
480
|
+
this.coincap,
|
|
481
|
+
this.dexScreener,
|
|
482
|
+
this.geckoTerminal,
|
|
483
|
+
this.cryptoCompare
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
isAddress(query) {
|
|
487
|
+
return query.startsWith("0x") && query.length === 42 || query.length > 30;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Search for a token or coin across multiple providers in parallel
|
|
491
|
+
*/
|
|
492
|
+
async search(query) {
|
|
493
|
+
const promises = [
|
|
494
|
+
this.coinGecko.search(query),
|
|
495
|
+
this.dexScreener.search(query),
|
|
496
|
+
this.coinpaprika.search(query),
|
|
497
|
+
this.coincap.search(query)
|
|
498
|
+
];
|
|
499
|
+
const results = await Promise.allSettled(promises);
|
|
500
|
+
const combined = [];
|
|
501
|
+
for (const res of results) {
|
|
502
|
+
if (res.status === "fulfilled" && res.value) {
|
|
503
|
+
combined.push(...res.value);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
507
|
+
return combined.filter((item) => {
|
|
508
|
+
const key = `${item.symbol}`;
|
|
509
|
+
if (seen.has(key)) return false;
|
|
510
|
+
seen.add(key);
|
|
511
|
+
return true;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Get the current price of a cryptocurrency
|
|
516
|
+
* @param query Can be a symbol (e.g., 'BTC') or a contract address
|
|
517
|
+
* @param network Optional network for DEX addresses (e.g., 'eth', 'bsc')
|
|
518
|
+
*/
|
|
519
|
+
async price(query, network) {
|
|
520
|
+
if (this.isAddress(query)) {
|
|
521
|
+
let res = await this.dexScreener.getPrice(query);
|
|
522
|
+
if (res) return res;
|
|
523
|
+
if (network) {
|
|
524
|
+
res = await this.geckoTerminal.getPrice(query, network);
|
|
525
|
+
if (res) return res;
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
let res = await this.binance.getPrice(query);
|
|
529
|
+
if (res) return res;
|
|
530
|
+
res = await this.coincap.getPrice(query);
|
|
531
|
+
if (res) return res;
|
|
532
|
+
res = await this.coinGecko.getPrice(query);
|
|
533
|
+
if (res) return res;
|
|
534
|
+
res = await this.coinpaprika.getPrice(query);
|
|
535
|
+
if (res) return res;
|
|
536
|
+
res = await this.cryptoCompare.getPrice(query);
|
|
537
|
+
if (res) return res;
|
|
538
|
+
}
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Get detailed token info (market cap, volume, liquidity, etc.)
|
|
543
|
+
* @param query Can be a symbol (e.g., 'BTC') or a contract address
|
|
544
|
+
* @param network Optional network for DEX addresses
|
|
545
|
+
*/
|
|
546
|
+
async info(query, network) {
|
|
547
|
+
if (this.isAddress(query)) {
|
|
548
|
+
let res = await this.dexScreener.getTokenInfo(query);
|
|
549
|
+
if (res) return res;
|
|
550
|
+
if (network) {
|
|
551
|
+
res = await this.geckoTerminal.getTokenInfo(query, network);
|
|
552
|
+
if (res) return res;
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
let res = await this.coinGecko.getTokenInfo(query);
|
|
556
|
+
if (res) return res;
|
|
557
|
+
res = await this.coinpaprika.getTokenInfo(query);
|
|
558
|
+
if (res) return res;
|
|
559
|
+
res = await this.coincap.getTokenInfo(query);
|
|
560
|
+
if (res) return res;
|
|
561
|
+
res = await this.binance.getTokenInfo(query);
|
|
562
|
+
if (res) return res;
|
|
563
|
+
res = await this.cryptoCompare.getTokenInfo(query);
|
|
564
|
+
if (res) return res;
|
|
565
|
+
}
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Get global market data
|
|
570
|
+
*/
|
|
571
|
+
async market() {
|
|
572
|
+
try {
|
|
573
|
+
const res = await fetch(`https://api.coingecko.com/api/v3/global`);
|
|
574
|
+
if (!res.ok) return null;
|
|
575
|
+
const data = await res.json();
|
|
576
|
+
const d = data.data;
|
|
577
|
+
return {
|
|
578
|
+
totalMarketCapUsd: d.total_market_cap.usd,
|
|
579
|
+
totalVolume24hUsd: d.total_volume.usd,
|
|
580
|
+
bitcoinDominancePercentage: d.market_cap_percentage.btc,
|
|
581
|
+
ethereumDominancePercentage: d.market_cap_percentage.eth,
|
|
582
|
+
activeCryptocurrencies: d.active_cryptocurrencies,
|
|
583
|
+
lastUpdated: d.updated_at * 1e3
|
|
584
|
+
};
|
|
585
|
+
} catch (e) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Get trending coins
|
|
591
|
+
*/
|
|
592
|
+
async trending() {
|
|
593
|
+
try {
|
|
594
|
+
const res = await fetch(`https://api.coingecko.com/api/v3/search/trending`);
|
|
595
|
+
if (!res.ok) return [];
|
|
596
|
+
const data = await res.json();
|
|
597
|
+
return data.coins.slice(0, 10).map((c) => ({
|
|
598
|
+
id: c.item.id,
|
|
599
|
+
name: c.item.name,
|
|
600
|
+
symbol: c.item.symbol.toUpperCase(),
|
|
601
|
+
marketCapRank: c.item.market_cap_rank,
|
|
602
|
+
priceUsd: c.item.data?.price,
|
|
603
|
+
priceChange24h: c.item.data?.price_change_percentage_24h?.usd,
|
|
604
|
+
volume24h: c.item.data?.total_volume
|
|
605
|
+
}));
|
|
606
|
+
} catch (e) {
|
|
607
|
+
return [];
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Get top 50 coins by market cap
|
|
612
|
+
*/
|
|
613
|
+
async top() {
|
|
614
|
+
try {
|
|
615
|
+
const res = await fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=50&page=1&sparkline=false`);
|
|
616
|
+
if (!res.ok) return [];
|
|
617
|
+
const data = await res.json();
|
|
618
|
+
return data.map((c) => ({
|
|
619
|
+
id: c.id,
|
|
620
|
+
name: c.name,
|
|
621
|
+
symbol: c.symbol.toUpperCase(),
|
|
622
|
+
marketCapRank: c.market_cap_rank,
|
|
623
|
+
priceUsd: c.current_price,
|
|
624
|
+
marketCap: c.market_cap,
|
|
625
|
+
volume24h: c.total_volume,
|
|
626
|
+
priceChange24h: c.price_change_percentage_24h
|
|
627
|
+
}));
|
|
628
|
+
} catch (e) {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get fear and greed index
|
|
634
|
+
*/
|
|
635
|
+
async sentiment() {
|
|
636
|
+
try {
|
|
637
|
+
const res = await fetch(`https://api.alternative.me/fng/?limit=1`);
|
|
638
|
+
if (!res.ok) return null;
|
|
639
|
+
const data = await res.json();
|
|
640
|
+
const item = data.data[0];
|
|
641
|
+
return {
|
|
642
|
+
value: parseInt(item.value),
|
|
643
|
+
classification: item.value_classification,
|
|
644
|
+
lastUpdated: parseInt(item.timestamp) * 1e3
|
|
645
|
+
};
|
|
646
|
+
} catch (e) {
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Get current ethereum gas prices (in Gwei)
|
|
652
|
+
*/
|
|
653
|
+
async gas() {
|
|
654
|
+
try {
|
|
655
|
+
const res = await fetch(`https://api.etherscan.io/api?module=gastracker&action=gasoracle`);
|
|
656
|
+
if (!res.ok) return null;
|
|
657
|
+
const data = await res.json();
|
|
658
|
+
if (data.status !== "1") return null;
|
|
659
|
+
return {
|
|
660
|
+
network: "Ethereum",
|
|
661
|
+
low: parseFloat(data.result.SafeGasPrice),
|
|
662
|
+
average: parseFloat(data.result.ProposeGasPrice),
|
|
663
|
+
high: parseFloat(data.result.FastGasPrice),
|
|
664
|
+
baseFee: parseFloat(data.result.suggestBaseFee)
|
|
665
|
+
};
|
|
666
|
+
} catch (e) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Get historical chart data for a coin
|
|
672
|
+
* @param query Coin symbol or ID
|
|
673
|
+
* @param days Number of days (e.g. 1, 7, 30)
|
|
674
|
+
*/
|
|
675
|
+
async chart(query, days = 7) {
|
|
676
|
+
try {
|
|
677
|
+
let id = query.toLowerCase();
|
|
678
|
+
const searchRes = await this.coinGecko.search(id);
|
|
679
|
+
const match = searchRes.find((s) => s.symbol.toLowerCase() === id || s.id === id);
|
|
680
|
+
if (match && match.id) {
|
|
681
|
+
id = match.id;
|
|
682
|
+
}
|
|
683
|
+
const res = await fetch(`https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=${days}`);
|
|
684
|
+
if (!res.ok) return null;
|
|
685
|
+
const data = await res.json();
|
|
686
|
+
if (!data.prices || data.prices.length === 0) return null;
|
|
687
|
+
const prices = data.prices.map((p) => p[1]);
|
|
688
|
+
const timestamps = data.prices.map((p) => p[0]);
|
|
689
|
+
return {
|
|
690
|
+
prices,
|
|
691
|
+
timestamps,
|
|
692
|
+
minPrice: Math.min(...prices),
|
|
693
|
+
maxPrice: Math.max(...prices)
|
|
694
|
+
};
|
|
695
|
+
} catch (e) {
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
// src/index.ts
|
|
702
|
+
var crypull = new Crypull();
|
|
703
|
+
|
|
704
|
+
// src/cli.ts
|
|
705
|
+
var program = new Command();
|
|
706
|
+
program.name("crypull").description("A human-friendly CLI to fetch crypto prices and info from multiple providers").version("1.0.0");
|
|
707
|
+
program.command("price <query>").description("Get the current price of a cryptocurrency (symbol or contract address)").action(async (query) => {
|
|
708
|
+
console.log(pc.cyan(`
|
|
709
|
+
Fetching price for ${pc.bold(query)}...`));
|
|
710
|
+
const data = await crypull.price(query);
|
|
711
|
+
if (!data) {
|
|
712
|
+
console.log(pc.red(`
|
|
713
|
+
\u2716 Could not find price for "${query}".`));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
console.log(pc.green(`
|
|
717
|
+
\u2714 Success! Found via ${pc.bold(data.source)}`));
|
|
718
|
+
console.log(pc.white(`----------------------------------------`));
|
|
719
|
+
console.log(`${pc.gray("Symbol:")} ${pc.yellow(pc.bold(data.symbol))}`);
|
|
720
|
+
console.log(`${pc.gray("Price:")} ${pc.green("$" + data.priceUsd.toLocaleString(void 0, { maximumFractionDigits: 6 }))}`);
|
|
721
|
+
console.log(`${pc.gray("Updated:")} ${new Date(data.lastUpdated).toLocaleTimeString()}`);
|
|
722
|
+
console.log(pc.white(`----------------------------------------
|
|
723
|
+
`));
|
|
724
|
+
});
|
|
725
|
+
program.command("info <query>").description("Get detailed info about a cryptocurrency (market cap, volume, FDV, etc.)").action(async (query) => {
|
|
726
|
+
console.log(pc.cyan(`
|
|
727
|
+
Fetching detailed info for ${pc.bold(query)}...`));
|
|
728
|
+
const data = await crypull.info(query);
|
|
729
|
+
if (!data) {
|
|
730
|
+
console.log(pc.red(`
|
|
731
|
+
\u2716 Could not find info for "${query}".`));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
console.log(pc.green(`
|
|
735
|
+
\u2714 Success! Found via ${pc.bold(data.source)}`));
|
|
736
|
+
console.log(pc.white(`----------------------------------------`));
|
|
737
|
+
console.log(`${pc.gray("Name:")} ${pc.white(pc.bold(data.name))}`);
|
|
738
|
+
console.log(`${pc.gray("Symbol:")} ${pc.yellow(pc.bold(data.symbol))}`);
|
|
739
|
+
console.log(`${pc.gray("Price:")} ${pc.green("$" + data.priceUsd.toLocaleString(void 0, { maximumFractionDigits: 6 }))}`);
|
|
740
|
+
if (data.marketCap) {
|
|
741
|
+
console.log(`${pc.gray("MarketCap:")} ${pc.white("$" + data.marketCap.toLocaleString())}`);
|
|
742
|
+
}
|
|
743
|
+
if (data.marketCapRank) {
|
|
744
|
+
console.log(`${pc.gray("Rank:")} ${pc.magenta("#" + data.marketCapRank)}`);
|
|
745
|
+
}
|
|
746
|
+
if (data.fdv) {
|
|
747
|
+
console.log(`${pc.gray("FDV:")} ${pc.white("$" + data.fdv.toLocaleString())}`);
|
|
748
|
+
}
|
|
749
|
+
if (data.circulatingSupply) {
|
|
750
|
+
console.log(`${pc.gray("Circ. Sup:")} ${pc.white(data.circulatingSupply.toLocaleString())}`);
|
|
751
|
+
}
|
|
752
|
+
if (data.totalSupply) {
|
|
753
|
+
console.log(`${pc.gray("Total Sup:")} ${pc.white(data.totalSupply.toLocaleString())}`);
|
|
754
|
+
}
|
|
755
|
+
if (data.maxSupply) {
|
|
756
|
+
console.log(`${pc.gray("Max Sup:")} ${pc.white(data.maxSupply.toLocaleString())}`);
|
|
757
|
+
}
|
|
758
|
+
if (data.volume24h) {
|
|
759
|
+
console.log(`${pc.gray("24h Vol:")} ${pc.white("$" + data.volume24h.toLocaleString())}`);
|
|
760
|
+
}
|
|
761
|
+
if (data.priceChangePercentage24h !== void 0) {
|
|
762
|
+
const pc24 = data.priceChangePercentage24h;
|
|
763
|
+
const color = pc24 >= 0 ? pc.green : pc.red;
|
|
764
|
+
console.log(`${pc.gray("24h %:")} ${color(pc24.toFixed(2) + "%")}`);
|
|
765
|
+
}
|
|
766
|
+
if (data.liquidityUsd) {
|
|
767
|
+
console.log(`${pc.gray("Liquidity:")} ${pc.white("$" + data.liquidityUsd.toLocaleString())}`);
|
|
768
|
+
}
|
|
769
|
+
if (data.ath) {
|
|
770
|
+
console.log(`${pc.gray("ATH:")} ${pc.green("$" + data.ath.toLocaleString())} ${data.athDate ? pc.gray(`(${new Date(data.athDate).toLocaleDateString()})`) : ""}`);
|
|
771
|
+
}
|
|
772
|
+
if (data.atl) {
|
|
773
|
+
console.log(`${pc.gray("ATL:")} ${pc.red("$" + data.atl.toLocaleString())} ${data.atlDate ? pc.gray(`(${new Date(data.atlDate).toLocaleDateString()})`) : ""}`);
|
|
774
|
+
}
|
|
775
|
+
if (data.network) {
|
|
776
|
+
console.log(`${pc.gray("Network:")} ${pc.magenta(data.network)}`);
|
|
777
|
+
}
|
|
778
|
+
if (data.address) {
|
|
779
|
+
console.log(`${pc.gray("Address:")} ${pc.gray(data.address)}`);
|
|
780
|
+
}
|
|
781
|
+
if (data.links && Object.values(data.links).some((l) => !!l)) {
|
|
782
|
+
console.log(pc.white(`
|
|
783
|
+
--- Links ---`));
|
|
784
|
+
if (data.links.website) console.log(`${pc.gray("Website:")} ${pc.blue(data.links.website)}`);
|
|
785
|
+
if (data.links.twitter) console.log(`${pc.gray("Twitter:")} ${pc.blue(data.links.twitter)}`);
|
|
786
|
+
if (data.links.telegram) console.log(`${pc.gray("Telegram:")} ${pc.blue(data.links.telegram)}`);
|
|
787
|
+
if (data.links.discord) console.log(`${pc.gray("Discord:")} ${pc.blue(data.links.discord)}`);
|
|
788
|
+
if (data.links.github) console.log(`${pc.gray("GitHub:")} ${pc.blue(data.links.github)}`);
|
|
789
|
+
}
|
|
790
|
+
if (data.description && data.description.length > 0) {
|
|
791
|
+
console.log(pc.white(`
|
|
792
|
+
--- Description ---`));
|
|
793
|
+
const desc = data.description.replace(/(<([^>]+)>)/gi, "");
|
|
794
|
+
const truncated = desc.length > 300 ? desc.substring(0, 300) + "..." : desc;
|
|
795
|
+
console.log(pc.gray(truncated));
|
|
796
|
+
}
|
|
797
|
+
if (data.pairs && data.pairs.length > 0) {
|
|
798
|
+
console.log(pc.white(`
|
|
799
|
+
--- Top Trading Pairs ---`));
|
|
800
|
+
data.pairs.slice(0, 5).forEach((p, i) => {
|
|
801
|
+
const title = `${p.baseTokenSymbol}/${p.quoteTokenSymbol}`;
|
|
802
|
+
const priceStr = p.priceUsd ? `$${p.priceUsd.toLocaleString(void 0, { maximumFractionDigits: 6 })}` : "N/A";
|
|
803
|
+
const volStr = p.volume24h ? `$${p.volume24h.toLocaleString(void 0, { maximumFractionDigits: 0 })}` : "N/A";
|
|
804
|
+
console.log(`${pc.blue(String(i + 1) + ".")} ${pc.yellow(title.padEnd(12, " "))} | ${pc.green(priceStr.padEnd(12, " "))} | Vol: ${pc.white(volStr)} | DEX: ${pc.magenta(p.dexId)}`);
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
console.log(pc.white(`
|
|
808
|
+
----------------------------------------`));
|
|
809
|
+
console.log(`${pc.gray("Updated:")} ${new Date(data.lastUpdated).toLocaleTimeString()}`);
|
|
810
|
+
console.log(pc.white(`----------------------------------------
|
|
811
|
+
`));
|
|
812
|
+
});
|
|
813
|
+
program.command("top").description("Show top 50 cryptocurrencies by market cap").action(async () => {
|
|
814
|
+
console.log(pc.cyan(`
|
|
815
|
+
Fetching top 50 cryptocurrencies...`));
|
|
816
|
+
const results = await crypull.top();
|
|
817
|
+
if (!results || results.length === 0) {
|
|
818
|
+
console.log(pc.red(`
|
|
819
|
+
\u2716 Could not fetch top coins at this time.`));
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
console.log(pc.green(`
|
|
823
|
+
\u2714 Top 50 Cryptocurrencies:`));
|
|
824
|
+
console.log(pc.white(`----------------------------------------`));
|
|
825
|
+
results.forEach((res, i) => {
|
|
826
|
+
const priceStr = res.priceUsd ? `$${res.priceUsd.toLocaleString(void 0, { maximumFractionDigits: 6 })}` : "N/A";
|
|
827
|
+
const mcapStr = res.marketCap ? `$${res.marketCap.toLocaleString()}` : "N/A";
|
|
828
|
+
let changeStr = "";
|
|
829
|
+
if (res.priceChange24h !== void 0 && res.priceChange24h !== null) {
|
|
830
|
+
const color = res.priceChange24h >= 0 ? pc.green : pc.red;
|
|
831
|
+
changeStr = color(`(${res.priceChange24h >= 0 ? "+" : ""}${res.priceChange24h.toFixed(2)}%)`);
|
|
832
|
+
}
|
|
833
|
+
console.log(`${pc.blue(String(i + 1).padStart(2, " "))}. ${pc.yellow(pc.bold(res.symbol.padEnd(8, " ")))} | ${pc.green(priceStr.padEnd(12, " "))} ${changeStr.padEnd(15, " ")} | Mcap: ${pc.white(mcapStr)}`);
|
|
834
|
+
});
|
|
835
|
+
console.log(pc.white(`----------------------------------------
|
|
836
|
+
`));
|
|
837
|
+
});
|
|
838
|
+
program.command("search <query>").description("Search for tokens or coins across multiple providers").action(async (query) => {
|
|
839
|
+
console.log(pc.cyan(`
|
|
840
|
+
Searching for ${pc.bold(query)}...`));
|
|
841
|
+
const results = await crypull.search(query);
|
|
842
|
+
if (!results || results.length === 0) {
|
|
843
|
+
console.log(pc.red(`
|
|
844
|
+
\u2716 No results found for "${query}".`));
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
console.log(pc.green(`
|
|
848
|
+
\u2714 Found ${results.length} results:`));
|
|
849
|
+
console.log(pc.white(`----------------------------------------`));
|
|
850
|
+
results.forEach((res, i) => {
|
|
851
|
+
console.log(`${pc.blue(String(i + 1).padStart(2, " "))}. ${pc.yellow(pc.bold(res.symbol.padEnd(8, " ")))} | ${pc.white(res.name)} ${pc.gray(`(via ${res.source})`)}`);
|
|
852
|
+
if (res.network) {
|
|
853
|
+
console.log(` ${pc.gray("Network:")} ${pc.magenta(res.network)}`);
|
|
854
|
+
}
|
|
855
|
+
if (res.id && res.id.length > 20) {
|
|
856
|
+
console.log(` ${pc.gray("Address:")} ${pc.gray(res.id)}`);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
console.log(pc.white(`----------------------------------------
|
|
860
|
+
`));
|
|
861
|
+
});
|
|
862
|
+
program.command("trending").description("Show top 10 trending coins/tokens right now").action(async () => {
|
|
863
|
+
console.log(pc.cyan(`
|
|
864
|
+
Fetching top trending coins...`));
|
|
865
|
+
const results = await crypull.trending();
|
|
866
|
+
if (!results || results.length === 0) {
|
|
867
|
+
console.log(pc.red(`
|
|
868
|
+
\u2716 Could not fetch trending data at this time.`));
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
console.log(pc.green(`
|
|
872
|
+
\u2714 Top 10 Trending Coins:`));
|
|
873
|
+
console.log(pc.white(`----------------------------------------`));
|
|
874
|
+
results.forEach((res, i) => {
|
|
875
|
+
const priceStr = res.priceUsd ? `$${res.priceUsd.toLocaleString(void 0, { maximumFractionDigits: 6 })}` : "N/A";
|
|
876
|
+
let changeStr = "";
|
|
877
|
+
if (res.priceChange24h !== void 0) {
|
|
878
|
+
const color = res.priceChange24h >= 0 ? pc.green : pc.red;
|
|
879
|
+
changeStr = color(`(${res.priceChange24h >= 0 ? "+" : ""}${res.priceChange24h.toFixed(2)}%)`);
|
|
880
|
+
}
|
|
881
|
+
const rankStr = res.marketCapRank ? pc.magenta(`#${res.marketCapRank}`) : "";
|
|
882
|
+
console.log(`${pc.blue(String(i + 1).padStart(2, " "))}. ${pc.yellow(pc.bold(res.symbol.padEnd(8, " ")))} | ${pc.green(priceStr.padEnd(12, " "))} ${changeStr.padEnd(15, " ")} | ${rankStr}`);
|
|
883
|
+
});
|
|
884
|
+
console.log(pc.white(`----------------------------------------
|
|
885
|
+
`));
|
|
886
|
+
});
|
|
887
|
+
program.command("market").description("Show global cryptocurrency market overview").action(async () => {
|
|
888
|
+
console.log(pc.cyan(`
|
|
889
|
+
Fetching global market data...`));
|
|
890
|
+
const data = await crypull.market();
|
|
891
|
+
if (!data) {
|
|
892
|
+
console.log(pc.red(`
|
|
893
|
+
\u2716 Could not fetch global market data at this time.`));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
console.log(pc.green(`
|
|
897
|
+
\u2714 Global Market Overview:`));
|
|
898
|
+
console.log(pc.white(`----------------------------------------`));
|
|
899
|
+
console.log(`${pc.gray("Total Market Cap:")} ${pc.white("$" + data.totalMarketCapUsd.toLocaleString())}`);
|
|
900
|
+
console.log(`${pc.gray("24h Volume:")} ${pc.white("$" + data.totalVolume24hUsd.toLocaleString())}`);
|
|
901
|
+
console.log(`${pc.gray("BTC Dominance:")} ${pc.yellow(data.bitcoinDominancePercentage.toFixed(2) + "%")}`);
|
|
902
|
+
console.log(`${pc.gray("ETH Dominance:")} ${pc.blue(data.ethereumDominancePercentage.toFixed(2) + "%")}`);
|
|
903
|
+
console.log(`${pc.gray("Active Coins:")} ${pc.white(data.activeCryptocurrencies.toLocaleString())}`);
|
|
904
|
+
console.log(pc.white(`----------------------------------------`));
|
|
905
|
+
console.log(`${pc.gray("Updated:")} ${new Date(data.lastUpdated).toLocaleTimeString()}`);
|
|
906
|
+
console.log(pc.white(`----------------------------------------
|
|
907
|
+
`));
|
|
908
|
+
});
|
|
909
|
+
program.command("sentiment").description("Show current Crypto Fear & Greed Index").action(async () => {
|
|
910
|
+
console.log(pc.cyan(`
|
|
911
|
+
Fetching Fear & Greed Index...`));
|
|
912
|
+
const data = await crypull.sentiment();
|
|
913
|
+
if (!data) {
|
|
914
|
+
console.log(pc.red(`
|
|
915
|
+
\u2716 Could not fetch sentiment data at this time.`));
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const value = data.value;
|
|
919
|
+
let color = pc.white;
|
|
920
|
+
if (value <= 25) color = pc.red;
|
|
921
|
+
else if (value <= 45) color = pc.yellow;
|
|
922
|
+
else if (value <= 55) color = pc.white;
|
|
923
|
+
else if (value <= 75) color = pc.green;
|
|
924
|
+
else color = pc.blue;
|
|
925
|
+
console.log(pc.green(`
|
|
926
|
+
\u2714 Current Market Sentiment:`));
|
|
927
|
+
console.log(pc.white(`----------------------------------------`));
|
|
928
|
+
console.log(`${pc.gray("Score:")} ${color(pc.bold(value.toString()) + " / 100")}`);
|
|
929
|
+
console.log(`${pc.gray("Classification:")} ${color(pc.bold(data.classification))}`);
|
|
930
|
+
console.log(pc.white(`----------------------------------------
|
|
931
|
+
`));
|
|
932
|
+
});
|
|
933
|
+
program.command("gas").description("Show current Ethereum network gas prices").action(async () => {
|
|
934
|
+
console.log(pc.cyan(`
|
|
935
|
+
Fetching network gas prices...`));
|
|
936
|
+
const data = await crypull.gas();
|
|
937
|
+
if (!data) {
|
|
938
|
+
console.log(pc.red(`
|
|
939
|
+
\u2716 Could not fetch gas prices at this time.`));
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
console.log(pc.green(`
|
|
943
|
+
\u2714 ${data.network} Gas Tracker (Gwei):`));
|
|
944
|
+
console.log(pc.white(`----------------------------------------`));
|
|
945
|
+
console.log(`${pc.gray("Low (Safe):")} ${pc.green(data.low + " gwei")}`);
|
|
946
|
+
console.log(`${pc.gray("Average:")} ${pc.yellow(data.average + " gwei")}`);
|
|
947
|
+
console.log(`${pc.gray("High (Fast):")} ${pc.red(data.high + " gwei")}`);
|
|
948
|
+
if (data.baseFee) {
|
|
949
|
+
console.log(`${pc.gray("Base Fee:")} ${pc.white(data.baseFee + " gwei")}`);
|
|
950
|
+
}
|
|
951
|
+
console.log(pc.white(`----------------------------------------
|
|
952
|
+
`));
|
|
953
|
+
});
|
|
954
|
+
program.command("chart <query>").description("Draw a terminal ASCII chart of historical prices").option("-d, --days <days>", "Number of days to chart", "7").action(async (query, options) => {
|
|
955
|
+
const days = parseInt(options.days) || 7;
|
|
956
|
+
console.log(pc.cyan(`
|
|
957
|
+
Fetching ${days}-day chart data for ${pc.bold(query)}...`));
|
|
958
|
+
const data = await crypull.chart(query, days);
|
|
959
|
+
if (!data || !data.prices || data.prices.length === 0) {
|
|
960
|
+
console.log(pc.red(`
|
|
961
|
+
\u2716 Could not fetch chart data for "${query}". Note: Charts may only be available for major coins.`));
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
const firstPrice = data.prices[0];
|
|
965
|
+
const lastPrice = data.prices[data.prices.length - 1];
|
|
966
|
+
const isUp = lastPrice >= firstPrice;
|
|
967
|
+
const chartConfig = {
|
|
968
|
+
colors: [isUp ? asciichart.lightgreen : asciichart.lightred],
|
|
969
|
+
height: 10
|
|
970
|
+
};
|
|
971
|
+
console.log(pc.green(`
|
|
972
|
+
\u2714 ${days}-Day Chart for ${query.toUpperCase()}:`));
|
|
973
|
+
console.log(pc.gray(`Min: $${data.minPrice.toLocaleString(void 0, { maximumFractionDigits: 6 })} | Max: $${data.maxPrice.toLocaleString(void 0, { maximumFractionDigits: 6 })}`));
|
|
974
|
+
console.log(pc.white(`
|
|
975
|
+
` + asciichart.plot(data.prices, chartConfig) + `
|
|
976
|
+
`));
|
|
977
|
+
});
|
|
978
|
+
program.parse(process.argv);
|
|
979
|
+
//# sourceMappingURL=cli.mjs.map
|
|
980
|
+
//# sourceMappingURL=cli.mjs.map
|