@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.
Files changed (2) hide show
  1. package/fundtracer-mcp.js +99 -2948
  2. 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/services/SolanaFundingTreeService.ts
223
- var SolanaFundingTreeService_exports = {};
224
- __export(SolanaFundingTreeService_exports, {
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 buildApiKeyConfig() {
2951
- const alchemyKeys = process.env.ALCHEMY_API_KEYS?.split(",") || [];
2952
- return {
2953
- alchemy: process.env.DEFAULT_ALCHEMY_API_KEY || alchemyKeys[0] || "",
2954
- moralis: process.env.MORALIS_API_KEY || "",
2955
- etherscan: process.env.ETHERSCAN_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || "",
2956
- lineascan: process.env.LINEASCAN_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || "",
2957
- arbiscan: process.env.ARBISCAN_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || "",
2958
- basescan: process.env.BASESCAN_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || "",
2959
- optimism: process.env.OPTIMISM_API_KEY || process.env.DEFAULT_ETHERSCAN_API_KEY || "",
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 init_handlers = __esm({
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 { WalletAnalyzer } = await import("fundtracer-core");
2992
- const analyzer = new WalletAnalyzer(buildApiKeyConfig(), (progress) => {
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
- balanceEth: result.wallet.balanceInEth,
3002
- txCount: result.wallet.txCount,
3003
- isContract: result.wallet.isContract,
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
- return err(`Wallet analysis failed: ${error.message}`);
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 { WalletAnalyzer } = await import("fundtracer-core");
3026
- const analyzer = new WalletAnalyzer(buildApiKeyConfig());
3027
- const treeConfig = { maxDepth, direction };
3028
- if (chainId === "solana") {
3029
- const { SolanaFundingTreeService: SolanaFundingTreeService2 } = await Promise.resolve().then(() => (init_SolanaFundingTreeService(), SolanaFundingTreeService_exports));
3030
- const heliusKey = process.env.HELIUS_KEY_1 || process.env.DEFAULT_ALCHEMY_API_KEY || "";
3031
- const svc = new SolanaFundingTreeService2(heliusKey.startsWith("http") ? process.env.DEFAULT_ALCHEMY_API_KEY || "" : heliusKey);
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
- return err(`Fund tracing failed: ${error.message}`);
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 { WalletAnalyzer } = await import("fundtracer-core");
3050
- const analyzer = new WalletAnalyzer(buildApiKeyConfig());
3051
- const result = await analyzer.compareWallets(addrList, chainId);
3052
- return ok(JSON.stringify({
3053
- wallets: addrList,
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
- return err(`Wallet comparison failed: ${error.message}`);
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 { WalletAnalyzer } = await import("fundtracer-core");
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
- totalInteractors: result.totalInteractors,
3078
- riskScore: result.riskScore,
3079
- sharedFundingGroups: result.sharedFundingGroups.slice(0, 20),
3080
- suspiciousPatterns: result.suspiciousPatterns
3081
- }, null, 2));
299
+ maxInteractors
300
+ });
301
+ return ok(JSON.stringify(res.data, null, 2));
3082
302
  } catch (error) {
3083
- return err(`Contract analysis failed: ${error.message}`);
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 { SybilAnalyzer } = await import("fundtracer-core");
3092
- const alchemyKey = process.env.DEFAULT_ALCHEMY_API_KEY || "";
3093
- const sybilConfig = buildSybilConfig();
3094
- const analyzer = new SybilAnalyzer(chainId, sybilConfig);
3095
- const result = await analyzer.analyzeAddresses(addrList, { minClusterSize: 2 });
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
- return err(`Sybil detection failed: ${error.message}`);
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
- if (chainId === "solana") {
3118
- const { solanaPortfolioService: solanaPortfolioService2 } = await Promise.resolve().then(() => (init_SolanaPortfolioService(), SolanaPortfolioService_exports));
3119
- const portfolio = await solanaPortfolioService2.getPortfolio(address);
3120
- return ok(JSON.stringify(portfolio, null, 2));
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
- return err(`Portfolio fetch failed: ${error.message}`);
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 { WalletAnalyzer } = await import("fundtracer-core");
3135
- const analyzer = new WalletAnalyzer(buildApiKeyConfig());
3136
- const result = await analyzer.analyze(address, chainId, {
3137
- transactionLimit: limit,
3138
- skipFundingTree: true
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: result.transactions.slice(0, limit).map((tx) => ({
3144
- hash: tx.hash,
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
- return err(`Transaction fetch failed: ${error.message}`);
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 entity = EntityService2.lookupEntity(chain, query);
3167
- if (entity) return ok(JSON.stringify(entity, null, 2));
3168
- return ok(JSON.stringify({ address: query, label: "Unknown address", chain }, null, 2));
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 { searchEntities: searchEntities2 } = await Promise.resolve().then(() => (init_entities(), entities_exports));
3171
- const results = searchEntities2(query);
3172
- return ok(JSON.stringify({ query, results: results.length > 0 ? results.slice(0, 20) : "No entities found" }, null, 2));
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
- return err(`Entity lookup failed: ${error.message}`);
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 { default: axios } = await import("axios");
3180
- const alchemyKey = process.env.DEFAULT_ALCHEMY_API_KEY;
3181
- if (!alchemyKey) return err("Alchemy API key not configured");
3182
- const chains = {
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
- return err(`Gas price fetch failed: ${error.message}`);
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 { default: axios } = await import("axios");
3216
- const coingeckoUrl = "https://api.coingecko.com/api/v3";
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(dsRes.data?.pairs?.slice(0, 5) || { note: "No data found" }, null, 2));
395
+ return ok(JSON.stringify(res.data, null, 2));
3246
396
  } catch (error) {
3247
- return err(`Token info fetch failed: ${error.message}`);
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: fetch5 } = await import("node-fetch");
3327
- const res = await fetch5(`${API_URL}/api/user/mcp-validate`, {
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(() => (init_handlers(), handlers_exports));
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",