crypull 1.0.0 → 1.0.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/src/Crypull.ts CHANGED
@@ -1,288 +1,288 @@
1
- import { ChartData, DexPairInfo, GasData, GlobalMarketData, ICryptoProvider, PriceData, SearchResult, SentimentData, TokenInfo, TrendingCoin, TopCoin } from './types.js';
2
- import { CoinGeckoProvider } from './providers/coingecko.js';
3
- import { DexScreenerProvider } from './providers/dexscreener.js';
4
- import { GeckoTerminalProvider } from './providers/geckoterminal.js';
5
- import { BinanceProvider } from './providers/binance.js';
6
- import { CoinpaprikaProvider } from './providers/coinpaprika.js';
7
- import { CoinCapProvider } from './providers/coincap.js';
8
- import { CryptoCompareProvider } from './providers/cryptocompare.js';
9
-
10
- export interface CrypullOptions {
11
- providers?: ICryptoProvider[];
12
- }
13
-
14
- export class Crypull {
15
- public providers: ICryptoProvider[];
16
-
17
- // Standard free providers
18
- private coinGecko = new CoinGeckoProvider();
19
- private dexScreener = new DexScreenerProvider();
20
- private geckoTerminal = new GeckoTerminalProvider();
21
- private binance = new BinanceProvider();
22
- private coinpaprika = new CoinpaprikaProvider();
23
- private coincap = new CoinCapProvider();
24
- private cryptoCompare = new CryptoCompareProvider();
25
-
26
- constructor(options?: CrypullOptions) {
27
- this.providers = options?.providers || [
28
- this.binance,
29
- this.coinGecko,
30
- this.coinpaprika,
31
- this.coincap,
32
- this.dexScreener,
33
- this.geckoTerminal,
34
- this.cryptoCompare
35
- ];
36
- }
37
-
38
- private isAddress(query: string): boolean {
39
- return (query.startsWith('0x') && query.length === 42) || query.length > 30;
40
- }
41
-
42
- /**
43
- * Search for a token or coin across multiple providers in parallel
44
- */
45
- async search(query: string): Promise<SearchResult[]> {
46
- const promises = [
47
- this.coinGecko.search(query),
48
- this.dexScreener.search(query),
49
- this.coinpaprika.search(query),
50
- this.coincap.search(query)
51
- ];
52
-
53
- const results = await Promise.allSettled(promises);
54
- const combined: SearchResult[] = [];
55
-
56
- for (const res of results) {
57
- if (res.status === 'fulfilled' && res.value) {
58
- combined.push(...res.value);
59
- }
60
- }
61
-
62
- // Deduplicate by symbol to keep results clean
63
- const seen = new Set();
64
- return combined.filter(item => {
65
- const key = `${item.symbol}`;
66
- if (seen.has(key)) return false;
67
- seen.add(key);
68
- return true;
69
- });
70
- }
71
-
72
- /**
73
- * Get the current price of a cryptocurrency
74
- * @param query Can be a symbol (e.g., 'BTC') or a contract address
75
- * @param network Optional network for DEX addresses (e.g., 'eth', 'bsc')
76
- */
77
- async price(query: string, network?: string): Promise<PriceData | null> {
78
- if (this.isAddress(query)) {
79
- let res = await this.dexScreener.getPrice(query);
80
- if (res) return res;
81
-
82
- if (network) {
83
- res = await this.geckoTerminal.getPrice(query, network);
84
- if (res) return res;
85
- }
86
- } else {
87
- // Fast top-tier providers first
88
- let res = await this.binance.getPrice(query);
89
- if (res) return res;
90
-
91
- res = await this.coincap.getPrice(query);
92
- if (res) return res;
93
-
94
- res = await this.coinGecko.getPrice(query);
95
- if (res) return res;
96
-
97
- res = await this.coinpaprika.getPrice(query);
98
- if (res) return res;
99
-
100
- res = await this.cryptoCompare.getPrice(query);
101
- if (res) return res;
102
- }
103
- return null;
104
- }
105
-
106
- /**
107
- * Get detailed token info (market cap, volume, liquidity, etc.)
108
- * @param query Can be a symbol (e.g., 'BTC') or a contract address
109
- * @param network Optional network for DEX addresses
110
- */
111
- async info(query: string, network?: string): Promise<TokenInfo | null> {
112
- if (this.isAddress(query)) {
113
- let res = await this.dexScreener.getTokenInfo(query);
114
- if (res) return res;
115
-
116
- if (network) {
117
- res = await this.geckoTerminal.getTokenInfo(query, network);
118
- if (res) return res;
119
- }
120
- } else {
121
- // Attempt in order of best info
122
- let res = await this.coinGecko.getTokenInfo(query);
123
- if (res) return res;
124
-
125
- res = await this.coinpaprika.getTokenInfo(query);
126
- if (res) return res;
127
-
128
- res = await this.coincap.getTokenInfo(query);
129
- if (res) return res;
130
-
131
- res = await this.binance.getTokenInfo(query);
132
- if (res) return res;
133
-
134
- res = await this.cryptoCompare.getTokenInfo(query);
135
- if (res) return res;
136
- }
137
- return null;
138
- }
139
-
140
- /**
141
- * Get global market data
142
- */
143
- async market(): Promise<GlobalMarketData | null> {
144
- try {
145
- const res = await fetch(`https://api.coingecko.com/api/v3/global`);
146
- if (!res.ok) return null;
147
- const data = await res.json();
148
- const d = data.data;
149
-
150
- return {
151
- totalMarketCapUsd: d.total_market_cap.usd,
152
- totalVolume24hUsd: d.total_volume.usd,
153
- bitcoinDominancePercentage: d.market_cap_percentage.btc,
154
- ethereumDominancePercentage: d.market_cap_percentage.eth,
155
- activeCryptocurrencies: d.active_cryptocurrencies,
156
- lastUpdated: d.updated_at * 1000,
157
- };
158
- } catch (e) {
159
- return null;
160
- }
161
- }
162
-
163
- /**
164
- * Get trending coins
165
- */
166
- async trending(): Promise<TrendingCoin[]> {
167
- try {
168
- const res = await fetch(`https://api.coingecko.com/api/v3/search/trending`);
169
- if (!res.ok) return [];
170
- const data = await res.json();
171
-
172
- return data.coins.slice(0, 10).map((c: any) => ({
173
- id: c.item.id,
174
- name: c.item.name,
175
- symbol: c.item.symbol.toUpperCase(),
176
- marketCapRank: c.item.market_cap_rank,
177
- priceUsd: c.item.data?.price,
178
- priceChange24h: c.item.data?.price_change_percentage_24h?.usd,
179
- volume24h: c.item.data?.total_volume,
180
- }));
181
- } catch (e) {
182
- return [];
183
- }
184
- }
185
-
186
- /**
187
- * Get top 50 coins by market cap
188
- */
189
- async top(): Promise<TopCoin[]> {
190
- try {
191
- 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`);
192
- if (!res.ok) return [];
193
- const data = await res.json();
194
-
195
- return data.map((c: any) => ({
196
- id: c.id,
197
- name: c.name,
198
- symbol: c.symbol.toUpperCase(),
199
- marketCapRank: c.market_cap_rank,
200
- priceUsd: c.current_price,
201
- marketCap: c.market_cap,
202
- volume24h: c.total_volume,
203
- priceChange24h: c.price_change_percentage_24h,
204
- }));
205
- } catch (e) {
206
- return [];
207
- }
208
- }
209
-
210
- /**
211
- * Get fear and greed index
212
- */
213
- async sentiment(): Promise<SentimentData | null> {
214
- try {
215
- const res = await fetch(`https://api.alternative.me/fng/?limit=1`);
216
- if (!res.ok) return null;
217
- const data = await res.json();
218
- const item = data.data[0];
219
-
220
- return {
221
- value: parseInt(item.value),
222
- classification: item.value_classification,
223
- lastUpdated: parseInt(item.timestamp) * 1000,
224
- };
225
- } catch (e) {
226
- return null;
227
- }
228
- }
229
-
230
- /**
231
- * Get current ethereum gas prices (in Gwei)
232
- */
233
- async gas(): Promise<GasData | null> {
234
- try {
235
- // Using Etherscan public API (rate limited but works for basic usage)
236
- const res = await fetch(`https://api.etherscan.io/api?module=gastracker&action=gasoracle`);
237
- if (!res.ok) return null;
238
- const data = await res.json();
239
-
240
- if (data.status !== '1') return null;
241
-
242
- return {
243
- network: 'Ethereum',
244
- low: parseFloat(data.result.SafeGasPrice),
245
- average: parseFloat(data.result.ProposeGasPrice),
246
- high: parseFloat(data.result.FastGasPrice),
247
- baseFee: parseFloat(data.result.suggestBaseFee),
248
- };
249
- } catch (e) {
250
- return null;
251
- }
252
- }
253
-
254
- /**
255
- * Get historical chart data for a coin
256
- * @param query Coin symbol or ID
257
- * @param days Number of days (e.g. 1, 7, 30)
258
- */
259
- async chart(query: string, days: number = 7): Promise<ChartData | null> {
260
- try {
261
- let id = query.toLowerCase();
262
- // Try to find the correct coingecko ID if a symbol is passed
263
- const searchRes = await this.coinGecko.search(id);
264
- const match = searchRes.find(s => s.symbol.toLowerCase() === id || s.id === id);
265
- if (match && match.id) {
266
- id = match.id;
267
- }
268
-
269
- const res = await fetch(`https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=${days}`);
270
- if (!res.ok) return null;
271
- const data = await res.json();
272
-
273
- if (!data.prices || data.prices.length === 0) return null;
274
-
275
- const prices = data.prices.map((p: any) => p[1]);
276
- const timestamps = data.prices.map((p: any) => p[0]);
277
-
278
- return {
279
- prices,
280
- timestamps,
281
- minPrice: Math.min(...prices),
282
- maxPrice: Math.max(...prices),
283
- };
284
- } catch (e) {
285
- return null;
286
- }
287
- }
1
+ import { ChartData, DexPairInfo, GasData, GlobalMarketData, ICryptoProvider, PriceData, SearchResult, SentimentData, TokenInfo, TrendingCoin, TopCoin } from './types.js';
2
+ import { CoinGeckoProvider } from './providers/coingecko.js';
3
+ import { DexScreenerProvider } from './providers/dexscreener.js';
4
+ import { GeckoTerminalProvider } from './providers/geckoterminal.js';
5
+ import { BinanceProvider } from './providers/binance.js';
6
+ import { CoinpaprikaProvider } from './providers/coinpaprika.js';
7
+ import { CoinCapProvider } from './providers/coincap.js';
8
+ import { CryptoCompareProvider } from './providers/cryptocompare.js';
9
+
10
+ export interface CrypullOptions {
11
+ providers?: ICryptoProvider[];
12
+ }
13
+
14
+ export class Crypull {
15
+ public providers: ICryptoProvider[];
16
+
17
+ // Standard free providers
18
+ private coinGecko = new CoinGeckoProvider();
19
+ private dexScreener = new DexScreenerProvider();
20
+ private geckoTerminal = new GeckoTerminalProvider();
21
+ private binance = new BinanceProvider();
22
+ private coinpaprika = new CoinpaprikaProvider();
23
+ private coincap = new CoinCapProvider();
24
+ private cryptoCompare = new CryptoCompareProvider();
25
+
26
+ constructor(options?: CrypullOptions) {
27
+ this.providers = options?.providers || [
28
+ this.binance,
29
+ this.coinGecko,
30
+ this.coinpaprika,
31
+ this.coincap,
32
+ this.dexScreener,
33
+ this.geckoTerminal,
34
+ this.cryptoCompare
35
+ ];
36
+ }
37
+
38
+ private isAddress(query: string): boolean {
39
+ return (query.startsWith('0x') && query.length === 42) || query.length > 30;
40
+ }
41
+
42
+ /**
43
+ * Search for a token or coin across multiple providers in parallel
44
+ */
45
+ async search(query: string): Promise<SearchResult[]> {
46
+ const promises = [
47
+ this.coinGecko.search(query),
48
+ this.dexScreener.search(query),
49
+ this.coinpaprika.search(query),
50
+ this.coincap.search(query)
51
+ ];
52
+
53
+ const results = await Promise.allSettled(promises);
54
+ const combined: SearchResult[] = [];
55
+
56
+ for (const res of results) {
57
+ if (res.status === 'fulfilled' && res.value) {
58
+ combined.push(...res.value);
59
+ }
60
+ }
61
+
62
+ // Deduplicate by symbol to keep results clean
63
+ const seen = new Set();
64
+ return combined.filter(item => {
65
+ const key = `${item.symbol}`;
66
+ if (seen.has(key)) return false;
67
+ seen.add(key);
68
+ return true;
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Get the current price of a cryptocurrency
74
+ * @param query Can be a symbol (e.g., 'BTC') or a contract address
75
+ * @param network Optional network for DEX addresses (e.g., 'eth', 'bsc')
76
+ */
77
+ async price(query: string, network?: string): Promise<PriceData | null> {
78
+ if (this.isAddress(query)) {
79
+ let res = await this.dexScreener.getPrice(query);
80
+ if (res) return res;
81
+
82
+ if (network) {
83
+ res = await this.geckoTerminal.getPrice(query, network);
84
+ if (res) return res;
85
+ }
86
+ } else {
87
+ // Fast top-tier providers first
88
+ let res = await this.binance.getPrice(query);
89
+ if (res) return res;
90
+
91
+ res = await this.coincap.getPrice(query);
92
+ if (res) return res;
93
+
94
+ res = await this.coinGecko.getPrice(query);
95
+ if (res) return res;
96
+
97
+ res = await this.coinpaprika.getPrice(query);
98
+ if (res) return res;
99
+
100
+ res = await this.cryptoCompare.getPrice(query);
101
+ if (res) return res;
102
+ }
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * Get detailed token info (market cap, volume, liquidity, etc.)
108
+ * @param query Can be a symbol (e.g., 'BTC') or a contract address
109
+ * @param network Optional network for DEX addresses
110
+ */
111
+ async info(query: string, network?: string): Promise<TokenInfo | null> {
112
+ if (this.isAddress(query)) {
113
+ let res = await this.dexScreener.getTokenInfo(query);
114
+ if (res) return res;
115
+
116
+ if (network) {
117
+ res = await this.geckoTerminal.getTokenInfo(query, network);
118
+ if (res) return res;
119
+ }
120
+ } else {
121
+ // Attempt in order of best info
122
+ let res = await this.coinGecko.getTokenInfo(query);
123
+ if (res) return res;
124
+
125
+ res = await this.coinpaprika.getTokenInfo(query);
126
+ if (res) return res;
127
+
128
+ res = await this.coincap.getTokenInfo(query);
129
+ if (res) return res;
130
+
131
+ res = await this.binance.getTokenInfo(query);
132
+ if (res) return res;
133
+
134
+ res = await this.cryptoCompare.getTokenInfo(query);
135
+ if (res) return res;
136
+ }
137
+ return null;
138
+ }
139
+
140
+ /**
141
+ * Get global market data
142
+ */
143
+ async market(): Promise<GlobalMarketData | null> {
144
+ try {
145
+ const res = await fetch(`https://api.coingecko.com/api/v3/global`);
146
+ if (!res.ok) return null;
147
+ const data = await res.json();
148
+ const d = data.data;
149
+
150
+ return {
151
+ totalMarketCapUsd: d.total_market_cap.usd,
152
+ totalVolume24hUsd: d.total_volume.usd,
153
+ bitcoinDominancePercentage: d.market_cap_percentage.btc,
154
+ ethereumDominancePercentage: d.market_cap_percentage.eth,
155
+ activeCryptocurrencies: d.active_cryptocurrencies,
156
+ lastUpdated: d.updated_at * 1000,
157
+ };
158
+ } catch (e) {
159
+ return null;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get trending coins
165
+ */
166
+ async trending(): Promise<TrendingCoin[]> {
167
+ try {
168
+ const res = await fetch(`https://api.coingecko.com/api/v3/search/trending`);
169
+ if (!res.ok) return [];
170
+ const data = await res.json();
171
+
172
+ return data.coins.slice(0, 10).map((c: any) => ({
173
+ id: c.item.id,
174
+ name: c.item.name,
175
+ symbol: c.item.symbol.toUpperCase(),
176
+ marketCapRank: c.item.market_cap_rank,
177
+ priceUsd: c.item.data?.price,
178
+ priceChange24h: c.item.data?.price_change_percentage_24h?.usd,
179
+ volume24h: c.item.data?.total_volume,
180
+ }));
181
+ } catch (e) {
182
+ return [];
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Get top 50 coins by market cap
188
+ */
189
+ async top(): Promise<TopCoin[]> {
190
+ try {
191
+ 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`);
192
+ if (!res.ok) return [];
193
+ const data = await res.json();
194
+
195
+ return data.map((c: any) => ({
196
+ id: c.id,
197
+ name: c.name,
198
+ symbol: c.symbol.toUpperCase(),
199
+ marketCapRank: c.market_cap_rank,
200
+ priceUsd: c.current_price,
201
+ marketCap: c.market_cap,
202
+ volume24h: c.total_volume,
203
+ priceChange24h: c.price_change_percentage_24h,
204
+ }));
205
+ } catch (e) {
206
+ return [];
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Get fear and greed index
212
+ */
213
+ async sentiment(): Promise<SentimentData | null> {
214
+ try {
215
+ const res = await fetch(`https://api.alternative.me/fng/?limit=1`);
216
+ if (!res.ok) return null;
217
+ const data = await res.json();
218
+ const item = data.data[0];
219
+
220
+ return {
221
+ value: parseInt(item.value),
222
+ classification: item.value_classification,
223
+ lastUpdated: parseInt(item.timestamp) * 1000,
224
+ };
225
+ } catch (e) {
226
+ return null;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Get current ethereum gas prices (in Gwei)
232
+ */
233
+ async gas(): Promise<GasData | null> {
234
+ try {
235
+ // Using Etherscan public API V2 (rate limited but works for basic usage)
236
+ const res = await fetch(`https://api.etherscan.io/v2/api?chainid=1&module=gastracker&action=gasoracle`);
237
+ if (!res.ok) return null;
238
+ const data = await res.json();
239
+
240
+ if (data.status !== '1') return null;
241
+
242
+ return {
243
+ network: 'Ethereum',
244
+ low: parseFloat(data.result.SafeGasPrice),
245
+ average: parseFloat(data.result.ProposeGasPrice),
246
+ high: parseFloat(data.result.FastGasPrice),
247
+ baseFee: parseFloat(data.result.suggestBaseFee),
248
+ };
249
+ } catch (e) {
250
+ return null;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Get historical chart data for a coin
256
+ * @param query Coin symbol or ID
257
+ * @param days Number of days (e.g. 1, 7, 30)
258
+ */
259
+ async chart(query: string, days: number = 7): Promise<ChartData | null> {
260
+ try {
261
+ let id = query.toLowerCase();
262
+ // Try to find the correct coingecko ID if a symbol is passed
263
+ const searchRes = await this.coinGecko.search(id);
264
+ const match = searchRes.find(s => s.symbol.toLowerCase() === id || s.id === id);
265
+ if (match && match.id) {
266
+ id = match.id;
267
+ }
268
+
269
+ const res = await fetch(`https://api.coingecko.com/api/v3/coins/${id}/market_chart?vs_currency=usd&days=${days}`);
270
+ if (!res.ok) return null;
271
+ const data = await res.json();
272
+
273
+ if (!data.prices || data.prices.length === 0) return null;
274
+
275
+ const prices = data.prices.map((p: any) => p[1]);
276
+ const timestamps = data.prices.map((p: any) => p[0]);
277
+
278
+ return {
279
+ prices,
280
+ timestamps,
281
+ minPrice: Math.min(...prices),
282
+ maxPrice: Math.max(...prices),
283
+ };
284
+ } catch (e) {
285
+ return null;
286
+ }
287
+ }
288
288
  }