@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.
- package/README.md +230 -0
- package/dist/analysis/formatOutput.d.ts +4 -0
- package/dist/analysis/formatOutput.js +209 -0
- package/dist/analysis/formatOutput.js.map +1 -0
- package/dist/analysis/riskScorer.d.ts +6 -0
- package/dist/analysis/riskScorer.js +104 -0
- package/dist/analysis/riskScorer.js.map +1 -0
- package/dist/analysis/summaryGenerator.d.ts +68 -0
- package/dist/analysis/summaryGenerator.js +171 -0
- package/dist/analysis/summaryGenerator.js.map +1 -0
- package/dist/cache/memoryCache.d.ts +13 -0
- package/dist/cache/memoryCache.js +31 -0
- package/dist/cache/memoryCache.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +76 -0
- package/dist/server.js.map +1 -0
- package/dist/sources/dexscreener.d.ts +20 -0
- package/dist/sources/dexscreener.js +117 -0
- package/dist/sources/dexscreener.js.map +1 -0
- package/dist/sources/gmgn.d.ts +8 -0
- package/dist/sources/gmgn.js +210 -0
- package/dist/sources/gmgn.js.map +1 -0
- package/dist/sources/solanaRpc.d.ts +14 -0
- package/dist/sources/solanaRpc.js +60 -0
- package/dist/sources/solanaRpc.js.map +1 -0
- package/dist/tools/analyzeToken.d.ts +8 -0
- package/dist/tools/analyzeToken.js +253 -0
- package/dist/tools/analyzeToken.js.map +1 -0
- package/dist/tools/getPrice.d.ts +17 -0
- package/dist/tools/getPrice.js +13 -0
- package/dist/tools/getPrice.js.map +1 -0
- package/dist/tools/getTopTraders.d.ts +45 -0
- package/dist/tools/getTopTraders.js +44 -0
- package/dist/tools/getTopTraders.js.map +1 -0
- package/dist/tools/getTrending.d.ts +9 -0
- package/dist/tools/getTrending.js +16 -0
- package/dist/tools/getTrending.js.map +1 -0
- package/dist/types/index.d.ts +127 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- 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"}
|
package/dist/index.d.ts
ADDED
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"}
|
package/dist/server.d.ts
ADDED
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>;
|