@0xprotovox/deficlaw 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +230 -0
  2. package/dist/analysis/formatOutput.d.ts +4 -0
  3. package/dist/analysis/formatOutput.js +209 -0
  4. package/dist/analysis/formatOutput.js.map +1 -0
  5. package/dist/analysis/riskScorer.d.ts +6 -0
  6. package/dist/analysis/riskScorer.js +104 -0
  7. package/dist/analysis/riskScorer.js.map +1 -0
  8. package/dist/analysis/summaryGenerator.d.ts +68 -0
  9. package/dist/analysis/summaryGenerator.js +171 -0
  10. package/dist/analysis/summaryGenerator.js.map +1 -0
  11. package/dist/cache/memoryCache.d.ts +13 -0
  12. package/dist/cache/memoryCache.js +31 -0
  13. package/dist/cache/memoryCache.js.map +1 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +12 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/server.d.ts +7 -0
  18. package/dist/server.js +76 -0
  19. package/dist/server.js.map +1 -0
  20. package/dist/sources/dexscreener.d.ts +20 -0
  21. package/dist/sources/dexscreener.js +117 -0
  22. package/dist/sources/dexscreener.js.map +1 -0
  23. package/dist/sources/gmgn.d.ts +8 -0
  24. package/dist/sources/gmgn.js +210 -0
  25. package/dist/sources/gmgn.js.map +1 -0
  26. package/dist/sources/solanaRpc.d.ts +14 -0
  27. package/dist/sources/solanaRpc.js +60 -0
  28. package/dist/sources/solanaRpc.js.map +1 -0
  29. package/dist/tools/analyzeToken.d.ts +8 -0
  30. package/dist/tools/analyzeToken.js +253 -0
  31. package/dist/tools/analyzeToken.js.map +1 -0
  32. package/dist/tools/getPrice.d.ts +17 -0
  33. package/dist/tools/getPrice.js +13 -0
  34. package/dist/tools/getPrice.js.map +1 -0
  35. package/dist/tools/getTopTraders.d.ts +45 -0
  36. package/dist/tools/getTopTraders.js +44 -0
  37. package/dist/tools/getTopTraders.js.map +1 -0
  38. package/dist/tools/getTrending.d.ts +9 -0
  39. package/dist/tools/getTrending.js +16 -0
  40. package/dist/tools/getTrending.js.map +1 -0
  41. package/dist/types/index.d.ts +127 -0
  42. package/dist/types/index.js +2 -0
  43. package/dist/types/index.js.map +1 -0
  44. package/package.json +46 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * AI-like Summary Generator — code-generated, no API calls
3
+ * Produces human-readable analysis from raw data
4
+ */
5
+ function fmt(n) {
6
+ if (Math.abs(n) >= 1_000_000)
7
+ return `$${(n / 1_000_000).toFixed(1)}M`;
8
+ if (Math.abs(n) >= 1_000)
9
+ return `$${(n / 1_000).toFixed(1)}K`;
10
+ return `$${n.toFixed(0)}`;
11
+ }
12
+ function pct(n) {
13
+ return `${(n * 100).toFixed(1)}%`;
14
+ }
15
+ export function generateSummary(data) {
16
+ const lines = [];
17
+ const { token, price, market, risk, holders, kols, security } = data;
18
+ // Opening line — what is this token?
19
+ const dexName = market.dex === 'pumpswap' ? 'PumpSwap' : market.dex === 'raydium' ? 'Raydium' : market.dex;
20
+ lines.push(`${token.symbol} is a ${token.age} old ${dexName} token with ${fmt(market.liquidity)} liquidity and ${fmt(market.marketCap)} market cap.`);
21
+ // Price action
22
+ const priceSignals = [];
23
+ if (Math.abs(price.change24h) > 20) {
24
+ priceSignals.push(price.change24h > 0
25
+ ? `up ${price.change24h.toFixed(0)}% in 24h, strong momentum`
26
+ : `down ${Math.abs(price.change24h).toFixed(0)}% in 24h, heavy selling`);
27
+ }
28
+ else if (Math.abs(price.change24h) > 5) {
29
+ priceSignals.push(price.change24h > 0
30
+ ? `up ${price.change24h.toFixed(1)}% today`
31
+ : `down ${Math.abs(price.change24h).toFixed(1)}% today`);
32
+ }
33
+ else {
34
+ priceSignals.push('price is flat today');
35
+ }
36
+ if (market.volume1h === 0 && market.volume24h < 1000) {
37
+ priceSignals.push('volume is dead right now');
38
+ }
39
+ else if (market.volume1h > market.volume24h * 0.2) {
40
+ priceSignals.push('volume spiking in the last hour');
41
+ }
42
+ if (market.volumeLiquidityRatio < 0.1) {
43
+ priceSignals.push('very low trading activity relative to liquidity');
44
+ }
45
+ else if (market.volumeLiquidityRatio > 2) {
46
+ priceSignals.push('high turnover, lots of trading relative to pool size');
47
+ }
48
+ lines.push(priceSignals[0][0].toUpperCase() + priceSignals[0].slice(1) + (priceSignals.length > 1 ? ', ' + priceSignals.slice(1).join(', ') : '') + '.');
49
+ // Holder analysis
50
+ if (holders) {
51
+ const { concentration, categories, sentiment, pressure, devWallet } = holders;
52
+ // Concentration
53
+ if (concentration.top5Pct > 0.5) {
54
+ lines.push(`Top 5 wallets control ${pct(concentration.top5Pct)} of supply, very concentrated.`);
55
+ }
56
+ else if (concentration.top10Pct > 0.5) {
57
+ lines.push(`Top 10 hold ${pct(concentration.top10Pct)}, moderately concentrated.`);
58
+ }
59
+ else {
60
+ lines.push(`Supply is well distributed, top 10 hold ${pct(concentration.top10Pct)}.`);
61
+ }
62
+ // Diamond hands vs paper hands
63
+ if (categories.diamondHands > holders.total * 0.4) {
64
+ lines.push(`${categories.diamondHands} diamond hands (${pct(categories.diamondHands / holders.total)}), strong holder conviction.`);
65
+ }
66
+ // Profit/loss sentiment
67
+ if (sentiment.profitRatio < 0.2) {
68
+ lines.push(`Only ${pct(sentiment.profitRatio)} of holders are in profit, most are underwater with avg loss of ${fmt(Math.abs(sentiment.avgPnlPerHolder))} per wallet.`);
69
+ }
70
+ else if (sentiment.profitRatio > 0.5) {
71
+ lines.push(`${pct(sentiment.profitRatio)} of holders are profitable, healthy sentiment.`);
72
+ }
73
+ else {
74
+ lines.push(`${pct(sentiment.profitRatio)} in profit, ${pct(1 - sentiment.profitRatio)} underwater.`);
75
+ }
76
+ // Buy/sell pressure
77
+ if (pressure.buySellRatio > 3) {
78
+ lines.push(`Buy pressure is strong at ${pressure.buySellRatio.toFixed(1)}:1 ratio (${pressure.totalBuyTx} buys vs ${pressure.totalSellTx} sells), accumulation phase.`);
79
+ }
80
+ else if (pressure.buySellRatio > 1.5) {
81
+ lines.push(`More buying than selling (${pressure.buySellRatio.toFixed(1)}:1), slight accumulation.`);
82
+ }
83
+ else if (pressure.buySellRatio < 0.5) {
84
+ lines.push(`Heavy sell pressure (${pressure.buySellRatio.toFixed(2)}:1), distribution phase.`);
85
+ }
86
+ else {
87
+ lines.push(`Buy/sell pressure is balanced.`);
88
+ }
89
+ // Snipers & fresh wallets
90
+ if (categories.snipers > 3) {
91
+ lines.push(`${categories.snipers} sniper wallets detected, watch for dumps.`);
92
+ }
93
+ if (categories.freshWallets > holders.total * 0.2) {
94
+ lines.push(`${pct(categories.freshWallets / holders.total)} fresh wallets, possible wash trading or bundle.`);
95
+ }
96
+ // Dev wallet
97
+ if (devWallet) {
98
+ if (devWallet.status === 'sold_most') {
99
+ lines.push(`Dev wallet sold most of their tokens, no longer aligned with holders.`);
100
+ }
101
+ else if (devWallet.status === 'selling') {
102
+ lines.push(`Dev is actively selling, holding ${pct(devWallet.holdingPercent)}.`);
103
+ }
104
+ else {
105
+ lines.push(`Dev still holding ${pct(devWallet.holdingPercent)}, aligned with project.`);
106
+ }
107
+ }
108
+ else {
109
+ lines.push(`No dev wallet detected in top holders.`);
110
+ }
111
+ }
112
+ // KOL analysis
113
+ if (kols && kols.length > 0) {
114
+ const kolsWithTwitter = kols.filter(k => k.twitterHandle);
115
+ const kolsSelling = kols.filter(k => k.status === 'selling');
116
+ const kolsHolding = kols.filter(k => k.status === 'holding');
117
+ const kolsProfitable = kols.filter(k => k.pnl > 0);
118
+ lines.push(`${kols.length} KOLs tracked: ${kolsHolding.length} holding, ${kolsSelling.length} selling.`);
119
+ if (kolsSelling.length > kolsHolding.length) {
120
+ lines.push(`Most KOLs are exiting, bearish signal.`);
121
+ }
122
+ else if (kolsHolding.length > kols.length * 0.6) {
123
+ lines.push(`Majority of KOLs still holding, they see potential.`);
124
+ }
125
+ if (kolsProfitable.length === 0 && kols.length > 3) {
126
+ lines.push(`None of the KOLs are profitable on this token.`);
127
+ }
128
+ }
129
+ // Security
130
+ if (security) {
131
+ const securityIssues = [];
132
+ if (security.mintAuthority === 'active')
133
+ securityIssues.push('mint authority is active (dev can print tokens)');
134
+ if (security.freezeAuthority === 'active')
135
+ securityIssues.push('freeze authority active (tokens can be frozen)');
136
+ if (securityIssues.length > 0) {
137
+ lines.push(`⚠️ Security concerns: ${securityIssues.join(', ')}.`);
138
+ }
139
+ else {
140
+ lines.push(`Contract looks safe: mint and freeze authorities revoked.`);
141
+ }
142
+ if (security.isLpTopHolder) {
143
+ lines.push(`Top holder is the LP pool, real top holder owns ${pct(security.realTopHolderPct)}.`);
144
+ }
145
+ }
146
+ // Risk verdict
147
+ if (risk.level === 'CRITICAL') {
148
+ lines.push(`🔴 HIGH RISK — multiple red flags, extreme caution advised.`);
149
+ }
150
+ else if (risk.level === 'HIGH') {
151
+ lines.push(`🟠 Elevated risk, do thorough research before entering.`);
152
+ }
153
+ else if (risk.level === 'MEDIUM') {
154
+ lines.push(`🟡 Moderate risk, some concerns but nothing critical.`);
155
+ }
156
+ else {
157
+ lines.push(`🟢 Lower risk profile based on available data.`);
158
+ }
159
+ // Final actionable insight
160
+ if (market.volume1h === 0 && market.volume24h < 500) {
161
+ lines.push(`⚠️ Token appears dormant. Check if community and socials are still active before entering.`);
162
+ }
163
+ else if (holders && holders.pressure.buySellRatio > 3 && holders.sentiment.profitRatio < 0.25) {
164
+ lines.push(`Interesting setup: strong buy pressure despite most holders being underwater. Could mean accumulation before a move, or dead cat buying.`);
165
+ }
166
+ else if (holders && holders.categories.diamondHands > holders.total * 0.5 && holders.sentiment.profitRatio < 0.2) {
167
+ lines.push(`Holders refuse to sell despite being down. Either strong conviction or stuck with illiquid bags. Watch volume for signs of life.`);
168
+ }
169
+ return lines.join('\n');
170
+ }
171
+ //# sourceMappingURL=summaryGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaryGenerator.js","sourceRoot":"","sources":["../../src/analysis/summaryGenerator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * TTL-based in-memory cache
3
+ * Pattern from TradingBox dexScreenerService.js
4
+ */
5
+ export declare class MemoryCache<T> {
6
+ private cache;
7
+ private ttlMs;
8
+ constructor(ttlMs: number);
9
+ get(key: string): T | null;
10
+ set(key: string, data: T): void;
11
+ has(key: string): boolean;
12
+ clear(): void;
13
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * TTL-based in-memory cache
3
+ * Pattern from TradingBox dexScreenerService.js
4
+ */
5
+ export class MemoryCache {
6
+ cache = new Map();
7
+ ttlMs;
8
+ constructor(ttlMs) {
9
+ this.ttlMs = ttlMs;
10
+ }
11
+ get(key) {
12
+ const entry = this.cache.get(key);
13
+ if (!entry)
14
+ return null;
15
+ if (Date.now() - entry.ts > this.ttlMs) {
16
+ this.cache.delete(key);
17
+ return null;
18
+ }
19
+ return entry.data;
20
+ }
21
+ set(key, data) {
22
+ this.cache.set(key, { data, ts: Date.now() });
23
+ }
24
+ has(key) {
25
+ return this.get(key) !== null;
26
+ }
27
+ clear() {
28
+ this.cache.clear();
29
+ }
30
+ }
31
+ //# sourceMappingURL=memoryCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memoryCache.js","sourceRoot":"","sources":["../../src/cache/memoryCache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,WAAW;IACd,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;IACnD,KAAK,CAAS;IAEtB,YAAY,KAAa;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,IAAO;QACtB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * deficlaw — DeFi MCP Server for Claude Code
4
+ * First open-source DeFi MCP. Token analysis, prices, trending, holder intelligence.
5
+ * by @0xprotovox
6
+ */
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { createServer } from './server.js';
9
+ const server = createServer();
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;AAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * DeFi MCP Server
3
+ * First open-source DeFi MCP for Claude Code
4
+ * by @0xprotovox
5
+ */
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ export declare function createServer(): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * DeFi MCP Server
3
+ * First open-source DeFi MCP for Claude Code
4
+ * by @0xprotovox
5
+ */
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { z } from 'zod';
8
+ import { handleAnalyzeToken } from './tools/analyzeToken.js';
9
+ import { handleGetPrice } from './tools/getPrice.js';
10
+ import { handleGetTrending } from './tools/getTrending.js';
11
+ import { handleGetTopTraders } from './tools/getTopTraders.js';
12
+ import { formatAnalysis } from './analysis/formatOutput.js';
13
+ export function createServer() {
14
+ const server = new McpServer({
15
+ name: 'deficlaw',
16
+ version: '0.1.0',
17
+ });
18
+ // ── analyze_token ──
19
+ server.tool('analyze_token', 'Full token analysis: price, volume, liquidity, holder intelligence, risk scoring. Supports Solana tokens with GMGN holder data.', {
20
+ address: z.string().describe('Token contract address (e.g. Solana mint address)'),
21
+ chain: z.string().default('solana').describe('Blockchain: solana, ethereum, bsc, base, arbitrum'),
22
+ include_holders: z.boolean().default(true).describe('Include GMGN holder analysis (slower, needs Playwright)'),
23
+ }, async (args) => {
24
+ try {
25
+ const result = await handleAnalyzeToken(args);
26
+ if ('error' in result)
27
+ return { content: [{ type: 'text', text: JSON.stringify(result) }], isError: true };
28
+ const formatted = formatAnalysis(result);
29
+ return { content: [{ type: 'text', text: formatted }] };
30
+ }
31
+ catch (err) {
32
+ return { content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }], isError: true };
33
+ }
34
+ });
35
+ // ── get_price ──
36
+ server.tool('get_price', 'Quick token price lookup with 24h change, volume, liquidity, and market cap.', {
37
+ token: z.string().describe('Token address or search query'),
38
+ chain: z.string().default('solana').describe('Blockchain: solana, ethereum, bsc, base, arbitrum'),
39
+ }, async (args) => {
40
+ try {
41
+ const result = await handleGetPrice(args);
42
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
43
+ }
44
+ catch (err) {
45
+ return { content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }], isError: true };
46
+ }
47
+ });
48
+ // ── get_trending ──
49
+ server.tool('get_trending', 'Get trending/boosted tokens on a blockchain. Returns top tokens by volume and boost activity.', {
50
+ chain: z.string().default('solana').describe('Blockchain: solana, ethereum, bsc, base, arbitrum'),
51
+ limit: z.number().default(20).describe('Number of tokens to return (max 50)'),
52
+ }, async (args) => {
53
+ try {
54
+ const result = await handleGetTrending(args);
55
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
56
+ }
57
+ catch (err) {
58
+ return { content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }], isError: true };
59
+ }
60
+ });
61
+ // ── get_top_traders ──
62
+ server.tool('get_top_traders', 'Find who made and lost money on a token. Shows top winners and losers with PnL, tags, and Twitter handles.', {
63
+ address: z.string().describe('Token contract address'),
64
+ limit: z.number().default(10).describe('Number of top winners/losers to show'),
65
+ }, async (args) => {
66
+ try {
67
+ const result = await handleGetTopTraders(args);
68
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
69
+ }
70
+ catch (err) {
71
+ return { content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }], isError: true };
72
+ }
73
+ });
74
+ return server;
75
+ }
76
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { DexPair } from '../types/index.js';
2
+ /** Get best pair for a token address (highest liquidity) */
3
+ export declare function getTokenPair(address: string, chain?: string): Promise<DexPair | null>;
4
+ /** Quick price lookup */
5
+ export declare function getPrice(address: string, chain?: string): Promise<{
6
+ address: string;
7
+ symbol: string;
8
+ name: string;
9
+ priceUsd: number;
10
+ priceChange1h: number;
11
+ priceChange24h: number;
12
+ volume24h: number;
13
+ liquidity: number;
14
+ marketCap: number;
15
+ dex: string;
16
+ } | null>;
17
+ /** Search tokens by name or symbol */
18
+ export declare function searchTokens(query: string): Promise<DexPair[]>;
19
+ /** Get trending/boosted tokens */
20
+ export declare function getTrending(chain?: string, limit?: number): Promise<any[]>;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * DexScreener API Client
3
+ * Pattern from TradingBox dexScreenerService.js
4
+ * Free API, no auth needed. Rate limit: ~300 req/min (we cap at 150)
5
+ */
6
+ import { MemoryCache } from '../cache/memoryCache.js';
7
+ const BASE_URL = 'https://api.dexscreener.com';
8
+ const PAIR_CACHE_TTL = 5 * 60 * 1000; // 5 min
9
+ const PRICE_CACHE_TTL = 30 * 1000; // 30s
10
+ const TRENDING_CACHE_TTL = 30 * 1000; // 30s
11
+ // Simple rate limiter — sequential queue with min interval
12
+ let lastRequestMs = 0;
13
+ const MIN_INTERVAL = 200; // 200ms between requests
14
+ async function throttledFetch(url, timeoutMs = 8000) {
15
+ const now = Date.now();
16
+ const wait = MIN_INTERVAL - (now - lastRequestMs);
17
+ if (wait > 0)
18
+ await new Promise(r => setTimeout(r, wait));
19
+ lastRequestMs = Date.now();
20
+ const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
21
+ if (!res.ok)
22
+ throw new Error(`DexScreener ${res.status}: ${res.statusText}`);
23
+ return res.json();
24
+ }
25
+ const pairCache = new MemoryCache(PAIR_CACHE_TTL);
26
+ const priceCache = new MemoryCache(PRICE_CACHE_TTL);
27
+ const trendingCache = new MemoryCache(TRENDING_CACHE_TTL);
28
+ /** Get best pair for a token address (highest liquidity) */
29
+ export async function getTokenPair(address, chain = 'solana') {
30
+ const cacheKey = `${chain}:${address}`;
31
+ const cached = pairCache.get(cacheKey);
32
+ if (cached)
33
+ return cached;
34
+ const data = await throttledFetch(`${BASE_URL}/latest/dex/tokens/${address}`);
35
+ const pairs = data.pairs || [];
36
+ // Filter by chain and sort by liquidity
37
+ const chainPairs = pairs
38
+ .filter(p => p.chainId === chain)
39
+ .sort((a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0));
40
+ const best = chainPairs[0] || pairs[0] || null;
41
+ if (best)
42
+ pairCache.set(cacheKey, best);
43
+ return best;
44
+ }
45
+ /** Quick price lookup */
46
+ export async function getPrice(address, chain = 'solana') {
47
+ const cacheKey = `price:${chain}:${address}`;
48
+ const cached = priceCache.get(cacheKey);
49
+ if (cached)
50
+ return cached;
51
+ const pair = await getTokenPair(address, chain);
52
+ if (!pair)
53
+ return null;
54
+ const result = {
55
+ address: pair.baseToken.address,
56
+ symbol: pair.baseToken.symbol,
57
+ name: pair.baseToken.name,
58
+ priceUsd: parseFloat(pair.priceUsd) || 0,
59
+ priceChange1h: pair.priceChange?.h1 || 0,
60
+ priceChange24h: pair.priceChange?.h24 || 0,
61
+ volume24h: pair.volume?.h24 || 0,
62
+ liquidity: pair.liquidity?.usd || 0,
63
+ marketCap: pair.marketCap || 0,
64
+ dex: pair.dexId,
65
+ };
66
+ priceCache.set(cacheKey, result);
67
+ return result;
68
+ }
69
+ /** Search tokens by name or symbol */
70
+ export async function searchTokens(query) {
71
+ const data = await throttledFetch(`${BASE_URL}/latest/dex/search?q=${encodeURIComponent(query)}`);
72
+ return (data.pairs || []).slice(0, 20);
73
+ }
74
+ /** Get trending/boosted tokens */
75
+ export async function getTrending(chain = 'solana', limit = 20) {
76
+ const cacheKey = `trending:${chain}`;
77
+ const cached = trendingCache.get(cacheKey);
78
+ if (cached)
79
+ return cached.slice(0, limit);
80
+ const [topData, latestData] = await Promise.all([
81
+ throttledFetch(`${BASE_URL}/token-boosts/top/v1`).catch(() => []),
82
+ throttledFetch(`${BASE_URL}/token-boosts/latest/v1`).catch(() => []),
83
+ ]);
84
+ const top = Array.isArray(topData) ? topData : [];
85
+ const latest = Array.isArray(latestData) ? latestData : [];
86
+ // Merge and deduplicate
87
+ const seen = new Set();
88
+ const merged = [];
89
+ for (const token of [...top, ...latest]) {
90
+ if (!token.tokenAddress || seen.has(token.tokenAddress))
91
+ continue;
92
+ if (token.chainId !== chain)
93
+ continue;
94
+ seen.add(token.tokenAddress);
95
+ merged.push({
96
+ address: token.tokenAddress,
97
+ symbol: token.description?.split('/')[0] || token.tokenAddress.slice(0, 6),
98
+ name: token.description || '',
99
+ chainId: token.chainId,
100
+ url: token.url,
101
+ boostAmount: token.totalAmount || 0,
102
+ });
103
+ }
104
+ // Enrich top items with price data
105
+ const enriched = await Promise.all(merged.slice(0, limit).map(async (t) => {
106
+ try {
107
+ const price = await getPrice(t.address, chain);
108
+ return { ...t, ...price };
109
+ }
110
+ catch {
111
+ return t;
112
+ }
113
+ }));
114
+ trendingCache.set(cacheKey, enriched);
115
+ return enriched.slice(0, limit);
116
+ }
117
+ //# sourceMappingURL=dexscreener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dexscreener.js","sourceRoot":"","sources":["../../src/sources/dexscreener.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Holder } from '../types/index.js';
2
+ export declare function isPlaywrightAvailable(): Promise<boolean>;
3
+ /** Get holders — try curl first, then Playwright */
4
+ export declare function getHolders(address: string): Promise<{
5
+ holders: Holder[];
6
+ kolHolders: Holder[];
7
+ }>;
8
+ export declare function shutdown(): Promise<void>;