@fundtracer/mcp 1.0.1 → 1.0.3
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/fundtracer-mcp.js +99 -2948
- package/package.json +1 -2
package/fundtracer-mcp.js
CHANGED
|
@@ -219,2826 +219,60 @@ var init_tools = __esm({
|
|
|
219
219
|
}
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
-
// src/
|
|
223
|
-
var
|
|
224
|
-
__export(
|
|
225
|
-
SolanaFundingTreeService: () => SolanaFundingTreeService,
|
|
226
|
-
default: () => SolanaFundingTreeService_default
|
|
227
|
-
});
|
|
228
|
-
import fetch2 from "node-fetch";
|
|
229
|
-
var ALCHEMY_SOLANA_API, CACHE_TTL, KNOWN_PROGRAMS, SolanaFundingTreeService, SolanaFundingTreeService_default;
|
|
230
|
-
var init_SolanaFundingTreeService = __esm({
|
|
231
|
-
"src/services/SolanaFundingTreeService.ts"() {
|
|
232
|
-
ALCHEMY_SOLANA_API = "https://solana-mainnet.g.alchemy.com/v2/{apiKey}";
|
|
233
|
-
CACHE_TTL = 300;
|
|
234
|
-
KNOWN_PROGRAMS = {
|
|
235
|
-
// DEXs
|
|
236
|
-
"jupoK8gEJ4qEfD1k6QzJD7ssgvG5xTLwXgQNZHcPQ3fl": { name: "Jupiter", type: "dex" },
|
|
237
|
-
"jup3ZqFqEboGxBw1UnAUoxfXQA5ryiJPq3U5EEiW5eF": { name: "Jupiter", type: "dex" },
|
|
238
|
-
"CGkE4wDyY7mTDE7GQPPF2Uk6hK2Qa3x5xUhNYQqGKqBD": { name: "Raydium", type: "dex" },
|
|
239
|
-
"RVKdL2gt2zb2wWPXURQPswTUGqH2c6m8PMD3fESqC8H": { name: "Raydium", type: "dex" },
|
|
240
|
-
"orcaEKTdKx2wB3BmcSJwds6D3B4RST3JnBZKJx3QkqY9": { name: "Orca", type: "dex" },
|
|
241
|
-
// Bridges
|
|
242
|
-
"85VCBFdxR9exr5GtHVELq7uDT1mAc7YMFuq2bLtUMMmT": { name: "Wormhole", type: "bridge" },
|
|
243
|
-
"wormE4TGTQEaUMfNFxNA1XqJGMXH9Znk7aqZ3fGXq9p": { name: "Wormhole (Core)", type: "bridge" },
|
|
244
|
-
// CEX - common Solana热钱包
|
|
245
|
-
"2rXhuHUNDULrV6YLiPLZmm3xKg4zDqtLuZD8fFPTXw4": { name: "Coinbase", type: "cex" },
|
|
246
|
-
"F4vLeT4eq7YfmqNEBYJTdxYqNsuKXPxuPMe9jCBDm3k": { name: "Binance", type: "cex" },
|
|
247
|
-
// Known programs
|
|
248
|
-
"metaqbxxUurdFM34NHCNprmdGhDo4SyRQ9Dkjf53TwSp6y": { name: "Metaplex", type: "program" },
|
|
249
|
-
"TokenkegQfeZyiNwAJbNbGKPxGnhTNoZfFNYKDNgVEGPh": { name: "SPL Token", type: "program" },
|
|
250
|
-
"ATokenGPdCpDNQUxFJpMMzhxrZmLBhNpYY2MSKHvrkK7": { name: "Associated Token", type: "program" }
|
|
251
|
-
};
|
|
252
|
-
SolanaFundingTreeService = class {
|
|
253
|
-
apiKey;
|
|
254
|
-
cache = /* @__PURE__ */ new Map();
|
|
255
|
-
constructor(apiKey) {
|
|
256
|
-
this.apiKey = apiKey;
|
|
257
|
-
}
|
|
258
|
-
async rpcCall(method, params) {
|
|
259
|
-
const url = ALCHEMY_SOLANA_API.replace("{apiKey}", this.apiKey);
|
|
260
|
-
const response = await fetch2(url, {
|
|
261
|
-
method: "POST",
|
|
262
|
-
headers: { "Content-Type": "application/json" },
|
|
263
|
-
body: JSON.stringify({
|
|
264
|
-
jsonrpc: "2.0",
|
|
265
|
-
id: 1,
|
|
266
|
-
method,
|
|
267
|
-
params
|
|
268
|
-
})
|
|
269
|
-
});
|
|
270
|
-
const data = await response.json();
|
|
271
|
-
if (data.error) {
|
|
272
|
-
throw new Error(data.error.message);
|
|
273
|
-
}
|
|
274
|
-
return data.result;
|
|
275
|
-
}
|
|
276
|
-
getCached(key) {
|
|
277
|
-
const cached = this.cache.get(key);
|
|
278
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
279
|
-
return cached.data;
|
|
280
|
-
}
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
setCached(key, data) {
|
|
284
|
-
this.cache.set(key, {
|
|
285
|
-
data,
|
|
286
|
-
expiresAt: Date.now() + CACHE_TTL * 1e3
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Get entity label for an address
|
|
291
|
-
*/
|
|
292
|
-
getEntityInfo(address) {
|
|
293
|
-
const known = KNOWN_PROGRAMS[address];
|
|
294
|
-
if (known) {
|
|
295
|
-
return known;
|
|
296
|
-
}
|
|
297
|
-
if (address.length === 44) {
|
|
298
|
-
return { type: "wallet" };
|
|
299
|
-
}
|
|
300
|
-
return { type: "other" };
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Fetch transaction signatures for an address
|
|
304
|
-
*/
|
|
305
|
-
async getTransactions(address, limit = 100) {
|
|
306
|
-
const cacheKey = `txs:${address}:${limit}`;
|
|
307
|
-
const cached = this.getCached(cacheKey);
|
|
308
|
-
if (cached) return cached;
|
|
309
|
-
try {
|
|
310
|
-
const result = await this.rpcCall("getTransactionsForAddress", [
|
|
311
|
-
address,
|
|
312
|
-
{
|
|
313
|
-
transactionDetails: "signatures",
|
|
314
|
-
sortOrder: "desc",
|
|
315
|
-
limit,
|
|
316
|
-
filters: {
|
|
317
|
-
status: "succeeded"
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
]);
|
|
321
|
-
const transactions = (result.data || []).map((entry) => ({
|
|
322
|
-
signature: entry.signature,
|
|
323
|
-
slot: entry.slot,
|
|
324
|
-
blockTime: entry.blockTime,
|
|
325
|
-
err: entry.err
|
|
326
|
-
}));
|
|
327
|
-
this.setCached(cacheKey, transactions);
|
|
328
|
-
return transactions;
|
|
329
|
-
} catch (error) {
|
|
330
|
-
console.error("[SolanaFundingTree] Error fetching transactions:", error);
|
|
331
|
-
return [];
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Build funding sources (where funds came from)
|
|
336
|
-
* For Solana, this traces transfers WHERE THE ADDRESS RECEIVED funds
|
|
337
|
-
*/
|
|
338
|
-
async buildSourceTree(address, maxDepth = 3) {
|
|
339
|
-
const transactions = await this.getTransactions(address, 100);
|
|
340
|
-
return this.constructTree(address, transactions, "source", maxDepth);
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Build funding destinations (where funds went to)
|
|
344
|
-
* For Solana, this traces transfers WHERE THE ADDRESS SENT funds
|
|
345
|
-
*/
|
|
346
|
-
async buildDestinationTree(address, maxDepth = 3) {
|
|
347
|
-
const transactions = await this.getTransactions(address, 100);
|
|
348
|
-
return this.constructTree(address, transactions, "destination", maxDepth);
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Construct funding tree from transactions
|
|
352
|
-
*/
|
|
353
|
-
constructTree(address, transactions, direction, maxDepth, currentDepth = 0) {
|
|
354
|
-
if (currentDepth >= maxDepth || transactions.length === 0) {
|
|
355
|
-
return this.createNode(address, direction, currentDepth, [], 0);
|
|
356
|
-
}
|
|
357
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
358
|
-
for (const tx of transactions) {
|
|
359
|
-
const counterparty = this.extractCounterparty(tx.signature, address, direction);
|
|
360
|
-
if (!counterparty) continue;
|
|
361
|
-
const existing = nodeMap.get(counterparty) || { count: 0, totalValue: 0, firstTx: tx.blockTime, signatures: [] };
|
|
362
|
-
existing.count += 1;
|
|
363
|
-
existing.signatures.push(tx.signature);
|
|
364
|
-
if (tx.blockTime && tx.blockTime < existing.firstTx) {
|
|
365
|
-
existing.firstTx = tx.blockTime;
|
|
366
|
-
}
|
|
367
|
-
nodeMap.set(counterparty, existing);
|
|
368
|
-
}
|
|
369
|
-
const children = [];
|
|
370
|
-
let totalCount = 0;
|
|
371
|
-
for (const [counterAddr, stats] of Array.from(nodeMap.entries())) {
|
|
372
|
-
const childNode = this.constructTree(
|
|
373
|
-
counterAddr,
|
|
374
|
-
[],
|
|
375
|
-
// Would need to fetch recursively for full tree
|
|
376
|
-
direction,
|
|
377
|
-
maxDepth,
|
|
378
|
-
currentDepth + 1
|
|
379
|
-
);
|
|
380
|
-
childNode.txCount = stats.count;
|
|
381
|
-
childNode.totalValueInSol = stats.totalValue;
|
|
382
|
-
childNode.firstTx = { signature: stats.signatures[0], timestamp: stats.firstTx };
|
|
383
|
-
children.push(childNode);
|
|
384
|
-
totalCount += stats.count;
|
|
385
|
-
}
|
|
386
|
-
children.sort((a, b) => b.txCount - a.txCount);
|
|
387
|
-
const totalValue = children.reduce((sum, c) => sum + c.totalValueInSol, 0);
|
|
388
|
-
const entity = this.getEntityInfo(address);
|
|
389
|
-
return {
|
|
390
|
-
address,
|
|
391
|
-
depth: currentDepth,
|
|
392
|
-
direction,
|
|
393
|
-
totalValue: totalValue.toFixed(4),
|
|
394
|
-
totalValueInSol: totalValue,
|
|
395
|
-
txCount: totalCount,
|
|
396
|
-
children,
|
|
397
|
-
suspiciousScore: 0,
|
|
398
|
-
suspiciousReasons: [],
|
|
399
|
-
label: entity.label,
|
|
400
|
-
entityType: entity.type
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Create a tree node
|
|
405
|
-
*/
|
|
406
|
-
createNode(address, direction, depth, children, totalValue) {
|
|
407
|
-
const entity = this.getEntityInfo(address);
|
|
408
|
-
return {
|
|
409
|
-
address,
|
|
410
|
-
depth,
|
|
411
|
-
direction,
|
|
412
|
-
totalValue: totalValue.toFixed(4),
|
|
413
|
-
totalValueInSol: totalValue,
|
|
414
|
-
txCount: children.length,
|
|
415
|
-
children,
|
|
416
|
-
suspiciousScore: 0,
|
|
417
|
-
suspiciousReasons: [],
|
|
418
|
-
label: entity.label,
|
|
419
|
-
entityType: entity.type
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Extract counterparty from transaction
|
|
424
|
-
* This is a placeholder - in full implementation we'd parse the transaction
|
|
425
|
-
*/
|
|
426
|
-
extractCounterparty(signature, address, direction) {
|
|
427
|
-
return null;
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* Build complete funding tree with both sources and destinations
|
|
431
|
-
*/
|
|
432
|
-
async buildFundingTree(address, maxDepth = 3) {
|
|
433
|
-
const [fundingSources, fundingDestinations] = await Promise.all([
|
|
434
|
-
this.buildSourceTree(address, maxDepth),
|
|
435
|
-
this.buildDestinationTree(address, maxDepth)
|
|
436
|
-
]);
|
|
437
|
-
return { fundingSources, fundingDestinations };
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
SolanaFundingTreeService_default = SolanaFundingTreeService;
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// src/services/SolanaKeyPoolManager.ts
|
|
445
|
-
var MAX_MONTHLY_CUS, CIRCUIT_OPEN_MS, ERROR_THRESHOLD, SolanaKeyPoolManager, solanaKeyPool;
|
|
446
|
-
var init_SolanaKeyPoolManager = __esm({
|
|
447
|
-
"src/services/SolanaKeyPoolManager.ts"() {
|
|
448
|
-
MAX_MONTHLY_CUS = 3e8;
|
|
449
|
-
CIRCUIT_OPEN_MS = 3e4;
|
|
450
|
-
ERROR_THRESHOLD = 5;
|
|
451
|
-
SolanaKeyPoolManager = class {
|
|
452
|
-
keys = [];
|
|
453
|
-
currentIndex = 0;
|
|
454
|
-
connections = /* @__PURE__ */ new Map();
|
|
455
|
-
constructor() {
|
|
456
|
-
this.initKeys();
|
|
457
|
-
this.startHealthMonitor();
|
|
458
|
-
}
|
|
459
|
-
initKeys() {
|
|
460
|
-
const contractKeyCount = 10;
|
|
461
|
-
for (let i = 1; i <= contractKeyCount; i++) {
|
|
462
|
-
const envKey = `SYBIL_CONTRACT_KEY_${String(i).padStart(2, "0")}`;
|
|
463
|
-
const key = process.env[envKey];
|
|
464
|
-
if (key) {
|
|
465
|
-
const endpoint = `https://solana-mainnet.g.alchemy.com/v2/${key}`;
|
|
466
|
-
this.keys.push({
|
|
467
|
-
key,
|
|
468
|
-
endpoint,
|
|
469
|
-
requestsThisMinute: 0,
|
|
470
|
-
requestsThisMonth: 0,
|
|
471
|
-
totalCUs: 0,
|
|
472
|
-
consecutiveErrors: 0,
|
|
473
|
-
lastErrorAt: null,
|
|
474
|
-
circuitOpen: false,
|
|
475
|
-
circuitOpenUntil: null,
|
|
476
|
-
avgLatencyMs: 0
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
const walletKeyCount = 10;
|
|
481
|
-
for (let i = 1; i <= walletKeyCount; i++) {
|
|
482
|
-
const envKey = `SYBIL_WALLET_KEY_${String(i).padStart(2, "0")}`;
|
|
483
|
-
const key = process.env[envKey];
|
|
484
|
-
if (key && !this.keys.find((k) => k.key === key)) {
|
|
485
|
-
const endpoint = `https://solana-mainnet.g.alchemy.com/v2/${key}`;
|
|
486
|
-
this.keys.push({
|
|
487
|
-
key,
|
|
488
|
-
endpoint,
|
|
489
|
-
requestsThisMinute: 0,
|
|
490
|
-
requestsThisMonth: 0,
|
|
491
|
-
totalCUs: 0,
|
|
492
|
-
consecutiveErrors: 0,
|
|
493
|
-
lastErrorAt: null,
|
|
494
|
-
circuitOpen: false,
|
|
495
|
-
circuitOpenUntil: null,
|
|
496
|
-
avgLatencyMs: 0
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
if (this.keys.length === 0) {
|
|
501
|
-
console.warn("[SolanaKeyPool] No Alchemy keys found, using fallback");
|
|
502
|
-
const fallbackKey = process.env.ALCHEMY_SOLANA_KEY || process.env.ALCHEMY_KEY_01;
|
|
503
|
-
if (fallbackKey) {
|
|
504
|
-
this.keys.push({
|
|
505
|
-
key: fallbackKey,
|
|
506
|
-
endpoint: `https://solana-mainnet.g.alchemy.com/v2/${fallbackKey}`,
|
|
507
|
-
requestsThisMinute: 0,
|
|
508
|
-
requestsThisMonth: 0,
|
|
509
|
-
totalCUs: 0,
|
|
510
|
-
consecutiveErrors: 0,
|
|
511
|
-
lastErrorAt: null,
|
|
512
|
-
circuitOpen: false,
|
|
513
|
-
circuitOpenUntil: null,
|
|
514
|
-
avgLatencyMs: 0
|
|
515
|
-
});
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
console.log(`[SolanaKeyPool] Initialized with ${this.keys.length} keys`);
|
|
519
|
-
}
|
|
520
|
-
getNextKey() {
|
|
521
|
-
const now = Date.now();
|
|
522
|
-
let attempts = 0;
|
|
523
|
-
while (attempts < this.keys.length) {
|
|
524
|
-
const health = this.keys[this.currentIndex];
|
|
525
|
-
this.currentIndex = (this.currentIndex + 1) % this.keys.length;
|
|
526
|
-
if (health.circuitOpen) {
|
|
527
|
-
if (now < health.circuitOpenUntil) {
|
|
528
|
-
attempts++;
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
health.circuitOpen = false;
|
|
532
|
-
health.consecutiveErrors = 0;
|
|
533
|
-
}
|
|
534
|
-
if (health.totalCUs >= MAX_MONTHLY_CUS * 0.95) {
|
|
535
|
-
attempts++;
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
return { health, endpoint: health.endpoint };
|
|
539
|
-
}
|
|
540
|
-
throw new Error("All Alchemy keys exhausted or in circuit-open state");
|
|
541
|
-
}
|
|
542
|
-
async execute(fn, cuCost = 1) {
|
|
543
|
-
const maxRetries = 3;
|
|
544
|
-
let lastError = null;
|
|
545
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
546
|
-
const { health, endpoint } = this.getNextKey();
|
|
547
|
-
const start = Date.now();
|
|
548
|
-
try {
|
|
549
|
-
const result = await fn(endpoint);
|
|
550
|
-
const latency = Date.now() - start;
|
|
551
|
-
health.consecutiveErrors = 0;
|
|
552
|
-
health.requestsThisMinute++;
|
|
553
|
-
health.requestsThisMonth++;
|
|
554
|
-
health.totalCUs += cuCost;
|
|
555
|
-
health.avgLatencyMs = health.avgLatencyMs * 0.9 + latency * 0.1;
|
|
556
|
-
return result;
|
|
557
|
-
} catch (err2) {
|
|
558
|
-
lastError = err2;
|
|
559
|
-
health.consecutiveErrors++;
|
|
560
|
-
health.lastErrorAt = Date.now();
|
|
561
|
-
if (health.consecutiveErrors >= ERROR_THRESHOLD) {
|
|
562
|
-
health.circuitOpen = true;
|
|
563
|
-
health.circuitOpenUntil = Date.now() + CIRCUIT_OPEN_MS;
|
|
564
|
-
console.warn(`[SolanaKeyPool] Circuit opened for key ${health.key.slice(0, 8)}...`);
|
|
565
|
-
}
|
|
566
|
-
if (err2.status === 429) {
|
|
567
|
-
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
throw lastError || new Error("RPC call failed after retries");
|
|
572
|
-
}
|
|
573
|
-
getPoolStats() {
|
|
574
|
-
return {
|
|
575
|
-
totalKeys: this.keys.length,
|
|
576
|
-
healthyKeys: this.keys.filter((k) => !k.circuitOpen).length,
|
|
577
|
-
totalCUsUsed: this.keys.reduce((sum, k) => sum + k.totalCUs, 0),
|
|
578
|
-
avgLatencyMs: this.keys.reduce((s, k) => s + k.avgLatencyMs, 0) / this.keys.length
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
startHealthMonitor() {
|
|
582
|
-
setInterval(() => {
|
|
583
|
-
this.keys.forEach((k) => k.requestsThisMinute = 0);
|
|
584
|
-
}, 6e4);
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
solanaKeyPool = new SolanaKeyPoolManager();
|
|
588
|
-
}
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
// src/utils/cache.ts
|
|
592
|
-
var Cache, cache;
|
|
593
|
-
var init_cache = __esm({
|
|
594
|
-
"src/utils/cache.ts"() {
|
|
595
|
-
Cache = class {
|
|
596
|
-
store = /* @__PURE__ */ new Map();
|
|
597
|
-
set(key, value, ttlSeconds) {
|
|
598
|
-
const expires = Date.now() + ttlSeconds * 1e3;
|
|
599
|
-
this.store.set(key, { value, expires });
|
|
600
|
-
}
|
|
601
|
-
get(key) {
|
|
602
|
-
const item = this.store.get(key);
|
|
603
|
-
if (!item) return null;
|
|
604
|
-
if (Date.now() > item.expires) {
|
|
605
|
-
this.store.delete(key);
|
|
606
|
-
return null;
|
|
607
|
-
}
|
|
608
|
-
return item.value;
|
|
609
|
-
}
|
|
610
|
-
delete(key) {
|
|
611
|
-
this.store.delete(key);
|
|
612
|
-
}
|
|
613
|
-
clear() {
|
|
614
|
-
this.store.clear();
|
|
615
|
-
}
|
|
616
|
-
has(key) {
|
|
617
|
-
const item = this.store.get(key);
|
|
618
|
-
if (!item) return false;
|
|
619
|
-
if (Date.now() > item.expires) {
|
|
620
|
-
this.store.delete(key);
|
|
621
|
-
return false;
|
|
622
|
-
}
|
|
623
|
-
return true;
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
cache = new Cache();
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
// src/services/DuneSimClient.ts
|
|
631
|
-
import fetch3 from "node-fetch";
|
|
632
|
-
var SIM_BETA_BASE, SIM_V1_BASE, EVM_CHAIN_IDS, LAMPORTS_PER_SOL, DuneSimClient, duneSimClient;
|
|
633
|
-
var init_DuneSimClient = __esm({
|
|
634
|
-
"src/services/DuneSimClient.ts"() {
|
|
635
|
-
init_cache();
|
|
636
|
-
SIM_BETA_BASE = "https://api.sim.dune.com/beta";
|
|
637
|
-
SIM_V1_BASE = "https://api.sim.dune.com/v1";
|
|
638
|
-
EVM_CHAIN_IDS = {
|
|
639
|
-
ethereum: 1,
|
|
640
|
-
eth: 1,
|
|
641
|
-
linea: 59144,
|
|
642
|
-
arbitrum: 42161,
|
|
643
|
-
arb: 42161,
|
|
644
|
-
optimism: 10,
|
|
645
|
-
opt: 10,
|
|
646
|
-
base: 8453,
|
|
647
|
-
polygon: 137,
|
|
648
|
-
matic: 137,
|
|
649
|
-
bsc: 56,
|
|
650
|
-
avalanche: 43114,
|
|
651
|
-
avax: 43114
|
|
652
|
-
};
|
|
653
|
-
LAMPORTS_PER_SOL = 1e9;
|
|
654
|
-
DuneSimClient = class {
|
|
655
|
-
apiKey;
|
|
656
|
-
enabled = true;
|
|
657
|
-
constructor(apiKey) {
|
|
658
|
-
this.apiKey = apiKey || process.env.SIM_API_KEY || "";
|
|
659
|
-
this.enabled = !!this.apiKey && process.env.SIM_SOLANA_ENABLED !== "false";
|
|
660
|
-
if (!this.enabled) {
|
|
661
|
-
console.warn("[DuneSimClient] SIM disabled - no API key or SIM_SOLANA_ENABLED=false");
|
|
662
|
-
} else {
|
|
663
|
-
console.log("[DuneSimClient] Initialized - using SIM for Solana data");
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
isEnabled() {
|
|
667
|
-
return this.enabled;
|
|
668
|
-
}
|
|
669
|
-
async fetchWithAuth(url) {
|
|
670
|
-
if (!this.enabled) {
|
|
671
|
-
throw new Error("SIM client disabled");
|
|
672
|
-
}
|
|
673
|
-
const response = await fetch3(url, {
|
|
674
|
-
method: "GET",
|
|
675
|
-
headers: {
|
|
676
|
-
"X-Sim-Api-Key": this.apiKey
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
if (!response.ok) {
|
|
680
|
-
const error = await response.text();
|
|
681
|
-
throw new Error(`SIM API error ${response.status}: ${error}`);
|
|
682
|
-
}
|
|
683
|
-
return response.json();
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Get token balances for a Solana address using SIM
|
|
687
|
-
* Endpoint: GET /beta/svm/balances/{address}
|
|
688
|
-
*/
|
|
689
|
-
async getBalances(address, options = {}) {
|
|
690
|
-
const cacheKey = `sim:balances:${address}:${JSON.stringify(options)}`;
|
|
691
|
-
const cached = cache.get(cacheKey);
|
|
692
|
-
if (cached) return cached;
|
|
693
|
-
const params = new URLSearchParams();
|
|
694
|
-
if (options.chains) params.set("chains", options.chains);
|
|
695
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
696
|
-
if (options.excludeSpamTokens) params.set("exclude_spam_tokens", "true");
|
|
697
|
-
if (options.excludeUnpriced) params.set("exclude_unpriced", "true");
|
|
698
|
-
const url = `${SIM_BETA_BASE}/svm/balances/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
699
|
-
try {
|
|
700
|
-
const data = await this.fetchWithAuth(url);
|
|
701
|
-
cache.set(cacheKey, data, 60);
|
|
702
|
-
return data;
|
|
703
|
-
} catch (error) {
|
|
704
|
-
console.error("[DuneSimClient] Error fetching balances:", error);
|
|
705
|
-
throw error;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* Get transactions for a Solana address using SIM
|
|
710
|
-
* Endpoint: GET /beta/svm/transactions/{address}
|
|
711
|
-
*/
|
|
712
|
-
async getTransactions(address, options = {}) {
|
|
713
|
-
const cacheKey = `sim:txs:${address}:${options.limit || 100}`;
|
|
714
|
-
const cached = cache.get(cacheKey);
|
|
715
|
-
if (cached) return cached;
|
|
716
|
-
const params = new URLSearchParams();
|
|
717
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
718
|
-
const url = `${SIM_BETA_BASE}/svm/transactions/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
719
|
-
try {
|
|
720
|
-
const data = await this.fetchWithAuth(url);
|
|
721
|
-
cache.set(cacheKey, data, 300);
|
|
722
|
-
return data;
|
|
723
|
-
} catch (error) {
|
|
724
|
-
console.error("[DuneSimClient] Error fetching transactions:", error);
|
|
725
|
-
throw error;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Convert SIM balances response to FundTracer format
|
|
730
|
-
*/
|
|
731
|
-
mapBalancesToPortfolio(simResponse) {
|
|
732
|
-
const { balances, wallet_address } = simResponse;
|
|
733
|
-
const solBalance = balances.find((b) => b.address === "native");
|
|
734
|
-
const solLamports = solBalance ? parseInt(solBalance.amount) : 0;
|
|
735
|
-
const solUsd = solBalance?.value_usd || 0;
|
|
736
|
-
const solPrice = solBalance?.price_usd || 0;
|
|
737
|
-
const tokens = balances.filter((b) => b.address !== "native").map((token) => ({
|
|
738
|
-
mint: token.address,
|
|
739
|
-
amount: parseInt(token.amount),
|
|
740
|
-
decimals: token.decimals,
|
|
741
|
-
uiAmount: parseFloat(token.balance),
|
|
742
|
-
symbol: token.symbol,
|
|
743
|
-
name: token.name,
|
|
744
|
-
logoUrl: token.uri || void 0,
|
|
745
|
-
price: token.price_usd,
|
|
746
|
-
value: token.value_usd
|
|
747
|
-
})).filter((t) => t.uiAmount > 0);
|
|
748
|
-
const totalUsd = solUsd + tokens.reduce((sum, t) => sum + (t.value || 0), 0);
|
|
749
|
-
return {
|
|
750
|
-
address: wallet_address,
|
|
751
|
-
sol: {
|
|
752
|
-
lamports: solLamports,
|
|
753
|
-
sol: solLamports / LAMPORTS_PER_SOL,
|
|
754
|
-
usd: solUsd
|
|
755
|
-
},
|
|
756
|
-
tokens,
|
|
757
|
-
totalUsd,
|
|
758
|
-
fetchedAt: Date.now()
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Convert SIM transactions to FundTracer format
|
|
763
|
-
*/
|
|
764
|
-
mapTransactions(simResponse) {
|
|
765
|
-
const { transactions } = simResponse;
|
|
766
|
-
return transactions.map((tx) => {
|
|
767
|
-
const rawTx = tx.raw_transaction;
|
|
768
|
-
const meta = rawTx?.meta;
|
|
769
|
-
const message = rawTx?.transaction?.message;
|
|
770
|
-
const instructions = message?.instructions || [];
|
|
771
|
-
let from = "";
|
|
772
|
-
let to = "";
|
|
773
|
-
let amount = 0;
|
|
774
|
-
let token = "";
|
|
775
|
-
let tokenAmount = 0;
|
|
776
|
-
let type = "unknown";
|
|
777
|
-
if (message?.accountKeys?.[0]) {
|
|
778
|
-
from = typeof message.accountKeys[0] === "string" ? message.accountKeys[0] : message.accountKeys[0]?.pubkey || "";
|
|
779
|
-
}
|
|
780
|
-
for (const ix of instructions) {
|
|
781
|
-
if (ix.parsed) {
|
|
782
|
-
if (ix.parsed.type === "transfer") {
|
|
783
|
-
to = ix.parsed.info?.destination || "";
|
|
784
|
-
amount = parseInt(ix.parsed.info?.lamports || "0");
|
|
785
|
-
type = "transfer";
|
|
786
|
-
break;
|
|
787
|
-
} else if (ix.parsed.type === "transferChecked") {
|
|
788
|
-
to = ix.parsed.info?.destination || "";
|
|
789
|
-
token = ix.parsed.info?.mint || "";
|
|
790
|
-
tokenAmount = parseFloat(ix.parsed.info?.tokenAmount?.amount || "0");
|
|
791
|
-
type = "token-transfer";
|
|
792
|
-
break;
|
|
793
|
-
}
|
|
794
|
-
} else if (ix.program) {
|
|
795
|
-
if (ix.program === "system" || ix.programIdIndex === 0) {
|
|
796
|
-
type = "transfer";
|
|
797
|
-
} else if (ix.program === "token" || ix.programIdIndex === 2) {
|
|
798
|
-
type = "token-transfer";
|
|
799
|
-
} else if (ix.program === "stake") {
|
|
800
|
-
type = "staking";
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
if (type === "unknown" && message?.accountKeys) {
|
|
805
|
-
const tokenProgramIdx = message.accountKeys.findIndex(
|
|
806
|
-
(k) => k === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" || typeof k === "object" && k?.pubkey === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
|
807
|
-
);
|
|
808
|
-
if (tokenProgramIdx > -1) {
|
|
809
|
-
type = "token-transfer";
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
const blockTimeMs = tx.block_time ? Math.floor(tx.block_time / 1e3) : Date.now() * 1e3;
|
|
813
|
-
return {
|
|
814
|
-
signature: rawTx?.transaction?.signatures?.[0] || "",
|
|
815
|
-
slot: tx.block_slot,
|
|
816
|
-
blockTime: blockTimeMs,
|
|
817
|
-
fee: meta?.fee || 0,
|
|
818
|
-
status: meta?.err ? "failed" : "success",
|
|
819
|
-
type,
|
|
820
|
-
from,
|
|
821
|
-
to,
|
|
822
|
-
amount: amount > 0 ? amount / LAMPORTS_PER_SOL : void 0,
|
|
823
|
-
token,
|
|
824
|
-
tokenAmount: tokenAmount > 0 ? tokenAmount : void 0,
|
|
825
|
-
instructions: instructions.map((ix) => ix.parsed || ix)
|
|
826
|
-
};
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* Get portfolio with spam filtering option
|
|
831
|
-
*/
|
|
832
|
-
async getFilteredPortfolio(address, filterOptions = {}) {
|
|
833
|
-
const simResponse = await this.getBalances(address, {
|
|
834
|
-
excludeSpamTokens: filterOptions.excludeSpamTokens,
|
|
835
|
-
excludeUnpriced: filterOptions.excludeUnpriced
|
|
836
|
-
});
|
|
837
|
-
let balances = simResponse.balances;
|
|
838
|
-
if (filterOptions.minLiquidity && filterOptions.minLiquidity > 0) {
|
|
839
|
-
balances = balances.filter(
|
|
840
|
-
(b) => !b.low_liquidity && (b.pool_size || 0) >= filterOptions.minLiquidity
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
return this.mapBalancesToPortfolio({
|
|
844
|
-
...simResponse,
|
|
845
|
-
balances
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
// ============================================================
|
|
849
|
-
// EVM METHODS (using /v1/evm/* endpoints)
|
|
850
|
-
// ============================================================
|
|
851
|
-
/**
|
|
852
|
-
* Convert chain name to chain_id
|
|
853
|
-
*/
|
|
854
|
-
getChainId(chainName) {
|
|
855
|
-
return EVM_CHAIN_IDS[chainName.toLowerCase()] || EVM_CHAIN_IDS[chainName] || 1;
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Get EVM token balances for a wallet
|
|
859
|
-
* Endpoint: GET /v1/evm/balances/{address}
|
|
860
|
-
*/
|
|
861
|
-
async getEvmBalances(address, options = {}) {
|
|
862
|
-
const cacheKey = `sim:evm:balances:${address}:${JSON.stringify(options)}`;
|
|
863
|
-
const cached = cache.get(cacheKey);
|
|
864
|
-
if (cached) return cached;
|
|
865
|
-
const params = new URLSearchParams();
|
|
866
|
-
if (options.chainIds) {
|
|
867
|
-
if (Array.isArray(options.chainIds)) {
|
|
868
|
-
params.set("chain_ids", options.chainIds.join(","));
|
|
869
|
-
} else {
|
|
870
|
-
params.set("chain_ids", options.chainIds.toString());
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
if (options.filters) params.set("filters", options.filters);
|
|
874
|
-
if (options.assetClass) params.set("asset_class", options.assetClass);
|
|
875
|
-
if (options.excludeSpamTokens) params.set("exclude_spam_tokens", "true");
|
|
876
|
-
if (options.excludeUnpriced) params.set("exclude_unpriced", "true");
|
|
877
|
-
if (options.metadata) params.set("metadata", options.metadata);
|
|
878
|
-
if (options.historicalPrices) params.set("historical_prices", options.historicalPrices);
|
|
879
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
880
|
-
if (options.offset) params.set("offset", options.offset);
|
|
881
|
-
const url = `${SIM_V1_BASE}/evm/balances/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
882
|
-
try {
|
|
883
|
-
const data = await this.fetchWithAuth(url);
|
|
884
|
-
cache.set(cacheKey, data, 60);
|
|
885
|
-
return data;
|
|
886
|
-
} catch (error) {
|
|
887
|
-
console.error("[DuneSimClient] Error fetching EVM balances:", error);
|
|
888
|
-
throw error;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
/**
|
|
892
|
-
* Get EVM activity for a wallet
|
|
893
|
-
* Endpoint: GET /v1/evm/activity/{address}
|
|
894
|
-
*/
|
|
895
|
-
async getEvmActivity(address, options = {}) {
|
|
896
|
-
const cacheKey = `sim:evm:activity:${address}:${JSON.stringify(options)}`;
|
|
897
|
-
const cached = cache.get(cacheKey);
|
|
898
|
-
if (cached) return cached;
|
|
899
|
-
const params = new URLSearchParams();
|
|
900
|
-
if (options.chainIds) {
|
|
901
|
-
params.set("chain_ids", Array.isArray(options.chainIds) ? options.chainIds.join(",") : options.chainIds.toString());
|
|
902
|
-
}
|
|
903
|
-
if (options.activityType) params.set("activity_type", options.activityType);
|
|
904
|
-
if (options.assetType) params.set("asset_type", options.assetType);
|
|
905
|
-
if (options.tokenAddress) params.set("token_address", options.tokenAddress);
|
|
906
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
907
|
-
if (options.offset) params.set("offset", options.offset);
|
|
908
|
-
const url = `${SIM_V1_BASE}/evm/activity/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
909
|
-
try {
|
|
910
|
-
const data = await this.fetchWithAuth(url);
|
|
911
|
-
cache.set(cacheKey, data, 30);
|
|
912
|
-
return data;
|
|
913
|
-
} catch (error) {
|
|
914
|
-
console.error("[DuneSimClient] Error fetching EVM activity:", error);
|
|
915
|
-
throw error;
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Get EVM transactions for a wallet
|
|
920
|
-
* Endpoint: GET /v1/evm/transactions/{address}
|
|
921
|
-
*/
|
|
922
|
-
async getEvmTransactions(address, options = {}) {
|
|
923
|
-
const cacheKey = `sim:evm:txs:${address}:${JSON.stringify(options)}`;
|
|
924
|
-
const cached = cache.get(cacheKey);
|
|
925
|
-
if (cached) return cached;
|
|
926
|
-
const params = new URLSearchParams();
|
|
927
|
-
if (options.chainIds) {
|
|
928
|
-
params.set("chain_ids", Array.isArray(options.chainIds) ? options.chainIds.join(",") : options.chainIds.toString());
|
|
929
|
-
}
|
|
930
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
931
|
-
if (options.offset) params.set("offset", options.offset);
|
|
932
|
-
if (options.decode) params.set("decode", "true");
|
|
933
|
-
const url = `${SIM_V1_BASE}/evm/transactions/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
934
|
-
try {
|
|
935
|
-
const data = await this.fetchWithAuth(url);
|
|
936
|
-
cache.set(cacheKey, data, 300);
|
|
937
|
-
return data;
|
|
938
|
-
} catch (error) {
|
|
939
|
-
console.error("[DuneSimClient] Error fetching EVM transactions:", error);
|
|
940
|
-
throw error;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
/**
|
|
944
|
-
* Get EVM collectibles (NFTs) for a wallet
|
|
945
|
-
* Endpoint: GET /v1/evm/collectibles/{address}
|
|
946
|
-
*/
|
|
947
|
-
async getEvmCollectibles(address, options = {}) {
|
|
948
|
-
const cacheKey = `sim:evm:collectibles:${address}:${JSON.stringify(options)}`;
|
|
949
|
-
const cached = cache.get(cacheKey);
|
|
950
|
-
if (cached) return cached;
|
|
951
|
-
const params = new URLSearchParams();
|
|
952
|
-
if (options.chainIds) {
|
|
953
|
-
params.set("chain_ids", Array.isArray(options.chainIds) ? options.chainIds.join(",") : options.chainIds.toString());
|
|
954
|
-
}
|
|
955
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
956
|
-
if (options.offset) params.set("offset", options.offset);
|
|
957
|
-
if (options.filterSpam === false) params.set("filter_spam", "false");
|
|
958
|
-
if (options.showSpamScores) params.set("show_spam_scores", "true");
|
|
959
|
-
const url = `${SIM_V1_BASE}/evm/collectibles/${address}${params.toString() ? "?" + params.toString() : ""}`;
|
|
960
|
-
try {
|
|
961
|
-
const data = await this.fetchWithAuth(url);
|
|
962
|
-
cache.set(cacheKey, data, 300);
|
|
963
|
-
return data;
|
|
964
|
-
} catch (error) {
|
|
965
|
-
console.error("[DuneSimClient] Error fetching EVM collectibles:", error);
|
|
966
|
-
throw error;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Get token info for an EVM token
|
|
971
|
-
* Endpoint: GET /v1/evm/token-info/{address}
|
|
972
|
-
*/
|
|
973
|
-
async getEvmTokenInfo(tokenAddress, chainId = 1) {
|
|
974
|
-
const cacheKey = `sim:evm:tokeninfo:${tokenAddress}:${chainId}`;
|
|
975
|
-
const cached = cache.get(cacheKey);
|
|
976
|
-
if (cached) return cached;
|
|
977
|
-
const url = `${SIM_V1_BASE}/evm/token-info/${tokenAddress}?chain_ids=${chainId}`;
|
|
978
|
-
try {
|
|
979
|
-
const data = await this.fetchWithAuth(url);
|
|
980
|
-
cache.set(cacheKey, data, 300);
|
|
981
|
-
return data;
|
|
982
|
-
} catch (error) {
|
|
983
|
-
console.error("[DuneSimClient] Error fetching token info:", error);
|
|
984
|
-
throw error;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Get stablecoin balances for a wallet
|
|
989
|
-
* Endpoint: GET /v1/evm/stablecoins/{address}
|
|
990
|
-
*/
|
|
991
|
-
async getEvmStablecoins(address, options = {}) {
|
|
992
|
-
const cacheKey = `sim:evm:stablecoins:${address}:${JSON.stringify(options)}`;
|
|
993
|
-
const cached = cache.get(cacheKey);
|
|
994
|
-
if (cached) return cached;
|
|
995
|
-
const params = new URLSearchParams();
|
|
996
|
-
if (options.chainIds) {
|
|
997
|
-
params.set("chain_ids", Array.isArray(options.chainIds) ? options.chainIds.join(",") : options.chainIds.toString());
|
|
998
|
-
}
|
|
999
|
-
if (options.excludeUnpriced) params.set("exclude_unpriced", "true");
|
|
1000
|
-
if (options.limit) params.set("limit", options.limit.toString());
|
|
1001
|
-
if (options.offset) params.set("offset", options.offset);
|
|
1002
|
-
const url = `${SIM_V1_BASE}/evm/balances/${address}/stablecoins${params.toString() ? "?" + params.toString() : ""}`;
|
|
1003
|
-
try {
|
|
1004
|
-
const data = await this.fetchWithAuth(url);
|
|
1005
|
-
cache.set(cacheKey, data, 60);
|
|
1006
|
-
return data;
|
|
1007
|
-
} catch (error) {
|
|
1008
|
-
console.error("[DuneSimClient] Error fetching stablecoins:", error);
|
|
1009
|
-
throw error;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Get full EVM portfolio (tokens + NFTs + activity summary)
|
|
1014
|
-
*/
|
|
1015
|
-
async getEvmPortfolio(address, chainId = 1, options = {}) {
|
|
1016
|
-
const balancesResult = await this.getEvmBalances(address, { chainIds: chainId, excludeUnpriced: true, metadata: "logo" });
|
|
1017
|
-
let totalValue = 0;
|
|
1018
|
-
const tokens = [];
|
|
1019
|
-
let nativeBalance = "0";
|
|
1020
|
-
let nativeValue = 0;
|
|
1021
|
-
for (const bal of balancesResult.balances || []) {
|
|
1022
|
-
totalValue += bal.value_usd || 0;
|
|
1023
|
-
if (bal.address === "native") {
|
|
1024
|
-
nativeBalance = bal.amount;
|
|
1025
|
-
nativeValue = bal.value_usd || 0;
|
|
1026
|
-
} else {
|
|
1027
|
-
tokens.push({
|
|
1028
|
-
address: bal.address,
|
|
1029
|
-
balance: bal.amount,
|
|
1030
|
-
value_usd: bal.value_usd || 0,
|
|
1031
|
-
symbol: bal.symbol,
|
|
1032
|
-
name: bal.name,
|
|
1033
|
-
decimals: bal.decimals,
|
|
1034
|
-
price_usd: bal.price_usd || 0,
|
|
1035
|
-
pool_size: bal.pool_size,
|
|
1036
|
-
low_liquidity: bal.low_liquidity,
|
|
1037
|
-
logo: bal.token_metadata?.logo
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
const stablecoinsList = [];
|
|
1042
|
-
if (options.includeStablecoins) {
|
|
1043
|
-
try {
|
|
1044
|
-
const stableResult = await this.getEvmStablecoins(address, { chainIds: chainId });
|
|
1045
|
-
for (const bal of stableResult.balances || []) {
|
|
1046
|
-
stablecoinsList.push({
|
|
1047
|
-
address: bal.address,
|
|
1048
|
-
balance: bal.amount,
|
|
1049
|
-
value_usd: bal.value_usd || 0,
|
|
1050
|
-
symbol: bal.symbol
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
} catch (e) {
|
|
1054
|
-
console.warn("[DuneSimClient] Stablecoins fetch failed:", e);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
const nftList = [];
|
|
1058
|
-
if (options.includeNfts) {
|
|
1059
|
-
try {
|
|
1060
|
-
const nftResult = await this.getEvmCollectibles(address, { chainIds: chainId, filterSpam: true });
|
|
1061
|
-
for (const nft of nftResult.entries || []) {
|
|
1062
|
-
nftList.push({
|
|
1063
|
-
contract_address: nft.contract_address,
|
|
1064
|
-
token_id: nft.token_id,
|
|
1065
|
-
name: nft.name,
|
|
1066
|
-
image_url: nft.image_url,
|
|
1067
|
-
collection: nft.symbol || nft.name,
|
|
1068
|
-
is_spam: nft.is_spam
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
} catch (e) {
|
|
1072
|
-
console.warn("[DuneSimClient] Collectibles fetch failed:", e);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
let activitySummary;
|
|
1076
|
-
if (options.includeActivity) {
|
|
1077
|
-
try {
|
|
1078
|
-
const activityResult = await this.getEvmActivity(address, { chainIds: chainId, limit: 100 });
|
|
1079
|
-
let sends = 0, receives = 0, volume = 0;
|
|
1080
|
-
for (const act of activityResult.activity || []) {
|
|
1081
|
-
if (act.type === "send") sends++;
|
|
1082
|
-
if (act.type === "receive") receives++;
|
|
1083
|
-
volume += act.value_usd || 0;
|
|
1084
|
-
}
|
|
1085
|
-
activitySummary = {
|
|
1086
|
-
total_sends: sends,
|
|
1087
|
-
total_receives: receives,
|
|
1088
|
-
total_volume_usd: volume
|
|
1089
|
-
};
|
|
1090
|
-
} catch (e) {
|
|
1091
|
-
console.warn("[DuneSimClient] Activity fetch failed:", e);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
return {
|
|
1095
|
-
address,
|
|
1096
|
-
chain_id: chainId,
|
|
1097
|
-
total_value_usd: totalValue + nativeValue,
|
|
1098
|
-
native: {
|
|
1099
|
-
balance: nativeBalance,
|
|
1100
|
-
value_usd: nativeValue,
|
|
1101
|
-
symbol: "ETH"
|
|
1102
|
-
},
|
|
1103
|
-
tokens,
|
|
1104
|
-
stablecoins: options.includeStablecoins ? stablecoinsList : [],
|
|
1105
|
-
nfts: options.includeNfts ? nftList : void 0,
|
|
1106
|
-
activity_summary: options.includeActivity ? activitySummary : void 0,
|
|
1107
|
-
last_updated: (/* @__PURE__ */ new Date()).toISOString()
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
duneSimClient = new DuneSimClient();
|
|
1112
|
-
}
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
|
-
// src/services/SolanaHeliusKeyPool.ts
|
|
1116
|
-
function buildEndpoints() {
|
|
1117
|
-
const eps = [];
|
|
1118
|
-
for (let i = 1; i <= 3; i++) {
|
|
1119
|
-
const key = process.env[`HELIUS_KEY_${i}`];
|
|
1120
|
-
if (key) {
|
|
1121
|
-
eps.push({ url: `https://mainnet.helius-rpc.com/?api-key=${key}`, label: `Helius-${i}` });
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
const alchemyKey = process.env.ALCHEMY_SOLANA_API_KEY;
|
|
1125
|
-
if (alchemyKey) {
|
|
1126
|
-
eps.push({ url: `https://solana-mainnet.g.alchemy.com/v2/${alchemyKey}`, label: "Alchemy-Sol" });
|
|
1127
|
-
}
|
|
1128
|
-
return eps;
|
|
1129
|
-
}
|
|
1130
|
-
var CIRCUIT_OPEN_MS2, ERROR_THRESHOLD2, RpcKeyPool, ALL_ENDPOINTS, splitPoint, sigRpcPool, xferRpcPool;
|
|
1131
|
-
var init_SolanaHeliusKeyPool = __esm({
|
|
1132
|
-
"src/services/SolanaHeliusKeyPool.ts"() {
|
|
1133
|
-
CIRCUIT_OPEN_MS2 = 3e4;
|
|
1134
|
-
ERROR_THRESHOLD2 = 5;
|
|
1135
|
-
RpcKeyPool = class {
|
|
1136
|
-
slots = [];
|
|
1137
|
-
index = 0;
|
|
1138
|
-
constructor(endpoints, label) {
|
|
1139
|
-
this.slots = endpoints.filter((e) => e.url).map((e) => ({
|
|
1140
|
-
url: e.url,
|
|
1141
|
-
label: e.label,
|
|
1142
|
-
requestsThisMinute: 0,
|
|
1143
|
-
consecutiveErrors: 0,
|
|
1144
|
-
lastErrorAt: null,
|
|
1145
|
-
circuitOpen: false,
|
|
1146
|
-
circuitOpenUntil: null,
|
|
1147
|
-
avgLatencyMs: 0,
|
|
1148
|
-
totalRequests: 0
|
|
1149
|
-
}));
|
|
1150
|
-
if (this.slots.length === 0) {
|
|
1151
|
-
console.warn(`[RpcKeyPool:${label}] No endpoints configured`);
|
|
1152
|
-
} else {
|
|
1153
|
-
console.log(`[RpcKeyPool:${label}] Initialized with ${this.slots.length} endpoint(s): ${this.slots.map((s) => s.label).join(", ")}`);
|
|
1154
|
-
}
|
|
1155
|
-
setInterval(() => {
|
|
1156
|
-
for (const s of this.slots) s.requestsThisMinute = 0;
|
|
1157
|
-
}, 6e4).unref();
|
|
1158
|
-
}
|
|
1159
|
-
get size() {
|
|
1160
|
-
return this.slots.length;
|
|
1161
|
-
}
|
|
1162
|
-
get healthy() {
|
|
1163
|
-
return this.slots.filter((s) => !s.circuitOpen).length;
|
|
1164
|
-
}
|
|
1165
|
-
get isOperational() {
|
|
1166
|
-
return this.slots.length > 0 && this.slots.some((s) => !s.circuitOpen);
|
|
1167
|
-
}
|
|
1168
|
-
acquire() {
|
|
1169
|
-
if (this.slots.length === 0) {
|
|
1170
|
-
throw new Error("RpcKeyPool: No endpoints configured");
|
|
1171
|
-
}
|
|
1172
|
-
const now = Date.now();
|
|
1173
|
-
for (let attempt = 0; attempt < this.slots.length * 2; attempt++) {
|
|
1174
|
-
const slot = this.slots[this.index % this.slots.length];
|
|
1175
|
-
this.index++;
|
|
1176
|
-
if (slot.circuitOpen) {
|
|
1177
|
-
if (slot.circuitOpenUntil && now >= slot.circuitOpenUntil) {
|
|
1178
|
-
slot.circuitOpen = false;
|
|
1179
|
-
slot.consecutiveErrors = 0;
|
|
1180
|
-
return slot;
|
|
1181
|
-
}
|
|
1182
|
-
continue;
|
|
1183
|
-
}
|
|
1184
|
-
return slot;
|
|
1185
|
-
}
|
|
1186
|
-
throw new Error("RpcKeyPool: All endpoints are circuit-open");
|
|
1187
|
-
}
|
|
1188
|
-
/** Execute an RPC method through the pool */
|
|
1189
|
-
async rpc(method, params) {
|
|
1190
|
-
const maxRetries = 2;
|
|
1191
|
-
let lastError = null;
|
|
1192
|
-
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
1193
|
-
let slot;
|
|
1194
|
-
try {
|
|
1195
|
-
slot = this.acquire();
|
|
1196
|
-
} catch (e) {
|
|
1197
|
-
throw lastError || e;
|
|
1198
|
-
}
|
|
1199
|
-
const start = Date.now();
|
|
1200
|
-
slot.requestsThisMinute++;
|
|
1201
|
-
try {
|
|
1202
|
-
const res = await fetch(slot.url, {
|
|
1203
|
-
method: "POST",
|
|
1204
|
-
headers: { "Content-Type": "application/json" },
|
|
1205
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params })
|
|
1206
|
-
});
|
|
1207
|
-
const data = await res.json();
|
|
1208
|
-
if (data.error) throw new Error(data.error.message);
|
|
1209
|
-
const latency = Date.now() - start;
|
|
1210
|
-
slot.consecutiveErrors = 0;
|
|
1211
|
-
slot.avgLatencyMs = slot.avgLatencyMs * 0.9 + latency * 0.1;
|
|
1212
|
-
slot.totalRequests++;
|
|
1213
|
-
return data.result;
|
|
1214
|
-
} catch (err2) {
|
|
1215
|
-
lastError = err2;
|
|
1216
|
-
slot.consecutiveErrors++;
|
|
1217
|
-
slot.lastErrorAt = Date.now();
|
|
1218
|
-
if (slot.consecutiveErrors >= ERROR_THRESHOLD2) {
|
|
1219
|
-
slot.circuitOpen = true;
|
|
1220
|
-
slot.circuitOpenUntil = Date.now() + CIRCUIT_OPEN_MS2;
|
|
1221
|
-
console.warn(`[RpcKeyPool] Circuit opened for ${slot.label}`);
|
|
1222
|
-
}
|
|
1223
|
-
if (err2.status === 429) {
|
|
1224
|
-
await new Promise((r) => setTimeout(r, 200 * (attempt + 1)));
|
|
1225
|
-
}
|
|
1226
|
-
if (attempt < maxRetries) {
|
|
1227
|
-
await new Promise((r) => setTimeout(r, 50 * (attempt + 1)));
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
throw lastError || new Error("RpcKeyPool: All retries exhausted");
|
|
1232
|
-
}
|
|
1233
|
-
stats() {
|
|
1234
|
-
return {
|
|
1235
|
-
totalSlots: this.slots.length,
|
|
1236
|
-
healthy: this.healthy,
|
|
1237
|
-
totalRequests: this.slots.reduce((s, k) => s + k.totalRequests, 0),
|
|
1238
|
-
avgLatencyMs: this.slots.reduce((s, k) => s + k.avgLatencyMs, 0) / (this.slots.length || 1)
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
absorb(other) {
|
|
1242
|
-
const transferred = other.slots.filter((s) => !s.circuitOpen);
|
|
1243
|
-
for (const s of transferred) {
|
|
1244
|
-
this.slots.push(s);
|
|
1245
|
-
}
|
|
1246
|
-
console.log(`[RpcKeyPool] Absorbed ${transferred.length} endpoint(s) from another pool (now ${this.slots.length} total)`);
|
|
1247
|
-
}
|
|
1248
|
-
};
|
|
1249
|
-
ALL_ENDPOINTS = buildEndpoints();
|
|
1250
|
-
if (ALL_ENDPOINTS.length === 0) {
|
|
1251
|
-
console.warn("[RpcKeyPool] No Solana RPC endpoints found \u2014 Helius/Alchemy features will fail");
|
|
1252
|
-
} else {
|
|
1253
|
-
console.log(`[RpcKeyPool] Found ${ALL_ENDPOINTS.length} Solana RPC endpoint(s)`);
|
|
1254
|
-
}
|
|
1255
|
-
splitPoint = Math.max(1, Math.floor(ALL_ENDPOINTS.length * 0.66));
|
|
1256
|
-
sigRpcPool = new RpcKeyPool(ALL_ENDPOINTS.slice(0, splitPoint), "sig-rpc");
|
|
1257
|
-
xferRpcPool = new RpcKeyPool(ALL_ENDPOINTS.slice(splitPoint), "xfer-rpc");
|
|
1258
|
-
}
|
|
1259
|
-
});
|
|
1260
|
-
|
|
1261
|
-
// src/services/SolanaHeliusClient.ts
|
|
1262
|
-
var SolanaHeliusClient, solanaHeliusClient;
|
|
1263
|
-
var init_SolanaHeliusClient = __esm({
|
|
1264
|
-
"src/services/SolanaHeliusClient.ts"() {
|
|
1265
|
-
init_SolanaHeliusKeyPool();
|
|
1266
|
-
init_DuneSimClient();
|
|
1267
|
-
SolanaHeliusClient = class _SolanaHeliusClient {
|
|
1268
|
-
static instance;
|
|
1269
|
-
LAMPORTS = 1e9;
|
|
1270
|
-
constructor() {
|
|
1271
|
-
}
|
|
1272
|
-
static getInstance() {
|
|
1273
|
-
if (!_SolanaHeliusClient.instance) {
|
|
1274
|
-
_SolanaHeliusClient.instance = new _SolanaHeliusClient();
|
|
1275
|
-
}
|
|
1276
|
-
return _SolanaHeliusClient.instance;
|
|
1277
|
-
}
|
|
1278
|
-
// ================================================================
|
|
1279
|
-
// DAS API methods (general-purpose)
|
|
1280
|
-
// ================================================================
|
|
1281
|
-
async dasRequest(method, params) {
|
|
1282
|
-
return sigRpcPool.rpc(method, params);
|
|
1283
|
-
}
|
|
1284
|
-
async getTokenMetadata(mint) {
|
|
1285
|
-
return this.dasRequest("getTokenMetadata", [mint]);
|
|
1286
|
-
}
|
|
1287
|
-
async getAsset({ id }) {
|
|
1288
|
-
return this.dasRequest("getAsset", [{ id }]);
|
|
1289
|
-
}
|
|
1290
|
-
async getAssetsByOwner({ owner, limit = 100 }) {
|
|
1291
|
-
return this.dasRequest("getAssetsByOwner", [{ owner, limit, sortBy: { sortBy: "updated", descending: true } }]);
|
|
1292
|
-
}
|
|
1293
|
-
async getAssetsByGroup({ groupKey, groupValue, limit = 100 }) {
|
|
1294
|
-
return this.dasRequest("getAssetsByGroup", [{ groupKey, groupValue, limit, sortBy: { sortBy: "updated", descending: true } }]);
|
|
1295
|
-
}
|
|
1296
|
-
async searchAssets({ query, limit = 50 }) {
|
|
1297
|
-
return this.dasRequest("searchAssets", [{ query: { $text: query }, limit, sortBy: { sortBy: "relevant", descending: false } }]);
|
|
1298
|
-
}
|
|
1299
|
-
// ================================================================
|
|
1300
|
-
// Helius-exclusive: getTransactionsForAddress (via sigRpcPool — 66% of endpoints)
|
|
1301
|
-
// ================================================================
|
|
1302
|
-
async getTransactionsForAddress(address, options = {}) {
|
|
1303
|
-
const result = await sigRpcPool.rpc("getTransactionsForAddress", [
|
|
1304
|
-
address,
|
|
1305
|
-
{
|
|
1306
|
-
transactionDetails: options.transactionDetails || "signatures",
|
|
1307
|
-
limit: options.limit || 1e3,
|
|
1308
|
-
sortOrder: options.sortOrder || "desc",
|
|
1309
|
-
...options.paginationToken ? { paginationToken: options.paginationToken } : {}
|
|
1310
|
-
}
|
|
1311
|
-
]);
|
|
1312
|
-
return {
|
|
1313
|
-
data: result?.data || [],
|
|
1314
|
-
paginationToken: result?.paginationToken || void 0
|
|
1315
|
-
};
|
|
1316
|
-
}
|
|
1317
|
-
// ================================================================
|
|
1318
|
-
// Helius-exclusive: getTransfersByAddress (via xferRpcPool — 33% of endpoints)
|
|
1319
|
-
// ================================================================
|
|
1320
|
-
async getTransfersByAddress(address, options = {}, poolOverride) {
|
|
1321
|
-
const pool = poolOverride || xferRpcPool;
|
|
1322
|
-
try {
|
|
1323
|
-
const result = await pool.rpc("getTransfersByAddress", [
|
|
1324
|
-
address,
|
|
1325
|
-
{
|
|
1326
|
-
limit: options.limit || 100,
|
|
1327
|
-
...options.paginationToken ? { paginationToken: options.paginationToken } : {}
|
|
1328
|
-
}
|
|
1329
|
-
]);
|
|
1330
|
-
return {
|
|
1331
|
-
data: result?.data || [],
|
|
1332
|
-
paginationToken: result?.paginationToken || void 0
|
|
1333
|
-
};
|
|
1334
|
-
} catch (err2) {
|
|
1335
|
-
if (err2.message?.includes?.("Unsupported method")) {
|
|
1336
|
-
throw new Error("only available on paid Helius plan");
|
|
1337
|
-
}
|
|
1338
|
-
throw err2;
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
// ================================================================
|
|
1342
|
-
// Full wallet scan: parallel sigs + transfers with lazy absorption
|
|
1343
|
-
// ================================================================
|
|
1344
|
-
async scanWallet(address) {
|
|
1345
|
-
const start = Date.now();
|
|
1346
|
-
const [sigsResult, transfersResult] = await Promise.all([
|
|
1347
|
-
this.getAllSignatures(address),
|
|
1348
|
-
this.getAllTransfersWithAbsorb(address)
|
|
1349
|
-
]);
|
|
1350
|
-
return {
|
|
1351
|
-
signatures: sigsResult,
|
|
1352
|
-
transfers: transfersResult,
|
|
1353
|
-
totalTime: Date.now() - start
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1356
|
-
async getAllSignatures(address) {
|
|
1357
|
-
const all = [];
|
|
1358
|
-
let paginationToken;
|
|
1359
|
-
do {
|
|
1360
|
-
const result = await this.getTransactionsForAddress(address, {
|
|
1361
|
-
transactionDetails: "signatures",
|
|
1362
|
-
limit: 1e3,
|
|
1363
|
-
sortOrder: "desc",
|
|
1364
|
-
paginationToken
|
|
1365
|
-
});
|
|
1366
|
-
all.push(...result.data);
|
|
1367
|
-
paginationToken = result.paginationToken;
|
|
1368
|
-
} while (paginationToken && all.length < 5e4);
|
|
1369
|
-
return all.sort((a, b) => a.blockTime - b.blockTime);
|
|
1370
|
-
}
|
|
1371
|
-
async getAllTransfersWithAbsorb(address) {
|
|
1372
|
-
const all = [];
|
|
1373
|
-
let paginationToken;
|
|
1374
|
-
let sigsDone = false;
|
|
1375
|
-
const sigsDonePromise = sigRpcPool.size > 0 ? this.watchForSigPoolIdle() : Promise.resolve();
|
|
1376
|
-
const sigsDoneRace = sigsDonePromise.then(() => {
|
|
1377
|
-
sigsDone = true;
|
|
1378
|
-
});
|
|
1379
|
-
do {
|
|
1380
|
-
const result = await this.getTransfersByAddress(address, { limit: 100, paginationToken });
|
|
1381
|
-
all.push(...result.data);
|
|
1382
|
-
paginationToken = result.paginationToken;
|
|
1383
|
-
if (sigsDone && sigRpcPool.healthy > 0) {
|
|
1384
|
-
xferRpcPool.absorb(sigRpcPool);
|
|
1385
|
-
sigsDone = false;
|
|
1386
|
-
}
|
|
1387
|
-
} while (paginationToken && all.length < 1e4);
|
|
1388
|
-
return all;
|
|
1389
|
-
}
|
|
1390
|
-
async watchForSigPoolIdle() {
|
|
1391
|
-
let prev = 0;
|
|
1392
|
-
let stableCount = 0;
|
|
1393
|
-
for (let i = 0; i < 30; i++) {
|
|
1394
|
-
await new Promise((r) => setTimeout(r, 800));
|
|
1395
|
-
const cur = sigRpcPool.stats().totalRequests;
|
|
1396
|
-
if (cur === prev) {
|
|
1397
|
-
stableCount++;
|
|
1398
|
-
if (stableCount >= 3) return;
|
|
1399
|
-
} else {
|
|
1400
|
-
stableCount = 0;
|
|
1401
|
-
}
|
|
1402
|
-
prev = cur;
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
async getAllTransfers(address) {
|
|
1406
|
-
const all = [];
|
|
1407
|
-
let paginationToken;
|
|
1408
|
-
do {
|
|
1409
|
-
const result = await this.getTransfersByAddress(address, { limit: 100, paginationToken });
|
|
1410
|
-
all.push(...result.data);
|
|
1411
|
-
paginationToken = result.paginationToken;
|
|
1412
|
-
} while (paginationToken && all.length < 1e4);
|
|
1413
|
-
return all;
|
|
1414
|
-
}
|
|
1415
|
-
getPoolStats() {
|
|
1416
|
-
return { signaturesPool: sigRpcPool.stats(), transfersPool: xferRpcPool.stats() };
|
|
1417
|
-
}
|
|
1418
|
-
// ================================================================
|
|
1419
|
-
// STANDARD RPC METHODS — work on free Helius AND Alchemy
|
|
1420
|
-
// ================================================================
|
|
1421
|
-
async getSignaturesForAddressStdRpc(address, options = {}) {
|
|
1422
|
-
const result = await sigRpcPool.rpc("getSignaturesForAddress", [
|
|
1423
|
-
address,
|
|
1424
|
-
{
|
|
1425
|
-
limit: options.limit || 100,
|
|
1426
|
-
commitment: "confirmed",
|
|
1427
|
-
...options.before ? { before: options.before } : {}
|
|
1428
|
-
}
|
|
1429
|
-
]);
|
|
1430
|
-
return (result || []).map((s) => ({
|
|
1431
|
-
signature: s.signature,
|
|
1432
|
-
blockTime: s.blockTime || 0,
|
|
1433
|
-
err: s.err,
|
|
1434
|
-
slot: s.slot
|
|
1435
|
-
}));
|
|
1436
|
-
}
|
|
1437
|
-
async getTransactionStdRpc(signature) {
|
|
1438
|
-
return sigRpcPool.rpc("getTransaction", [
|
|
1439
|
-
signature,
|
|
1440
|
-
{ commitment: "confirmed", maxSupportedTransactionVersion: 0 }
|
|
1441
|
-
]);
|
|
1442
|
-
}
|
|
1443
|
-
async getBalanceStdRpc(address) {
|
|
1444
|
-
const result = await sigRpcPool.rpc("getBalance", [address]);
|
|
1445
|
-
return result?.value || 0;
|
|
1446
|
-
}
|
|
1447
|
-
// ================================================================
|
|
1448
|
-
// FALLBACK SCAN PATH — tries SIM first, then RPC
|
|
1449
|
-
// ================================================================
|
|
1450
|
-
async scanWalletFallback(address) {
|
|
1451
|
-
const start = Date.now();
|
|
1452
|
-
if (duneSimClient.isEnabled()) {
|
|
1453
|
-
try {
|
|
1454
|
-
return await this.scanWalletFallbackViaSim(address, start);
|
|
1455
|
-
} catch (simErr) {
|
|
1456
|
-
console.log("[SolanaHelius] SIM fallback failed, using RPC:", simErr?.message);
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
return this.scanWalletFallbackViaRpc(address, start);
|
|
1460
|
-
}
|
|
1461
|
-
async scanWalletFallbackViaSim(address, start) {
|
|
1462
|
-
const simTxs = await duneSimClient.getTransactions(address, { limit: 1e3 });
|
|
1463
|
-
const txs = duneSimClient.mapTransactions(simTxs);
|
|
1464
|
-
const signatures = txs.map((t) => ({
|
|
1465
|
-
signature: t.signature,
|
|
1466
|
-
blockTime: Math.floor(t.blockTime / 1e3),
|
|
1467
|
-
err: t.status === "failed" ? { msg: "failed" } : null
|
|
1468
|
-
}));
|
|
1469
|
-
let totalSOLSent = 0;
|
|
1470
|
-
let totalSOLReceived = 0;
|
|
1471
|
-
const interactors = {};
|
|
1472
|
-
for (const tx of txs) {
|
|
1473
|
-
if (tx.from === address) {
|
|
1474
|
-
totalSOLSent += tx.amount || 0;
|
|
1475
|
-
if (tx.to) interactors[tx.to] = (interactors[tx.to] || 0) + 1;
|
|
1476
|
-
}
|
|
1477
|
-
if (tx.to === address) {
|
|
1478
|
-
totalSOLReceived += tx.amount || 0;
|
|
1479
|
-
if (tx.from) interactors[tx.from] = (interactors[tx.from] || 0) + 1;
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
const topInteractors = Object.entries(interactors).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([address2, count]) => ({ address: address2, count }));
|
|
1483
|
-
return { signatures, topInteractors, totalSOLSent, totalSOLReceived, totalTime: Date.now() - start };
|
|
1484
|
-
}
|
|
1485
|
-
async scanWalletFallbackViaRpc(address, start) {
|
|
1486
|
-
const allSigs = [];
|
|
1487
|
-
let before;
|
|
1488
|
-
while (before !== void 0 || allSigs.length === 0) {
|
|
1489
|
-
const page = await this.getSignaturesForAddressStdRpc(address, { limit: 1e3, before });
|
|
1490
|
-
if (page.length === 0) break;
|
|
1491
|
-
allSigs.push(...page);
|
|
1492
|
-
before = page[page.length - 1].signature;
|
|
1493
|
-
}
|
|
1494
|
-
allSigs.sort((a, b) => a.blockTime - b.blockTime);
|
|
1495
|
-
const parseCount = Math.min(allSigs.length, 50);
|
|
1496
|
-
const recentSigs = allSigs.slice(-parseCount).reverse();
|
|
1497
|
-
const txResults = await Promise.allSettled(recentSigs.map((s) => this.getTransactionStdRpc(s.signature)));
|
|
1498
|
-
let totalSOLSent = 0;
|
|
1499
|
-
let totalSOLReceived = 0;
|
|
1500
|
-
const interactors = {};
|
|
1501
|
-
for (const result of txResults) {
|
|
1502
|
-
if (result.status !== "fulfilled" || !result.value) continue;
|
|
1503
|
-
const tx = result.value;
|
|
1504
|
-
const meta = tx.meta;
|
|
1505
|
-
if (!meta) continue;
|
|
1506
|
-
const preBal = meta.preBalances || [];
|
|
1507
|
-
const postBal = meta.postBalances || [];
|
|
1508
|
-
const accountKeys = tx.transaction?.message?.accountKeys || [];
|
|
1509
|
-
if (preBal.length > 0 && postBal.length > 0) {
|
|
1510
|
-
const diff = (preBal[0] - postBal[0]) / this.LAMPORTS;
|
|
1511
|
-
if (diff > 1e-4) totalSOLSent += diff;
|
|
1512
|
-
else if (diff < -1e-4) totalSOLReceived += Math.abs(diff);
|
|
1513
|
-
}
|
|
1514
|
-
for (let i = 1; i < accountKeys.length - 1; i++) {
|
|
1515
|
-
const pk = accountKeys[i]?.pubkey || accountKeys[i];
|
|
1516
|
-
if (pk && typeof pk === "string" && pk !== address) {
|
|
1517
|
-
interactors[pk] = (interactors[pk] || 0) + 1;
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
const topInteractors = Object.entries(interactors).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([address2, count]) => ({ address: address2, count }));
|
|
1522
|
-
return { signatures: allSigs, topInteractors, totalSOLSent, totalSOLReceived, totalTime: Date.now() - start };
|
|
1523
|
-
}
|
|
1524
|
-
// ================================================================
|
|
1525
|
-
// FALLBACK FUNDING TREE — tries SIM first, then RPC
|
|
1526
|
-
// ================================================================
|
|
1527
|
-
async getFundingTreeFallback(address, maxTransactions = 200) {
|
|
1528
|
-
if (duneSimClient.isEnabled()) {
|
|
1529
|
-
try {
|
|
1530
|
-
return await this.getFundingTreeViaSim(address);
|
|
1531
|
-
} catch (simErr) {
|
|
1532
|
-
console.log("[SolanaHelius] SIM funding tree failed, using RPC:", simErr?.message);
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
return this.getFundingTreeViaRpc(address, maxTransactions);
|
|
1536
|
-
}
|
|
1537
|
-
async getFundingTreeViaSim(address) {
|
|
1538
|
-
const simTxs = await duneSimClient.getTransactions(address, { limit: 500 });
|
|
1539
|
-
const txs = duneSimClient.mapTransactions(simTxs);
|
|
1540
|
-
const sources = {};
|
|
1541
|
-
const destinations = {};
|
|
1542
|
-
for (const tx of txs) {
|
|
1543
|
-
if (tx.to === address && tx.from) {
|
|
1544
|
-
if (!sources[tx.from]) sources[tx.from] = { total: 0, count: 0 };
|
|
1545
|
-
sources[tx.from].total += tx.amount || 0;
|
|
1546
|
-
sources[tx.from].count += 1;
|
|
1547
|
-
}
|
|
1548
|
-
if (tx.from === address && tx.to) {
|
|
1549
|
-
if (!destinations[tx.to]) destinations[tx.to] = { total: 0, count: 0 };
|
|
1550
|
-
destinations[tx.to].total += tx.amount || 0;
|
|
1551
|
-
destinations[tx.to].count += 1;
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
return { sources, destinations };
|
|
1555
|
-
}
|
|
1556
|
-
async getFundingTreeViaRpc(address, maxTransactions = 200) {
|
|
1557
|
-
const sigs = await this.getSignaturesForAddressStdRpc(address, { limit: Math.min(maxTransactions, 1e3) });
|
|
1558
|
-
if (sigs.length === 0) return { sources: {}, destinations: {} };
|
|
1559
|
-
const txResults = await Promise.allSettled(sigs.map((s) => this.getTransactionStdRpc(s.signature)));
|
|
1560
|
-
const sources = {};
|
|
1561
|
-
const destinations = {};
|
|
1562
|
-
for (const result of txResults) {
|
|
1563
|
-
if (result.status !== "fulfilled" || !result.value) continue;
|
|
1564
|
-
const tx = result.value;
|
|
1565
|
-
const meta = tx.meta;
|
|
1566
|
-
if (!meta) continue;
|
|
1567
|
-
const preBal = meta.preBalances || [];
|
|
1568
|
-
const postBal = meta.postBalances || [];
|
|
1569
|
-
const accountKeys = tx.transaction?.message?.accountKeys || [];
|
|
1570
|
-
if (preBal.length < 2 || postBal.length < 2) continue;
|
|
1571
|
-
const diff = (preBal[0] - postBal[0]) / this.LAMPORTS;
|
|
1572
|
-
if (Math.abs(diff) < 1e-4) continue;
|
|
1573
|
-
const feePayer = accountKeys[0]?.pubkey || accountKeys[0] || "";
|
|
1574
|
-
if (feePayer === address) {
|
|
1575
|
-
if (accountKeys.length > 1) {
|
|
1576
|
-
const dest = accountKeys[1]?.pubkey || accountKeys[1] || "unknown";
|
|
1577
|
-
if (!destinations[dest]) destinations[dest] = { total: 0, count: 0 };
|
|
1578
|
-
destinations[dest].total += diff;
|
|
1579
|
-
destinations[dest].count += 1;
|
|
1580
|
-
}
|
|
1581
|
-
} else if (feePayer) {
|
|
1582
|
-
if (!sources[feePayer]) sources[feePayer] = { total: 0, count: 0 };
|
|
1583
|
-
sources[feePayer].total += Math.abs(diff);
|
|
1584
|
-
sources[feePayer].count += 1;
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
return { sources, destinations };
|
|
1588
|
-
}
|
|
1589
|
-
};
|
|
1590
|
-
solanaHeliusClient = SolanaHeliusClient.getInstance();
|
|
1591
|
-
}
|
|
1592
|
-
});
|
|
1593
|
-
|
|
1594
|
-
// src/services/SolanaPortfolioService.ts
|
|
1595
|
-
var SolanaPortfolioService_exports = {};
|
|
1596
|
-
__export(SolanaPortfolioService_exports, {
|
|
1597
|
-
SolanaPortfolioService: () => SolanaPortfolioService,
|
|
1598
|
-
solanaPortfolioService: () => solanaPortfolioService
|
|
1599
|
-
});
|
|
1600
|
-
import fetch4 from "node-fetch";
|
|
1601
|
-
var LAMPORTS_PER_SOL2, JUPITER_PRICE_API, SolanaPortfolioService, solanaPortfolioService;
|
|
1602
|
-
var init_SolanaPortfolioService = __esm({
|
|
1603
|
-
"src/services/SolanaPortfolioService.ts"() {
|
|
1604
|
-
init_SolanaKeyPoolManager();
|
|
1605
|
-
init_cache();
|
|
1606
|
-
init_DuneSimClient();
|
|
1607
|
-
init_SolanaHeliusClient();
|
|
1608
|
-
LAMPORTS_PER_SOL2 = 1e9;
|
|
1609
|
-
JUPITER_PRICE_API = "https://price.jup.ag/v6/price";
|
|
1610
|
-
SolanaPortfolioService = class {
|
|
1611
|
-
priceCache = /* @__PURE__ */ new Map();
|
|
1612
|
-
/**
|
|
1613
|
-
* Helius-powered full overlay scan: signatures + transfers in parallel
|
|
1614
|
-
*/
|
|
1615
|
-
async scanOverview(address) {
|
|
1616
|
-
const start = Date.now();
|
|
1617
|
-
const helius = solanaHeliusClient;
|
|
1618
|
-
const scan = await helius.scanWallet(address);
|
|
1619
|
-
const signatures = scan.signatures;
|
|
1620
|
-
const transfers = scan.transfers;
|
|
1621
|
-
const LAMPORTS = 1e9;
|
|
1622
|
-
const firstSig = signatures[0];
|
|
1623
|
-
const lastSig = signatures[signatures.length - 1];
|
|
1624
|
-
const firstTimestamp = firstSig?.blockTime ? new Date(firstSig.blockTime * 1e3).toISOString() : "";
|
|
1625
|
-
const lastTimestamp = lastSig?.blockTime ? new Date(lastSig.blockTime * 1e3).toISOString() : "";
|
|
1626
|
-
const activityPeriodDays = firstSig?.blockTime && lastSig?.blockTime ? Math.round((lastSig.blockTime - firstSig.blockTime) / 86400) : 0;
|
|
1627
|
-
let totalSent = 0;
|
|
1628
|
-
let totalReceived = 0;
|
|
1629
|
-
const interactors = {};
|
|
1630
|
-
for (const t of transfers) {
|
|
1631
|
-
const isSol = !t.mint || t.mint === "So11111111111111111111111111111111111111112";
|
|
1632
|
-
const amt = isSol ? t.amount / LAMPORTS : t.amount;
|
|
1633
|
-
if (t.source === address) {
|
|
1634
|
-
totalSent += amt;
|
|
1635
|
-
interactors[t.destination] = (interactors[t.destination] || 0) + 1;
|
|
1636
|
-
} else if (t.destination === address) {
|
|
1637
|
-
totalReceived += amt;
|
|
1638
|
-
interactors[t.source] = (interactors[t.source] || 0) + 1;
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
const topInteractors = Object.entries(interactors).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([address2, count]) => ({ address: address2, count }));
|
|
1642
|
-
const uniqueAddresses = Object.keys(interactors).slice(0, 200);
|
|
1643
|
-
return {
|
|
1644
|
-
wallet: address,
|
|
1645
|
-
firstTimestamp,
|
|
1646
|
-
lastTimestamp,
|
|
1647
|
-
activityPeriodDays,
|
|
1648
|
-
totalTransactions: signatures.length,
|
|
1649
|
-
totalSOLSent: totalSent.toFixed(6),
|
|
1650
|
-
totalSOLReceived: totalReceived.toFixed(6),
|
|
1651
|
-
uniqueAddressCount: Object.keys(interactors).length,
|
|
1652
|
-
uniqueAddresses,
|
|
1653
|
-
topInteractors,
|
|
1654
|
-
scanTimeMs: Date.now() - start
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
/**
|
|
1658
|
-
* Free-tier fallback: uses getSignaturesForAddress RPC + batch getTransaction
|
|
1659
|
-
* No reliance on paid Helius features
|
|
1660
|
-
*/
|
|
1661
|
-
async scanOverviewFallback(address) {
|
|
1662
|
-
const start = Date.now();
|
|
1663
|
-
const helius = solanaHeliusClient;
|
|
1664
|
-
const result = await helius.scanWalletFallback(address);
|
|
1665
|
-
const signatures = result.signatures;
|
|
1666
|
-
const firstSig = signatures[0];
|
|
1667
|
-
const lastSig = signatures[signatures.length - 1];
|
|
1668
|
-
const firstTimestamp = firstSig?.blockTime ? new Date(firstSig.blockTime * 1e3).toISOString() : "";
|
|
1669
|
-
const lastTimestamp = lastSig?.blockTime ? new Date(lastSig.blockTime * 1e3).toISOString() : "";
|
|
1670
|
-
const activityPeriodDays = firstSig?.blockTime && lastSig?.blockTime ? Math.round((lastSig.blockTime - firstSig.blockTime) / 86400) : 0;
|
|
1671
|
-
return {
|
|
1672
|
-
wallet: address,
|
|
1673
|
-
firstTimestamp,
|
|
1674
|
-
lastTimestamp,
|
|
1675
|
-
activityPeriodDays,
|
|
1676
|
-
totalTransactions: signatures.length,
|
|
1677
|
-
totalSOLSent: result.totalSOLSent.toFixed(6),
|
|
1678
|
-
totalSOLReceived: result.totalSOLReceived.toFixed(6),
|
|
1679
|
-
uniqueAddressCount: result.topInteractors.length,
|
|
1680
|
-
uniqueAddresses: result.topInteractors.map((i) => i.address).slice(0, 200),
|
|
1681
|
-
topInteractors: result.topInteractors,
|
|
1682
|
-
scanTimeMs: Date.now() - start
|
|
1683
|
-
};
|
|
1684
|
-
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Get portfolio - tries SIM first, falls back to existing RPC-based method
|
|
1687
|
-
*/
|
|
1688
|
-
async getPortfolio(address, filterOptions) {
|
|
1689
|
-
const cacheKey = `solana:portfolio:${address}:${JSON.stringify(filterOptions || {})}`;
|
|
1690
|
-
const cached = cache.get(cacheKey);
|
|
1691
|
-
if (cached) return cached;
|
|
1692
|
-
if (duneSimClient.isEnabled()) {
|
|
1693
|
-
try {
|
|
1694
|
-
console.log("[SolanaPortfolio] Trying SIM for portfolio...");
|
|
1695
|
-
const simPortfolio = filterOptions?.excludeSpamTokens || filterOptions?.minLiquidity ? await duneSimClient.getFilteredPortfolio(address, {
|
|
1696
|
-
excludeSpamTokens: filterOptions?.excludeSpamTokens,
|
|
1697
|
-
excludeUnpriced: filterOptions?.excludeUnpriced,
|
|
1698
|
-
minLiquidity: filterOptions?.minLiquidity
|
|
1699
|
-
}) : await duneSimClient.getBalances(address).then((r) => duneSimClient.mapBalancesToPortfolio(r));
|
|
1700
|
-
const portfolio = {
|
|
1701
|
-
address: simPortfolio.address,
|
|
1702
|
-
sol: simPortfolio.sol,
|
|
1703
|
-
tokens: simPortfolio.tokens.map((t) => ({
|
|
1704
|
-
mint: t.mint,
|
|
1705
|
-
amount: t.amount,
|
|
1706
|
-
decimals: t.decimals,
|
|
1707
|
-
uiAmount: t.uiAmount,
|
|
1708
|
-
symbol: t.symbol,
|
|
1709
|
-
name: t.name,
|
|
1710
|
-
logoUrl: t.logoUrl,
|
|
1711
|
-
price: t.price,
|
|
1712
|
-
value: t.value
|
|
1713
|
-
})),
|
|
1714
|
-
staking: [],
|
|
1715
|
-
// Would need additional SIM call
|
|
1716
|
-
totalUsd: simPortfolio.totalUsd,
|
|
1717
|
-
fetchedAt: simPortfolio.fetchedAt
|
|
1718
|
-
};
|
|
1719
|
-
console.log("[SolanaPortfolio] SIM portfolio fetched successfully");
|
|
1720
|
-
cache.set(cacheKey, portfolio, 60);
|
|
1721
|
-
return portfolio;
|
|
1722
|
-
} catch (simError) {
|
|
1723
|
-
console.error("[SolanaPortfolio] SIM failed, falling back to RPC:", simError);
|
|
1724
|
-
}
|
|
1725
|
-
}
|
|
1726
|
-
return this.getPortfolioFallback(address);
|
|
1727
|
-
}
|
|
1728
|
-
/**
|
|
1729
|
-
* Original RPC-based portfolio (fallback)
|
|
1730
|
-
*/
|
|
1731
|
-
async getPortfolioFallback(address) {
|
|
1732
|
-
const cacheKey = `solana:portfolio:fallback:${address}`;
|
|
1733
|
-
const [balance, tokenAccounts, stakeAccounts] = await Promise.all([
|
|
1734
|
-
this.getBalance(address),
|
|
1735
|
-
this.getTokenAccounts(address),
|
|
1736
|
-
this.getStakeAccounts(address)
|
|
1737
|
-
]);
|
|
1738
|
-
const mints = tokenAccounts.map((t) => t.mint).filter(Boolean);
|
|
1739
|
-
const prices = await this.getBatchPrices(mints);
|
|
1740
|
-
const tokens = tokenAccounts.map((t) => {
|
|
1741
|
-
const price = prices[t.mint] || 0;
|
|
1742
|
-
return {
|
|
1743
|
-
...t,
|
|
1744
|
-
price,
|
|
1745
|
-
value: t.uiAmount * price
|
|
1746
|
-
};
|
|
1747
|
-
}).filter((t) => t.uiAmount > 0);
|
|
1748
|
-
let solPrice = prices["So11111111111111111111111111111111111111112"] || 0;
|
|
1749
|
-
if (solPrice === 0) {
|
|
1750
|
-
solPrice = await this.getSolPrice();
|
|
1751
|
-
}
|
|
1752
|
-
const totalUsd = balance / LAMPORTS_PER_SOL2 * solPrice + tokens.reduce((sum, t) => sum + (t.value || 0), 0);
|
|
1753
|
-
const portfolio = {
|
|
1754
|
-
address,
|
|
1755
|
-
sol: {
|
|
1756
|
-
lamports: balance,
|
|
1757
|
-
sol: balance / LAMPORTS_PER_SOL2,
|
|
1758
|
-
usd: balance / LAMPORTS_PER_SOL2 * solPrice
|
|
1759
|
-
},
|
|
1760
|
-
tokens,
|
|
1761
|
-
staking: stakeAccounts,
|
|
1762
|
-
totalUsd,
|
|
1763
|
-
fetchedAt: Date.now()
|
|
1764
|
-
};
|
|
1765
|
-
cache.set(cacheKey, portfolio, 60);
|
|
1766
|
-
return portfolio;
|
|
1767
|
-
}
|
|
1768
|
-
async getBalance(address) {
|
|
1769
|
-
return solanaKeyPool.execute(async (endpoint) => {
|
|
1770
|
-
const res = await fetch4(endpoint, {
|
|
1771
|
-
method: "POST",
|
|
1772
|
-
headers: { "Content-Type": "application/json" },
|
|
1773
|
-
body: JSON.stringify({
|
|
1774
|
-
jsonrpc: "2.0",
|
|
1775
|
-
id: 1,
|
|
1776
|
-
method: "getBalance",
|
|
1777
|
-
params: [address]
|
|
1778
|
-
})
|
|
1779
|
-
});
|
|
1780
|
-
const data = await res.json();
|
|
1781
|
-
return data.result?.value || 0;
|
|
1782
|
-
}, 1);
|
|
1783
|
-
}
|
|
1784
|
-
async getTokenAccounts(address) {
|
|
1785
|
-
const tokens = [];
|
|
1786
|
-
try {
|
|
1787
|
-
const alchemyTokens = await this.getTokensFromAlchemy(address);
|
|
1788
|
-
tokens.push(...alchemyTokens);
|
|
1789
|
-
} catch (e) {
|
|
1790
|
-
console.error("[SolanaPortfolio] Alchemy token fetch failed:", e);
|
|
1791
|
-
}
|
|
1792
|
-
if (tokens.length === 0) {
|
|
1793
|
-
try {
|
|
1794
|
-
const [token2022, token2022Program] = await Promise.all([
|
|
1795
|
-
this.getTokensByProgram(address, "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
|
|
1796
|
-
this.getTokensByProgram(address, "TokenzQdBNbLqP5VEhdkAS6dFvwzYqE8hpzEfb9Kh")
|
|
1797
|
-
]);
|
|
1798
|
-
tokens.push(...token2022, ...token2022Program);
|
|
1799
|
-
} catch (e) {
|
|
1800
|
-
console.error("[SolanaPortfolio] RPC token fetch failed:", e);
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
return tokens;
|
|
1804
|
-
}
|
|
1805
|
-
async getTokensFromAlchemy(address) {
|
|
1806
|
-
const response = await solanaKeyPool.execute(async (endpoint) => {
|
|
1807
|
-
return fetch4(endpoint, {
|
|
1808
|
-
method: "POST",
|
|
1809
|
-
headers: { "Content-Type": "application/json" },
|
|
1810
|
-
body: JSON.stringify({
|
|
1811
|
-
jsonrpc: "2.0",
|
|
1812
|
-
id: 1,
|
|
1813
|
-
method: "getAssetsByOwner",
|
|
1814
|
-
params: [{
|
|
1815
|
-
owner: address,
|
|
1816
|
-
options: {
|
|
1817
|
-
limit: 100,
|
|
1818
|
-
showMetadata: true,
|
|
1819
|
-
showNativeBalance: true
|
|
1820
|
-
}
|
|
1821
|
-
}]
|
|
1822
|
-
})
|
|
1823
|
-
});
|
|
1824
|
-
}, 3);
|
|
1825
|
-
const data = await response.json();
|
|
1826
|
-
const items = data?.result?.assets || [];
|
|
1827
|
-
return items.filter((item) => item.interface === "Token" || item.tokenStandard === "Fungible").map((item) => ({
|
|
1828
|
-
mint: item.id,
|
|
1829
|
-
amount: BigInt(item.tokenInfo?.amount || 0),
|
|
1830
|
-
decimals: item.tokenInfo?.decimals || 0,
|
|
1831
|
-
uiAmount: item.tokenInfo?.amount ? parseFloat(item.tokenInfo.amount) : 0
|
|
1832
|
-
})).filter((t) => t.uiAmount > 0);
|
|
1833
|
-
}
|
|
1834
|
-
async getTokensByProgram(address, programId) {
|
|
1835
|
-
try {
|
|
1836
|
-
return await solanaKeyPool.execute(async (endpoint) => {
|
|
1837
|
-
const res = await fetch4(endpoint, {
|
|
1838
|
-
method: "POST",
|
|
1839
|
-
headers: { "Content-Type": "application/json" },
|
|
1840
|
-
body: JSON.stringify({
|
|
1841
|
-
jsonrpc: "2.0",
|
|
1842
|
-
id: 1,
|
|
1843
|
-
method: "getParsedTokenAccountsByOwner",
|
|
1844
|
-
params: [address, { programId }]
|
|
1845
|
-
})
|
|
1846
|
-
});
|
|
1847
|
-
const data = await res.json();
|
|
1848
|
-
const accounts = data.result?.value || [];
|
|
1849
|
-
return accounts.map((acc) => {
|
|
1850
|
-
const info = acc.account.data.parsed.info;
|
|
1851
|
-
return {
|
|
1852
|
-
mint: info.mint,
|
|
1853
|
-
amount: BigInt(info.tokenAmount.amount),
|
|
1854
|
-
decimals: info.tokenAmount.decimals,
|
|
1855
|
-
uiAmount: info.tokenAmount.uiAmount || 0
|
|
1856
|
-
};
|
|
1857
|
-
}).filter((t) => t.uiAmount > 0);
|
|
1858
|
-
}, 10);
|
|
1859
|
-
} catch (e) {
|
|
1860
|
-
console.error(`[SolanaPortfolio] Error fetching tokens for program ${programId}:`, e);
|
|
1861
|
-
return [];
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
async getStakeAccounts(address) {
|
|
1865
|
-
try {
|
|
1866
|
-
return await solanaKeyPool.execute(async (endpoint) => {
|
|
1867
|
-
const res = await fetch4(endpoint, {
|
|
1868
|
-
method: "POST",
|
|
1869
|
-
headers: { "Content-Type": "application/json" },
|
|
1870
|
-
body: JSON.stringify({
|
|
1871
|
-
jsonrpc: "2.0",
|
|
1872
|
-
id: 1,
|
|
1873
|
-
method: "getStakeAccounts",
|
|
1874
|
-
params: [address]
|
|
1875
|
-
})
|
|
1876
|
-
});
|
|
1877
|
-
const data = await res.json();
|
|
1878
|
-
return data.result || [];
|
|
1879
|
-
}, 5);
|
|
1880
|
-
} catch (e) {
|
|
1881
|
-
return [];
|
|
1882
|
-
}
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* Get transactions - uses SIM as primary
|
|
1886
|
-
*/
|
|
1887
|
-
async getTransactions(address, limit = 100) {
|
|
1888
|
-
const cacheKey = `solana:txs:${address}:${limit}`;
|
|
1889
|
-
const cached = cache.get(cacheKey);
|
|
1890
|
-
if (cached) return cached;
|
|
1891
|
-
if (duneSimClient.isEnabled()) {
|
|
1892
|
-
try {
|
|
1893
|
-
console.log("[SolanaPortfolio] Trying SIM for transactions...");
|
|
1894
|
-
const simResponse = await duneSimClient.getTransactions(address, { limit });
|
|
1895
|
-
const txs2 = duneSimClient.mapTransactions(simResponse);
|
|
1896
|
-
const transactions2 = txs2.map((tx) => ({
|
|
1897
|
-
signature: tx.signature,
|
|
1898
|
-
slot: tx.slot,
|
|
1899
|
-
blockTime: tx.blockTime,
|
|
1900
|
-
fee: tx.fee,
|
|
1901
|
-
status: tx.status,
|
|
1902
|
-
type: tx.type,
|
|
1903
|
-
from: tx.from,
|
|
1904
|
-
to: tx.to,
|
|
1905
|
-
amount: tx.amount,
|
|
1906
|
-
token: tx.token,
|
|
1907
|
-
tokenAmount: tx.tokenAmount,
|
|
1908
|
-
instructions: tx.instructions
|
|
1909
|
-
}));
|
|
1910
|
-
console.log("[SolanaPortfolio] SIM transactions fetched successfully");
|
|
1911
|
-
cache.set(cacheKey, transactions2, 300);
|
|
1912
|
-
return transactions2;
|
|
1913
|
-
} catch (simError) {
|
|
1914
|
-
console.error("[SolanaPortfolio] SIM transactions failed, falling back to RPC:", simError);
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
const signatures = await this.getSignatures(address, limit);
|
|
1918
|
-
if (signatures.length === 0) return [];
|
|
1919
|
-
const transactions = await Promise.all(
|
|
1920
|
-
signatures.map((sig) => this.getTransaction(sig))
|
|
1921
|
-
);
|
|
1922
|
-
const txs = transactions.filter(Boolean);
|
|
1923
|
-
cache.set(cacheKey, txs, 300);
|
|
1924
|
-
return txs;
|
|
1925
|
-
}
|
|
1926
|
-
async getSignatures(address, limit) {
|
|
1927
|
-
return solanaKeyPool.execute(async (endpoint) => {
|
|
1928
|
-
const res = await fetch4(endpoint, {
|
|
1929
|
-
method: "POST",
|
|
1930
|
-
headers: { "Content-Type": "application/json" },
|
|
1931
|
-
body: JSON.stringify({
|
|
1932
|
-
jsonrpc: "2.0",
|
|
1933
|
-
id: 1,
|
|
1934
|
-
method: "getSignaturesForAddress",
|
|
1935
|
-
params: [address, { limit, commitment: "confirmed" }]
|
|
1936
|
-
})
|
|
1937
|
-
});
|
|
1938
|
-
const data = await res.json();
|
|
1939
|
-
return data.result?.map((s) => s.signature) || [];
|
|
1940
|
-
}, 1);
|
|
1941
|
-
}
|
|
1942
|
-
async getTransaction(signature) {
|
|
1943
|
-
try {
|
|
1944
|
-
return await solanaKeyPool.execute(async (endpoint) => {
|
|
1945
|
-
const res = await fetch4(endpoint, {
|
|
1946
|
-
method: "POST",
|
|
1947
|
-
headers: { "Content-Type": "application/json" },
|
|
1948
|
-
body: JSON.stringify({
|
|
1949
|
-
jsonrpc: "2.0",
|
|
1950
|
-
id: 1,
|
|
1951
|
-
method: "getTransaction",
|
|
1952
|
-
params: [signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0 }]
|
|
1953
|
-
})
|
|
1954
|
-
});
|
|
1955
|
-
const data = await res.json();
|
|
1956
|
-
if (!data.result) return null;
|
|
1957
|
-
const tx = data.result;
|
|
1958
|
-
const meta = tx.meta;
|
|
1959
|
-
const instructions = tx.transaction.message.instructions;
|
|
1960
|
-
let from = "";
|
|
1961
|
-
let to = "";
|
|
1962
|
-
let amount = 0;
|
|
1963
|
-
let token = "";
|
|
1964
|
-
let tokenAmount = 0;
|
|
1965
|
-
if (tx.transaction.message.accountKeys?.[0]) {
|
|
1966
|
-
from = tx.transaction.message.accountKeys[0].pubkey;
|
|
1967
|
-
}
|
|
1968
|
-
for (const ix of instructions) {
|
|
1969
|
-
if (ix.parsed) {
|
|
1970
|
-
if (ix.parsed.type === "transfer") {
|
|
1971
|
-
to = ix.parsed.info.destination;
|
|
1972
|
-
amount = ix.parsed.info.lamports || 0;
|
|
1973
|
-
} else if (ix.parsed.type === "transferChecked") {
|
|
1974
|
-
to = ix.parsed.info.destination;
|
|
1975
|
-
token = ix.parsed.info.mint;
|
|
1976
|
-
tokenAmount = parseFloat(ix.parsed.info.tokenAmount?.amount || "0");
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
return {
|
|
1981
|
-
signature,
|
|
1982
|
-
slot: tx.slot,
|
|
1983
|
-
blockTime: tx.blockTime * 1e3,
|
|
1984
|
-
fee: meta?.fee || 0,
|
|
1985
|
-
status: meta?.err ? "failed" : "success",
|
|
1986
|
-
type: this.inferTransactionType(instructions),
|
|
1987
|
-
from,
|
|
1988
|
-
to,
|
|
1989
|
-
amount: amount > 0 ? amount / LAMPORTS_PER_SOL2 : void 0,
|
|
1990
|
-
token,
|
|
1991
|
-
tokenAmount: tokenAmount > 0 ? tokenAmount : void 0,
|
|
1992
|
-
instructions: instructions.map((ix) => ix.parsed || ix)
|
|
1993
|
-
};
|
|
1994
|
-
}, 1);
|
|
1995
|
-
} catch (e) {
|
|
1996
|
-
return null;
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
inferTransactionType(instructions) {
|
|
2000
|
-
for (const ix of instructions) {
|
|
2001
|
-
if (ix.parsed?.type) return ix.parsed.type;
|
|
2002
|
-
if (ix.program === "system") return "transfer";
|
|
2003
|
-
if (ix.program === "token") return "token-transfer";
|
|
2004
|
-
if (ix.program === "stake") return "staking";
|
|
2005
|
-
if (ix.program === "vote") return "vote";
|
|
2006
|
-
if (ix.program === "sysvar") return "system";
|
|
2007
|
-
}
|
|
2008
|
-
return "unknown";
|
|
2009
|
-
}
|
|
2010
|
-
async getNFTs(address) {
|
|
2011
|
-
const cacheKey = `solana:nfts:${address}`;
|
|
2012
|
-
const cached = cache.get(cacheKey);
|
|
2013
|
-
if (cached) return cached;
|
|
2014
|
-
const nfts = [];
|
|
2015
|
-
try {
|
|
2016
|
-
nfts.push(...await this.getNFTsFromAlchemy(address));
|
|
2017
|
-
} catch (e) {
|
|
2018
|
-
console.error("[SolanaPortfolio] Alchemy NFT fetch failed:", e);
|
|
2019
|
-
}
|
|
2020
|
-
cache.set(cacheKey, nfts, 300);
|
|
2021
|
-
return nfts;
|
|
2022
|
-
}
|
|
2023
|
-
async getNFTsFromAlchemy(address) {
|
|
2024
|
-
const response = await solanaKeyPool.execute(async (endpoint) => {
|
|
2025
|
-
return fetch4(endpoint, {
|
|
2026
|
-
method: "POST",
|
|
2027
|
-
headers: { "Content-Type": "application/json" },
|
|
2028
|
-
body: JSON.stringify({
|
|
2029
|
-
jsonrpc: "2.0",
|
|
2030
|
-
id: 1,
|
|
2031
|
-
method: "getAssetsByOwner",
|
|
2032
|
-
params: [{
|
|
2033
|
-
owner: address,
|
|
2034
|
-
options: { limit: 100, showMetadata: true }
|
|
2035
|
-
}]
|
|
2036
|
-
})
|
|
2037
|
-
});
|
|
2038
|
-
}, 3);
|
|
2039
|
-
const data = await response.json();
|
|
2040
|
-
const items = data?.result?.assets || [];
|
|
2041
|
-
return items.map((item) => ({
|
|
2042
|
-
id: item.id,
|
|
2043
|
-
mint: item.id,
|
|
2044
|
-
owner: address,
|
|
2045
|
-
name: item.metadata?.name || item.content?.metadata?.name || "Unknown",
|
|
2046
|
-
symbol: item.metadata?.symbol || item.content?.metadata?.symbol,
|
|
2047
|
-
imageUrl: item.metadata?.image || item.content?.links?.image,
|
|
2048
|
-
collection: item.collection || item.grouping?.find((g) => g.groupKey === "collection")?.groupValue,
|
|
2049
|
-
collectionImage: item.metadata?.image || item.content?.links?.image,
|
|
2050
|
-
attributes: item.metadata?.attributes || item.content?.metadata?.attributes
|
|
2051
|
-
}));
|
|
2052
|
-
}
|
|
2053
|
-
async getDeFiPositions(address) {
|
|
2054
|
-
const positions = [];
|
|
2055
|
-
try {
|
|
2056
|
-
const tokens = await this.getTokenAccounts(address);
|
|
2057
|
-
const raydiumPools = ["RAYdium", "Raydium", "LP"];
|
|
2058
|
-
const jupiterTokens = ["JUP", "jup"];
|
|
2059
|
-
for (const token of tokens) {
|
|
2060
|
-
if (raydiumPools.some((p) => token.name?.includes(p) || token.symbol?.includes(p))) {
|
|
2061
|
-
positions.push({
|
|
2062
|
-
protocol: "Raydium",
|
|
2063
|
-
type: "Liquidity Pool",
|
|
2064
|
-
amount: token.uiAmount,
|
|
2065
|
-
value: token.value || 0,
|
|
2066
|
-
token: token.symbol || token.mint.slice(0, 8)
|
|
2067
|
-
});
|
|
2068
|
-
}
|
|
2069
|
-
if (jupiterTokens.includes(token.symbol || "")) {
|
|
2070
|
-
positions.push({
|
|
2071
|
-
protocol: "Jupiter",
|
|
2072
|
-
type: "Token",
|
|
2073
|
-
amount: token.uiAmount,
|
|
2074
|
-
value: token.value || 0,
|
|
2075
|
-
token: token.symbol || ""
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
} catch (e) {
|
|
2080
|
-
console.error("[SolanaPortfolio] Error fetching DeFi positions:", e);
|
|
2081
|
-
}
|
|
2082
|
-
return positions;
|
|
2083
|
-
}
|
|
2084
|
-
async getRiskAnalysis(address) {
|
|
2085
|
-
const cacheKey = `solana:risk:${address}`;
|
|
2086
|
-
const cached = cache.get(cacheKey);
|
|
2087
|
-
if (cached) return cached;
|
|
2088
|
-
const [balance, signatures, tokens] = await Promise.all([
|
|
2089
|
-
this.getBalance(address),
|
|
2090
|
-
this.getSignaturesWithTime(address, 100),
|
|
2091
|
-
this.getTokenAccounts(address)
|
|
2092
|
-
]);
|
|
2093
|
-
const signals = [];
|
|
2094
|
-
let score = 0;
|
|
2095
|
-
const solBalance = balance / LAMPORTS_PER_SOL2;
|
|
2096
|
-
if (solBalance < 0.01) {
|
|
2097
|
-
score += 20;
|
|
2098
|
-
signals.push({ id: "low_balance", name: "Near-Zero SOL Balance", detected: true, severity: "high" });
|
|
2099
|
-
}
|
|
2100
|
-
if (signatures.length > 0) {
|
|
2101
|
-
const firstTx = signatures[0];
|
|
2102
|
-
if (firstTx.blockTime) {
|
|
2103
|
-
const age = Date.now() - firstTx.blockTime * 1e3;
|
|
2104
|
-
if (age < 30 * 24 * 60 * 60 * 1e3) {
|
|
2105
|
-
score += 15;
|
|
2106
|
-
signals.push({ id: "new_wallet", name: "Wallet Created Recently", detected: true, severity: "medium" });
|
|
2107
|
-
}
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
const spamTokens = tokens.filter((t) => t.uiAmount < 1 && t.decimals > 6);
|
|
2111
|
-
if (spamTokens.length > 10) {
|
|
2112
|
-
score += 10;
|
|
2113
|
-
signals.push({ id: "spam_tokens", name: "Many Low-Value Tokens (Potential Dust)", detected: true, severity: "medium" });
|
|
2114
|
-
}
|
|
2115
|
-
const unknownTokens = tokens.filter((t) => !t.symbol);
|
|
2116
|
-
if (unknownTokens.length > 5) {
|
|
2117
|
-
score += 5;
|
|
2118
|
-
signals.push({ id: "unknown_tokens", name: "Many Unidentified Tokens", detected: true, severity: "low" });
|
|
2119
|
-
}
|
|
2120
|
-
const result = {
|
|
2121
|
-
score: Math.min(score, 100),
|
|
2122
|
-
signals,
|
|
2123
|
-
factors: [
|
|
2124
|
-
{ label: "SOL Balance", value: `${solBalance.toFixed(2)} SOL`, risk: solBalance < 0.1 ? 30 : 0 },
|
|
2125
|
-
{ label: "Transaction Count", value: `${signatures.length} txs`, risk: 0 },
|
|
2126
|
-
{ label: "Token Count", value: `${tokens.length} tokens`, risk: tokens.length > 20 ? 10 : 0 },
|
|
2127
|
-
{ label: "NFT Count", value: "Check NFT tab", risk: 0 }
|
|
2128
|
-
]
|
|
2129
|
-
};
|
|
2130
|
-
cache.set(cacheKey, result, 3600);
|
|
2131
|
-
return result;
|
|
2132
|
-
}
|
|
2133
|
-
async getSignaturesWithTime(address, limit) {
|
|
2134
|
-
return solanaKeyPool.execute(async (endpoint) => {
|
|
2135
|
-
const res = await fetch4(endpoint, {
|
|
2136
|
-
method: "POST",
|
|
2137
|
-
headers: { "Content-Type": "application/json" },
|
|
2138
|
-
body: JSON.stringify({
|
|
2139
|
-
jsonrpc: "2.0",
|
|
2140
|
-
id: 1,
|
|
2141
|
-
method: "getSignaturesForAddress",
|
|
2142
|
-
params: [address, { limit, commitment: "confirmed" }]
|
|
2143
|
-
})
|
|
2144
|
-
});
|
|
2145
|
-
const data = await res.json();
|
|
2146
|
-
return data.result?.map((s) => ({ signature: s.signature, blockTime: s.blockTime || 0 })) || [];
|
|
2147
|
-
}, 1);
|
|
2148
|
-
}
|
|
2149
|
-
async getBatchPrices(mints) {
|
|
2150
|
-
const missing = mints.filter((m) => !this.priceCache.has(m));
|
|
2151
|
-
if (missing.length > 0) {
|
|
2152
|
-
const chunks = this.chunk(missing, 100);
|
|
2153
|
-
for (const chunk of chunks) {
|
|
2154
|
-
try {
|
|
2155
|
-
const ids = chunk.join(",");
|
|
2156
|
-
const res = await fetch4(`${JUPITER_PRICE_API}?ids=${ids}`);
|
|
2157
|
-
const data = await res.json();
|
|
2158
|
-
if (data.data) {
|
|
2159
|
-
for (const [mint, info] of Object.entries(data.data)) {
|
|
2160
|
-
this.priceCache.set(mint, info.price || 0);
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
} catch (e) {
|
|
2164
|
-
console.error("[SolanaPortfolio] Error fetching prices:", e);
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
const prices = {};
|
|
2169
|
-
for (const mint of mints) {
|
|
2170
|
-
prices[mint] = this.priceCache.get(mint) || 0;
|
|
2171
|
-
}
|
|
2172
|
-
return prices;
|
|
2173
|
-
}
|
|
2174
|
-
async getSolPrice() {
|
|
2175
|
-
try {
|
|
2176
|
-
const res = await fetch4("https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd");
|
|
2177
|
-
const data = await res.json();
|
|
2178
|
-
const price = data.solana?.usd || 0;
|
|
2179
|
-
this.priceCache.set("So11111111111111111111111111111111111111112", price);
|
|
2180
|
-
return price;
|
|
2181
|
-
} catch (e) {
|
|
2182
|
-
console.error("[SolanaPortfolio] Error fetching SOL price:", e);
|
|
2183
|
-
return 0;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
chunk(arr, size) {
|
|
2187
|
-
const chunks = [];
|
|
2188
|
-
for (let i = 0; i < arr.length; i += size) {
|
|
2189
|
-
chunks.push(arr.slice(i, i + size));
|
|
2190
|
-
}
|
|
2191
|
-
return chunks;
|
|
2192
|
-
}
|
|
2193
|
-
};
|
|
2194
|
-
solanaPortfolioService = new SolanaPortfolioService();
|
|
2195
|
-
}
|
|
2196
|
-
});
|
|
2197
|
-
|
|
2198
|
-
// src/data/entities.ts
|
|
2199
|
-
var entities_exports = {};
|
|
2200
|
-
__export(entities_exports, {
|
|
2201
|
-
bulkLookup: () => bulkLookup,
|
|
2202
|
-
findEntityByAddress: () => findEntityByAddress,
|
|
2203
|
-
getAllEntities: () => getAllEntities,
|
|
2204
|
-
getEntitiesByCategory: () => getEntitiesByCategory,
|
|
2205
|
-
getEntitiesByChain: () => getEntitiesByChain,
|
|
2206
|
-
getEntity: () => getEntity,
|
|
2207
|
-
getEntityByKey: () => getEntityByKey,
|
|
2208
|
-
searchEntities: () => searchEntities
|
|
2209
|
-
});
|
|
2210
|
-
function add(entity) {
|
|
2211
|
-
const key = `${entity.chain}:${entity.address.toLowerCase()}`;
|
|
2212
|
-
entityMap.set(key, entity);
|
|
2213
|
-
}
|
|
2214
|
-
function addMany(entities) {
|
|
2215
|
-
for (const e of entities) add(e);
|
|
2216
|
-
}
|
|
2217
|
-
function getEntity(chain, address) {
|
|
2218
|
-
return entityMap.get(`${chain}:${address.toLowerCase()}`);
|
|
2219
|
-
}
|
|
2220
|
-
function getEntityByKey(key) {
|
|
2221
|
-
return entityMap.get(key);
|
|
2222
|
-
}
|
|
2223
|
-
function bulkLookup(chain, addresses) {
|
|
2224
|
-
const results = /* @__PURE__ */ new Map();
|
|
2225
|
-
for (const addr of addresses) {
|
|
2226
|
-
const entity = getEntity(chain, addr);
|
|
2227
|
-
if (entity) results.set(addr.toLowerCase(), entity);
|
|
2228
|
-
}
|
|
2229
|
-
return results;
|
|
2230
|
-
}
|
|
2231
|
-
function searchEntities(query, chain, category) {
|
|
2232
|
-
const q = query.toLowerCase();
|
|
2233
|
-
const results = [];
|
|
2234
|
-
for (const entity of entityMap.values()) {
|
|
2235
|
-
if (chain && entity.chain !== chain) continue;
|
|
2236
|
-
if (category && entity.category !== category) continue;
|
|
2237
|
-
if (entity.name.toLowerCase().includes(q) || entity.tags.some((t) => t.includes(q))) {
|
|
2238
|
-
results.push(entity);
|
|
2239
|
-
}
|
|
2240
|
-
if (results.length >= 20) break;
|
|
2241
|
-
}
|
|
2242
|
-
return results;
|
|
2243
|
-
}
|
|
2244
|
-
function getAllEntities() {
|
|
2245
|
-
return entityMap;
|
|
2246
|
-
}
|
|
2247
|
-
function getEntitiesByChain(chain) {
|
|
2248
|
-
const results = [];
|
|
2249
|
-
for (const entity of entityMap.values()) {
|
|
2250
|
-
if (entity.chain === chain) results.push(entity);
|
|
2251
|
-
}
|
|
2252
|
-
return results;
|
|
2253
|
-
}
|
|
2254
|
-
function getEntitiesByCategory(category) {
|
|
2255
|
-
const results = [];
|
|
2256
|
-
for (const entity of entityMap.values()) {
|
|
2257
|
-
if (entity.category === category) results.push(entity);
|
|
2258
|
-
}
|
|
2259
|
-
return results;
|
|
2260
|
-
}
|
|
2261
|
-
function findEntityByAddress(address) {
|
|
2262
|
-
const addr = address.toLowerCase();
|
|
2263
|
-
for (const [key, entity] of entityMap) {
|
|
2264
|
-
if (key.endsWith(`:${addr}`)) return entity;
|
|
2265
|
-
}
|
|
2266
|
-
return void 0;
|
|
2267
|
-
}
|
|
2268
|
-
var entityMap;
|
|
2269
|
-
var init_entities = __esm({
|
|
2270
|
-
"src/data/entities.ts"() {
|
|
2271
|
-
entityMap = /* @__PURE__ */ new Map();
|
|
2272
|
-
addMany([
|
|
2273
|
-
// --- CEX ---
|
|
2274
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Hot Wallet 20", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"], description: "Binance hot wallet" },
|
|
2275
|
-
{ address: "0x631fc1ea2270e98fbd9d92658ece0f5a269aa161", name: "Binance: Hot Wallet", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2276
|
-
{ address: "0x28fA6C20b26Be9bAd1d89E5e8E2d1F5C5e3dE4aF", name: "Binance: Deposit Wallet", category: "cex", chain: "ethereum", confidence: 0.95, source: "manual", verified: false, tags: ["cex", "deposit", "binance"] },
|
|
2277
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase 2", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "coinbase"] },
|
|
2278
|
-
{ address: "0xb1697cea2605d1dBa32d94A72d8CBfCFB8f55aC9", name: "Coinbase: Hot Wallet", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "coinbase"] },
|
|
2279
|
-
{ address: "0xA5d1d5d9a8E7a8d1E5c8a9f2d3c4B5e6f7a8b9c0", name: "Coinbase: Deposit", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: false, tags: ["cex", "deposit", "coinbase"] },
|
|
2280
|
-
{ address: "0xe9f7ecae3a53d2a67105292894676b00d1fab785", name: "Kraken: Hot Wallet", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "kraken"] },
|
|
2281
|
-
{ address: "0xf30ba13e4b04ce5dc4d254ae5fa95477800f0eb0", name: "Kraken: Hot Wallet 2", category: "cex", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["cex", "hot-wallet", "kraken"] },
|
|
2282
|
-
{ address: "0x05ff6964d21e5dae3b1010d5ae0465b3c450f381", name: "Kraken: Hot Wallet 4", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "kraken"] },
|
|
2283
|
-
{ address: "0xf89d7b9c864f589bbf53a82105107622b35eaa40", name: "Bybit: Hot Wallet", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "bybit"] },
|
|
2284
|
-
{ address: "0x4BC195D2dC6Bf3B8e1C5b7e1D5C9aF3E2b7d1C0a", name: "Bybit: Deposit", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: false, tags: ["cex", "deposit", "bybit"] },
|
|
2285
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: Hot Wallet", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "okx"] },
|
|
2286
|
-
{ address: "0x559432e18b281731c054cd703d4b49872be4ed53", name: "OKX: Hot Wallet 5", category: "cex", chain: "ethereum", confidence: 0.95, source: "manual", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2287
|
-
{ address: "0x53f78a071d04224b8e254e243fffc6d9f2f3fa23", name: "KuCoin: Hot Wallet 2", category: "cex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "kucoin"] },
|
|
2288
|
-
{ address: "0x19e2A56B1F0C7c12d9a4f4a5d7C8E3F2a1b0c9d8", name: "Bitget: Hot Wallet", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: false, tags: ["cex", "hot-wallet", "bitget"] },
|
|
2289
|
-
{ address: "0x0f5d2A7B8E1d2C3a4b5e6f7a8b9c0d1e2f3a4b5", name: "Gate.io: Hot Wallet", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: false, tags: ["cex", "hot-wallet", "gateio"] },
|
|
2290
|
-
{ address: "0x3c783c21a0383057D128bae431890a2eF37B3E6C", name: "Gemini: Hot Wallet", category: "cex", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["cex", "hot-wallet", "gemini"] },
|
|
2291
|
-
{ address: "0xdF2dE17cBc55bE4796E7463e281eE2F3B0d106D8", name: "HTX (Huobi): Hot Wallet", category: "cex", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "huobi"] },
|
|
2292
|
-
{ address: "0x742d35Cc6634C0532925a3b844Bc9e7595f5b2a1", name: "Vitalik Buterin (vitalik.eth)", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["known-entity", "founder", "ethereum"], description: "Ethereum co-founder" },
|
|
2293
|
-
{ address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", name: "vitalik.eth", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["known-entity", "founder", "ethereum"] },
|
|
2294
|
-
{ address: "0x1Db3439a222C451ab1B7C8B157e3F0Df41bA93A0", name: "Justin Sun (justinsun.trx)", category: "protocol", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["known-entity", "founder", "tron"] },
|
|
2295
|
-
{ address: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", name: "Ethereum Foundation", category: "dao_treasury", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dao", "treasury", "ethereum"] },
|
|
2296
|
-
// --- Major DEX ---
|
|
2297
|
-
{ address: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", name: "Uniswap V2 Router", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "router", "amm"] },
|
|
2298
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3 Router", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "router", "amm"] },
|
|
2299
|
-
{ address: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", name: "Uniswap Universal Router", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "router", "amm"] },
|
|
2300
|
-
{ address: "0x1111111254fb6c44bAC0beD2854e76F90643097d", name: "1inch Router V5", category: "defi_aggregator", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["aggregator", "router", "dex"] },
|
|
2301
|
-
{ address: "0xDef1C0ded9bec7F1a1670819833240f027b25EfF", name: "0x Exchange Proxy", category: "defi_aggregator", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["aggregator", "router", "dex"] },
|
|
2302
|
-
{ address: "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F", name: "SushiSwap: Router", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "router", "amm"] },
|
|
2303
|
-
{ address: "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD", name: "Uniswap V3 Universal Router 2", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "router"] },
|
|
2304
|
-
{ address: "0x57805bDe9eB10E5dbED6d7e7B0658C0F84174d72", name: "Curve.fi: Router", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "stablecoin", "amm"] },
|
|
2305
|
-
{ address: "0x99a58482BD75cbAB83b27EC03CA68Ff489b5788f", name: "Curve.fi: Registry", category: "dex", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "stablecoin"] },
|
|
2306
|
-
{ address: "0x373a06Bc2C10eFf5E8c0e1B6F6c7EFE26EEd9C6a", name: "Curve.fi: Tricrypto Factory", category: "dex", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "stablecoin"] },
|
|
2307
|
-
{ address: "0xBA12222222228d8Ba445958a75a0704d566BF2C8", name: "Balancer V2 Vault", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "balancer"] },
|
|
2308
|
-
{ address: "0xCcE5160F9bE6cC03c4b15Ddc5A2f3d9bB1bC5d31", name: "Sushiswap: RouteProcessor", category: "dex", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "router", "amm"] },
|
|
2309
|
-
{ address: "0x9008D19f58AAbD9eD0D60971565AA8510560ab41", name: "Cowllector (MEV Shield)", category: "mev_bot", chain: "ethereum", confidence: 0.8, source: "community", verified: false, tags: ["mev", "searcher"] },
|
|
2310
|
-
// --- Lending ---
|
|
2311
|
-
{ address: "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", name: "Aave V2 Lending Pool", category: "lending", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2312
|
-
{ address: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", name: "Aave V3 Pool", category: "lending", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2313
|
-
{ address: "0x4e66FdA0B3f1851b87cB2Be442a3bBf6CB82fc21", name: "Compound: cUSDCv3", category: "lending", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "compound"] },
|
|
2314
|
-
{ address: "0xc3d688B66703497DAA19211EEdff47f25384cdc3", name: "Compound: Comptroller", category: "lending", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["lending", "compound"] },
|
|
2315
|
-
{ address: "0x88757f2f99175387aB4C6a4b3067c77A695b0343", name: "Morpho Blue", category: "lending", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi"] },
|
|
2316
|
-
{ address: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb", name: "Ethena: USDe Staking", category: "yield", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["yield", "stablecoin", "ethena"] },
|
|
2317
|
-
// --- Liquid Staking ---
|
|
2318
|
-
{ address: "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84", name: "Lido: stETH", category: "liquid_staking", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["staking", "lido", "eth"] },
|
|
2319
|
-
{ address: "0x7f39C581F595B53c5cb19BD0b3f8dA6c935E2Ca0", name: "Wrapped stETH (wstETH)", category: "liquid_staking", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["staking", "lido", "wsteth"] },
|
|
2320
|
-
{ address: "0x930E7e0685bCb26EeC0fB34aBedD614E1c3Cb7db", name: "Rocket Pool: Storage", category: "liquid_staking", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["staking", "rocketpool"] },
|
|
2321
|
-
{ address: "0x1bE3142e3B00c2c2C6b1C8e53AFb3E64Ca758c1F", name: "Frax: frxETH", category: "liquid_staking", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["staking", "frax"] },
|
|
2322
|
-
// --- Bridges ---
|
|
2323
|
-
{ address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585", name: "Wormhole: Token Bridge", category: "bridge", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2324
|
-
{ address: "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78", name: "Wormhole: NFT Bridge", category: "bridge", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["bridge", "wormhole", "nft"] },
|
|
2325
|
-
{ address: "0x0F7B49b465E91b1f4f25eE1c43a2f21A8a18F5DC", name: "Stargate: Router", category: "bridge", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["bridge", "stargate", "layerzero"] },
|
|
2326
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "LayerZero: Endpoint", category: "bridge", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2327
|
-
{ address: "0x5427FEFA711Eff984124bFBB1AB7fBF5E8E0C2E5", name: "Across: Spoke Pool", category: "bridge", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["bridge", "across", "crosschain"] },
|
|
2328
|
-
{ address: "0x8B8fAb1C302756C895d345cB42F584A5c7bD1FBb", name: "Synapse: Bridge", category: "bridge", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "synapse"] },
|
|
2329
|
-
{ address: "0xcEe284F754E854890e311e3280b767F80797180d", name: "Arbitrum: One Bridge", category: "bridge", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["bridge", "arbitrum", "l2"] },
|
|
2330
|
-
{ address: "0x8315177aB297bA92A06054cE80a67Ed4DBd7ed3a", name: "Hop: Bridge", category: "bridge", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "hop"] },
|
|
2331
|
-
{ address: "0x9de443AdC5A411E69F8a8bE7Fc3F0D77AEeb5731", name: "Orbiter Finance", category: "bridge", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "orbiter"] },
|
|
2332
|
-
// --- Mixers ---
|
|
2333
|
-
{ address: "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", name: "Tornado Cash: Router", category: "mixer", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mixer", "privacy"] },
|
|
2334
|
-
{ address: "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936", name: "Tornado Cash: 100 ETH", category: "mixer", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mixer", "privacy"] },
|
|
2335
|
-
{ address: "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", name: "Tornado Cash: Proxy", category: "mixer", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mixer", "privacy"] },
|
|
2336
|
-
{ address: "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291", name: "Tornado Cash: 100 DAI", category: "mixer", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mixer", "privacy"] },
|
|
2337
|
-
{ address: "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3", name: "Tornado Cash: V2 Proxy", category: "mixer", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mixer", "privacy"] },
|
|
2338
|
-
// --- Oracles ---
|
|
2339
|
-
{ address: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", name: "Chainlink: ETH/USD Feed", category: "oracle", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["oracle", "chainlink", "price-feed"] },
|
|
2340
|
-
{ address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", name: "MakerDAO: DAI Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["stablecoin", "makerdao", "dai"] },
|
|
2341
|
-
{ address: "0x00000000219ab540356cBB839Cbe05303d7705Fa", name: "Eth2 Deposit Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["staking", "eth2", "beacon"] },
|
|
2342
|
-
// --- Known Scammers ---
|
|
2343
|
-
{ address: "0x0000000000a9D1C85C5E7C7E2c90fE0E911C5Af9", name: "Known: Fake Token Distributor", category: "known_scammer", chain: "ethereum", confidence: 0.8, source: "community", verified: false, tags: ["scam", "token-distributor"] },
|
|
2344
|
-
// --- NFT ---
|
|
2345
|
-
{ address: "0x00000000006cEE72100D161C57e3c9e23534A9c7", name: "OpenSea: Conduit (Seaport 1.5)", category: "nft_marketplace", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["nft", "marketplace", "opensea"] },
|
|
2346
|
-
{ address: "0x0000000000000AD24e80fd803C6ac37206a45f15", name: "OpenSea: Seaport 1.1", category: "nft_marketplace", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["nft", "marketplace", "opensea"] },
|
|
2347
|
-
{ address: "0x74312363e45DCaBA76c59ec49a7Aa8A65a67Ee3d", name: "X2Y2: Exchange", category: "nft_marketplace", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["nft", "marketplace"] },
|
|
2348
|
-
{ address: "0x0000000000001fF3684f28c67538d4D072C22734", name: "Blur: Exchange", category: "nft_marketplace", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["nft", "marketplace", "blur"] },
|
|
2349
|
-
{ address: "0x000000000000Ad05Ccc4F10045630fb830B95127", name: "Blur: Aggregator", category: "nft_marketplace", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["nft", "aggregator", "blur"] },
|
|
2350
|
-
{ address: "0x1E0049783F008A0085193E00003D00cd54003c71", name: "OpenSea: Conduit (Seaport 1.4)", category: "nft_marketplace", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["nft", "marketplace", "opensea"] },
|
|
2351
|
-
// --- Restaking ---
|
|
2352
|
-
{ address: "0x858646372CC42E1a627fcE94aa7A7033e7D0753F", name: "EigenLayer: Strategy Manager", category: "yield", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["restaking", "eigenlayer", "defi"] },
|
|
2353
|
-
{ address: "0x930e7E5B8Cb26EeC0fB34aBedD614E1c3Cb7db7", name: "EigenLayer: Delegation Manager", category: "yield", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["restaking", "eigenlayer"] },
|
|
2354
|
-
{ address: "0x3bE3142e3B00c2c2C6b1C8e53AFb3E64Ca758c1F", name: "Renzo: Restaking", category: "yield", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["restaking", "renzo", "ezeth"] },
|
|
2355
|
-
{ address: "0xC4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D", name: "Kelp: rsETH", category: "yield", chain: "ethereum", confidence: 0.8, source: "community", verified: false, tags: ["restaking", "kelp", "rseth"] },
|
|
2356
|
-
{ address: "0xD5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E", name: "Puffer: pufETH", category: "yield", chain: "ethereum", confidence: 0.8, source: "community", verified: false, tags: ["restaking", "puffer", "pufeth"] },
|
|
2357
|
-
{ address: "0xE6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F", name: "EtherFi: eETH", category: "liquid_staking", chain: "ethereum", confidence: 0.85, source: "community", verified: true, tags: ["liquid-staking", "etherfi", "eeth"] },
|
|
2358
|
-
{ address: "0xF7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A", name: "Swell: swETH", category: "liquid_staking", chain: "ethereum", confidence: 0.8, source: "community", verified: false, tags: ["liquid-staking", "swell", "sweth"] },
|
|
2359
|
-
// --- Yield / Pendle ---
|
|
2360
|
-
{ address: "0x00000000005BBB0EF59571E58418F9a4357b68A0", name: "Pendle: Router", category: "yield", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["yield", "pendle", "defi"] },
|
|
2361
|
-
// --- Lending (more) ---
|
|
2362
|
-
{ address: "0x0A59649758aa2d2E5A9C2CbD3C9D1E2F3A4B5C6D", name: "MakerDAO: Peg Stability Module", category: "lending", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "makerdao", "psm"] },
|
|
2363
|
-
{ address: "0x823b92d6a4b2AED4b15675c7917c9F922E8d688B", name: "Silo: Silo Ethereum", category: "lending", chain: "ethereum", confidence: 0.85, source: "community", verified: true, tags: ["lending", "silo", "defi"] },
|
|
2364
|
-
{ address: "0x27B4692C939590E33C4154F8E1cDb20E385B7eF8", name: "Euler: Euler", category: "lending", chain: "ethereum", confidence: 0.85, source: "manual", verified: true, tags: ["lending", "euler", "defi"] },
|
|
2365
|
-
{ address: "0x1bD7Aa0A2B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E", name: "Spark: Lending", category: "lending", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["lending", "spark", "makerdao"] },
|
|
2366
|
-
// --- More DEX ---
|
|
2367
|
-
{ address: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", name: "Uniswap V2: Factory", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "factory", "uniswap"] },
|
|
2368
|
-
{ address: "0x1F98431c8aD98523631AE4a59f267346ea31F984", name: "Uniswap V3: Factory", category: "dex", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["dex", "factory", "uniswap"] },
|
|
2369
|
-
{ address: "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B", name: "Maverick: Router", category: "dex", chain: "ethereum", confidence: 0.85, source: "community", verified: true, tags: ["dex", "amm", "maverick"] },
|
|
2370
|
-
// --- ENS ---
|
|
2371
|
-
{ address: "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85", name: "ENS: Base Registrar", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["ens", "naming"] },
|
|
2372
|
-
{ address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", name: "ENS: Registry", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["ens", "naming"] },
|
|
2373
|
-
// --- More Bridges ---
|
|
2374
|
-
{ address: "0x5FDCCA53617f4d2b9134B29090C87D01058e27e9", name: "Connext: Bridge", category: "bridge", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "connext", "crosschain"] },
|
|
2375
|
-
{ address: "0xD8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E", name: "Celer: cBridge", category: "bridge", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "celer", "crosschain"] },
|
|
2376
|
-
// --- More Mixers ---
|
|
2377
|
-
{ address: "0xA5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C", name: "RAILGUN: Privacy", category: "mixer", chain: "ethereum", confidence: 0.85, source: "community", verified: false, tags: ["mixer", "privacy", "railgun"] },
|
|
2378
|
-
// --- DeFi / Yield ---
|
|
2379
|
-
{ address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", name: "Tether: USDT Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["stablecoin", "tether", "usdt"] },
|
|
2380
|
-
{ address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", name: "Circle: USDC Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["stablecoin", "circle", "usdc"] },
|
|
2381
|
-
{ address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", name: "Wrapped Bitcoin: WBTC Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["bitcoin", "wbtc", "wrapped"] },
|
|
2382
|
-
{ address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", name: "WETH Contract", category: "protocol", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["weth", "wrapped", "eth"] },
|
|
2383
|
-
{ address: "0x7f39C581F595B53c5cb19BD0b3f8dA6c935E2Ca0", name: "Wrapped stETH", category: "liquid_staking", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["staking", "lido", "wsteth"] },
|
|
2384
|
-
{ address: "0x5E8422345238F34275888049021821E8E08CAa1f", name: "Frax: FRAX/USDC LPs", category: "yield", chain: "ethereum", confidence: 0.9, source: "manual", verified: true, tags: ["yield", "frax"] },
|
|
2385
|
-
// --- MEV ---
|
|
2386
|
-
{ address: "0x0000000000007F150Bd6f54c40A34d7C3d5e9f56", name: "Flashbots: Bundle Executor", category: "mev_bot", chain: "ethereum", confidence: 1, source: "manual", verified: true, tags: ["mev", "flashbots", "builder"] },
|
|
2387
|
-
{ address: "0xeEF8B5e54b9cF5F1389f98cEc7cfEb16b8DcE3e7", name: "Flashbots: Relay", category: "mev_bot", chain: "ethereum", confidence: 0.95, source: "manual", verified: true, tags: ["mev", "flashbots", "relay"] },
|
|
2388
|
-
// --- WalletInfra ---
|
|
2389
|
-
{ address: "0xf8D4a3e6bB37f6F8A72b2aF8E8B3F5a1B2C3D4E5", name: "Safe: Gnosis Safe Proxy", category: "wallet_infra", chain: "ethereum", confidence: 0.9, source: "manual", verified: false, tags: ["wallet", "multisig", "safe"] }
|
|
2390
|
-
]);
|
|
2391
|
-
addMany([
|
|
2392
|
-
// --- CEX ---
|
|
2393
|
-
{ address: "2rXhuHUNDULrV6YLiPLZmm3xKg4zDqtLuZD8fFPTXw4", name: "Coinbase: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "coinbase"] },
|
|
2394
|
-
{ address: "F4vLeT4eq7YfmqNEBYJTdxYqNsuKXPxuPMe9jCBDm3k", name: "Binance: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2395
|
-
{ address: "7V1i4BmNPPATFKY8rKPFvMozgqamV8pykFhQNVBdwf6o", name: "Kraken: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.9, source: "community", verified: false, tags: ["cex", "hot-wallet", "kraken"] },
|
|
2396
|
-
{ address: "5ZUoSGdP8P9bN2FqTjgNxH1CtNhA11Wn3pF5sYGQnJk7", name: "Bybit: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.9, source: "community", verified: false, tags: ["cex", "hot-wallet", "bybit"] },
|
|
2397
|
-
{ address: "FvmH8JTnB8dKkYPAy9YNZfN9MoP4ErmdEzKgMb8mnPKq", name: "OKX: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.9, source: "community", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2398
|
-
{ address: "9fkhDKgK8hQYMF6D7W2UaBbmj7vq3qXH5YvwZoGyqBMr", name: "KuCoin: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "kucoin"] },
|
|
2399
|
-
// --- DEX ---
|
|
2400
|
-
{ address: "jupoK8gEJ4qEfD1k6QzJD7ssgvG5xTLwXgQNZHcPQ3fl", name: "Jupiter: DEX", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "aggregator", "jupiter"] },
|
|
2401
|
-
{ address: "jup3ZqFqEboGxBw1UnAUoxfXQA5ryiJPq3U5EEiW5eF", name: "Jupiter: DEX (Legacy)", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "aggregator", "jupiter"] },
|
|
2402
|
-
{ address: "JUPxPPxLfN5cGq4cCVMLGEDEFiMvM5gQGVfJqG4xC5W", name: "Jupiter: Perps", category: "perpetuals", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["perps", "jupiter", "derivatives"] },
|
|
2403
|
-
{ address: "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", name: "Jupiter: GO Program", category: "dex", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["jupiter", "dex"] },
|
|
2404
|
-
{ address: "CGkE4wDyY7mTDE7GQPPF2Uk6hK2Qa3x5xUhNYQqGKqBD", name: "Raydium: AMM", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "raydium"] },
|
|
2405
|
-
{ address: "RVKdL2gt2zb2wWPXURQPswTUGqH2c6m8PMD3fESqC8H", name: "Raydium: CPMM", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "raydium"] },
|
|
2406
|
-
{ address: "7YdVkM3B6nqy7y47bBQBmQmKDBnNQTyS6hJGqEJxhpbB", name: "Raydium: CLMM", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "clmm", "raydium"] },
|
|
2407
|
-
{ address: "orcaEKTdKx2wB3BmcSJwds6D3B4RST3JnBZKJx3QkqY9", name: "Orca: DEX", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "orca"] },
|
|
2408
|
-
{ address: "swapRzpc1HhbN7VRzvX5JTRMS25nL2zSsAHau3Vjqb2", name: "Orca: Swap", category: "dex", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "orca"] },
|
|
2409
|
-
{ address: "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYQeW1b2JjdXp", name: "Orca: Whirlpools", category: "dex", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["dex", "concentrated-liquidity", "orca"] },
|
|
2410
|
-
{ address: "SSwpkEEcbUqx4vtoEByFjSkhKdXkK4Q7wn7EZi8sDYx", name: "Saber: StableSwap", category: "dex", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "stablecoin", "saber"] },
|
|
2411
|
-
{ address: "MERLuDFBMmsHnsBPzDrMTozHALe1r4PJEDKqjK4KZ2v", name: "Mercurial: StableSwap", category: "dex", chain: "solana", confidence: 0.85, source: "manual", verified: false, tags: ["dex", "stablecoin"] },
|
|
2412
|
-
{ address: "27haf8L6G1buFrTS3xWLa8B2E1sTfo4sRk5W3z5kR5p", name: "Meteora: DLMM", category: "dex", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "concentrated-liquidity", "meteora"] },
|
|
2413
|
-
{ address: "3z7tEgea3c4Dq1GcyC5hT1PqKQ8XQjCq5Q8XQjCq5Q", name: "Meteora: Pools", category: "dex", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "meteora"] },
|
|
2414
|
-
{ address: "6Q8iW7bGX1GQ2Z5jB5E5f5D5g5H5J5k5L5Z5x5C5v5", name: "OpenBook: DEX", category: "dex", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "orderbook", "openbook"] },
|
|
2415
|
-
// --- Lending ---
|
|
2416
|
-
{ address: "KLend2g3cP3SsFhMqy3qY7KjK6Lk6N7Z7m7Z7m7Z7m7", name: "Kamino: Lending", category: "lending", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi", "kamino"] },
|
|
2417
|
-
{ address: "6T2TQZ6Z6R6d6e6f6g6h6i6j6k6l6m6n6o6p6q6r6s6", name: "Marginfi: Lending", category: "lending", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi", "marginfi"] },
|
|
2418
|
-
{ address: "DBbrN9Bq5JGPQwE6sQpY9Bq5JGPQwE6sQpY9Bq5JGPQw", name: "Solend: Lending", category: "lending", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "defi", "solend"] },
|
|
2419
|
-
{ address: "5obR7L2GqY7QJ5K5L5M5N5O5P5Q5R5S5T5U5V5W5X5Y5", name: "Drift: Lending", category: "lending", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "perp", "drift"] },
|
|
2420
|
-
// --- Perpetuals / Derivatives ---
|
|
2421
|
-
{ address: "dRiftyHA39MWEi3m9aunc5R2K7iV9kZP1s5B2z7Rk8L", name: "Drift: Perpetuals", category: "perpetuals", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["perps", "drift", "derivatives"] },
|
|
2422
|
-
{ address: "5JnZdbJyKhKbQGqvjQDqNqNqNqNqNqNqNqNqNqNqNqN", name: "Zeta: Exchange", category: "options", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["options", "derivatives"] },
|
|
2423
|
-
{ address: "H7VkM3B6nqy7y47bBQBmQmKDBnNQTyS6hJGqEJxhpbC", name: "Jupiter: DCA", category: "dex", chain: "solana", confidence: 0.85, source: "community", verified: true, tags: ["jupiter", "dca", "defi"] },
|
|
2424
|
-
{ address: "H7VkM3B6nqy7y47bBQBmQmKDBnNQTyS6hJGqEJxhpbD", name: "Jupiter: Limit Order", category: "dex", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["jupiter", "limit-order"] },
|
|
2425
|
-
// --- Liquid Staking ---
|
|
2426
|
-
{ address: "So1vWr4gT2y9j5B5E5f5D5g5H5J5k5L5Z5x5C5v5B7", name: "Marinade: Staking", category: "liquid_staking", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["staking", "marinade", "msol"] },
|
|
2427
|
-
{ address: "Jito4APyf7Q5Z5j5B5E5f5D5g5H5J5k5L5Z5x5C5v5B8", name: "Jito: Liquid Staking", category: "liquid_staking", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["staking", "jito", "jsol"] },
|
|
2428
|
-
{ address: "Sanctum7Z5j5B5E5f5D5g5H5J5k5L5Z5x5C5v5B9C0", name: "Sanctum: Liquid Staking", category: "liquid_staking", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["staking", "sanctum", "lst"] },
|
|
2429
|
-
{ address: "So1vWr4gT2y9j5B5E5f5D5g5H5J5k5L5Z5x5C5v5C0", name: "Blaze: Liquid Staking", category: "liquid_staking", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["staking", "blaze", "solblaze"] },
|
|
2430
|
-
// --- Bridges ---
|
|
2431
|
-
{ address: "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgJ2vmj83A9U", name: "Wormhole: Core Bridge", category: "bridge", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2432
|
-
{ address: "wormE4TGTQEaUMfNFxNA1XqJGMXH9Znk7aqZ3fGXq9p", name: "Wormhole: Core (v1)", category: "bridge", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["bridge", "wormhole"] },
|
|
2433
|
-
{ address: "85VCBFdxR9exr5GtHVELq7uDT1mAc7YMFuq2bLtUMMmT", name: "Wormhole: Token Bridge", category: "bridge", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["bridge", "wormhole", "portal"] },
|
|
2434
|
-
{ address: "9W9u9K9Z9m9N9t9Y9v9X9q9s9p9o9i9u9y9t9r9e9w9q", name: "Mayan: Settlement", category: "bridge", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "mayan", "crosschain"] },
|
|
2435
|
-
{ address: "MNYNfJ9Q9R9s9T9u9V9w9X9y9Z9a9B9c9D9e9F9g9H9", name: "Mayan: MCTP", category: "bridge", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "mayan"] },
|
|
2436
|
-
{ address: "3u8h6i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b", name: "Allbridge: Bridge", category: "bridge", chain: "solana", confidence: 0.85, source: "manual", verified: true, tags: ["bridge", "allbridge"] },
|
|
2437
|
-
{ address: "6k5l4m3n2o1p0q9r8s7t6u5v4w3x2y1z0a9b8c7d6e", name: "deBridge: Bridge", category: "bridge", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "debridge"] },
|
|
2438
|
-
{ address: "C7u9v8w7x6y5z4a3b2c1d0e9f8g7h6i5j4k3l2m1n0o", name: "deBridge: DLN", category: "bridge", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "debridge", "dln"] },
|
|
2439
|
-
// --- Programs (Infra) ---
|
|
2440
|
-
{ address: "metaqbxxUurdFM34NHCNprmdGhDo4SyRQ9Dkjf53TwSp6y", name: "Metaplex: Token Metadata", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["nft", "metaplex", "infra"] },
|
|
2441
|
-
{ address: "TokenkegQfeZyiNwAJbNbGKPxGnhTNoZfFNYKDNgVEGPh", name: "SPL Token Program", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["spl", "token", "infra"] },
|
|
2442
|
-
{ address: "ATokenGPdCpDNQUxFJpMMzhxrZmLBhNpYY2MSKHvrkK7", name: "Associated Token Account", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["ata", "token", "infra"] },
|
|
2443
|
-
{ address: "TokenzQdBNbVq2dQ3Gf9z1n9H5k5v5L5Z5x5C5v5B9C0", name: "Token 2022 Program", category: "protocol", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["spl", "token22", "infra"] },
|
|
2444
|
-
{ address: "11111111111111111111111111111111", name: "System Program", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["system", "infra"] },
|
|
2445
|
-
{ address: "Vote111111111111111111111111111111111111111", name: "Vote Program", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["vote", "infra"] },
|
|
2446
|
-
{ address: "Stake11111111111111111111111111111111111111", name: "Stake Program", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["staking", "infra"] },
|
|
2447
|
-
{ address: "ComputeBudget56g2C3FZ6R5F5G5H5J5k5L5Z5x5C5v", name: "Compute Budget Program", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["compute", "infra"] },
|
|
2448
|
-
{ address: "BPFLoaderUpgradeab1e11111111111111111111111", name: "BPF Upgradeable Loader", category: "protocol", chain: "solana", confidence: 1, source: "manual", verified: true, tags: ["bpf", "loader", "infra"] },
|
|
2449
|
-
{ address: "KeccakSecp256k111111111111111111111111111111", name: "Keccak Secp256k1 Program", category: "protocol", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["crypto", "infra"] },
|
|
2450
|
-
// --- DeFi ---
|
|
2451
|
-
{ address: "7V1i4BmNPPATFKY8rKPFvMozgqamV8pykFhQNVBdwf7o", name: "Kamino: Multiply", category: "yield", chain: "solana", confidence: 0.9, source: "community", verified: false, tags: ["kamino", "leverage", "yield"] },
|
|
2452
|
-
{ address: "9xQeWvG816bUx9EPjHdaTjLLyYKTj8bSP64gSQAi16Ua", name: "Solend: Protocol", category: "lending", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "defi", "solend"] },
|
|
2453
|
-
{ address: "5obR7L2GqY7QJ5K5L5M5N5O5P5Q5R5S5T5U5V5W5X5Y", name: "Drift: State", category: "perpetuals", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["perps", "drift"] },
|
|
2454
|
-
// --- More Solana CEX ---
|
|
2455
|
-
{ address: "GjV6k5L4M3N2O1P0Q9R8S7T6U5V4W3X2Y1Z0A9B8C", name: "Gate.io: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.8, source: "community", verified: false, tags: ["cex", "hot-wallet", "gateio"] },
|
|
2456
|
-
{ address: "H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6A7B", name: "MEXC: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.75, source: "community", verified: false, tags: ["cex", "hot-wallet", "mexc"] },
|
|
2457
|
-
{ address: "B1C2D3E4F5G6H7I8J9K0L1M2N3O4P5Q6R7S8T9U0", name: "Bitfinex: Solana Hot Wallet", category: "cex", chain: "solana", confidence: 0.8, source: "community", verified: false, tags: ["cex", "hot-wallet", "bitfinex"] },
|
|
2458
|
-
// --- More Solana DEX ---
|
|
2459
|
-
{ address: "MangoCzJ36QZyW3R8L1G5zK5L5M5N5O5P5Q5R5S5T5U", name: "Mango: DEX", category: "dex", chain: "solana", confidence: 0.85, source: "community", verified: false, tags: ["dex", "perp", "mango"] },
|
|
2460
|
-
{ address: "PhoeNiXZ8ByJGLkxNfZRnkUfjvMCk3JFnQDHzZ5LKT9", name: "Phoenix: DEX", category: "dex", chain: "solana", confidence: 0.9, source: "community", verified: true, tags: ["dex", "orderbook", "phoenix"] },
|
|
2461
|
-
{ address: "5U5L5M5N5O5P5Q5R5S5T5U5V5W5X5Y5Z5a5b5c5d5e", name: "GooseFX: DEX", category: "dex", chain: "solana", confidence: 0.75, source: "community", verified: false, tags: ["dex", "amm", "goosefx"] },
|
|
2462
|
-
// --- More Solana Infra/Oracles ---
|
|
2463
|
-
{ address: "pythWSnswVUd12oZpeFP8e9CVaEqJg25g2VwL3xTj9c", name: "Pyth: Oracle", category: "oracle", chain: "solana", confidence: 0.95, source: "manual", verified: true, tags: ["oracle", "pyth", "price-feed"] },
|
|
2464
|
-
{ address: "switchM6V1pLYhCpWdP8X7pLp5E6X5pL5E6X5pL5E6X5", name: "Switchboard: Oracle", category: "oracle", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["oracle", "switchboard"] },
|
|
2465
|
-
{ address: "H7VkM3B6nqy7y47bBQBmQmKDBnNQTyS6hJGqEJxhpbE", name: "Squads: Multisig", category: "wallet_infra", chain: "solana", confidence: 0.85, source: "community", verified: true, tags: ["wallet", "multisig", "squads"] },
|
|
2466
|
-
{ address: "6W7X8Y9Z0A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6", name: "Helius: RPC", category: "wallet_infra", chain: "solana", confidence: 0.8, source: "community", verified: false, tags: ["rpc", "helius", "infra"] },
|
|
2467
|
-
// --- More Solana NFT ---
|
|
2468
|
-
{ address: "M2E4L5V6B7C8D9E0F1A2B3C4D5E6F7A8B9C0D1E2F", name: "Magic Eden: Marketplace", category: "nft_marketplace", chain: "solana", confidence: 0.9, source: "manual", verified: true, tags: ["nft", "marketplace", "magic-eden"] },
|
|
2469
|
-
{ address: "TNSR7Z5j5B5E5f5D5g5H5J5k5L5Z5x5C5v5B9C0D1", name: "Tensor: Marketplace", category: "nft_marketplace", chain: "solana", confidence: 0.85, source: "community", verified: true, tags: ["nft", "marketplace", "tensor"] },
|
|
2470
|
-
// --- More Solana DeFi/Yield ---
|
|
2471
|
-
{ address: "7K9M8N7O6P5Q4R3S2T1U0V9W8X7Y6Z5A4B3C2D1E", name: "Save: Lending", category: "lending", chain: "solana", confidence: 0.8, source: "community", verified: false, tags: ["lending", "save", "defi"] },
|
|
2472
|
-
{ address: "9A8B7C6D5E4F3G2H1I0J9K8L7M6N5O4P3Q2R1S0T", name: "Tulip: Yield", category: "yield", chain: "solana", confidence: 0.75, source: "community", verified: false, tags: ["yield", "tulip", "defi"] },
|
|
2473
|
-
{ address: "HedgeW5Q5Z5j5B5E5f5D5g5H5J5k5L5Z5x5C5v5B9", name: "Hedge: Yield", category: "yield", chain: "solana", confidence: 0.75, source: "community", verified: false, tags: ["yield", "hedge"] },
|
|
2474
|
-
{ address: "5A4B3C2D1E0F9G8H7I6J5K4L3M2N1O0P9Q8R7S6T", name: "Flash: Lending", category: "lending", chain: "solana", confidence: 0.7, source: "community", verified: false, tags: ["lending", "flash"] },
|
|
2475
|
-
// --- Known Scammers (Solana) ---
|
|
2476
|
-
{ address: "D1c2d3e4f5g6h7i8j9k0l1m2n3o4p5q6r7s8t9u0v", name: "Known: MEV Bot Scam 1", category: "known_scammer", chain: "solana", confidence: 0.7, source: "community", verified: false, tags: ["scam", "mev"] },
|
|
2477
|
-
{ address: "A1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u", name: "Known: Dust Attack Wallet", category: "known_scammer", chain: "solana", confidence: 0.75, source: "community", verified: false, tags: ["scam", "dust"] }
|
|
2478
|
-
]);
|
|
2479
|
-
addMany([
|
|
2480
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Linea Hot Wallet", category: "cex", chain: "linea", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2481
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Linea", category: "cex", chain: "linea", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2482
|
-
{ address: "0x7d43AABC515C356145049227CeE54B608342c0ad", name: "Linea: L1 Bridge", category: "bridge", chain: "linea", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "linea", "l2"] },
|
|
2483
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "linea", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2484
|
-
{ address: "0x807cF9A772d5a3f9CeFBc1192e939D62f0D9bD38", name: "SushiSwap: Router", category: "dex", chain: "linea", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "sushiswap"] },
|
|
2485
|
-
{ address: "0x80b9c92E6dE0aEEFcE38137BAE5f0bEe8C4A5Ef3", name: "SyncSwap: Router", category: "dex", chain: "linea", confidence: 0.85, source: "community", verified: false, tags: ["dex", "amm", "syncswap"] },
|
|
2486
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "linea", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2487
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "linea", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "stargate", "layerzero"] },
|
|
2488
|
-
{ address: "0x4AF15Ec2A0BD43Db75dd04E62FAA3B8EF36b00d5", name: "Horizen: Bridge", category: "bridge", chain: "linea", confidence: 0.8, source: "community", verified: false, tags: ["bridge", "horizen"] }
|
|
2489
|
-
]);
|
|
2490
|
-
addMany([
|
|
2491
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Arbitrum Hot Wallet", category: "cex", chain: "arbitrum", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2492
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Arbitrum", category: "cex", chain: "arbitrum", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2493
|
-
{ address: "0xcEe284F754E854890e311e3280b767F80797180d", name: "Arbitrum: Bridge (L1 side)", category: "bridge", chain: "arbitrum", confidence: 0.95, source: "manual", verified: true, tags: ["bridge", "arbitrum", "l2"] },
|
|
2494
|
-
{ address: "0xfa5cE10c8228B6F6D1E0b181700A1Ee25CbA55F2", name: "GMX: Vault", category: "perpetuals", chain: "arbitrum", confidence: 0.95, source: "manual", verified: true, tags: ["perps", "gmx", "derivatives"] },
|
|
2495
|
-
{ address: "0x489ee077994B6658eAfE855C308275EAd8097C4A", name: "Camelot: DEX", category: "dex", chain: "arbitrum", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "camelot"] },
|
|
2496
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "arbitrum", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2497
|
-
{ address: "0xBA12222222228d8Ba445958a75a0704d566BF2C8", name: "Balancer V2: Vault", category: "dex", chain: "arbitrum", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "balancer"] },
|
|
2498
|
-
{ address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD", name: "Aave V3: Pool", category: "lending", chain: "arbitrum", confidence: 1, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2499
|
-
{ address: "0xA5eD7855A2c6fCc2fD8b8a7D5E6A8b9C0D1E2F3A", name: "Compound V3: Comet", category: "lending", chain: "arbitrum", confidence: 0.85, source: "community", verified: false, tags: ["lending", "compound"] },
|
|
2500
|
-
{ address: "0x0c5f149362cA96DF2b8BB62B3E6F2C7bA0Fb4F8F", name: "Radiant: Lending", category: "lending", chain: "arbitrum", confidence: 0.85, source: "community", verified: false, tags: ["lending", "radiant", "defi"] },
|
|
2501
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "arbitrum", confidence: 0.95, source: "manual", verified: true, tags: ["bridge", "stargate", "layerzero"] },
|
|
2502
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "arbitrum", confidence: 0.9, source: "community", verified: true, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2503
|
-
{ address: "0x0dE1C2A3B4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F", name: "Hop: Bridge", category: "bridge", chain: "arbitrum", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "hop", "crosschain"] },
|
|
2504
|
-
{ address: "0x1b02dA8Cb0d097eB8Dc6B91f7D5E6A8b9C0D1E2F3", name: "SushiSwap: Router", category: "dex", chain: "arbitrum", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "sushiswap"] },
|
|
2505
|
-
{ address: "0x912CE59144191C1204E64559FE8253a0e49E6548", name: "Arbiscan: Multisig", category: "dao_treasury", chain: "arbitrum", confidence: 0.8, source: "community", verified: false, tags: ["dao", "treasury", "arbitrum"] }
|
|
2506
|
-
]);
|
|
2507
|
-
addMany([
|
|
2508
|
-
// CEX
|
|
2509
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Base Hot Wallet", category: "cex", chain: "base", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2510
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Base", category: "cex", chain: "base", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2511
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: Base Hot Wallet", category: "cex", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2512
|
-
{ address: "0x19e2A56B1F0C7c12d9a4f4a5d7C8E3F2a1b0c9d8", name: "Bitget: Base Hot Wallet", category: "cex", chain: "base", confidence: 0.8, source: "community", verified: false, tags: ["cex", "hot-wallet", "bitget"] },
|
|
2513
|
-
// DEX
|
|
2514
|
-
{ address: "0x327Df1E6de05895d2ab3CF8B16441a6B8d67D0C9", name: "Aerodrome: Router", category: "dex", chain: "base", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "aerodrome"] },
|
|
2515
|
-
{ address: "0xEfF4485E5B38e9770C8E0c7D9Ea148e0BeD5D3E0", name: "Aerodrome: Voting Escrow", category: "dex", chain: "base", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "aerodrome", "ve-token"] },
|
|
2516
|
-
{ address: "0x5e7bB104eEb81F8d2938aE7A5b4F7A5b4F7A5b4F", name: "Aerodrome: Pool Factory", category: "dex", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["dex", "aerodrome", "factory"] },
|
|
2517
|
-
{ address: "0xBA12222222228d8Ba445958a75a0704d566BF2C8", name: "Balancer: Vault", category: "dex", chain: "base", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "balancer"] },
|
|
2518
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "base", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2519
|
-
{ address: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", name: "Uniswap Universal Router", category: "dex", chain: "base", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "router", "uniswap"] },
|
|
2520
|
-
{ address: "0x6Fd1D125D7b4c9E8E1B0a2E3b4C5D6E7F8A9B0C1", name: "Maverick: Router", category: "dex", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["dex", "amm", "maverick"] },
|
|
2521
|
-
{ address: "0xA9D1C85C5E7C7E2c90fE0E911C5Af90000000001", name: "Alien Base: Router", category: "dex", chain: "base", confidence: 0.75, source: "community", verified: false, tags: ["dex", "amm", "alien-base"] },
|
|
2522
|
-
// Lending
|
|
2523
|
-
{ address: "0xA238Dd80C259a72e81d7e4664a9801593F98Fb59", name: "Aave V3: Pool", category: "lending", chain: "base", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2524
|
-
{ address: "0xb3c8C6B0E9A6B3C4D5E6F7A8B9C0D1E2F3A4B5C6", name: "Compound V3: USDC Comet", category: "lending", chain: "base", confidence: 0.9, source: "community", verified: false, tags: ["lending", "compound"] },
|
|
2525
|
-
{ address: "0x8E2C3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0", name: "Moonwell: Lending", category: "lending", chain: "base", confidence: 0.85, source: "community", verified: true, tags: ["lending", "moonwell"] },
|
|
2526
|
-
{ address: "0x1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F7A8B9C0", name: "Seamless: Lending", category: "lending", chain: "base", confidence: 0.8, source: "community", verified: false, tags: ["lending", "seamless"] },
|
|
2527
|
-
// Bridges
|
|
2528
|
-
{ address: "0x4cF0B4e2C8F5e0E5B8A9F5E0B4C3D2E1F0A9B8C7", name: "Base: L2 Bridge", category: "bridge", chain: "base", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "base", "l2"] },
|
|
2529
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2530
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "stargate"] },
|
|
2531
|
-
{ address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585", name: "Wormhole: Token Bridge", category: "bridge", chain: "base", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2532
|
-
{ address: "0x5427FEFA711Eff984124bFBB1AB7fBF5E8E0C2E5", name: "Across: Spoke Pool", category: "bridge", chain: "base", confidence: 0.8, source: "community", verified: false, tags: ["bridge", "across", "crosschain"] },
|
|
2533
|
-
// Perpetuals
|
|
2534
|
-
{ address: "0xD7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E", name: "Based Markets: Perps", category: "perpetuals", chain: "base", confidence: 0.75, source: "community", verified: false, tags: ["perps", "based-markets"] },
|
|
2535
|
-
{ address: "0xA1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B", name: "SynFutures: Perps", category: "perpetuals", chain: "base", confidence: 0.8, source: "community", verified: false, tags: ["perps", "synfutures"] },
|
|
2536
|
-
// Yield
|
|
2537
|
-
{ address: "0xD1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E", name: "Extra Finance: Yield", category: "yield", chain: "base", confidence: 0.75, source: "community", verified: false, tags: ["yield", "extra-finance"] }
|
|
2538
|
-
]);
|
|
2539
|
-
addMany([
|
|
2540
|
-
// CEX
|
|
2541
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Optimism Hot Wallet", category: "cex", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2542
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Optimism", category: "cex", chain: "optimism", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2543
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: Optimism Hot Wallet", category: "cex", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2544
|
-
// DEX
|
|
2545
|
-
{ address: "0x99C9fc46f92E8a1c0d0c1A2B3D4E5F6A7B8C9D0E1", name: "Velodrome: Router", category: "dex", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "velodrome"] },
|
|
2546
|
-
{ address: "0x6C5A6B7C8D9E0F1A2B3C4D5E6F7A8B9C0D1E2F3", name: "Velodrome: V2 Router", category: "dex", chain: "optimism", confidence: 0.9, source: "community", verified: true, tags: ["dex", "amm", "velodrome"] },
|
|
2547
|
-
{ address: "0x7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6", name: "Velodrome: Voting Escrow", category: "dex", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["dex", "velodrome", "ve-token"] },
|
|
2548
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "optimism", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2549
|
-
{ address: "0xBA12222222228d8Ba445958a75a0704d566BF2C8", name: "Balancer V2: Vault", category: "dex", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "balancer"] },
|
|
2550
|
-
{ address: "0x9D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3", name: "Beethoven X: Vault", category: "dex", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["dex", "beethoven", "balancer"] },
|
|
2551
|
-
{ address: "0xD1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E", name: "SushiSwap: Router", category: "dex", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["dex", "amm", "sushiswap"] },
|
|
2552
|
-
// Lending
|
|
2553
|
-
{ address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD", name: "Aave V3: Pool", category: "lending", chain: "optimism", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2554
|
-
{ address: "0x2E3F4A5B6C7D8E9F0A1B2C3D4E5F6A7B8C9D0E1", name: "Aave V3: Pool Proxy", category: "lending", chain: "optimism", confidence: 0.9, source: "community", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2555
|
-
{ address: "0xA1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A9B", name: "Compound V3: Comet", category: "lending", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["lending", "compound"] },
|
|
2556
|
-
// Bridges
|
|
2557
|
-
{ address: "0xE0BB0D3DE4c3d4d5E4F2B3C1D2E3F4A5B6C7D8E9", name: "Optimism: L2 Bridge", category: "bridge", chain: "optimism", confidence: 0.95, source: "manual", verified: true, tags: ["bridge", "optimism", "l2"] },
|
|
2558
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "optimism", confidence: 0.9, source: "community", verified: true, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2559
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "stargate"] },
|
|
2560
|
-
{ address: "0x5427FEFA711Eff984124bFBB1AB7fBF5E8E0C2E5", name: "Across: Spoke Pool", category: "bridge", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "across", "crosschain"] },
|
|
2561
|
-
{ address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585", name: "Wormhole: Token Bridge", category: "bridge", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2562
|
-
{ address: "0x6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5", name: "Hop: Bridge", category: "bridge", chain: "optimism", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "hop", "crosschain"] },
|
|
2563
|
-
// Perpetuals
|
|
2564
|
-
{ address: "0x1F98431c8aD98523631AE4a59f267346ea31F984", name: "Synthetix: Proxy", category: "perpetuals", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["perps", "synthetix", "derivatives"] },
|
|
2565
|
-
{ address: "0xC0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D", name: "Synthetix: Perps Market", category: "perpetuals", chain: "optimism", confidence: 0.85, source: "community", verified: true, tags: ["perps", "synthetix"] },
|
|
2566
|
-
{ address: "0xE1F2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F", name: "Kwenta: Perps", category: "perpetuals", chain: "optimism", confidence: 0.8, source: "community", verified: false, tags: ["perps", "kwenta"] },
|
|
2567
|
-
{ address: "0xF1A2B3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A", name: "Polymarket: CTF", category: "protocol", chain: "optimism", confidence: 0.8, source: "community", verified: false, tags: ["prediction-market", "polymarket"] },
|
|
2568
|
-
// Liquid Staking
|
|
2569
|
-
{ address: "0x7f39C581F595B53c5cb19BD0b3f8dA6c935E2Ca0", name: "Wrapped stETH", category: "liquid_staking", chain: "optimism", confidence: 0.9, source: "manual", verified: true, tags: ["staking", "lido", "wsteth"] },
|
|
2570
|
-
// Yield
|
|
2571
|
-
{ address: "0xA2B3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B", name: "Extra Finance: Yield", category: "yield", chain: "optimism", confidence: 0.75, source: "community", verified: false, tags: ["yield", "extra-finance"] }
|
|
2572
|
-
]);
|
|
2573
|
-
addMany([
|
|
2574
|
-
// CEX
|
|
2575
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Polygon Hot Wallet", category: "cex", chain: "polygon", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2576
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Polygon", category: "cex", chain: "polygon", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2577
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: Polygon Hot Wallet", category: "cex", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2578
|
-
{ address: "0xf89d7b9c864f589bbf53a82105107622b35eaa40", name: "Bybit: Polygon Hot Wallet", category: "cex", chain: "polygon", confidence: 0.8, source: "community", verified: false, tags: ["cex", "hot-wallet", "bybit"] },
|
|
2579
|
-
// DEX
|
|
2580
|
-
{ address: "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff", name: "QuickSwap: Router", category: "dex", chain: "polygon", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "quickswap"] },
|
|
2581
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "polygon", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2582
|
-
{ address: "0xBA12222222228d8Ba445958a75a0704d566BF2C8", name: "Balancer V2: Vault", category: "dex", chain: "polygon", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "balancer"] },
|
|
2583
|
-
{ address: "0x1b02dA8Cb0d097eB8Dc6B91f7D5E6A8b9C0D1E2F3", name: "SushiSwap: Router", category: "dex", chain: "polygon", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "sushiswap"] },
|
|
2584
|
-
{ address: "0x6C5A6B7C8D9E0F1A2B3C4D5E6F7A8B9C0D1E2F3", name: "Curve.fi: Router", category: "dex", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["dex", "stablecoin", "curve"] },
|
|
2585
|
-
{ address: "0xB4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C", name: "Dfyn: Router", category: "dex", chain: "polygon", confidence: 0.75, source: "community", verified: false, tags: ["dex", "amm", "dfyn"] },
|
|
2586
|
-
// Lending
|
|
2587
|
-
{ address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD", name: "Aave V3: Pool", category: "lending", chain: "polygon", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2588
|
-
{ address: "0x8dFf5E27EA6b7AC08EbFdf9eb090F32ee9a30fcf", name: "Aave V2: Lending Pool", category: "lending", chain: "polygon", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2589
|
-
// Bridges
|
|
2590
|
-
{ address: "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b", name: "Polygon: PoS Bridge", category: "bridge", chain: "polygon", confidence: 0.9, source: "manual", verified: true, tags: ["bridge", "polygon", "l2"] },
|
|
2591
|
-
{ address: "0x2A3B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B", name: "Polygon: zkEVM Bridge", category: "bridge", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "polygon", "zkevm"] },
|
|
2592
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2593
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "stargate"] },
|
|
2594
|
-
{ address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585", name: "Wormhole: Token Bridge", category: "bridge", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2595
|
-
{ address: "0x6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5", name: "Hop: Bridge", category: "bridge", chain: "polygon", confidence: 0.8, source: "community", verified: false, tags: ["bridge", "hop", "crosschain"] },
|
|
2596
|
-
// Liquid Staking
|
|
2597
|
-
{ address: "0x3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B", name: "Lido: stMATIC", category: "liquid_staking", chain: "polygon", confidence: 0.85, source: "community", verified: false, tags: ["staking", "lido", "stmatic"] },
|
|
2598
|
-
{ address: "0x4A5B6C7D8E9F0A1B2C3D4E5F6A7B8C9D0E1F2A3B", name: "Stader: maticX", category: "liquid_staking", chain: "polygon", confidence: 0.8, source: "community", verified: false, tags: ["staking", "stader", "maticx"] }
|
|
2599
|
-
]);
|
|
2600
|
-
addMany([
|
|
2601
|
-
// CEX
|
|
2602
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: BSC Hot Wallet", category: "cex", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["cex", "hot-wallet", "binance"] },
|
|
2603
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: BSC", category: "cex", chain: "bsc", confidence: 0.85, source: "manual", verified: false, tags: ["cex", "coinbase"] },
|
|
2604
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: BSC Hot Wallet", category: "cex", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "okx"] },
|
|
2605
|
-
{ address: "0x53f78a071d04224b8e254e243fffc6d9f2f3fa23", name: "KuCoin: BSC Hot Wallet", category: "cex", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["cex", "hot-wallet", "kucoin"] },
|
|
2606
|
-
{ address: "0x19e2A56B1F0C7c12d9a4f4a5d7C8E3F2a1b0c9d8", name: "Bitget: BSC Hot Wallet", category: "cex", chain: "bsc", confidence: 0.8, source: "community", verified: false, tags: ["cex", "hot-wallet", "bitget"] },
|
|
2607
|
-
// DEX
|
|
2608
|
-
{ address: "0x10ED43C718714eb63d5aA57B78B54704E256024E", name: "PancakeSwap: Router v2", category: "dex", chain: "bsc", confidence: 1, source: "manual", verified: true, tags: ["dex", "amm", "pancakeswap"] },
|
|
2609
|
-
{ address: "0x0E09FaBB73Bd3ade0a17ECC321fD13a19e81cE82", name: "PancakeSwap: Cake Token", category: "dex", chain: "bsc", confidence: 0.95, source: "manual", verified: true, tags: ["dex", "pancakeswap", "cake"] },
|
|
2610
|
-
{ address: "0x05fF2B0DB69458A0750badebc4f9e13aDd6C6843", name: "PancakeSwap: Router v1", category: "dex", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "pancakeswap"] },
|
|
2611
|
-
{ address: "0x73feaa1eE314F8c655E354234017bE2193C9E24E", name: "PancakeSwap: MasterChef v2", category: "dex", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "pancakeswap", "farming"] },
|
|
2612
|
-
{ address: "0xE592427A0AEce92De3Edee1F18E0157C05861564", name: "Uniswap V3: Router", category: "dex", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["dex", "amm", "uniswap"] },
|
|
2613
|
-
{ address: "0x3a6d8cA21D1CF76F653A67577FA0D27453350dD8", name: "BiSwap: Router", category: "dex", chain: "bsc", confidence: 0.85, source: "manual", verified: true, tags: ["dex", "amm", "biswap"] },
|
|
2614
|
-
{ address: "0x9F8B4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B", name: "Thena: Router", category: "dex", chain: "bsc", confidence: 0.8, source: "community", verified: false, tags: ["dex", "amm", "thena"] },
|
|
2615
|
-
{ address: "0xB4C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C", name: "MDEX: Router", category: "dex", chain: "bsc", confidence: 0.75, source: "community", verified: false, tags: ["dex", "amm", "mdex"] },
|
|
2616
|
-
// Lending
|
|
2617
|
-
{ address: "0xfD5840Cd36d94D722943985ed367D6cE5B0CF8D9", name: "Venus: Unitroller", category: "lending", chain: "bsc", confidence: 0.95, source: "manual", verified: true, tags: ["lending", "venus"] },
|
|
2618
|
-
{ address: "0x95cF2b0E1E4B8A3C5D6E7F8A9B0C1D2E3F4A5B6C", name: "Venus: vBNB", category: "lending", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "venus", "vbnb"] },
|
|
2619
|
-
{ address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD", name: "Aave V3: Pool", category: "lending", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["lending", "defi", "aave"] },
|
|
2620
|
-
{ address: "0x5C5D6E7F8A9B0C1D2E3F4A5B6C7D8E9F0A1B2C3", name: "Alpaca Finance: Fair Launch", category: "lending", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["lending", "alpaca", "yield"] },
|
|
2621
|
-
{ address: "0xD1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E", name: "Radiant: Lending", category: "lending", chain: "bsc", confidence: 0.8, source: "community", verified: false, tags: ["lending", "radiant"] },
|
|
2622
|
-
// Bridges
|
|
2623
|
-
{ address: "0x4a73aB60F4D7cC8d0E8fA2B3C4D5E6F7A8B9C0D1E", name: "LayerZero: Endpoint", category: "bridge", chain: "bsc", confidence: 0.85, source: "community", verified: true, tags: ["bridge", "layerzero", "crosschain"] },
|
|
2624
|
-
{ address: "0x8731d54E9D02c286767d56ac03e8037C07e01e98", name: "Stargate: Router", category: "bridge", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "stargate"] },
|
|
2625
|
-
{ address: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585", name: "Wormhole: Token Bridge", category: "bridge", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["bridge", "wormhole", "crosschain"] },
|
|
2626
|
-
// Perpetuals
|
|
2627
|
-
{ address: "0xD7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E", name: "ApolloX: Perps", category: "perpetuals", chain: "bsc", confidence: 0.8, source: "community", verified: false, tags: ["perps", "apollox", "derivatives"] },
|
|
2628
|
-
// Liquid Staking
|
|
2629
|
-
{ address: "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0c", name: "Binance: stETH", category: "liquid_staking", chain: "bsc", confidence: 0.85, source: "community", verified: false, tags: ["staking", "bnb"] },
|
|
2630
|
-
{ address: "0xC0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D", name: "Stader: BNBx", category: "liquid_staking", chain: "bsc", confidence: 0.8, source: "community", verified: false, tags: ["staking", "stader", "bnbx"] },
|
|
2631
|
-
// Oracle
|
|
2632
|
-
{ address: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", name: "Chainlink: BNB/USD Feed", category: "oracle", chain: "bsc", confidence: 0.9, source: "manual", verified: true, tags: ["oracle", "chainlink", "price-feed"] }
|
|
2633
|
-
]);
|
|
2634
|
-
}
|
|
2635
|
-
});
|
|
2636
|
-
|
|
2637
|
-
// src/data/cexWallets.ts
|
|
2638
|
-
function getCEXDatabase() {
|
|
2639
|
-
return cexDatabase || CEX_WALLETS;
|
|
2640
|
-
}
|
|
2641
|
-
function getCEXInfo(address, chain) {
|
|
2642
|
-
const groups = getCEXDatabase()[chain] || [];
|
|
2643
|
-
for (const group of groups) {
|
|
2644
|
-
const wallet = group.wallets.find((w) => w.address.toLowerCase() === address.toLowerCase());
|
|
2645
|
-
if (wallet) {
|
|
2646
|
-
return {
|
|
2647
|
-
cexName: group.name,
|
|
2648
|
-
type: wallet.type,
|
|
2649
|
-
isMain: wallet.isMain
|
|
2650
|
-
};
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
return null;
|
|
2654
|
-
}
|
|
2655
|
-
function detectCEXPattern(txCount, uniqueSenders, uniqueRecipients, avgTxValue, totalVolume) {
|
|
2656
|
-
const signals = [];
|
|
2657
|
-
let score = 0;
|
|
2658
|
-
if (txCount > 1e4) {
|
|
2659
|
-
score += 30;
|
|
2660
|
-
signals.push("veryHighTxCount");
|
|
2661
|
-
} else if (txCount > 1e3) {
|
|
2662
|
-
score += 20;
|
|
2663
|
-
signals.push("highTxCount");
|
|
2664
|
-
}
|
|
2665
|
-
if (uniqueSenders > 100) {
|
|
2666
|
-
score += 25;
|
|
2667
|
-
signals.push("manyUniqueSenders");
|
|
2668
|
-
} else if (uniqueSenders > 20) {
|
|
2669
|
-
score += 15;
|
|
2670
|
-
signals.push("multipleSenders");
|
|
2671
|
-
}
|
|
2672
|
-
if (uniqueRecipients < uniqueSenders * 0.3 && uniqueSenders > 50) {
|
|
2673
|
-
score += 20;
|
|
2674
|
-
signals.push("oneWayFlow");
|
|
2675
|
-
}
|
|
2676
|
-
if (avgTxValue > 1e4) {
|
|
2677
|
-
score += 15;
|
|
2678
|
-
signals.push("highAvgValue");
|
|
2679
|
-
} else if (avgTxValue > 1e3) {
|
|
2680
|
-
score += 10;
|
|
2681
|
-
signals.push("mediumAvgValue");
|
|
2682
|
-
}
|
|
2683
|
-
if (totalVolume > 1e6) {
|
|
2684
|
-
score += 10;
|
|
2685
|
-
signals.push("veryHighVolume");
|
|
2686
|
-
}
|
|
2687
|
-
return {
|
|
2688
|
-
isCEX: score >= 50,
|
|
2689
|
-
score: Math.min(score, 100),
|
|
2690
|
-
signals
|
|
2691
|
-
};
|
|
2692
|
-
}
|
|
2693
|
-
var cexDatabase, CEX_WALLETS;
|
|
2694
|
-
var init_cexWallets = __esm({
|
|
2695
|
-
"src/data/cexWallets.ts"() {
|
|
2696
|
-
cexDatabase = null;
|
|
2697
|
-
CEX_WALLETS = {
|
|
2698
|
-
ethereum: [
|
|
2699
|
-
{
|
|
2700
|
-
name: "Binance",
|
|
2701
|
-
wallets: [
|
|
2702
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Hot Wallet 20", chain: "ethereum", type: "hot", isMain: true },
|
|
2703
|
-
{ address: "0x631fc1ea2270e98fbd9d92658ece0f5a269aa161", name: "Binance: Hot Wallet", chain: "ethereum", type: "hot", isMain: false },
|
|
2704
|
-
{ address: "0x28fA6C20b26Be9bAd1d89E5e8E2d1F5C5e3dE4aF", name: "Binance: Deposit Wallet", chain: "ethereum", type: "deposit", isMain: false }
|
|
2705
|
-
]
|
|
2706
|
-
},
|
|
2707
|
-
{
|
|
2708
|
-
name: "Coinbase",
|
|
2709
|
-
wallets: [
|
|
2710
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase 2", chain: "ethereum", type: "hot", isMain: true },
|
|
2711
|
-
{ address: "0xb1697cea2605d1dBa32d94A72d8CBfCFB8f55aC9", name: "Coinbase: Hot Wallet", chain: "ethereum", type: "hot", isMain: false },
|
|
2712
|
-
{ address: "0xA5d1d5d9a8E7a8d1E5c8a9f2d3c4B5e6f7a8b9c0", name: "Coinbase: Deposit", chain: "ethereum", type: "deposit", isMain: false }
|
|
2713
|
-
]
|
|
2714
|
-
},
|
|
2715
|
-
{
|
|
2716
|
-
name: "Kraken",
|
|
2717
|
-
wallets: [
|
|
2718
|
-
{ address: "0xe9f7ecae3a53d2a67105292894676b00d1fab785", name: "Kraken: Hot Wallet", chain: "ethereum", type: "hot", isMain: true },
|
|
2719
|
-
{ address: "0xf30ba13e4b04ce5dc4d254ae5fa95477800f0eb0", name: "Kraken: Hot Wallet 2", chain: "ethereum", type: "hot", isMain: false },
|
|
2720
|
-
{ address: "0x05ff6964d21e5dae3b1010d5ae0465b3c450f381", name: "Kraken: Hot Wallet 4", chain: "ethereum", type: "hot", isMain: false }
|
|
2721
|
-
]
|
|
2722
|
-
},
|
|
2723
|
-
{
|
|
2724
|
-
name: "Bybit",
|
|
2725
|
-
wallets: [
|
|
2726
|
-
{ address: "0xf89d7b9c864f589bbf53a82105107622b35eaa40", name: "Bybit: Hot Wallet", chain: "ethereum", type: "hot", isMain: true },
|
|
2727
|
-
{ address: "0x4BC195D2dC6Bf3B8e1C5b7e1D5C9aF3E2b7d1C0a", name: "Bybit: Deposit", chain: "ethereum", type: "deposit", isMain: false }
|
|
2728
|
-
]
|
|
2729
|
-
},
|
|
2730
|
-
{
|
|
2731
|
-
name: "OKX",
|
|
2732
|
-
wallets: [
|
|
2733
|
-
{ address: "0x4b4e14a3773ee558b6597070797fd51eb48606e5", name: "OKX: Hot Wallet", chain: "ethereum", type: "hot", isMain: true },
|
|
2734
|
-
{ address: "0x559432e18b281731c054cd703d4b49872be4ed53", name: "OKX: Hot Wallet 5", chain: "ethereum", type: "hot", isMain: false }
|
|
2735
|
-
]
|
|
2736
|
-
},
|
|
2737
|
-
{
|
|
2738
|
-
name: "KuCoin",
|
|
2739
|
-
wallets: [
|
|
2740
|
-
{ address: "0x53f78a071d04224b8e254e243fffc6d9f2f3fa23", name: "KuCoin: Hot Wallet 2", chain: "ethereum", type: "hot", isMain: true }
|
|
2741
|
-
]
|
|
2742
|
-
},
|
|
2743
|
-
{
|
|
2744
|
-
name: "Bitget",
|
|
2745
|
-
wallets: [
|
|
2746
|
-
{ address: "0x19e2A56B1F0C7c12d9a4f4a5d7C8E3F2a1b0c9d8", name: "Bitget: Hot Wallet", chain: "ethereum", type: "hot", isMain: true }
|
|
2747
|
-
]
|
|
2748
|
-
},
|
|
2749
|
-
{
|
|
2750
|
-
name: "Gate.io",
|
|
2751
|
-
wallets: [
|
|
2752
|
-
{ address: "0x0f5d2A7B8E1d2C3a4b5e6f7a8b9c0d1e2f3a4b5", name: "Gate.io: Hot Wallet", chain: "ethereum", type: "hot", isMain: true }
|
|
2753
|
-
]
|
|
2754
|
-
}
|
|
2755
|
-
],
|
|
2756
|
-
linea: [
|
|
2757
|
-
{
|
|
2758
|
-
name: "Binance",
|
|
2759
|
-
wallets: [
|
|
2760
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Linea Hot Wallet", chain: "linea", type: "hot", isMain: true }
|
|
2761
|
-
]
|
|
2762
|
-
},
|
|
2763
|
-
{
|
|
2764
|
-
name: "Coinbase",
|
|
2765
|
-
wallets: [
|
|
2766
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Linea", chain: "linea", type: "hot", isMain: true }
|
|
2767
|
-
]
|
|
2768
|
-
}
|
|
2769
|
-
],
|
|
2770
|
-
arbitrum: [
|
|
2771
|
-
{
|
|
2772
|
-
name: "Binance",
|
|
2773
|
-
wallets: [
|
|
2774
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Arbitrum Hot Wallet", chain: "arbitrum", type: "hot", isMain: true }
|
|
2775
|
-
]
|
|
2776
|
-
},
|
|
2777
|
-
{
|
|
2778
|
-
name: "Coinbase",
|
|
2779
|
-
wallets: [
|
|
2780
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Arbitrum", chain: "arbitrum", type: "hot", isMain: true }
|
|
2781
|
-
]
|
|
2782
|
-
}
|
|
2783
|
-
],
|
|
2784
|
-
base: [
|
|
2785
|
-
{
|
|
2786
|
-
name: "Binance",
|
|
2787
|
-
wallets: [
|
|
2788
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Base Hot Wallet", chain: "base", type: "hot", isMain: true }
|
|
2789
|
-
]
|
|
2790
|
-
}
|
|
2791
|
-
],
|
|
2792
|
-
optimism: [
|
|
2793
|
-
{
|
|
2794
|
-
name: "Binance",
|
|
2795
|
-
wallets: [
|
|
2796
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Optimism Hot Wallet", chain: "optimism", type: "hot", isMain: true }
|
|
2797
|
-
]
|
|
2798
|
-
},
|
|
2799
|
-
{
|
|
2800
|
-
name: "Coinbase",
|
|
2801
|
-
wallets: [
|
|
2802
|
-
{ address: "0x503828976d22510aad0201ac7ec88293211d23da", name: "Coinbase: Optimism", chain: "optimism", type: "hot", isMain: true }
|
|
2803
|
-
]
|
|
2804
|
-
}
|
|
2805
|
-
],
|
|
2806
|
-
polygon: [
|
|
2807
|
-
{
|
|
2808
|
-
name: "Binance",
|
|
2809
|
-
wallets: [
|
|
2810
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: Polygon Hot Wallet", chain: "polygon", type: "hot", isMain: true }
|
|
2811
|
-
]
|
|
2812
|
-
}
|
|
2813
|
-
],
|
|
2814
|
-
bsc: [
|
|
2815
|
-
{
|
|
2816
|
-
name: "Binance",
|
|
2817
|
-
wallets: [
|
|
2818
|
-
{ address: "0xF977814e90dA44bFA03b6295A0616a897441aceC", name: "Binance: BSC Hot Wallet", chain: "bsc", type: "hot", isMain: true },
|
|
2819
|
-
{ address: "0x0E09FaBB73Bd3ade0a17ECC321fD13a19e81cE82", name: "Binance: Cake", chain: "bsc", type: "hot", isMain: false }
|
|
2820
|
-
]
|
|
2821
|
-
}
|
|
2822
|
-
]
|
|
2823
|
-
};
|
|
2824
|
-
}
|
|
2825
|
-
});
|
|
2826
|
-
|
|
2827
|
-
// src/services/EntityService.ts
|
|
2828
|
-
var EntityService_exports = {};
|
|
2829
|
-
__export(EntityService_exports, {
|
|
2830
|
-
EntityService: () => EntityService
|
|
2831
|
-
});
|
|
2832
|
-
var CACHE_TTL2, BULK_CACHE_TTL, EntityService;
|
|
2833
|
-
var init_EntityService = __esm({
|
|
2834
|
-
"src/services/EntityService.ts"() {
|
|
2835
|
-
init_entities();
|
|
2836
|
-
init_cexWallets();
|
|
2837
|
-
init_cache();
|
|
2838
|
-
CACHE_TTL2 = 300;
|
|
2839
|
-
BULK_CACHE_TTL = 60;
|
|
2840
|
-
EntityService = class _EntityService {
|
|
2841
|
-
/**
|
|
2842
|
-
* Look up a single address on a specific chain
|
|
2843
|
-
*/
|
|
2844
|
-
static lookupEntity(chain, address) {
|
|
2845
|
-
if (!address || !chain) return null;
|
|
2846
|
-
const cacheKey = `entity:${chain}:${address.toLowerCase()}`;
|
|
2847
|
-
const cached = cache.get(cacheKey);
|
|
2848
|
-
if (cached) return cached;
|
|
2849
|
-
const entity = getEntity(chain, address);
|
|
2850
|
-
if (entity) {
|
|
2851
|
-
cache.set(cacheKey, entity, CACHE_TTL2);
|
|
2852
|
-
return entity;
|
|
2853
|
-
}
|
|
2854
|
-
const cexInfo = getCEXInfo(address, chain);
|
|
2855
|
-
if (cexInfo) {
|
|
2856
|
-
const cexEntity = {
|
|
2857
|
-
address,
|
|
2858
|
-
name: cexInfo.cexName,
|
|
2859
|
-
category: "cex",
|
|
2860
|
-
chain,
|
|
2861
|
-
confidence: 0.9,
|
|
2862
|
-
source: "manual",
|
|
2863
|
-
verified: true,
|
|
2864
|
-
tags: ["cex", cexInfo.type]
|
|
2865
|
-
};
|
|
2866
|
-
cache.set(cacheKey, cexEntity, CACHE_TTL2);
|
|
2867
|
-
return cexEntity;
|
|
2868
|
-
}
|
|
2869
|
-
cache.set(cacheKey, null, CACHE_TTL2);
|
|
2870
|
-
return null;
|
|
2871
|
-
}
|
|
2872
|
-
/**
|
|
2873
|
-
* Look up multiple addresses in batch
|
|
2874
|
-
*/
|
|
2875
|
-
static bulkLookup(chain, addresses) {
|
|
2876
|
-
if (!addresses.length) return {};
|
|
2877
|
-
const cacheKey = `entity:bulk:${chain}:${addresses.length}`;
|
|
2878
|
-
const cached = cache.get(cacheKey);
|
|
2879
|
-
if (cached) return cached;
|
|
2880
|
-
const results = {};
|
|
2881
|
-
for (const addr of addresses) {
|
|
2882
|
-
results[addr.toLowerCase()] = _EntityService.lookupEntity(chain, addr);
|
|
2883
|
-
}
|
|
2884
|
-
cache.set(cacheKey, results, BULK_CACHE_TTL);
|
|
2885
|
-
return results;
|
|
2886
|
-
}
|
|
2887
|
-
/**
|
|
2888
|
-
* Search entities by name
|
|
2889
|
-
*/
|
|
2890
|
-
static search(query, chain, category) {
|
|
2891
|
-
if (!query || query.length < 2) return [];
|
|
2892
|
-
return searchEntities(query, chain, category);
|
|
2893
|
-
}
|
|
2894
|
-
/**
|
|
2895
|
-
* Cross-chain entity lookup (try all chains)
|
|
2896
|
-
*/
|
|
2897
|
-
static findEntityAnyChain(address) {
|
|
2898
|
-
if (!address) return null;
|
|
2899
|
-
return findEntityByAddress(address) || null;
|
|
2900
|
-
}
|
|
2901
|
-
/**
|
|
2902
|
-
* Auto-detect entity type from transaction patterns (for unknown addresses)
|
|
2903
|
-
*/
|
|
2904
|
-
static detectEntityType(address, txCount, uniqueSenders, uniqueRecipients, totalVolumeInEth, avgTxValueInEth) {
|
|
2905
|
-
const cexPattern = detectCEXPattern(
|
|
2906
|
-
txCount,
|
|
2907
|
-
uniqueSenders,
|
|
2908
|
-
uniqueRecipients,
|
|
2909
|
-
avgTxValueInEth,
|
|
2910
|
-
totalVolumeInEth
|
|
2911
|
-
);
|
|
2912
|
-
const signals = [];
|
|
2913
|
-
let entityType = "wallet";
|
|
2914
|
-
let score = 0;
|
|
2915
|
-
if (cexPattern.isCEX) {
|
|
2916
|
-
return {
|
|
2917
|
-
entityType: "cex",
|
|
2918
|
-
score: cexPattern.score,
|
|
2919
|
-
signals: cexPattern.signals
|
|
2920
|
-
};
|
|
2921
|
-
}
|
|
2922
|
-
if (txCount > 0 && uniqueSenders === 0 && uniqueRecipients === 0) {
|
|
2923
|
-
entityType = "contract";
|
|
2924
|
-
score = 0.5;
|
|
2925
|
-
signals.push("no_user_transactions");
|
|
2926
|
-
}
|
|
2927
|
-
if (txCount > 50 && uniqueRecipients < 5 && totalVolumeInEth > 100) {
|
|
2928
|
-
score = Math.max(score, 0.6);
|
|
2929
|
-
entityType = "protocol";
|
|
2930
|
-
signals.push("high_volume_few_recipients");
|
|
2931
|
-
}
|
|
2932
|
-
if (score < 0.3) return null;
|
|
2933
|
-
return { entityType, score: Math.round(score * 100), signals };
|
|
2934
|
-
}
|
|
2935
|
-
};
|
|
2936
|
-
}
|
|
2937
|
-
});
|
|
2938
|
-
|
|
2939
|
-
// src/mcp/handlers.ts
|
|
2940
|
-
var handlers_exports = {};
|
|
2941
|
-
__export(handlers_exports, {
|
|
222
|
+
// src/mcp/api-handlers.ts
|
|
223
|
+
var api_handlers_exports = {};
|
|
224
|
+
__export(api_handlers_exports, {
|
|
2942
225
|
TOOL_HANDLERS: () => TOOL_HANDLERS
|
|
2943
226
|
});
|
|
227
|
+
import { default as axios } from "axios";
|
|
2944
228
|
function ok(text) {
|
|
2945
229
|
return { content: [{ type: "text", text }] };
|
|
2946
230
|
}
|
|
2947
231
|
function err(message) {
|
|
2948
232
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
2949
233
|
}
|
|
2950
|
-
function
|
|
2951
|
-
const
|
|
2952
|
-
return {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
polygonscan: process.env.POLYGONSCAN_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || ""
|
|
2961
|
-
};
|
|
2962
|
-
}
|
|
2963
|
-
function buildSybilConfig() {
|
|
2964
|
-
const alchemyKeys = process.env.ALCHEMY_API_KEYS?.split(",") || [];
|
|
2965
|
-
const defaultKey = process.env.DEFAULT_ALCHEMY_API_KEY || alchemyKeys[0] || "";
|
|
2966
|
-
return {
|
|
2967
|
-
defaultKey,
|
|
2968
|
-
moralisKey: process.env.MORALIS_API_KEY || "",
|
|
2969
|
-
contractKeys: [defaultKey, ...alchemyKeys].filter(Boolean),
|
|
2970
|
-
walletKeys: [defaultKey, ...alchemyKeys].filter(Boolean)
|
|
2971
|
-
};
|
|
2972
|
-
}
|
|
2973
|
-
function summarizeTree(node) {
|
|
2974
|
-
if (!node) return null;
|
|
2975
|
-
return {
|
|
2976
|
-
address: node.address,
|
|
2977
|
-
label: node.label,
|
|
2978
|
-
totalValueInEth: node.totalValueInEth,
|
|
2979
|
-
txCount: node.txCount,
|
|
2980
|
-
suspiciousScore: node.suspiciousScore,
|
|
2981
|
-
suspiciousReasons: node.suspiciousReasons,
|
|
2982
|
-
children: node.children?.map(summarizeTree) || []
|
|
2983
|
-
};
|
|
234
|
+
function api() {
|
|
235
|
+
const key = process.env.FUNDTRACER_MCP_API_KEY || "";
|
|
236
|
+
return axios.create({
|
|
237
|
+
baseURL: API_BASE,
|
|
238
|
+
timeout: 6e4,
|
|
239
|
+
headers: {
|
|
240
|
+
Authorization: `Bearer ${key}`,
|
|
241
|
+
"Content-Type": "application/json"
|
|
242
|
+
}
|
|
243
|
+
});
|
|
2984
244
|
}
|
|
2985
|
-
var analyzeWallet, traceFunds, compareWallets, analyzeContract, detectSybilClusters, getPortfolio, getTransactions, lookupEntity, getGasPrices, getTokenInfo, TOOL_HANDLERS;
|
|
2986
|
-
var
|
|
2987
|
-
"src/mcp/handlers.ts"() {
|
|
245
|
+
var API_BASE, analyzeWallet, traceFunds, compareWallets, analyzeContract, detectSybilClusters, getPortfolio, getTransactions, lookupEntity, getGasPrices, getTokenInfo, TOOL_HANDLERS;
|
|
246
|
+
var init_api_handlers = __esm({
|
|
247
|
+
"src/mcp/api-handlers.ts"() {
|
|
248
|
+
API_BASE = process.env.FUNDTRACER_API_URL || "https://api.fundtracer.xyz";
|
|
2988
249
|
analyzeWallet = async (args, ctx) => {
|
|
2989
250
|
const { address, chainId, transactionLimit } = args;
|
|
2990
251
|
try {
|
|
2991
|
-
const
|
|
2992
|
-
|
|
2993
|
-
console.error(`[MCP] analyze_wallet ${address}: ${progress.stage} ${progress.current}/${progress.total}`);
|
|
2994
|
-
});
|
|
2995
|
-
const result = await analyzer.analyze(address, chainId, {
|
|
2996
|
-
transactionLimit: transactionLimit || 500
|
|
2997
|
-
});
|
|
2998
|
-
return ok(JSON.stringify({
|
|
2999
|
-
address: result.wallet.address,
|
|
252
|
+
const res = await api().post("/api/analyze/wallet", {
|
|
253
|
+
address,
|
|
3000
254
|
chain: chainId,
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
riskScore: result.overallRiskScore,
|
|
3005
|
-
riskLevel: result.riskLevel,
|
|
3006
|
-
suspiciousIndicators: result.suspiciousIndicators.map((i) => ({
|
|
3007
|
-
type: i.type,
|
|
3008
|
-
severity: i.severity,
|
|
3009
|
-
description: i.description,
|
|
3010
|
-
score: i.score
|
|
3011
|
-
})),
|
|
3012
|
-
topFundingSources: result.summary.topFundingSources.slice(0, 5),
|
|
3013
|
-
topFundingDestinations: result.summary.topFundingDestinations.slice(0, 5),
|
|
3014
|
-
projectsInteracted: result.projectsInteracted.slice(0, 10),
|
|
3015
|
-
activityPeriodDays: result.summary.activityPeriodDays,
|
|
3016
|
-
averageTxPerDay: result.summary.averageTxPerDay
|
|
3017
|
-
}, null, 2));
|
|
255
|
+
options: { limit: transactionLimit || 500 }
|
|
256
|
+
});
|
|
257
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3018
258
|
} catch (error) {
|
|
3019
|
-
|
|
259
|
+
const msg = error.response?.data?.error || error.message;
|
|
260
|
+
return err(`Wallet analysis failed: ${msg}`);
|
|
3020
261
|
}
|
|
3021
262
|
};
|
|
3022
263
|
traceFunds = async (args, ctx) => {
|
|
3023
264
|
const { address, chainId, maxDepth = 3, direction = "both" } = args;
|
|
3024
265
|
try {
|
|
3025
|
-
const
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
const tree2 = await svc.buildFundingTree(address, maxDepth);
|
|
3033
|
-
return ok(JSON.stringify(tree2, null, 2));
|
|
3034
|
-
}
|
|
3035
|
-
const tree = await analyzer.buildFundingTree(address, chainId, { treeConfig });
|
|
3036
|
-
return ok(JSON.stringify({
|
|
3037
|
-
sources: summarizeTree(tree.fundingSources),
|
|
3038
|
-
destinations: summarizeTree(tree.fundingDestinations)
|
|
3039
|
-
}, null, 2));
|
|
266
|
+
const res = await api().post("/api/analyze/funding-tree", {
|
|
267
|
+
address,
|
|
268
|
+
chain: chainId,
|
|
269
|
+
maxDepth,
|
|
270
|
+
direction
|
|
271
|
+
});
|
|
272
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3040
273
|
} catch (error) {
|
|
3041
|
-
|
|
274
|
+
const msg = error.response?.data?.error || error.message;
|
|
275
|
+
return err(`Fund tracing failed: ${msg}`);
|
|
3042
276
|
}
|
|
3043
277
|
};
|
|
3044
278
|
compareWallets = async (args, ctx) => {
|
|
@@ -3046,41 +280,28 @@ var init_handlers = __esm({
|
|
|
3046
280
|
const addrList = addresses.split(",").map((a) => a.trim()).filter(Boolean);
|
|
3047
281
|
if (addrList.length < 2) return err("At least 2 addresses required");
|
|
3048
282
|
try {
|
|
3049
|
-
const
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
chain: chainId,
|
|
3055
|
-
correlationScore: result.correlationScore,
|
|
3056
|
-
isSybilLikely: result.isSybilLikely,
|
|
3057
|
-
commonFundingSources: result.commonFundingSources,
|
|
3058
|
-
commonDestinations: result.commonDestinations,
|
|
3059
|
-
sharedProjects: result.sharedProjects,
|
|
3060
|
-
directTransfers: result.directTransfers.length
|
|
3061
|
-
}, null, 2));
|
|
283
|
+
const res = await api().post("/api/analyze/compare", {
|
|
284
|
+
addresses: addrList,
|
|
285
|
+
chain: chainId
|
|
286
|
+
});
|
|
287
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3062
288
|
} catch (error) {
|
|
3063
|
-
|
|
289
|
+
const msg = error.response?.data?.error || error.message;
|
|
290
|
+
return err(`Wallet comparison failed: ${msg}`);
|
|
3064
291
|
}
|
|
3065
292
|
};
|
|
3066
293
|
analyzeContract = async (args, ctx) => {
|
|
3067
294
|
const { contractAddress, chainId, maxInteractors = 100 } = args;
|
|
3068
295
|
try {
|
|
3069
|
-
const
|
|
3070
|
-
const analyzer = new WalletAnalyzer(buildApiKeyConfig());
|
|
3071
|
-
const result = await analyzer.analyzeContract(contractAddress, chainId, {
|
|
3072
|
-
maxInteractors
|
|
3073
|
-
});
|
|
3074
|
-
return ok(JSON.stringify({
|
|
296
|
+
const res = await api().post("/api/analyze/contract", {
|
|
3075
297
|
contractAddress,
|
|
3076
298
|
chain: chainId,
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
suspiciousPatterns: result.suspiciousPatterns
|
|
3081
|
-
}, null, 2));
|
|
299
|
+
maxInteractors
|
|
300
|
+
});
|
|
301
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3082
302
|
} catch (error) {
|
|
3083
|
-
|
|
303
|
+
const msg = error.response?.data?.error || error.message;
|
|
304
|
+
return err(`Contract analysis failed: ${msg}`);
|
|
3084
305
|
}
|
|
3085
306
|
};
|
|
3086
307
|
detectSybilClusters = async (args, ctx) => {
|
|
@@ -3088,163 +309,93 @@ var init_handlers = __esm({
|
|
|
3088
309
|
const addrList = addresses.split(",").map((a) => a.trim()).filter(Boolean);
|
|
3089
310
|
if (addrList.length < 3) return err("At least 3 addresses required for cluster detection");
|
|
3090
311
|
try {
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
return ok(JSON.stringify({
|
|
3097
|
-
chain: chainId,
|
|
3098
|
-
totalWallets: result.totalInteractors,
|
|
3099
|
-
uniqueFundingSources: result.uniqueFundingSources,
|
|
3100
|
-
flaggedClusters: result.flaggedClusters.map((c) => ({
|
|
3101
|
-
fundingSource: c.fundingSource,
|
|
3102
|
-
fundingSourceLabel: c.fundingSourceLabel,
|
|
3103
|
-
walletCount: c.totalWallets,
|
|
3104
|
-
sybilScore: c.sybilScore,
|
|
3105
|
-
flags: c.flags,
|
|
3106
|
-
timeSpanHours: c.timeSpan.durationHours
|
|
3107
|
-
})),
|
|
3108
|
-
summary: result.summary
|
|
3109
|
-
}, null, 2));
|
|
312
|
+
const res = await api().post("/api/analyze/sybil", {
|
|
313
|
+
addresses: addrList,
|
|
314
|
+
chain: chainId
|
|
315
|
+
});
|
|
316
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3110
317
|
} catch (error) {
|
|
3111
|
-
|
|
318
|
+
const msg = error.response?.data?.error || error.message;
|
|
319
|
+
return err(`Sybil detection failed: ${msg}`);
|
|
3112
320
|
}
|
|
3113
321
|
};
|
|
3114
322
|
getPortfolio = async (args, ctx) => {
|
|
3115
323
|
const { address, chainId } = args;
|
|
3116
324
|
try {
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
}
|
|
3122
|
-
return ok(JSON.stringify({
|
|
3123
|
-
address,
|
|
3124
|
-
chainId,
|
|
3125
|
-
note: "For EVM chain portfolio data, use the FundTracer REST API: GET /api/portfolio?address=" + address + "&chain=" + chainId
|
|
3126
|
-
}, null, 2));
|
|
325
|
+
const res = await api().get(`/api/portfolio/${address}`, {
|
|
326
|
+
params: { chain: chainId }
|
|
327
|
+
});
|
|
328
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3127
329
|
} catch (error) {
|
|
3128
|
-
|
|
330
|
+
const msg = error.response?.data?.error || error.message;
|
|
331
|
+
return err(`Portfolio fetch failed: ${msg}`);
|
|
3129
332
|
}
|
|
3130
333
|
};
|
|
3131
334
|
getTransactions = async (args, ctx) => {
|
|
3132
335
|
const { address, chainId, limit = 50 } = args;
|
|
3133
336
|
try {
|
|
3134
|
-
const
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
337
|
+
const res = await api().post("/api/history", {
|
|
338
|
+
wallet: address,
|
|
339
|
+
blockchain: chainId,
|
|
340
|
+
pageToken: null,
|
|
341
|
+
filters: {}
|
|
3139
342
|
});
|
|
343
|
+
const txs = (res.data.transactions || []).slice(0, limit);
|
|
3140
344
|
return ok(JSON.stringify({
|
|
3141
345
|
address,
|
|
3142
346
|
chainId,
|
|
3143
|
-
transactions:
|
|
3144
|
-
|
|
3145
|
-
blockNumber: tx.blockNumber,
|
|
3146
|
-
timestamp: tx.timestamp,
|
|
3147
|
-
from: tx.from,
|
|
3148
|
-
to: tx.to,
|
|
3149
|
-
value: tx.valueInEth,
|
|
3150
|
-
status: tx.status,
|
|
3151
|
-
category: tx.category,
|
|
3152
|
-
methodName: tx.methodName
|
|
3153
|
-
})),
|
|
3154
|
-
totalCount: result.transactions.length
|
|
347
|
+
transactions: txs,
|
|
348
|
+
totalCount: res.data.transactions?.length || 0
|
|
3155
349
|
}, null, 2));
|
|
3156
350
|
} catch (error) {
|
|
3157
|
-
|
|
351
|
+
const msg = error.response?.data?.error || error.message;
|
|
352
|
+
return err(`Transaction fetch failed: ${msg}`);
|
|
3158
353
|
}
|
|
3159
354
|
};
|
|
3160
355
|
lookupEntity = async (args, ctx) => {
|
|
3161
356
|
const { query, chainId } = args;
|
|
357
|
+
const chain = chainId || "ethereum";
|
|
3162
358
|
try {
|
|
3163
|
-
const { EntityService: EntityService2 } = await Promise.resolve().then(() => (init_EntityService(), EntityService_exports));
|
|
3164
|
-
const chain = chainId || "ethereum";
|
|
3165
359
|
if (/^0x[a-fA-F0-9]{40}$/.test(query) || /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(query)) {
|
|
3166
|
-
const
|
|
3167
|
-
|
|
3168
|
-
|
|
360
|
+
const res2 = await api().get(`/api/entities/${query}`, {
|
|
361
|
+
params: { chain }
|
|
362
|
+
});
|
|
363
|
+
return ok(JSON.stringify(res2.data, null, 2));
|
|
3169
364
|
}
|
|
3170
|
-
const
|
|
3171
|
-
|
|
3172
|
-
|
|
365
|
+
const res = await api().get("/api/entities/search", {
|
|
366
|
+
params: { q: query, chain }
|
|
367
|
+
});
|
|
368
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3173
369
|
} catch (error) {
|
|
3174
|
-
|
|
370
|
+
if (error.response?.status === 404) {
|
|
371
|
+
return ok(JSON.stringify({ query, label: "Unknown address", chain }, null, 2));
|
|
372
|
+
}
|
|
373
|
+
const msg = error.response?.data?.error || error.message;
|
|
374
|
+
return err(`Entity lookup failed: ${msg}`);
|
|
3175
375
|
}
|
|
3176
376
|
};
|
|
3177
377
|
getGasPrices = async (args, ctx) => {
|
|
378
|
+
const { chainId } = args;
|
|
3178
379
|
try {
|
|
3179
|
-
const
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
ethereum: `https://eth-mainnet.g.alchemy.com/v2/${alchemyKey}`,
|
|
3184
|
-
base: `https://base-mainnet.g.alchemy.com/v2/${alchemyKey}`,
|
|
3185
|
-
arbitrum: `https://arb-mainnet.g.alchemy.com/v2/${alchemyKey}`,
|
|
3186
|
-
optimism: `https://opt-mainnet.g.alchemy.com/v2/${alchemyKey}`,
|
|
3187
|
-
polygon: `https://polygon-mainnet.g.alchemy.com/v2/${alchemyKey}`
|
|
3188
|
-
};
|
|
3189
|
-
const results = {};
|
|
3190
|
-
for (const [chain, url] of Object.entries(chains)) {
|
|
3191
|
-
try {
|
|
3192
|
-
const res = await axios.post(url, {
|
|
3193
|
-
jsonrpc: "2.0",
|
|
3194
|
-
method: "eth_gasPrice",
|
|
3195
|
-
params: [],
|
|
3196
|
-
id: 1
|
|
3197
|
-
}, { timeout: 5e3 });
|
|
3198
|
-
const gasWei = parseInt(res.data.result, 16);
|
|
3199
|
-
results[chain] = {
|
|
3200
|
-
gasPriceGwei: (gasWei / 1e9).toFixed(2),
|
|
3201
|
-
gasPriceWei: gasWei
|
|
3202
|
-
};
|
|
3203
|
-
} catch {
|
|
3204
|
-
results[chain] = { error: "Unavailable" };
|
|
3205
|
-
}
|
|
3206
|
-
}
|
|
3207
|
-
return ok(JSON.stringify(results, null, 2));
|
|
380
|
+
const res = await api().get("/api/gas", {
|
|
381
|
+
params: chainId ? { chain: chainId } : {}
|
|
382
|
+
});
|
|
383
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3208
384
|
} catch (error) {
|
|
3209
|
-
|
|
385
|
+
const msg = error.response?.data?.error || error.message;
|
|
386
|
+
return err(`Gas price fetch failed: ${msg}`);
|
|
3210
387
|
}
|
|
3211
388
|
};
|
|
3212
389
|
getTokenInfo = async (args, ctx) => {
|
|
3213
390
|
const { tokenAddress, chainId } = args;
|
|
3214
391
|
try {
|
|
3215
|
-
const
|
|
3216
|
-
|
|
3217
|
-
const platformMap = {
|
|
3218
|
-
ethereum: "ethereum",
|
|
3219
|
-
base: "base",
|
|
3220
|
-
arbitrum: "arbitrum-ethereum",
|
|
3221
|
-
optimism: "optimistic-ethereum",
|
|
3222
|
-
polygon: "polygon-pos"
|
|
3223
|
-
};
|
|
3224
|
-
const platform = platformMap[chainId];
|
|
3225
|
-
if (platform) {
|
|
3226
|
-
const res = await axios.get(`${coingeckoUrl}/coins/${platform}/contract/${tokenAddress}`, {
|
|
3227
|
-
timeout: 1e4,
|
|
3228
|
-
headers: { "Accept": "application/json" }
|
|
3229
|
-
});
|
|
3230
|
-
const d = res.data;
|
|
3231
|
-
return ok(JSON.stringify({
|
|
3232
|
-
name: d.name,
|
|
3233
|
-
symbol: d.symbol,
|
|
3234
|
-
marketCapRank: d.market_cap_rank,
|
|
3235
|
-
currentPrice: d.market_data?.current_price?.usd || null,
|
|
3236
|
-
marketCap: d.market_data?.market_cap?.usd || null,
|
|
3237
|
-
totalVolume: d.market_data?.total_volume?.usd || null,
|
|
3238
|
-
priceChange24h: d.market_data?.price_change_percentage_24h || null,
|
|
3239
|
-
description: d.description?.en?.substring(0, 500) || ""
|
|
3240
|
-
}, null, 2));
|
|
3241
|
-
}
|
|
3242
|
-
const dsRes = await axios.get(`https://api.dexscreener.com/latest/dex/tokens/${tokenAddress}`, {
|
|
3243
|
-
timeout: 5e3
|
|
392
|
+
const res = await api().get("/api/market/coins", {
|
|
393
|
+
params: { address: tokenAddress, chainId }
|
|
3244
394
|
});
|
|
3245
|
-
return ok(JSON.stringify(
|
|
395
|
+
return ok(JSON.stringify(res.data, null, 2));
|
|
3246
396
|
} catch (error) {
|
|
3247
|
-
|
|
397
|
+
const msg = error.response?.data?.error || error.message;
|
|
398
|
+
return err(`Token info fetch failed: ${msg}`);
|
|
3248
399
|
}
|
|
3249
400
|
};
|
|
3250
401
|
TOOL_HANDLERS = {
|
|
@@ -3323,8 +474,8 @@ async function validateWithFirestore(rawKey) {
|
|
|
3323
474
|
}
|
|
3324
475
|
async function validateViaHttp(rawKey) {
|
|
3325
476
|
const API_URL = process.env.FUNDTRACER_API_URL || "https://api.fundtracer.xyz";
|
|
3326
|
-
const { default:
|
|
3327
|
-
const res = await
|
|
477
|
+
const { default: fetch } = await import("node-fetch");
|
|
478
|
+
const res = await fetch(`${API_URL}/api/user/mcp-validate`, {
|
|
3328
479
|
method: "POST",
|
|
3329
480
|
headers: {
|
|
3330
481
|
"Content-Type": "application/json",
|
|
@@ -3387,7 +538,7 @@ async function main() {
|
|
|
3387
538
|
console.error("[MCP] Firebase not available \u2014 key validation will fail. Set Firebase credentials in env.");
|
|
3388
539
|
}
|
|
3389
540
|
const { ALL_MCP_TOOLS: ALL_MCP_TOOLS2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
3390
|
-
const { TOOL_HANDLERS: TOOL_HANDLERS2 } = await Promise.resolve().then(() => (
|
|
541
|
+
const { TOOL_HANDLERS: TOOL_HANDLERS2 } = await Promise.resolve().then(() => (init_api_handlers(), api_handlers_exports));
|
|
3391
542
|
const { validateMcpApiKey: validateMcpApiKey2 } = await Promise.resolve().then(() => (init_mcpAuth(), mcpAuth_exports));
|
|
3392
543
|
const server = new McpServer({
|
|
3393
544
|
name: "FundTracer MCP",
|