@fundtracer/mcp 1.0.0 → 1.0.2

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