solana-privacy-scanner-core 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -276,23 +276,32 @@ var RPCClient = class {
276
276
  async function collectWalletData(client, address, options = {}) {
277
277
  const maxSignatures = options.maxSignatures ?? 100;
278
278
  const includeTokenAccounts = options.includeTokenAccounts ?? true;
279
- const signatures = await client.getSignaturesForAddress(address, {
280
- limit: maxSignatures
281
- });
279
+ let signatures = [];
280
+ try {
281
+ signatures = await client.getSignaturesForAddress(address, {
282
+ limit: maxSignatures
283
+ });
284
+ } catch (error) {
285
+ console.warn(`Failed to fetch signatures for ${address}:`, error);
286
+ }
282
287
  const transactions = [];
283
288
  const BATCH_SIZE = 10;
284
289
  for (let i = 0; i < signatures.length; i += BATCH_SIZE) {
285
290
  const batch = signatures.slice(i, i + BATCH_SIZE);
286
291
  const batchSignatures = batch.map((sig) => sig.signature);
287
- const txs = await client.getTransactions(batchSignatures, {
288
- maxSupportedTransactionVersion: 0
289
- });
290
- for (let j = 0; j < batch.length; j++) {
291
- transactions.push({
292
- signature: batch[j].signature,
293
- transaction: txs[j],
294
- blockTime: batch[j].blockTime
292
+ try {
293
+ const txs = await client.getTransactions(batchSignatures, {
294
+ maxSupportedTransactionVersion: 0
295
295
  });
296
+ for (let j = 0; j < batch.length; j++) {
297
+ transactions.push({
298
+ signature: batch[j].signature,
299
+ transaction: txs[j],
300
+ blockTime: batch[j].blockTime
301
+ });
302
+ }
303
+ } catch (error) {
304
+ console.warn(`Failed to fetch transaction batch for ${address}:`, error);
296
305
  }
297
306
  }
298
307
  let tokenAccounts = [];
@@ -312,9 +321,14 @@ async function collectWalletData(client, address, options = {}) {
312
321
  };
313
322
  }
314
323
  async function collectTransactionData(client, signature) {
315
- const transaction = await client.getTransaction(signature, {
316
- maxSupportedTransactionVersion: 0
317
- });
324
+ let transaction = null;
325
+ try {
326
+ transaction = await client.getTransaction(signature, {
327
+ maxSupportedTransactionVersion: 0
328
+ });
329
+ } catch (error) {
330
+ console.warn(`Failed to fetch transaction ${signature}:`, error);
331
+ }
318
332
  return {
319
333
  signature,
320
334
  transaction,
@@ -585,7 +599,8 @@ function calculateTimeRange(transactions) {
585
599
  function normalizeWalletData(rawData, labelProvider) {
586
600
  const allTransfers = [];
587
601
  const allInstructions = [];
588
- for (const rawTx of rawData.transactions) {
602
+ const transactions = rawData.transactions || [];
603
+ for (const rawTx of transactions) {
589
604
  if (!rawTx.transaction) continue;
590
605
  try {
591
606
  const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);
@@ -600,7 +615,7 @@ function normalizeWalletData(rawData, labelProvider) {
600
615
  }
601
616
  const counterparties = extractCounterparties(allTransfers, rawData.address);
602
617
  const labels = labelProvider ? labelProvider.lookupMany(Array.from(counterparties)) : /* @__PURE__ */ new Map();
603
- const timeRange = calculateTimeRange(rawData.transactions);
618
+ const timeRange = calculateTimeRange(transactions);
604
619
  const tokenAccounts = rawData.tokenAccounts.map((ta) => {
605
620
  try {
606
621
  return {
@@ -621,7 +636,7 @@ function normalizeWalletData(rawData, labelProvider) {
621
636
  labels: /* @__PURE__ */ new Map(),
622
637
  tokenAccounts,
623
638
  timeRange,
624
- transactionCount: rawData.transactions.length
639
+ transactionCount: transactions.length
625
640
  };
626
641
  }
627
642
  function normalizeTransactionData(rawData, labelProvider) {
@@ -655,17 +670,18 @@ function normalizeTransactionData(rawData, labelProvider) {
655
670
  labels: /* @__PURE__ */ new Map(),
656
671
  tokenAccounts: [],
657
672
  timeRange: {
658
- earliest: rawData.blockTime,
659
- latest: rawData.blockTime
673
+ earliest: rawData.transaction ? rawData.blockTime : null,
674
+ latest: rawData.transaction ? rawData.blockTime : null
660
675
  },
661
- transactionCount: 1
676
+ transactionCount: rawData.transaction ? 1 : 0
662
677
  };
663
678
  }
664
679
  function normalizeProgramData(rawData, labelProvider) {
665
680
  const allTransfers = [];
666
681
  const allInstructions = [];
667
682
  const counterparties = /* @__PURE__ */ new Set();
668
- for (const rawTx of rawData.relatedTransactions) {
683
+ const transactions = rawData.relatedTransactions || [];
684
+ for (const rawTx of transactions) {
669
685
  if (!rawTx.transaction) continue;
670
686
  try {
671
687
  const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);
@@ -685,7 +701,7 @@ function normalizeProgramData(rawData, labelProvider) {
685
701
  continue;
686
702
  }
687
703
  }
688
- const timeRange = calculateTimeRange(rawData.relatedTransactions);
704
+ const timeRange = calculateTimeRange(transactions);
689
705
  const labels = labelProvider ? labelProvider.lookupMany(Array.from(counterparties)) : /* @__PURE__ */ new Map();
690
706
  return {
691
707
  target: rawData.programId,
@@ -696,7 +712,7 @@ function normalizeProgramData(rawData, labelProvider) {
696
712
  labels: /* @__PURE__ */ new Map(),
697
713
  tokenAccounts: [],
698
714
  timeRange,
699
- transactionCount: rawData.relatedTransactions.length
715
+ transactionCount: transactions.length
700
716
  };
701
717
  }
702
718
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts", "../src/rpc/client.ts", "../src/collectors/index.ts", "../src/normalizer/index.ts", "../src/heuristics/counterparty-reuse.ts", "../src/heuristics/amount-reuse.ts", "../src/heuristics/timing-patterns.ts", "../src/heuristics/known-entity.ts", "../src/heuristics/balance-traceability.ts", "../src/scanner/index.ts", "../src/labels/provider.ts"],
4
- "sourcesContent": ["// Export all types\nexport * from './types/index.js';\n\n// Export RPC client\nexport * from './rpc/index.js';\n\n// Export data collectors\nexport * from './collectors/index.js';\n\n// Export normalizer\nexport * from './normalizer/index.js';\n\n// Export heuristics\nexport * from './heuristics/index.js';\n\n// Export scanner (report generation)\nexport * from './scanner/index.js';\n\n// Export label provider\nexport * from './labels/index.js';\n\n// Export version\nexport const VERSION = '0.1.0';\n", "import { Connection, ConnectionConfig } from '@solana/web3.js';\n\n/**\n * Configuration for the RPC client\n */\nexport interface RPCClientConfig {\n /**\n * RPC endpoint URL (Helius, QuickNode, or any Solana-compatible RPC)\n */\n rpcUrl: string;\n\n /**\n * Maximum number of retries for failed requests\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay for exponential backoff (ms)\n * @default 1000\n */\n retryDelay?: number;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Maximum number of concurrent requests\n * @default 10\n */\n maxConcurrency?: number;\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Rate limiter to control concurrent requests\n */\nclass RateLimiter {\n private activeRequests = 0;\n private queue: Array<() => void> = [];\n\n constructor(private maxConcurrency: number) {}\n\n async acquire(): Promise<void> {\n if (this.activeRequests < this.maxConcurrency) {\n this.activeRequests++;\n return;\n }\n\n return new Promise((resolve) => {\n this.queue.push(() => {\n this.activeRequests++;\n resolve();\n });\n });\n }\n\n release(): void {\n this.activeRequests--;\n const next = this.queue.shift();\n if (next) {\n next();\n }\n }\n\n getActiveCount(): number {\n return this.activeRequests;\n }\n\n getQueueLength(): number {\n return this.queue.length;\n }\n}\n\n/**\n * Sleep utility for retry delays\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * RPC client wrapper with rate limiting and retry logic\n * \n * This class wraps the Solana Connection and provides:\n * - Automatic retries with exponential backoff\n * - Rate limiting for concurrent requests\n * - Centralized error handling\n * - No CLI or UI logic\n */\nexport class RPCClient {\n private connection: Connection;\n private config: Required<RPCClientConfig>;\n private rateLimiter: RateLimiter;\n\n constructor(configOrUrl: RPCClientConfig | string) {\n // Handle both string URL and config object\n const config: RPCClientConfig = typeof configOrUrl === 'string' \n ? { rpcUrl: configOrUrl }\n : configOrUrl;\n \n // Trim the RPC URL to handle trailing/leading whitespace\n const rpcUrl = config.rpcUrl.trim();\n \n this.config = {\n maxRetries: config.maxRetries ?? 3,\n retryDelay: config.retryDelay ?? 1000,\n timeout: config.timeout ?? 30000,\n maxConcurrency: config.maxConcurrency ?? 10,\n debug: config.debug ?? false,\n rpcUrl,\n };\n\n const connectionConfig: ConnectionConfig = {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: this.config.timeout,\n };\n\n this.connection = new Connection(this.config.rpcUrl, connectionConfig);\n this.rateLimiter = new RateLimiter(this.config.maxConcurrency);\n\n if (this.config.debug) {\n console.log(`[RPCClient] Initialized with URL: ${this.config.rpcUrl}`);\n console.log(`[RPCClient] Max concurrency: ${this.config.maxConcurrency}`);\n console.log(`[RPCClient] Max retries: ${this.config.maxRetries}`);\n }\n }\n\n /**\n * Execute an RPC call with retry logic and rate limiting\n */\n private async executeWithRetry<T>(\n operation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n await this.rateLimiter.acquire();\n\n let lastError: Error | null = null;\n \n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n if (this.config.debug && attempt > 0) {\n console.log(`[RPCClient] Retry attempt ${attempt} for ${operationName}`);\n }\n\n const result = await operation();\n this.rateLimiter.release();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n if (this.config.debug) {\n console.error(\n `[RPCClient] Error in ${operationName} (attempt ${attempt + 1}/${this.config.maxRetries + 1}):`,\n error\n );\n }\n\n // Don't retry on last attempt\n if (attempt < this.config.maxRetries) {\n // Exponential backoff: delay * 2^attempt\n const delay = this.config.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n this.rateLimiter.release();\n throw new Error(\n `RPC operation ${operationName} failed after ${this.config.maxRetries + 1} attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Get the underlying Solana Connection\n * Use this sparingly - prefer the wrapped methods for automatic retry/rate limiting\n */\n getConnection(): Connection {\n return this.connection;\n }\n\n /**\n * Get current rate limiter stats\n */\n getStats(): { activeRequests: number; queueLength: number } {\n return {\n activeRequests: this.rateLimiter.getActiveCount(),\n queueLength: this.rateLimiter.getQueueLength(),\n };\n }\n\n /**\n * Get signatures for an address with retry and rate limiting\n */\n async getSignaturesForAddress(\n address: string,\n options?: {\n limit?: number;\n before?: string;\n until?: string;\n }\n ) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getSignaturesForAddress(\n new PublicKey(address),\n options\n );\n },\n `getSignaturesForAddress(${address})`\n );\n }\n\n /**\n * Get transaction details with retry and rate limiting\n */\n async getTransaction(signature: string, options?: { maxSupportedTransactionVersion?: number }) {\n return this.executeWithRetry(\n async () => {\n return this.connection.getTransaction(signature, {\n maxSupportedTransactionVersion: options?.maxSupportedTransactionVersion ?? 0,\n });\n },\n `getTransaction(${signature})`\n );\n }\n\n /**\n * Get multiple transactions in parallel (respects rate limiting)\n */\n async getTransactions(signatures: string[], options?: { maxSupportedTransactionVersion?: number }): Promise<Array<any>> {\n const promises = signatures.map((sig) => this.getTransaction(sig, options));\n return Promise.all(promises);\n }\n\n /**\n * Get token accounts by owner with retry and rate limiting\n */\n async getTokenAccountsByOwner(ownerAddress: string, mintAddress?: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const owner = new PublicKey(ownerAddress);\n \n // SPL Token Program ID\n const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');\n \n if (mintAddress) {\n const mint = new PublicKey(mintAddress);\n return this.connection.getTokenAccountsByOwner(owner, { mint });\n } else {\n return this.connection.getTokenAccountsByOwner(owner, {\n programId: TOKEN_PROGRAM_ID,\n });\n }\n },\n `getTokenAccountsByOwner(${ownerAddress})`\n );\n }\n\n /**\n * Get program accounts with retry and rate limiting\n */\n async getProgramAccounts(programId: string, config?: any) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getProgramAccounts(new PublicKey(programId), config);\n },\n `getProgramAccounts(${programId})`\n );\n }\n\n /**\n * Get account info with retry and rate limiting\n */\n async getAccountInfo(address: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getAccountInfo(new PublicKey(address));\n },\n `getAccountInfo(${address})`\n );\n }\n\n /**\n * Get multiple account infos in parallel (respects rate limiting)\n */\n async getMultipleAccountsInfo(addresses: string[]) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const pubkeys = addresses.map((addr) => new PublicKey(addr));\n return this.connection.getMultipleAccountsInfo(pubkeys);\n },\n `getMultipleAccountsInfo(${addresses.length} addresses)`\n );\n }\n\n /**\n * Check if the RPC connection is healthy\n */\n async healthCheck(): Promise<boolean> {\n try {\n const version = await this.executeWithRetry(\n () => this.connection.getVersion(),\n 'healthCheck'\n );\n return !!version;\n } catch {\n return false;\n }\n }\n}\n", "import type { RPCClient } from '../rpc/client.js';\nimport type { \n ConfirmedSignatureInfo,\n ParsedTransactionWithMeta,\n TokenAccountBalancePair,\n} from '@solana/web3.js';\n\n/**\n * Raw transaction data fetched from RPC\n */\nexport interface RawTransaction {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw wallet data collection result\n */\nexport interface RawWalletData {\n address: string;\n signatures: ConfirmedSignatureInfo[];\n transactions: RawTransaction[];\n tokenAccounts: TokenAccountBalancePair[];\n}\n\n/**\n * Raw transaction scan result\n */\nexport interface RawTransactionData {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw program data collection result\n */\nexport interface RawProgramData {\n programId: string;\n accounts: Array<{\n pubkey: string;\n account: any;\n }>;\n relatedTransactions: RawTransaction[];\n}\n\n/**\n * Options for wallet data collection\n */\nexport interface WalletCollectionOptions {\n /**\n * Maximum number of signatures to fetch\n * @default 100\n */\n maxSignatures?: number;\n\n /**\n * Fetch token accounts\n * @default true\n */\n includeTokenAccounts?: boolean;\n}\n\n/**\n * Options for program data collection\n */\nexport interface ProgramCollectionOptions {\n /**\n * Maximum number of accounts to fetch\n * @default 100\n */\n maxAccounts?: number;\n\n /**\n * Maximum number of related transactions to fetch\n * @default 50\n */\n maxTransactions?: number;\n}\n\n/**\n * Collects raw wallet data from Solana RPC\n */\nexport async function collectWalletData(\n client: RPCClient,\n address: string,\n options: WalletCollectionOptions = {}\n): Promise<RawWalletData> {\n const maxSignatures = options.maxSignatures ?? 100;\n const includeTokenAccounts = options.includeTokenAccounts ?? true;\n\n // Fetch recent signatures (bounded)\n const signatures = await client.getSignaturesForAddress(address, {\n limit: maxSignatures,\n });\n\n // Fetch transactions for all signatures\n const transactions: RawTransaction[] = [];\n \n // Process in batches to avoid overwhelming the RPC\n const BATCH_SIZE = 10;\n for (let i = 0; i < signatures.length; i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n transactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n }\n\n // Fetch token accounts\n let tokenAccounts: TokenAccountBalancePair[] = [];\n if (includeTokenAccounts) {\n try {\n const response = await client.getTokenAccountsByOwner(address);\n tokenAccounts = response.value;\n } catch (error) {\n // Token accounts might not exist or query might fail - that's okay\n console.warn(`Failed to fetch token accounts for ${address}:`, error);\n }\n }\n\n return {\n address,\n signatures,\n transactions,\n tokenAccounts,\n };\n}\n\n/**\n * Collects raw transaction data from Solana RPC\n */\nexport async function collectTransactionData(\n client: RPCClient,\n signature: string\n): Promise<RawTransactionData> {\n // Fetch full transaction with metadata\n const transaction = await client.getTransaction(signature, {\n maxSupportedTransactionVersion: 0,\n });\n\n return {\n signature,\n transaction: transaction as ParsedTransactionWithMeta | null,\n blockTime: transaction?.blockTime ?? null,\n };\n}\n\n/**\n * Collects raw program data from Solana RPC\n */\nexport async function collectProgramData(\n client: RPCClient,\n programId: string,\n options: ProgramCollectionOptions = {}\n): Promise<RawProgramData> {\n const maxAccounts = options.maxAccounts ?? 100;\n const maxTransactions = options.maxTransactions ?? 50;\n\n // Fetch program accounts (limited)\n let accounts: Array<{ pubkey: string; account: any }> = [];\n try {\n const response = await client.getProgramAccounts(programId, {\n encoding: 'jsonParsed',\n dataSlice: { offset: 0, length: 0 }, // Don't fetch full account data\n });\n\n accounts = response.slice(0, maxAccounts).map((acc) => ({\n pubkey: acc.pubkey.toString(),\n account: acc.account,\n }));\n } catch (error) {\n console.warn(`Failed to fetch program accounts for ${programId}:`, error);\n }\n\n // Fetch recent signatures for the program\n let signatures: ConfirmedSignatureInfo[] = [];\n try {\n signatures = await client.getSignaturesForAddress(programId, {\n limit: maxTransactions,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for program ${programId}:`, error);\n }\n\n // Fetch transactions for those signatures\n const relatedTransactions: RawTransaction[] = [];\n const BATCH_SIZE = 10;\n \n for (let i = 0; i < Math.min(signatures.length, maxTransactions); i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n relatedTransactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for program ${programId}:`, error);\n }\n }\n\n return {\n programId,\n accounts,\n relatedTransactions,\n };\n}\n", "import type {\n Transfer,\n NormalizedInstruction,\n InstructionCategory,\n ScanContext,\n LabelProvider,\n} from '../types/index.js';\nimport type {\n RawWalletData,\n RawTransactionData,\n RawProgramData,\n} from '../collectors/index.js';\nimport type { ParsedTransactionWithMeta, PartiallyDecodedInstruction, ParsedInstruction } from '@solana/web3.js';\n\n/**\n * Known program IDs for instruction categorization\n */\nconst PROGRAM_IDS = {\n SYSTEM: '11111111111111111111111111111111',\n TOKEN: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',\n ASSOCIATED_TOKEN: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',\n STAKE: 'Stake11111111111111111111111111111111111111',\n VOTE: 'Vote111111111111111111111111111111111111111',\n MEMO: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',\n MEMO_V1: 'Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo',\n};\n\n/**\n * Extract SOL transfers from a parsed transaction\n */\nfunction extractSOLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.transaction) {\n return transfers;\n }\n\n const preBalances = tx.meta.preBalances;\n const postBalances = tx.meta.postBalances;\n const accountKeys = tx.transaction.message.accountKeys;\n\n // Defensive checks\n if (!accountKeys || !Array.isArray(accountKeys) || accountKeys.length === 0) {\n return transfers;\n }\n\n if (!preBalances || !postBalances) {\n return transfers;\n }\n\n // Compare pre and post balances to find transfers\n for (let i = 0; i < accountKeys.length; i++) {\n const pre = preBalances[i];\n const post = postBalances[i];\n \n // Skip if balances are undefined\n if (pre === undefined || post === undefined) {\n continue;\n }\n \n const diff = post - pre;\n\n // Skip if no change or if it's fee-related (usually account 0)\n if (diff === 0) continue;\n\n const account = accountKeys[i];\n if (!account) continue;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) continue;\n\n // If balance increased, this account received SOL\n if (diff > 0) {\n // Find who sent it (simple heuristic: first account with decreased balance)\n for (let j = 0; j < accountKeys.length; j++) {\n const preSender = preBalances[j];\n const postSender = postBalances[j];\n \n if (preSender === undefined || postSender === undefined) {\n continue;\n }\n \n if (postSender < preSender) {\n const sender = accountKeys[j];\n if (!sender) continue;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) continue;\n \n transfers.push({\n from: senderAddress,\n to: address,\n amount: diff / 1e9, // Convert lamports to SOL\n token: undefined,\n signature,\n blockTime: tx.blockTime,\n });\n break;\n }\n }\n }\n }\n\n return transfers;\n}\n\n/**\n * Extract SPL token transfers from a parsed transaction\n */\nfunction extractSPLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.meta.postTokenBalances || !tx.meta.preTokenBalances) {\n return transfers;\n }\n\n const preTokenBalances = tx.meta.preTokenBalances;\n const postTokenBalances = tx.meta.postTokenBalances;\n\n // Create a map of account index to token balance changes\n const balanceChanges = new Map<number, { mint: string; change: number; decimals: number }>();\n\n // Calculate changes\n for (const post of postTokenBalances) {\n const pre = preTokenBalances.find(\n (p) => p.accountIndex === post.accountIndex && p.mint === post.mint\n );\n\n const preAmount = pre?.uiTokenAmount.uiAmount ?? 0;\n const postAmount = post.uiTokenAmount.uiAmount ?? 0;\n const change = postAmount - preAmount;\n\n if (change !== 0) {\n balanceChanges.set(post.accountIndex, {\n mint: post.mint,\n change,\n decimals: post.uiTokenAmount.decimals,\n });\n }\n }\n\n // Match senders and receivers\n const accountKeys = tx.transaction.message.accountKeys;\n \n if (!accountKeys || !Array.isArray(accountKeys)) {\n return transfers;\n }\n \n balanceChanges.forEach((info, accountIndex) => {\n if (accountIndex >= accountKeys.length) {\n return; // Skip if index out of bounds\n }\n \n const account = accountKeys[accountIndex];\n if (!account) return;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) return;\n\n if (info.change > 0) {\n // This account received tokens - find sender\n balanceChanges.forEach((senderInfo, senderIndex) => {\n if (\n senderInfo.mint === info.mint &&\n senderInfo.change < 0 &&\n senderIndex !== accountIndex &&\n senderIndex < accountKeys.length\n ) {\n const sender = accountKeys[senderIndex];\n if (!sender) return;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) return;\n\n transfers.push({\n from: senderAddress,\n to: address,\n amount: info.change,\n token: info.mint,\n signature,\n blockTime: tx.blockTime,\n });\n }\n });\n }\n });\n\n return transfers;\n}\n\n/**\n * Categorize an instruction based on program ID and instruction type\n */\nfunction categorizeInstruction(\n instruction: ParsedInstruction | PartiallyDecodedInstruction\n): InstructionCategory {\n const programId = instruction.programId.toString();\n\n // System program instructions\n if (programId === PROGRAM_IDS.SYSTEM) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferWithSeed') {\n return 'transfer';\n }\n }\n return 'transfer';\n }\n\n // Token program instructions\n if (programId === PROGRAM_IDS.TOKEN || programId === PROGRAM_IDS.ASSOCIATED_TOKEN) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferChecked') {\n return 'transfer';\n }\n return 'token_operation';\n }\n return 'token_operation';\n }\n\n // Stake program\n if (programId === PROGRAM_IDS.STAKE) {\n return 'stake';\n }\n\n // Vote program\n if (programId === PROGRAM_IDS.VOTE) {\n return 'vote';\n }\n\n // Check for common swap programs (simplified heuristic)\n if (\n programId.includes('Swap') ||\n programId.includes('swap') ||\n programId === 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' || // Jupiter\n programId === 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc' // Orca Whirlpool\n ) {\n return 'swap';\n }\n\n // Default to program interaction\n return 'program_interaction';\n}\n\n/**\n * Extract normalized instructions from a transaction\n */\nfunction extractInstructions(\n tx: ParsedTransactionWithMeta,\n signature: string\n): NormalizedInstruction[] {\n const instructions: NormalizedInstruction[] = [];\n\n if (!tx.transaction || !tx.transaction.message) {\n return instructions;\n }\n\n const message = tx.transaction.message;\n const allInstructions = message.instructions;\n\n // Defensive check\n if (!allInstructions || !Array.isArray(allInstructions)) {\n return instructions;\n }\n\n for (const instruction of allInstructions) {\n // Defensive check for programId\n if (!instruction || !instruction.programId) {\n continue;\n }\n \n const programId = instruction.programId.toString();\n const category = categorizeInstruction(instruction);\n\n let data: Record<string, unknown> | undefined;\n if ('parsed' in instruction) {\n data = instruction.parsed as Record<string, unknown>;\n }\n\n instructions.push({\n programId,\n category,\n signature,\n blockTime: tx.blockTime,\n data,\n });\n }\n\n return instructions;\n}\n\n/**\n * Extract unique counterparties from transfers\n */\nfunction extractCounterparties(transfers: Transfer[], targetAddress: string): Set<string> {\n const counterparties = new Set<string>();\n\n for (const transfer of transfers) {\n // Add the other party in the transfer\n if (transfer.from === targetAddress) {\n counterparties.add(transfer.to);\n } else if (transfer.to === targetAddress) {\n counterparties.add(transfer.from);\n }\n }\n\n return counterparties;\n}\n\n/**\n * Calculate time range from transactions\n */\nfunction calculateTimeRange(transactions: RawTransactionData[]): {\n earliest: number | null;\n latest: number | null;\n} {\n let earliest: number | null = null;\n let latest: number | null = null;\n\n for (const tx of transactions) {\n if (tx.blockTime) {\n if (earliest === null || tx.blockTime < earliest) {\n earliest = tx.blockTime;\n }\n if (latest === null || tx.blockTime > latest) {\n latest = tx.blockTime;\n }\n }\n }\n\n return { earliest, latest };\n}\n\n/**\n * Normalize wallet data into a ScanContext\n */\nexport function normalizeWalletData(\n rawData: RawWalletData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n\n // Process each transaction\n for (const rawTx of rawData.transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n } catch (error) {\n // Skip problematic transactions but continue processing\n console.warn(`Failed to normalize transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Extract counterparties\n const counterparties = extractCounterparties(allTransfers, rawData.address);\n\n // Look up labels for counterparties if provider is available\n const labels = labelProvider \n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n // Calculate time range\n const timeRange = calculateTimeRange(rawData.transactions);\n\n // Normalize token accounts\n const tokenAccounts = rawData.tokenAccounts.map((ta) => {\n try {\n return {\n mint: ta.account.data.parsed.info.mint,\n address: ta.pubkey.toString(),\n balance: ta.account.data.parsed.info.tokenAmount.uiAmount ?? 0,\n };\n } catch (error) {\n // Skip malformed token accounts\n return null;\n }\n }).filter((ta): ta is NonNullable<typeof ta> => ta !== null);\n\n return {\n target: rawData.address,\n targetType: 'wallet',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts,\n timeRange,\n transactionCount: rawData.transactions.length,\n };\n}\n\n/**\n * Normalize transaction data into a ScanContext\n */\nexport function normalizeTransactionData(\n rawData: RawTransactionData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n if (rawData.transaction) {\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawData.transaction, rawData.signature);\n const splTransfers = extractSPLTransfers(rawData.transaction, rawData.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawData.transaction, rawData.signature);\n allInstructions.push(...instructions);\n\n // Extract all unique addresses involved\n const accountKeys = rawData.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize transaction ${rawData.signature}:`, error);\n }\n }\n\n return {\n target: rawData.signature,\n targetType: 'transaction',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange: {\n earliest: rawData.blockTime,\n latest: rawData.blockTime,\n },\n transactionCount: 1,\n };\n}\n\n/**\n * Normalize program data into a ScanContext\n */\nexport function normalizeProgramData(\n rawData: RawProgramData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n // Process related transactions\n for (const rawTx of rawData.relatedTransactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n\n // Extract counterparties\n const accountKeys = rawTx.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize program transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Calculate time range\n const timeRange = calculateTimeRange(rawData.relatedTransactions);\n\n // Look up labels for counterparties\n const labels = labelProvider\n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n return {\n target: rawData.programId,\n targetType: 'program',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange,\n transactionCount: rawData.relatedTransactions.length,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if a wallet frequently interacts with the same counterparties\n * This can enable clustering and linking of addresses\n */\nexport function detectCounterpartyReuse(context: ScanContext): RiskSignal | null {\n // Only applicable to wallet scans\n if (context.targetType !== 'wallet') {\n return null;\n }\n\n if (context.counterparties.size === 0 || context.transfers.length === 0) {\n return null;\n }\n\n // Count interactions per counterparty\n const interactionCounts = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const counterparty = transfer.from === context.target ? transfer.to : transfer.from;\n if (counterparty === context.target) continue;\n \n interactionCounts.set(counterparty, (interactionCounts.get(counterparty) || 0) + 1);\n }\n\n // Find counterparties with multiple interactions\n const reusedCounterparties = Array.from(interactionCounts.entries())\n .filter(([_, count]) => count >= 3)\n .sort((a, b) => b[1] - a[1]);\n\n if (reusedCounterparties.length === 0) {\n return null;\n }\n\n // Calculate severity based on concentration\n const totalInteractions = context.transfers.length;\n const topCounterpartyInteractions = reusedCounterparties[0][1];\n const concentration = topCounterpartyInteractions / totalInteractions;\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if (concentration > 0.5 || reusedCounterparties.length >= 5) {\n severity = 'HIGH';\n } else if (concentration > 0.3 || reusedCounterparties.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = reusedCounterparties.slice(0, 5).map(([addr, count]) => ({\n type: 'address',\n description: `${count} interactions with ${addr.slice(0, 8)}...${addr.slice(-8)}`,\n data: { address: addr, interactionCount: count },\n }));\n\n return {\n id: 'counterparty-reuse',\n name: 'Counterparty Reuse',\n severity,\n reason: `Wallet repeatedly interacts with ${reusedCounterparties.length} address(es)`,\n impact: 'Repeated interactions with the same addresses can be used to cluster wallets and build transaction graphs, enabling surveillance of your activity patterns.',\n evidence,\n mitigation: 'Use different wallets for different counterparties, or use privacy-preserving protocols that obscure transaction graphs.',\n confidence: 0.9,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if the wallet sends or receives suspiciously similar amounts\n * Round numbers or repeated exact amounts can be used for fingerprinting\n */\nexport function detectAmountReuse(context: ScanContext): RiskSignal | null {\n if (context.transfers.length < 3) {\n return null;\n }\n\n // Group amounts (rounded to avoid floating point issues)\n const amountCounts = new Map<string, number>();\n const roundNumbers: number[] = [];\n\n for (const transfer of context.transfers) {\n // Check for round numbers (e.g., 1.0, 10.0, 100.0)\n if (transfer.amount > 0 && Number.isInteger(transfer.amount) && transfer.amount >= 1) {\n roundNumbers.push(transfer.amount);\n }\n\n // Count exact amount reuse\n const amountKey = `${transfer.amount.toFixed(9)}-${transfer.token || 'SOL'}`;\n amountCounts.set(amountKey, (amountCounts.get(amountKey) || 0) + 1);\n }\n\n // Find amounts used multiple times\n const reusedAmounts = Array.from(amountCounts.entries())\n .filter(([_, count]) => count >= 2)\n .sort((a, b) => b[1] - a[1]);\n\n const hasRoundNumbers = roundNumbers.length >= 2;\n const hasReusedAmounts = reusedAmounts.length >= 2;\n\n if (!hasRoundNumbers && !hasReusedAmounts) {\n return null;\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if ((hasRoundNumbers && roundNumbers.length >= 5) || reusedAmounts.length >= 5) {\n severity = 'HIGH';\n } else if ((hasRoundNumbers && roundNumbers.length >= 3) || reusedAmounts.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = [];\n\n if (hasRoundNumbers) {\n evidence.push({\n type: 'amount',\n description: `${roundNumbers.length} round-number transfers detected`,\n data: { roundNumbers: roundNumbers.slice(0, 5) },\n });\n }\n\n if (hasReusedAmounts) {\n const topReused = reusedAmounts.slice(0, 3);\n for (const [amountKey, count] of topReused) {\n const [amount, token] = amountKey.split('-');\n evidence.push({\n type: 'amount',\n description: `Amount ${amount} ${token} used ${count} times`,\n data: { amount: parseFloat(amount), token, count },\n });\n }\n }\n\n return {\n id: 'amount-reuse',\n name: 'Deterministic Amount Patterns',\n severity,\n reason: `Wallet uses ${hasRoundNumbers ? 'round numbers' : 'repeated amounts'} in transactions`,\n impact: 'Using the same amounts repeatedly or sending round numbers creates fingerprints that can be used to link transactions and identify patterns in your activity.',\n evidence,\n mitigation: 'Vary transaction amounts slightly, avoid round numbers, and consider using privacy protocols that obscure amounts.',\n confidence: hasRoundNumbers ? 0.85 : 0.75,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect transaction bursts and timing patterns\n * Concentrated activity creates temporal fingerprints\n */\nexport function detectTimingPatterns(context: ScanContext): RiskSignal | null {\n if (!context.timeRange.earliest || !context.timeRange.latest) {\n return null;\n }\n\n if (context.transactionCount < 3) {\n return null;\n }\n\n // Calculate time span in hours\n const timeSpanSeconds = context.timeRange.latest - context.timeRange.earliest;\n const timeSpanHours = timeSpanSeconds / 3600;\n\n if (timeSpanHours === 0) {\n return null;\n }\n\n // Calculate transaction rate (txs per hour)\n const txRate = context.transactionCount / timeSpanHours;\n\n // Detect burst activity (high tx rate in short time)\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n let isBurst = false;\n\n if (txRate > 10) {\n severity = 'HIGH';\n isBurst = true;\n } else if (txRate > 5) {\n severity = 'MEDIUM';\n isBurst = true;\n } else if (timeSpanHours < 1 && context.transactionCount >= 3) {\n severity = 'MEDIUM';\n isBurst = true;\n }\n\n if (!isBurst) {\n return null;\n }\n\n const evidence: Evidence[] = [\n {\n type: 'timing',\n description: `${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n data: {\n transactionCount: context.transactionCount,\n timeSpanHours: timeSpanHours.toFixed(2),\n transactionRate: txRate.toFixed(2),\n },\n },\n ];\n\n return {\n id: 'timing-correlation',\n name: 'Transaction Burst Pattern',\n severity,\n reason: `Concentrated activity: ${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n impact: 'Concentrated transaction activity creates timing fingerprints that can be used to correlate your transactions and link them to specific events or behaviors.',\n evidence,\n mitigation: 'Spread transactions over longer time periods, use scheduled transactions, or batch operations to reduce timing correlation.',\n confidence: 0.8,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect interactions with known entities (CEXs, bridges, etc.)\n * These entities can link on-chain and off-chain identities\n */\nexport function detectKnownEntityInteraction(context: ScanContext): RiskSignal | null {\n if (context.labels.size === 0) {\n return null;\n }\n\n const entityInteractions: Evidence[] = [];\n\n for (const [address, label] of context.labels.entries()) {\n // Count interactions with this entity\n let interactionCount = 0;\n const relatedTxs: string[] = [];\n\n for (const transfer of context.transfers) {\n if (transfer.from === address || transfer.to === address) {\n interactionCount++;\n if (relatedTxs.length < 3) {\n relatedTxs.push(transfer.signature);\n }\n }\n }\n\n if (interactionCount > 0) {\n entityInteractions.push({\n type: 'label',\n description: `${interactionCount} interaction(s) with ${label.name} (${label.type})`,\n data: {\n entityName: label.name,\n entityType: label.type,\n address,\n interactionCount,\n transactions: relatedTxs,\n },\n reference: address,\n });\n }\n }\n\n if (entityInteractions.length === 0) {\n return null;\n }\n\n // Severity based on entity type and interaction count\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n \n const hasExchangeInteraction = Array.from(context.labels.values())\n .some(label => label.type === 'exchange');\n \n if (hasExchangeInteraction) {\n severity = 'HIGH';\n } else if (entityInteractions.length >= 3) {\n severity = 'HIGH';\n }\n\n return {\n id: 'known-entity-interaction',\n name: 'Known Entity Interaction',\n severity,\n reason: `Wallet interacted with ${entityInteractions.length} known entit${entityInteractions.length === 1 ? 'y' : 'ies'}`,\n impact: 'Interactions with centralized exchanges, bridges, or other known entities can link your on-chain address to your real-world identity through KYC data, IP addresses, and off-chain records.',\n evidence: entityInteractions,\n mitigation: 'Use privacy-preserving bridges, avoid direct CEX interactions from privacy-sensitive wallets, or use intermediate wallets to break the link.',\n confidence: 0.95,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if wallet balances can be easily traced\n * Full balance transfers or predictable balance changes reduce privacy\n */\nexport function detectBalanceTraceability(context: ScanContext): RiskSignal | null {\n if (context.targetType !== 'wallet' || context.transfers.length < 2) {\n return null;\n }\n\n const fullBalanceTransfers: Evidence[] = [];\n const suspiciousPatterns: string[] = [];\n\n // Look for transfers that represent full balance movements\n // (This is simplified - real implementation would need balance tracking)\n \n // Check for transfers where send and receive amounts match closely\n const amountPairs = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const amountKey = transfer.amount.toFixed(6);\n amountPairs.set(amountKey, (amountPairs.get(amountKey) || 0) + 1);\n }\n\n // Find matching send/receive pairs\n const matchingPairs = Array.from(amountPairs.entries())\n .filter(([_, count]) => count >= 2);\n\n if (matchingPairs.length >= 2) {\n suspiciousPatterns.push('Multiple matching send/receive amounts detected');\n }\n\n // Check for sequential transfers of similar amounts\n for (let i = 0; i < context.transfers.length - 1; i++) {\n const current = context.transfers[i];\n const next = context.transfers[i + 1];\n \n if (current.blockTime && next.blockTime) {\n const timeDiff = Math.abs((next.blockTime - current.blockTime));\n \n // If similar amounts within 1 hour\n if (timeDiff < 3600 && Math.abs(current.amount - next.amount) < current.amount * 0.1) {\n suspiciousPatterns.push('Sequential transfers of similar amounts');\n break;\n }\n }\n }\n\n if (suspiciousPatterns.length === 0 && matchingPairs.length === 0) {\n return null;\n }\n\n const evidence: Evidence[] = [];\n\n if (matchingPairs.length > 0) {\n evidence.push({\n type: 'pattern',\n description: `${matchingPairs.length} matching send/receive amount pair(s)`,\n data: { matchingPairs: matchingPairs.length },\n });\n }\n\n for (const pattern of suspiciousPatterns) {\n evidence.push({\n type: 'pattern',\n description: pattern,\n data: {},\n });\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n if (matchingPairs.length >= 3 || suspiciousPatterns.length >= 2) {\n severity = 'HIGH';\n }\n\n return {\n id: 'balance-traceability',\n name: 'Balance Traceability',\n severity,\n reason: 'Wallet shows patterns that enable balance tracking',\n impact: 'Traceable balance movements allow observers to follow funds through the blockchain, linking your transactions and revealing your financial activity.',\n evidence,\n mitigation: 'Split large transfers into multiple smaller ones, introduce timing delays, or use privacy protocols that obscure amounts.',\n confidence: 0.7,\n };\n}\n", "import type { ScanContext, RiskSignal, RiskLevel, PrivacyReport } from '../types/index.js';\nimport {\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n} from '../heuristics/index.js';\n\n/**\n * Current report schema version\n */\nconst REPORT_VERSION = '1.0.0';\n\n/**\n * All available heuristic functions\n */\nconst HEURISTICS = [\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n];\n\n/**\n * Calculate overall risk level from individual signals\n * Uses deterministic scoring based on severity and count\n */\nfunction calculateOverallRisk(signals: RiskSignal[]): RiskLevel {\n if (signals.length === 0) {\n return 'LOW';\n }\n\n // Count signals by severity\n const highCount = signals.filter(s => s.severity === 'HIGH').length;\n const mediumCount = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowCount = signals.filter(s => s.severity === 'LOW').length;\n\n // Deterministic thresholds\n if (highCount >= 2 || (highCount >= 1 && mediumCount >= 2)) {\n return 'HIGH';\n }\n\n if (highCount >= 1 || mediumCount >= 2 || (mediumCount >= 1 && lowCount >= 2)) {\n return 'MEDIUM';\n }\n\n return 'LOW';\n}\n\n/**\n * Generate general mitigation recommendations based on detected signals\n */\nfunction generateMitigations(signals: RiskSignal[]): string[] {\n const mitigations = new Set<string>();\n\n if (signals.length === 0) {\n return ['Continue practicing good privacy hygiene to maintain low exposure.'];\n }\n\n // Add general recommendations\n mitigations.add('Consider using multiple wallets to compartmentalize different activities.');\n \n // Check for specific signal types and add relevant mitigations\n const signalIds = new Set(signals.map(s => s.id));\n\n if (signalIds.has('known-entity-interaction')) {\n mitigations.add('Avoid direct interactions between privacy-sensitive wallets and KYC services.');\n }\n\n if (signalIds.has('counterparty-reuse')) {\n mitigations.add('Use different addresses for different counterparties or contexts.');\n }\n\n if (signalIds.has('timing-correlation') || signalIds.has('balance-traceability')) {\n mitigations.add('Introduce timing delays and vary transaction patterns to reduce correlation.');\n }\n\n if (signalIds.has('amount-reuse')) {\n mitigations.add('Vary transaction amounts to avoid creating fingerprints.');\n }\n\n // Always add this general advice\n mitigations.add('Research and consider privacy-preserving protocols when available.');\n\n return Array.from(mitigations);\n}\n\n/**\n * Evaluate all heuristics against a scan context\n */\nexport function evaluateHeuristics(context: ScanContext): RiskSignal[] {\n const signals: RiskSignal[] = [];\n\n for (const heuristic of HEURISTICS) {\n try {\n const signal = heuristic(context);\n if (signal) {\n signals.push(signal);\n }\n } catch (error) {\n // Log but don't fail if a heuristic errors\n console.warn(`Heuristic evaluation failed:`, error);\n }\n }\n\n // Sort signals by severity (HIGH -> MEDIUM -> LOW) for deterministic ordering\n signals.sort((a, b) => {\n const severityOrder = { HIGH: 0, MEDIUM: 1, LOW: 2 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n\n return signals;\n}\n\n/**\n * Generate a complete privacy report from a scan context\n */\nexport function generateReport(context: ScanContext): PrivacyReport {\n // Evaluate all heuristics\n const signals = evaluateHeuristics(context);\n\n // Calculate overall risk\n const overallRisk = calculateOverallRisk(signals);\n\n // Count signals by severity\n const highRiskSignals = signals.filter(s => s.severity === 'HIGH').length;\n const mediumRiskSignals = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowRiskSignals = signals.filter(s => s.severity === 'LOW').length;\n\n // Generate mitigations\n const mitigations = generateMitigations(signals);\n\n // Extract known entities from context\n const knownEntities = Array.from(context.labels.values());\n\n return {\n version: REPORT_VERSION,\n timestamp: Date.now(),\n targetType: context.targetType,\n target: context.target,\n overallRisk,\n signals,\n summary: {\n totalSignals: signals.length,\n highRiskSignals,\n mediumRiskSignals,\n lowRiskSignals,\n transactionsAnalyzed: context.transactionCount,\n },\n mitigations,\n knownEntities,\n };\n}\n", "import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport type { Label, LabelProvider } from '../types/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Static JSON label provider\n * Loads labels from a curated JSON file\n */\nexport class StaticLabelProvider implements LabelProvider {\n private labels: Map<string, Label>;\n\n constructor(labelsPath?: string) {\n this.labels = new Map();\n this.loadLabels(labelsPath);\n }\n\n /**\n * Load labels from JSON file\n */\n private loadLabels(customPath?: string): void {\n try {\n const path = customPath || join(__dirname, 'known-addresses.json');\n const data = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(data);\n\n if (!parsed.labels || !Array.isArray(parsed.labels)) {\n console.warn('Invalid labels file format');\n return;\n }\n\n for (const label of parsed.labels) {\n if (label.address && label.name && label.type) {\n this.labels.set(label.address, {\n address: label.address,\n name: label.name,\n type: label.type,\n description: label.description,\n relatedAddresses: label.relatedAddresses,\n });\n }\n }\n\n console.debug(`Loaded ${this.labels.size} address labels`);\n } catch (error) {\n console.warn('Failed to load labels file:', error);\n }\n }\n\n /**\n * Look up a label for an address\n */\n lookup(address: string): Label | null {\n return this.labels.get(address) || null;\n }\n\n /**\n * Look up multiple addresses at once\n */\n lookupMany(addresses: string[]): Map<string, Label> {\n const results = new Map<string, Label>();\n\n for (const address of addresses) {\n const label = this.lookup(address);\n if (label) {\n results.set(address, label);\n }\n }\n\n return results;\n }\n\n /**\n * Get all loaded labels\n */\n getAllLabels(): Label[] {\n return Array.from(this.labels.values());\n }\n\n /**\n * Get count of loaded labels\n */\n getCount(): number {\n return this.labels.size;\n }\n}\n\n/**\n * Create a default label provider instance\n */\nexport function createDefaultLabelProvider(): StaticLabelProvider {\n return new StaticLabelProvider();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Export all types\nexport * from './types/index.js';\n\n// Export RPC client\nexport * from './rpc/index.js';\n\n// Export data collectors\nexport * from './collectors/index.js';\n\n// Export normalizer\nexport * from './normalizer/index.js';\n\n// Export heuristics\nexport * from './heuristics/index.js';\n\n// Export scanner (report generation)\nexport * from './scanner/index.js';\n\n// Export label provider\nexport * from './labels/index.js';\n\n// Export version\nexport const VERSION = '0.1.0';\n", "import { Connection, ConnectionConfig } from '@solana/web3.js';\n\n/**\n * Configuration for the RPC client\n */\nexport interface RPCClientConfig {\n /**\n * RPC endpoint URL (Helius, QuickNode, or any Solana-compatible RPC)\n */\n rpcUrl: string;\n\n /**\n * Maximum number of retries for failed requests\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay for exponential backoff (ms)\n * @default 1000\n */\n retryDelay?: number;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Maximum number of concurrent requests\n * @default 10\n */\n maxConcurrency?: number;\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Rate limiter to control concurrent requests\n */\nclass RateLimiter {\n private activeRequests = 0;\n private queue: Array<() => void> = [];\n\n constructor(private maxConcurrency: number) {}\n\n async acquire(): Promise<void> {\n if (this.activeRequests < this.maxConcurrency) {\n this.activeRequests++;\n return;\n }\n\n return new Promise((resolve) => {\n this.queue.push(() => {\n this.activeRequests++;\n resolve();\n });\n });\n }\n\n release(): void {\n this.activeRequests--;\n const next = this.queue.shift();\n if (next) {\n next();\n }\n }\n\n getActiveCount(): number {\n return this.activeRequests;\n }\n\n getQueueLength(): number {\n return this.queue.length;\n }\n}\n\n/**\n * Sleep utility for retry delays\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * RPC client wrapper with rate limiting and retry logic\n * \n * This class wraps the Solana Connection and provides:\n * - Automatic retries with exponential backoff\n * - Rate limiting for concurrent requests\n * - Centralized error handling\n * - No CLI or UI logic\n */\nexport class RPCClient {\n private connection: Connection;\n private config: Required<RPCClientConfig>;\n private rateLimiter: RateLimiter;\n\n constructor(configOrUrl: RPCClientConfig | string) {\n // Handle both string URL and config object\n const config: RPCClientConfig = typeof configOrUrl === 'string' \n ? { rpcUrl: configOrUrl }\n : configOrUrl;\n \n // Trim the RPC URL to handle trailing/leading whitespace\n const rpcUrl = config.rpcUrl.trim();\n \n this.config = {\n maxRetries: config.maxRetries ?? 3,\n retryDelay: config.retryDelay ?? 1000,\n timeout: config.timeout ?? 30000,\n maxConcurrency: config.maxConcurrency ?? 10,\n debug: config.debug ?? false,\n rpcUrl,\n };\n\n const connectionConfig: ConnectionConfig = {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: this.config.timeout,\n };\n\n this.connection = new Connection(this.config.rpcUrl, connectionConfig);\n this.rateLimiter = new RateLimiter(this.config.maxConcurrency);\n\n if (this.config.debug) {\n console.log(`[RPCClient] Initialized with URL: ${this.config.rpcUrl}`);\n console.log(`[RPCClient] Max concurrency: ${this.config.maxConcurrency}`);\n console.log(`[RPCClient] Max retries: ${this.config.maxRetries}`);\n }\n }\n\n /**\n * Execute an RPC call with retry logic and rate limiting\n */\n private async executeWithRetry<T>(\n operation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n await this.rateLimiter.acquire();\n\n let lastError: Error | null = null;\n \n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n if (this.config.debug && attempt > 0) {\n console.log(`[RPCClient] Retry attempt ${attempt} for ${operationName}`);\n }\n\n const result = await operation();\n this.rateLimiter.release();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n if (this.config.debug) {\n console.error(\n `[RPCClient] Error in ${operationName} (attempt ${attempt + 1}/${this.config.maxRetries + 1}):`,\n error\n );\n }\n\n // Don't retry on last attempt\n if (attempt < this.config.maxRetries) {\n // Exponential backoff: delay * 2^attempt\n const delay = this.config.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n this.rateLimiter.release();\n throw new Error(\n `RPC operation ${operationName} failed after ${this.config.maxRetries + 1} attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Get the underlying Solana Connection\n * Use this sparingly - prefer the wrapped methods for automatic retry/rate limiting\n */\n getConnection(): Connection {\n return this.connection;\n }\n\n /**\n * Get current rate limiter stats\n */\n getStats(): { activeRequests: number; queueLength: number } {\n return {\n activeRequests: this.rateLimiter.getActiveCount(),\n queueLength: this.rateLimiter.getQueueLength(),\n };\n }\n\n /**\n * Get signatures for an address with retry and rate limiting\n */\n async getSignaturesForAddress(\n address: string,\n options?: {\n limit?: number;\n before?: string;\n until?: string;\n }\n ) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getSignaturesForAddress(\n new PublicKey(address),\n options\n );\n },\n `getSignaturesForAddress(${address})`\n );\n }\n\n /**\n * Get transaction details with retry and rate limiting\n */\n async getTransaction(signature: string, options?: { maxSupportedTransactionVersion?: number }) {\n return this.executeWithRetry(\n async () => {\n return this.connection.getTransaction(signature, {\n maxSupportedTransactionVersion: options?.maxSupportedTransactionVersion ?? 0,\n });\n },\n `getTransaction(${signature})`\n );\n }\n\n /**\n * Get multiple transactions in parallel (respects rate limiting)\n */\n async getTransactions(signatures: string[], options?: { maxSupportedTransactionVersion?: number }): Promise<Array<any>> {\n const promises = signatures.map((sig) => this.getTransaction(sig, options));\n return Promise.all(promises);\n }\n\n /**\n * Get token accounts by owner with retry and rate limiting\n */\n async getTokenAccountsByOwner(ownerAddress: string, mintAddress?: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const owner = new PublicKey(ownerAddress);\n \n // SPL Token Program ID\n const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');\n \n if (mintAddress) {\n const mint = new PublicKey(mintAddress);\n return this.connection.getTokenAccountsByOwner(owner, { mint });\n } else {\n return this.connection.getTokenAccountsByOwner(owner, {\n programId: TOKEN_PROGRAM_ID,\n });\n }\n },\n `getTokenAccountsByOwner(${ownerAddress})`\n );\n }\n\n /**\n * Get program accounts with retry and rate limiting\n */\n async getProgramAccounts(programId: string, config?: any) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getProgramAccounts(new PublicKey(programId), config);\n },\n `getProgramAccounts(${programId})`\n );\n }\n\n /**\n * Get account info with retry and rate limiting\n */\n async getAccountInfo(address: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getAccountInfo(new PublicKey(address));\n },\n `getAccountInfo(${address})`\n );\n }\n\n /**\n * Get multiple account infos in parallel (respects rate limiting)\n */\n async getMultipleAccountsInfo(addresses: string[]) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const pubkeys = addresses.map((addr) => new PublicKey(addr));\n return this.connection.getMultipleAccountsInfo(pubkeys);\n },\n `getMultipleAccountsInfo(${addresses.length} addresses)`\n );\n }\n\n /**\n * Check if the RPC connection is healthy\n */\n async healthCheck(): Promise<boolean> {\n try {\n const version = await this.executeWithRetry(\n () => this.connection.getVersion(),\n 'healthCheck'\n );\n return !!version;\n } catch {\n return false;\n }\n }\n}\n", "import type { RPCClient } from '../rpc/client.js';\nimport type { \n ConfirmedSignatureInfo,\n ParsedTransactionWithMeta,\n TokenAccountBalancePair,\n} from '@solana/web3.js';\n\n/**\n * Raw transaction data fetched from RPC\n */\nexport interface RawTransaction {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw wallet data collection result\n */\nexport interface RawWalletData {\n address: string;\n signatures: ConfirmedSignatureInfo[];\n transactions: RawTransaction[];\n tokenAccounts: TokenAccountBalancePair[];\n}\n\n/**\n * Raw transaction scan result\n */\nexport interface RawTransactionData {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw program data collection result\n */\nexport interface RawProgramData {\n programId: string;\n accounts: Array<{\n pubkey: string;\n account: any;\n }>;\n relatedTransactions: RawTransaction[];\n}\n\n/**\n * Options for wallet data collection\n */\nexport interface WalletCollectionOptions {\n /**\n * Maximum number of signatures to fetch\n * @default 100\n */\n maxSignatures?: number;\n\n /**\n * Fetch token accounts\n * @default true\n */\n includeTokenAccounts?: boolean;\n}\n\n/**\n * Options for program data collection\n */\nexport interface ProgramCollectionOptions {\n /**\n * Maximum number of accounts to fetch\n * @default 100\n */\n maxAccounts?: number;\n\n /**\n * Maximum number of related transactions to fetch\n * @default 50\n */\n maxTransactions?: number;\n}\n\n/**\n * Collects raw wallet data from Solana RPC\n */\nexport async function collectWalletData(\n client: RPCClient,\n address: string,\n options: WalletCollectionOptions = {}\n): Promise<RawWalletData> {\n const maxSignatures = options.maxSignatures ?? 100;\n const includeTokenAccounts = options.includeTokenAccounts ?? true;\n\n // Fetch recent signatures (bounded)\n let signatures = [];\n try {\n signatures = await client.getSignaturesForAddress(address, {\n limit: maxSignatures,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for ${address}:`, error);\n }\n\n // Fetch transactions for all signatures\n const transactions: RawTransaction[] = [];\n \n // Process in batches to avoid overwhelming the RPC\n const BATCH_SIZE = 10;\n for (let i = 0; i < signatures.length; i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n transactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for ${address}:`, error);\n }\n }\n\n // Fetch token accounts\n let tokenAccounts: TokenAccountBalancePair[] = [];\n if (includeTokenAccounts) {\n try {\n const response = await client.getTokenAccountsByOwner(address);\n tokenAccounts = response.value;\n } catch (error) {\n // Token accounts might not exist or query might fail - that's okay\n console.warn(`Failed to fetch token accounts for ${address}:`, error);\n }\n }\n\n return {\n address,\n signatures,\n transactions,\n tokenAccounts,\n };\n}\n\n/**\n * Collects raw transaction data from Solana RPC\n */\nexport async function collectTransactionData(\n client: RPCClient,\n signature: string\n): Promise<RawTransactionData> {\n // Fetch full transaction with metadata\n let transaction = null;\n try {\n transaction = await client.getTransaction(signature, {\n maxSupportedTransactionVersion: 0,\n });\n } catch (error) {\n console.warn(`Failed to fetch transaction ${signature}:`, error);\n }\n\n return {\n signature,\n transaction: transaction as ParsedTransactionWithMeta | null,\n blockTime: transaction?.blockTime ?? null,\n };\n}\n\n/**\n * Collects raw program data from Solana RPC\n */\nexport async function collectProgramData(\n client: RPCClient,\n programId: string,\n options: ProgramCollectionOptions = {}\n): Promise<RawProgramData> {\n const maxAccounts = options.maxAccounts ?? 100;\n const maxTransactions = options.maxTransactions ?? 50;\n\n // Fetch program accounts (limited)\n let accounts: Array<{ pubkey: string; account: any }> = [];\n try {\n const response = await client.getProgramAccounts(programId, {\n encoding: 'jsonParsed',\n dataSlice: { offset: 0, length: 0 }, // Don't fetch full account data\n });\n\n accounts = response.slice(0, maxAccounts).map((acc) => ({\n pubkey: acc.pubkey.toString(),\n account: acc.account,\n }));\n } catch (error) {\n console.warn(`Failed to fetch program accounts for ${programId}:`, error);\n }\n\n // Fetch recent signatures for the program\n let signatures: ConfirmedSignatureInfo[] = [];\n try {\n signatures = await client.getSignaturesForAddress(programId, {\n limit: maxTransactions,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for program ${programId}:`, error);\n }\n\n // Fetch transactions for those signatures\n const relatedTransactions: RawTransaction[] = [];\n const BATCH_SIZE = 10;\n \n for (let i = 0; i < Math.min(signatures.length, maxTransactions); i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n relatedTransactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for program ${programId}:`, error);\n }\n }\n\n return {\n programId,\n accounts,\n relatedTransactions,\n };\n}\n", "import type {\n Transfer,\n NormalizedInstruction,\n InstructionCategory,\n ScanContext,\n LabelProvider,\n} from '../types/index.js';\nimport type {\n RawWalletData,\n RawTransactionData,\n RawProgramData,\n} from '../collectors/index.js';\nimport type { ParsedTransactionWithMeta, PartiallyDecodedInstruction, ParsedInstruction } from '@solana/web3.js';\n\n/**\n * Known program IDs for instruction categorization\n */\nconst PROGRAM_IDS = {\n SYSTEM: '11111111111111111111111111111111',\n TOKEN: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',\n ASSOCIATED_TOKEN: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',\n STAKE: 'Stake11111111111111111111111111111111111111',\n VOTE: 'Vote111111111111111111111111111111111111111',\n MEMO: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',\n MEMO_V1: 'Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo',\n};\n\n/**\n * Extract SOL transfers from a parsed transaction\n */\nfunction extractSOLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.transaction) {\n return transfers;\n }\n\n const preBalances = tx.meta.preBalances;\n const postBalances = tx.meta.postBalances;\n const accountKeys = tx.transaction.message.accountKeys;\n\n // Defensive checks\n if (!accountKeys || !Array.isArray(accountKeys) || accountKeys.length === 0) {\n return transfers;\n }\n\n if (!preBalances || !postBalances) {\n return transfers;\n }\n\n // Compare pre and post balances to find transfers\n for (let i = 0; i < accountKeys.length; i++) {\n const pre = preBalances[i];\n const post = postBalances[i];\n \n // Skip if balances are undefined\n if (pre === undefined || post === undefined) {\n continue;\n }\n \n const diff = post - pre;\n\n // Skip if no change or if it's fee-related (usually account 0)\n if (diff === 0) continue;\n\n const account = accountKeys[i];\n if (!account) continue;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) continue;\n\n // If balance increased, this account received SOL\n if (diff > 0) {\n // Find who sent it (simple heuristic: first account with decreased balance)\n for (let j = 0; j < accountKeys.length; j++) {\n const preSender = preBalances[j];\n const postSender = postBalances[j];\n \n if (preSender === undefined || postSender === undefined) {\n continue;\n }\n \n if (postSender < preSender) {\n const sender = accountKeys[j];\n if (!sender) continue;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) continue;\n \n transfers.push({\n from: senderAddress,\n to: address,\n amount: diff / 1e9, // Convert lamports to SOL\n token: undefined,\n signature,\n blockTime: tx.blockTime,\n });\n break;\n }\n }\n }\n }\n\n return transfers;\n}\n\n/**\n * Extract SPL token transfers from a parsed transaction\n */\nfunction extractSPLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.meta.postTokenBalances || !tx.meta.preTokenBalances) {\n return transfers;\n }\n\n const preTokenBalances = tx.meta.preTokenBalances;\n const postTokenBalances = tx.meta.postTokenBalances;\n\n // Create a map of account index to token balance changes\n const balanceChanges = new Map<number, { mint: string; change: number; decimals: number }>();\n\n // Calculate changes\n for (const post of postTokenBalances) {\n const pre = preTokenBalances.find(\n (p) => p.accountIndex === post.accountIndex && p.mint === post.mint\n );\n\n const preAmount = pre?.uiTokenAmount.uiAmount ?? 0;\n const postAmount = post.uiTokenAmount.uiAmount ?? 0;\n const change = postAmount - preAmount;\n\n if (change !== 0) {\n balanceChanges.set(post.accountIndex, {\n mint: post.mint,\n change,\n decimals: post.uiTokenAmount.decimals,\n });\n }\n }\n\n // Match senders and receivers\n const accountKeys = tx.transaction.message.accountKeys;\n \n if (!accountKeys || !Array.isArray(accountKeys)) {\n return transfers;\n }\n \n balanceChanges.forEach((info, accountIndex) => {\n if (accountIndex >= accountKeys.length) {\n return; // Skip if index out of bounds\n }\n \n const account = accountKeys[accountIndex];\n if (!account) return;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) return;\n\n if (info.change > 0) {\n // This account received tokens - find sender\n balanceChanges.forEach((senderInfo, senderIndex) => {\n if (\n senderInfo.mint === info.mint &&\n senderInfo.change < 0 &&\n senderIndex !== accountIndex &&\n senderIndex < accountKeys.length\n ) {\n const sender = accountKeys[senderIndex];\n if (!sender) return;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) return;\n\n transfers.push({\n from: senderAddress,\n to: address,\n amount: info.change,\n token: info.mint,\n signature,\n blockTime: tx.blockTime,\n });\n }\n });\n }\n });\n\n return transfers;\n}\n\n/**\n * Categorize an instruction based on program ID and instruction type\n */\nfunction categorizeInstruction(\n instruction: ParsedInstruction | PartiallyDecodedInstruction\n): InstructionCategory {\n const programId = instruction.programId.toString();\n\n // System program instructions\n if (programId === PROGRAM_IDS.SYSTEM) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferWithSeed') {\n return 'transfer';\n }\n }\n return 'transfer';\n }\n\n // Token program instructions\n if (programId === PROGRAM_IDS.TOKEN || programId === PROGRAM_IDS.ASSOCIATED_TOKEN) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferChecked') {\n return 'transfer';\n }\n return 'token_operation';\n }\n return 'token_operation';\n }\n\n // Stake program\n if (programId === PROGRAM_IDS.STAKE) {\n return 'stake';\n }\n\n // Vote program\n if (programId === PROGRAM_IDS.VOTE) {\n return 'vote';\n }\n\n // Check for common swap programs (simplified heuristic)\n if (\n programId.includes('Swap') ||\n programId.includes('swap') ||\n programId === 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' || // Jupiter\n programId === 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc' // Orca Whirlpool\n ) {\n return 'swap';\n }\n\n // Default to program interaction\n return 'program_interaction';\n}\n\n/**\n * Extract normalized instructions from a transaction\n */\nfunction extractInstructions(\n tx: ParsedTransactionWithMeta,\n signature: string\n): NormalizedInstruction[] {\n const instructions: NormalizedInstruction[] = [];\n\n if (!tx.transaction || !tx.transaction.message) {\n return instructions;\n }\n\n const message = tx.transaction.message;\n const allInstructions = message.instructions;\n\n // Defensive check\n if (!allInstructions || !Array.isArray(allInstructions)) {\n return instructions;\n }\n\n for (const instruction of allInstructions) {\n // Defensive check for programId\n if (!instruction || !instruction.programId) {\n continue;\n }\n \n const programId = instruction.programId.toString();\n const category = categorizeInstruction(instruction);\n\n let data: Record<string, unknown> | undefined;\n if ('parsed' in instruction) {\n data = instruction.parsed as Record<string, unknown>;\n }\n\n instructions.push({\n programId,\n category,\n signature,\n blockTime: tx.blockTime,\n data,\n });\n }\n\n return instructions;\n}\n\n/**\n * Extract unique counterparties from transfers\n */\nfunction extractCounterparties(transfers: Transfer[], targetAddress: string): Set<string> {\n const counterparties = new Set<string>();\n\n for (const transfer of transfers) {\n // Add the other party in the transfer\n if (transfer.from === targetAddress) {\n counterparties.add(transfer.to);\n } else if (transfer.to === targetAddress) {\n counterparties.add(transfer.from);\n }\n }\n\n return counterparties;\n}\n\n/**\n * Calculate time range from transactions\n */\nfunction calculateTimeRange(transactions: RawTransactionData[]): {\n earliest: number | null;\n latest: number | null;\n} {\n let earliest: number | null = null;\n let latest: number | null = null;\n\n for (const tx of transactions) {\n if (tx.blockTime) {\n if (earliest === null || tx.blockTime < earliest) {\n earliest = tx.blockTime;\n }\n if (latest === null || tx.blockTime > latest) {\n latest = tx.blockTime;\n }\n }\n }\n\n return { earliest, latest };\n}\n\n/**\n * Normalize wallet data into a ScanContext\n */\nexport function normalizeWalletData(\n rawData: RawWalletData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n\n // Ensure transactions exists and is an array\n const transactions = rawData.transactions || [];\n\n // Process each transaction\n for (const rawTx of transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n } catch (error) {\n // Skip problematic transactions but continue processing\n console.warn(`Failed to normalize transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Extract counterparties\n const counterparties = extractCounterparties(allTransfers, rawData.address);\n\n // Look up labels for counterparties if provider is available\n const labels = labelProvider \n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n // Calculate time range\n const timeRange = calculateTimeRange(transactions);\n\n // Normalize token accounts\n const tokenAccounts = rawData.tokenAccounts.map((ta) => {\n try {\n return {\n mint: ta.account.data.parsed.info.mint,\n address: ta.pubkey.toString(),\n balance: ta.account.data.parsed.info.tokenAmount.uiAmount ?? 0,\n };\n } catch (error) {\n // Skip malformed token accounts\n return null;\n }\n }).filter((ta): ta is NonNullable<typeof ta> => ta !== null);\n\n return {\n target: rawData.address,\n targetType: 'wallet',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts,\n timeRange,\n transactionCount: transactions.length,\n };\n}\n\n/**\n * Normalize transaction data into a ScanContext\n */\nexport function normalizeTransactionData(\n rawData: RawTransactionData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n if (rawData.transaction) {\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawData.transaction, rawData.signature);\n const splTransfers = extractSPLTransfers(rawData.transaction, rawData.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawData.transaction, rawData.signature);\n allInstructions.push(...instructions);\n\n // Extract all unique addresses involved\n const accountKeys = rawData.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize transaction ${rawData.signature}:`, error);\n }\n }\n\n return {\n target: rawData.signature,\n targetType: 'transaction',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange: {\n earliest: rawData.transaction ? rawData.blockTime : null,\n latest: rawData.transaction ? rawData.blockTime : null,\n },\n transactionCount: rawData.transaction ? 1 : 0,\n };\n}\n\n/**\n * Normalize program data into a ScanContext\n */\nexport function normalizeProgramData(\n rawData: RawProgramData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n // Ensure relatedTransactions exists and is an array\n const transactions = rawData.relatedTransactions || [];\n\n // Process related transactions\n for (const rawTx of transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n\n // Extract counterparties\n const accountKeys = rawTx.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize program transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Calculate time range\n const timeRange = calculateTimeRange(transactions);\n\n // Look up labels for counterparties\n const labels = labelProvider\n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n return {\n target: rawData.programId,\n targetType: 'program',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange,\n transactionCount: transactions.length,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if a wallet frequently interacts with the same counterparties\n * This can enable clustering and linking of addresses\n */\nexport function detectCounterpartyReuse(context: ScanContext): RiskSignal | null {\n // Only applicable to wallet scans\n if (context.targetType !== 'wallet') {\n return null;\n }\n\n if (context.counterparties.size === 0 || context.transfers.length === 0) {\n return null;\n }\n\n // Count interactions per counterparty\n const interactionCounts = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const counterparty = transfer.from === context.target ? transfer.to : transfer.from;\n if (counterparty === context.target) continue;\n \n interactionCounts.set(counterparty, (interactionCounts.get(counterparty) || 0) + 1);\n }\n\n // Find counterparties with multiple interactions\n const reusedCounterparties = Array.from(interactionCounts.entries())\n .filter(([_, count]) => count >= 3)\n .sort((a, b) => b[1] - a[1]);\n\n if (reusedCounterparties.length === 0) {\n return null;\n }\n\n // Calculate severity based on concentration\n const totalInteractions = context.transfers.length;\n const topCounterpartyInteractions = reusedCounterparties[0][1];\n const concentration = topCounterpartyInteractions / totalInteractions;\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if (concentration > 0.5 || reusedCounterparties.length >= 5) {\n severity = 'HIGH';\n } else if (concentration > 0.3 || reusedCounterparties.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = reusedCounterparties.slice(0, 5).map(([addr, count]) => ({\n type: 'address',\n description: `${count} interactions with ${addr.slice(0, 8)}...${addr.slice(-8)}`,\n data: { address: addr, interactionCount: count },\n }));\n\n return {\n id: 'counterparty-reuse',\n name: 'Counterparty Reuse',\n severity,\n reason: `Wallet repeatedly interacts with ${reusedCounterparties.length} address(es)`,\n impact: 'Repeated interactions with the same addresses can be used to cluster wallets and build transaction graphs, enabling surveillance of your activity patterns.',\n evidence,\n mitigation: 'Use different wallets for different counterparties, or use privacy-preserving protocols that obscure transaction graphs.',\n confidence: 0.9,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if the wallet sends or receives suspiciously similar amounts\n * Round numbers or repeated exact amounts can be used for fingerprinting\n */\nexport function detectAmountReuse(context: ScanContext): RiskSignal | null {\n if (context.transfers.length < 3) {\n return null;\n }\n\n // Group amounts (rounded to avoid floating point issues)\n const amountCounts = new Map<string, number>();\n const roundNumbers: number[] = [];\n\n for (const transfer of context.transfers) {\n // Check for round numbers (e.g., 1.0, 10.0, 100.0)\n if (transfer.amount > 0 && Number.isInteger(transfer.amount) && transfer.amount >= 1) {\n roundNumbers.push(transfer.amount);\n }\n\n // Count exact amount reuse\n const amountKey = `${transfer.amount.toFixed(9)}-${transfer.token || 'SOL'}`;\n amountCounts.set(amountKey, (amountCounts.get(amountKey) || 0) + 1);\n }\n\n // Find amounts used multiple times\n const reusedAmounts = Array.from(amountCounts.entries())\n .filter(([_, count]) => count >= 2)\n .sort((a, b) => b[1] - a[1]);\n\n const hasRoundNumbers = roundNumbers.length >= 2;\n const hasReusedAmounts = reusedAmounts.length >= 2;\n\n if (!hasRoundNumbers && !hasReusedAmounts) {\n return null;\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if ((hasRoundNumbers && roundNumbers.length >= 5) || reusedAmounts.length >= 5) {\n severity = 'HIGH';\n } else if ((hasRoundNumbers && roundNumbers.length >= 3) || reusedAmounts.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = [];\n\n if (hasRoundNumbers) {\n evidence.push({\n type: 'amount',\n description: `${roundNumbers.length} round-number transfers detected`,\n data: { roundNumbers: roundNumbers.slice(0, 5) },\n });\n }\n\n if (hasReusedAmounts) {\n const topReused = reusedAmounts.slice(0, 3);\n for (const [amountKey, count] of topReused) {\n const [amount, token] = amountKey.split('-');\n evidence.push({\n type: 'amount',\n description: `Amount ${amount} ${token} used ${count} times`,\n data: { amount: parseFloat(amount), token, count },\n });\n }\n }\n\n return {\n id: 'amount-reuse',\n name: 'Deterministic Amount Patterns',\n severity,\n reason: `Wallet uses ${hasRoundNumbers ? 'round numbers' : 'repeated amounts'} in transactions`,\n impact: 'Using the same amounts repeatedly or sending round numbers creates fingerprints that can be used to link transactions and identify patterns in your activity.',\n evidence,\n mitigation: 'Vary transaction amounts slightly, avoid round numbers, and consider using privacy protocols that obscure amounts.',\n confidence: hasRoundNumbers ? 0.85 : 0.75,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect transaction bursts and timing patterns\n * Concentrated activity creates temporal fingerprints\n */\nexport function detectTimingPatterns(context: ScanContext): RiskSignal | null {\n if (!context.timeRange.earliest || !context.timeRange.latest) {\n return null;\n }\n\n if (context.transactionCount < 3) {\n return null;\n }\n\n // Calculate time span in hours\n const timeSpanSeconds = context.timeRange.latest - context.timeRange.earliest;\n const timeSpanHours = timeSpanSeconds / 3600;\n\n if (timeSpanHours === 0) {\n return null;\n }\n\n // Calculate transaction rate (txs per hour)\n const txRate = context.transactionCount / timeSpanHours;\n\n // Detect burst activity (high tx rate in short time)\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n let isBurst = false;\n\n if (txRate > 10) {\n severity = 'HIGH';\n isBurst = true;\n } else if (txRate > 5) {\n severity = 'MEDIUM';\n isBurst = true;\n } else if (timeSpanHours < 1 && context.transactionCount >= 3) {\n severity = 'MEDIUM';\n isBurst = true;\n }\n\n if (!isBurst) {\n return null;\n }\n\n const evidence: Evidence[] = [\n {\n type: 'timing',\n description: `${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n data: {\n transactionCount: context.transactionCount,\n timeSpanHours: timeSpanHours.toFixed(2),\n transactionRate: txRate.toFixed(2),\n },\n },\n ];\n\n return {\n id: 'timing-correlation',\n name: 'Transaction Burst Pattern',\n severity,\n reason: `Concentrated activity: ${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n impact: 'Concentrated transaction activity creates timing fingerprints that can be used to correlate your transactions and link them to specific events or behaviors.',\n evidence,\n mitigation: 'Spread transactions over longer time periods, use scheduled transactions, or batch operations to reduce timing correlation.',\n confidence: 0.8,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect interactions with known entities (CEXs, bridges, etc.)\n * These entities can link on-chain and off-chain identities\n */\nexport function detectKnownEntityInteraction(context: ScanContext): RiskSignal | null {\n if (context.labels.size === 0) {\n return null;\n }\n\n const entityInteractions: Evidence[] = [];\n\n for (const [address, label] of context.labels.entries()) {\n // Count interactions with this entity\n let interactionCount = 0;\n const relatedTxs: string[] = [];\n\n for (const transfer of context.transfers) {\n if (transfer.from === address || transfer.to === address) {\n interactionCount++;\n if (relatedTxs.length < 3) {\n relatedTxs.push(transfer.signature);\n }\n }\n }\n\n if (interactionCount > 0) {\n entityInteractions.push({\n type: 'label',\n description: `${interactionCount} interaction(s) with ${label.name} (${label.type})`,\n data: {\n entityName: label.name,\n entityType: label.type,\n address,\n interactionCount,\n transactions: relatedTxs,\n },\n reference: address,\n });\n }\n }\n\n if (entityInteractions.length === 0) {\n return null;\n }\n\n // Severity based on entity type and interaction count\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n \n const hasExchangeInteraction = Array.from(context.labels.values())\n .some(label => label.type === 'exchange');\n \n if (hasExchangeInteraction) {\n severity = 'HIGH';\n } else if (entityInteractions.length >= 3) {\n severity = 'HIGH';\n }\n\n return {\n id: 'known-entity-interaction',\n name: 'Known Entity Interaction',\n severity,\n reason: `Wallet interacted with ${entityInteractions.length} known entit${entityInteractions.length === 1 ? 'y' : 'ies'}`,\n impact: 'Interactions with centralized exchanges, bridges, or other known entities can link your on-chain address to your real-world identity through KYC data, IP addresses, and off-chain records.',\n evidence: entityInteractions,\n mitigation: 'Use privacy-preserving bridges, avoid direct CEX interactions from privacy-sensitive wallets, or use intermediate wallets to break the link.',\n confidence: 0.95,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if wallet balances can be easily traced\n * Full balance transfers or predictable balance changes reduce privacy\n */\nexport function detectBalanceTraceability(context: ScanContext): RiskSignal | null {\n if (context.targetType !== 'wallet' || context.transfers.length < 2) {\n return null;\n }\n\n const fullBalanceTransfers: Evidence[] = [];\n const suspiciousPatterns: string[] = [];\n\n // Look for transfers that represent full balance movements\n // (This is simplified - real implementation would need balance tracking)\n \n // Check for transfers where send and receive amounts match closely\n const amountPairs = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const amountKey = transfer.amount.toFixed(6);\n amountPairs.set(amountKey, (amountPairs.get(amountKey) || 0) + 1);\n }\n\n // Find matching send/receive pairs\n const matchingPairs = Array.from(amountPairs.entries())\n .filter(([_, count]) => count >= 2);\n\n if (matchingPairs.length >= 2) {\n suspiciousPatterns.push('Multiple matching send/receive amounts detected');\n }\n\n // Check for sequential transfers of similar amounts\n for (let i = 0; i < context.transfers.length - 1; i++) {\n const current = context.transfers[i];\n const next = context.transfers[i + 1];\n \n if (current.blockTime && next.blockTime) {\n const timeDiff = Math.abs((next.blockTime - current.blockTime));\n \n // If similar amounts within 1 hour\n if (timeDiff < 3600 && Math.abs(current.amount - next.amount) < current.amount * 0.1) {\n suspiciousPatterns.push('Sequential transfers of similar amounts');\n break;\n }\n }\n }\n\n if (suspiciousPatterns.length === 0 && matchingPairs.length === 0) {\n return null;\n }\n\n const evidence: Evidence[] = [];\n\n if (matchingPairs.length > 0) {\n evidence.push({\n type: 'pattern',\n description: `${matchingPairs.length} matching send/receive amount pair(s)`,\n data: { matchingPairs: matchingPairs.length },\n });\n }\n\n for (const pattern of suspiciousPatterns) {\n evidence.push({\n type: 'pattern',\n description: pattern,\n data: {},\n });\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n if (matchingPairs.length >= 3 || suspiciousPatterns.length >= 2) {\n severity = 'HIGH';\n }\n\n return {\n id: 'balance-traceability',\n name: 'Balance Traceability',\n severity,\n reason: 'Wallet shows patterns that enable balance tracking',\n impact: 'Traceable balance movements allow observers to follow funds through the blockchain, linking your transactions and revealing your financial activity.',\n evidence,\n mitigation: 'Split large transfers into multiple smaller ones, introduce timing delays, or use privacy protocols that obscure amounts.',\n confidence: 0.7,\n };\n}\n", "import type { ScanContext, RiskSignal, RiskLevel, PrivacyReport } from '../types/index.js';\nimport {\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n} from '../heuristics/index.js';\n\n/**\n * Current report schema version\n */\nconst REPORT_VERSION = '1.0.0';\n\n/**\n * All available heuristic functions\n */\nconst HEURISTICS = [\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n];\n\n/**\n * Calculate overall risk level from individual signals\n * Uses deterministic scoring based on severity and count\n */\nfunction calculateOverallRisk(signals: RiskSignal[]): RiskLevel {\n if (signals.length === 0) {\n return 'LOW';\n }\n\n // Count signals by severity\n const highCount = signals.filter(s => s.severity === 'HIGH').length;\n const mediumCount = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowCount = signals.filter(s => s.severity === 'LOW').length;\n\n // Deterministic thresholds\n if (highCount >= 2 || (highCount >= 1 && mediumCount >= 2)) {\n return 'HIGH';\n }\n\n if (highCount >= 1 || mediumCount >= 2 || (mediumCount >= 1 && lowCount >= 2)) {\n return 'MEDIUM';\n }\n\n return 'LOW';\n}\n\n/**\n * Generate general mitigation recommendations based on detected signals\n */\nfunction generateMitigations(signals: RiskSignal[]): string[] {\n const mitigations = new Set<string>();\n\n if (signals.length === 0) {\n return ['Continue practicing good privacy hygiene to maintain low exposure.'];\n }\n\n // Add general recommendations\n mitigations.add('Consider using multiple wallets to compartmentalize different activities.');\n \n // Check for specific signal types and add relevant mitigations\n const signalIds = new Set(signals.map(s => s.id));\n\n if (signalIds.has('known-entity-interaction')) {\n mitigations.add('Avoid direct interactions between privacy-sensitive wallets and KYC services.');\n }\n\n if (signalIds.has('counterparty-reuse')) {\n mitigations.add('Use different addresses for different counterparties or contexts.');\n }\n\n if (signalIds.has('timing-correlation') || signalIds.has('balance-traceability')) {\n mitigations.add('Introduce timing delays and vary transaction patterns to reduce correlation.');\n }\n\n if (signalIds.has('amount-reuse')) {\n mitigations.add('Vary transaction amounts to avoid creating fingerprints.');\n }\n\n // Always add this general advice\n mitigations.add('Research and consider privacy-preserving protocols when available.');\n\n return Array.from(mitigations);\n}\n\n/**\n * Evaluate all heuristics against a scan context\n */\nexport function evaluateHeuristics(context: ScanContext): RiskSignal[] {\n const signals: RiskSignal[] = [];\n\n for (const heuristic of HEURISTICS) {\n try {\n const signal = heuristic(context);\n if (signal) {\n signals.push(signal);\n }\n } catch (error) {\n // Log but don't fail if a heuristic errors\n console.warn(`Heuristic evaluation failed:`, error);\n }\n }\n\n // Sort signals by severity (HIGH -> MEDIUM -> LOW) for deterministic ordering\n signals.sort((a, b) => {\n const severityOrder = { HIGH: 0, MEDIUM: 1, LOW: 2 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n\n return signals;\n}\n\n/**\n * Generate a complete privacy report from a scan context\n */\nexport function generateReport(context: ScanContext): PrivacyReport {\n // Evaluate all heuristics\n const signals = evaluateHeuristics(context);\n\n // Calculate overall risk\n const overallRisk = calculateOverallRisk(signals);\n\n // Count signals by severity\n const highRiskSignals = signals.filter(s => s.severity === 'HIGH').length;\n const mediumRiskSignals = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowRiskSignals = signals.filter(s => s.severity === 'LOW').length;\n\n // Generate mitigations\n const mitigations = generateMitigations(signals);\n\n // Extract known entities from context\n const knownEntities = Array.from(context.labels.values());\n\n return {\n version: REPORT_VERSION,\n timestamp: Date.now(),\n targetType: context.targetType,\n target: context.target,\n overallRisk,\n signals,\n summary: {\n totalSignals: signals.length,\n highRiskSignals,\n mediumRiskSignals,\n lowRiskSignals,\n transactionsAnalyzed: context.transactionCount,\n },\n mitigations,\n knownEntities,\n };\n}\n", "import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport type { Label, LabelProvider } from '../types/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Static JSON label provider\n * Loads labels from a curated JSON file\n */\nexport class StaticLabelProvider implements LabelProvider {\n private labels: Map<string, Label>;\n\n constructor(labelsPath?: string) {\n this.labels = new Map();\n this.loadLabels(labelsPath);\n }\n\n /**\n * Load labels from JSON file\n */\n private loadLabels(customPath?: string): void {\n try {\n const path = customPath || join(__dirname, 'known-addresses.json');\n const data = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(data);\n\n if (!parsed.labels || !Array.isArray(parsed.labels)) {\n console.warn('Invalid labels file format');\n return;\n }\n\n for (const label of parsed.labels) {\n if (label.address && label.name && label.type) {\n this.labels.set(label.address, {\n address: label.address,\n name: label.name,\n type: label.type,\n description: label.description,\n relatedAddresses: label.relatedAddresses,\n });\n }\n }\n\n console.debug(`Loaded ${this.labels.size} address labels`);\n } catch (error) {\n console.warn('Failed to load labels file:', error);\n }\n }\n\n /**\n * Look up a label for an address\n */\n lookup(address: string): Label | null {\n return this.labels.get(address) || null;\n }\n\n /**\n * Look up multiple addresses at once\n */\n lookupMany(addresses: string[]): Map<string, Label> {\n const results = new Map<string, Label>();\n\n for (const address of addresses) {\n const label = this.lookup(address);\n if (label) {\n results.set(address, label);\n }\n }\n\n return results;\n }\n\n /**\n * Get all loaded labels\n */\n getAllLabels(): Label[] {\n return Array.from(this.labels.values());\n }\n\n /**\n * Get count of loaded labels\n */\n getCount(): number {\n return this.labels.size;\n }\n}\n\n/**\n * Create a default label provider instance\n */\nexport function createDefaultLabelProvider(): StaticLabelProvider {\n return new StaticLabelProvider();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -224,23 +224,32 @@ var RPCClient = class {
224
224
  async function collectWalletData(client, address, options = {}) {
225
225
  const maxSignatures = options.maxSignatures ?? 100;
226
226
  const includeTokenAccounts = options.includeTokenAccounts ?? true;
227
- const signatures = await client.getSignaturesForAddress(address, {
228
- limit: maxSignatures
229
- });
227
+ let signatures = [];
228
+ try {
229
+ signatures = await client.getSignaturesForAddress(address, {
230
+ limit: maxSignatures
231
+ });
232
+ } catch (error) {
233
+ console.warn(`Failed to fetch signatures for ${address}:`, error);
234
+ }
230
235
  const transactions = [];
231
236
  const BATCH_SIZE = 10;
232
237
  for (let i = 0; i < signatures.length; i += BATCH_SIZE) {
233
238
  const batch = signatures.slice(i, i + BATCH_SIZE);
234
239
  const batchSignatures = batch.map((sig) => sig.signature);
235
- const txs = await client.getTransactions(batchSignatures, {
236
- maxSupportedTransactionVersion: 0
237
- });
238
- for (let j = 0; j < batch.length; j++) {
239
- transactions.push({
240
- signature: batch[j].signature,
241
- transaction: txs[j],
242
- blockTime: batch[j].blockTime
240
+ try {
241
+ const txs = await client.getTransactions(batchSignatures, {
242
+ maxSupportedTransactionVersion: 0
243
243
  });
244
+ for (let j = 0; j < batch.length; j++) {
245
+ transactions.push({
246
+ signature: batch[j].signature,
247
+ transaction: txs[j],
248
+ blockTime: batch[j].blockTime
249
+ });
250
+ }
251
+ } catch (error) {
252
+ console.warn(`Failed to fetch transaction batch for ${address}:`, error);
244
253
  }
245
254
  }
246
255
  let tokenAccounts = [];
@@ -260,9 +269,14 @@ async function collectWalletData(client, address, options = {}) {
260
269
  };
261
270
  }
262
271
  async function collectTransactionData(client, signature) {
263
- const transaction = await client.getTransaction(signature, {
264
- maxSupportedTransactionVersion: 0
265
- });
272
+ let transaction = null;
273
+ try {
274
+ transaction = await client.getTransaction(signature, {
275
+ maxSupportedTransactionVersion: 0
276
+ });
277
+ } catch (error) {
278
+ console.warn(`Failed to fetch transaction ${signature}:`, error);
279
+ }
266
280
  return {
267
281
  signature,
268
282
  transaction,
@@ -533,7 +547,8 @@ function calculateTimeRange(transactions) {
533
547
  function normalizeWalletData(rawData, labelProvider) {
534
548
  const allTransfers = [];
535
549
  const allInstructions = [];
536
- for (const rawTx of rawData.transactions) {
550
+ const transactions = rawData.transactions || [];
551
+ for (const rawTx of transactions) {
537
552
  if (!rawTx.transaction) continue;
538
553
  try {
539
554
  const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);
@@ -548,7 +563,7 @@ function normalizeWalletData(rawData, labelProvider) {
548
563
  }
549
564
  const counterparties = extractCounterparties(allTransfers, rawData.address);
550
565
  const labels = labelProvider ? labelProvider.lookupMany(Array.from(counterparties)) : /* @__PURE__ */ new Map();
551
- const timeRange = calculateTimeRange(rawData.transactions);
566
+ const timeRange = calculateTimeRange(transactions);
552
567
  const tokenAccounts = rawData.tokenAccounts.map((ta) => {
553
568
  try {
554
569
  return {
@@ -569,7 +584,7 @@ function normalizeWalletData(rawData, labelProvider) {
569
584
  labels: /* @__PURE__ */ new Map(),
570
585
  tokenAccounts,
571
586
  timeRange,
572
- transactionCount: rawData.transactions.length
587
+ transactionCount: transactions.length
573
588
  };
574
589
  }
575
590
  function normalizeTransactionData(rawData, labelProvider) {
@@ -603,17 +618,18 @@ function normalizeTransactionData(rawData, labelProvider) {
603
618
  labels: /* @__PURE__ */ new Map(),
604
619
  tokenAccounts: [],
605
620
  timeRange: {
606
- earliest: rawData.blockTime,
607
- latest: rawData.blockTime
621
+ earliest: rawData.transaction ? rawData.blockTime : null,
622
+ latest: rawData.transaction ? rawData.blockTime : null
608
623
  },
609
- transactionCount: 1
624
+ transactionCount: rawData.transaction ? 1 : 0
610
625
  };
611
626
  }
612
627
  function normalizeProgramData(rawData, labelProvider) {
613
628
  const allTransfers = [];
614
629
  const allInstructions = [];
615
630
  const counterparties = /* @__PURE__ */ new Set();
616
- for (const rawTx of rawData.relatedTransactions) {
631
+ const transactions = rawData.relatedTransactions || [];
632
+ for (const rawTx of transactions) {
617
633
  if (!rawTx.transaction) continue;
618
634
  try {
619
635
  const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);
@@ -633,7 +649,7 @@ function normalizeProgramData(rawData, labelProvider) {
633
649
  continue;
634
650
  }
635
651
  }
636
- const timeRange = calculateTimeRange(rawData.relatedTransactions);
652
+ const timeRange = calculateTimeRange(transactions);
637
653
  const labels = labelProvider ? labelProvider.lookupMany(Array.from(counterparties)) : /* @__PURE__ */ new Map();
638
654
  return {
639
655
  target: rawData.programId,
@@ -644,7 +660,7 @@ function normalizeProgramData(rawData, labelProvider) {
644
660
  labels: /* @__PURE__ */ new Map(),
645
661
  tokenAccounts: [],
646
662
  timeRange,
647
- transactionCount: rawData.relatedTransactions.length
663
+ transactionCount: transactions.length
648
664
  };
649
665
  }
650
666
 
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/rpc/client.ts", "../src/collectors/index.ts", "../src/normalizer/index.ts", "../src/heuristics/counterparty-reuse.ts", "../src/heuristics/amount-reuse.ts", "../src/heuristics/timing-patterns.ts", "../src/heuristics/known-entity.ts", "../src/heuristics/balance-traceability.ts", "../src/scanner/index.ts", "../src/labels/provider.ts", "../src/index.ts"],
4
- "sourcesContent": ["import { Connection, ConnectionConfig } from '@solana/web3.js';\n\n/**\n * Configuration for the RPC client\n */\nexport interface RPCClientConfig {\n /**\n * RPC endpoint URL (Helius, QuickNode, or any Solana-compatible RPC)\n */\n rpcUrl: string;\n\n /**\n * Maximum number of retries for failed requests\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay for exponential backoff (ms)\n * @default 1000\n */\n retryDelay?: number;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Maximum number of concurrent requests\n * @default 10\n */\n maxConcurrency?: number;\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Rate limiter to control concurrent requests\n */\nclass RateLimiter {\n private activeRequests = 0;\n private queue: Array<() => void> = [];\n\n constructor(private maxConcurrency: number) {}\n\n async acquire(): Promise<void> {\n if (this.activeRequests < this.maxConcurrency) {\n this.activeRequests++;\n return;\n }\n\n return new Promise((resolve) => {\n this.queue.push(() => {\n this.activeRequests++;\n resolve();\n });\n });\n }\n\n release(): void {\n this.activeRequests--;\n const next = this.queue.shift();\n if (next) {\n next();\n }\n }\n\n getActiveCount(): number {\n return this.activeRequests;\n }\n\n getQueueLength(): number {\n return this.queue.length;\n }\n}\n\n/**\n * Sleep utility for retry delays\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * RPC client wrapper with rate limiting and retry logic\n * \n * This class wraps the Solana Connection and provides:\n * - Automatic retries with exponential backoff\n * - Rate limiting for concurrent requests\n * - Centralized error handling\n * - No CLI or UI logic\n */\nexport class RPCClient {\n private connection: Connection;\n private config: Required<RPCClientConfig>;\n private rateLimiter: RateLimiter;\n\n constructor(configOrUrl: RPCClientConfig | string) {\n // Handle both string URL and config object\n const config: RPCClientConfig = typeof configOrUrl === 'string' \n ? { rpcUrl: configOrUrl }\n : configOrUrl;\n \n // Trim the RPC URL to handle trailing/leading whitespace\n const rpcUrl = config.rpcUrl.trim();\n \n this.config = {\n maxRetries: config.maxRetries ?? 3,\n retryDelay: config.retryDelay ?? 1000,\n timeout: config.timeout ?? 30000,\n maxConcurrency: config.maxConcurrency ?? 10,\n debug: config.debug ?? false,\n rpcUrl,\n };\n\n const connectionConfig: ConnectionConfig = {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: this.config.timeout,\n };\n\n this.connection = new Connection(this.config.rpcUrl, connectionConfig);\n this.rateLimiter = new RateLimiter(this.config.maxConcurrency);\n\n if (this.config.debug) {\n console.log(`[RPCClient] Initialized with URL: ${this.config.rpcUrl}`);\n console.log(`[RPCClient] Max concurrency: ${this.config.maxConcurrency}`);\n console.log(`[RPCClient] Max retries: ${this.config.maxRetries}`);\n }\n }\n\n /**\n * Execute an RPC call with retry logic and rate limiting\n */\n private async executeWithRetry<T>(\n operation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n await this.rateLimiter.acquire();\n\n let lastError: Error | null = null;\n \n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n if (this.config.debug && attempt > 0) {\n console.log(`[RPCClient] Retry attempt ${attempt} for ${operationName}`);\n }\n\n const result = await operation();\n this.rateLimiter.release();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n if (this.config.debug) {\n console.error(\n `[RPCClient] Error in ${operationName} (attempt ${attempt + 1}/${this.config.maxRetries + 1}):`,\n error\n );\n }\n\n // Don't retry on last attempt\n if (attempt < this.config.maxRetries) {\n // Exponential backoff: delay * 2^attempt\n const delay = this.config.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n this.rateLimiter.release();\n throw new Error(\n `RPC operation ${operationName} failed after ${this.config.maxRetries + 1} attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Get the underlying Solana Connection\n * Use this sparingly - prefer the wrapped methods for automatic retry/rate limiting\n */\n getConnection(): Connection {\n return this.connection;\n }\n\n /**\n * Get current rate limiter stats\n */\n getStats(): { activeRequests: number; queueLength: number } {\n return {\n activeRequests: this.rateLimiter.getActiveCount(),\n queueLength: this.rateLimiter.getQueueLength(),\n };\n }\n\n /**\n * Get signatures for an address with retry and rate limiting\n */\n async getSignaturesForAddress(\n address: string,\n options?: {\n limit?: number;\n before?: string;\n until?: string;\n }\n ) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getSignaturesForAddress(\n new PublicKey(address),\n options\n );\n },\n `getSignaturesForAddress(${address})`\n );\n }\n\n /**\n * Get transaction details with retry and rate limiting\n */\n async getTransaction(signature: string, options?: { maxSupportedTransactionVersion?: number }) {\n return this.executeWithRetry(\n async () => {\n return this.connection.getTransaction(signature, {\n maxSupportedTransactionVersion: options?.maxSupportedTransactionVersion ?? 0,\n });\n },\n `getTransaction(${signature})`\n );\n }\n\n /**\n * Get multiple transactions in parallel (respects rate limiting)\n */\n async getTransactions(signatures: string[], options?: { maxSupportedTransactionVersion?: number }): Promise<Array<any>> {\n const promises = signatures.map((sig) => this.getTransaction(sig, options));\n return Promise.all(promises);\n }\n\n /**\n * Get token accounts by owner with retry and rate limiting\n */\n async getTokenAccountsByOwner(ownerAddress: string, mintAddress?: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const owner = new PublicKey(ownerAddress);\n \n // SPL Token Program ID\n const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');\n \n if (mintAddress) {\n const mint = new PublicKey(mintAddress);\n return this.connection.getTokenAccountsByOwner(owner, { mint });\n } else {\n return this.connection.getTokenAccountsByOwner(owner, {\n programId: TOKEN_PROGRAM_ID,\n });\n }\n },\n `getTokenAccountsByOwner(${ownerAddress})`\n );\n }\n\n /**\n * Get program accounts with retry and rate limiting\n */\n async getProgramAccounts(programId: string, config?: any) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getProgramAccounts(new PublicKey(programId), config);\n },\n `getProgramAccounts(${programId})`\n );\n }\n\n /**\n * Get account info with retry and rate limiting\n */\n async getAccountInfo(address: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getAccountInfo(new PublicKey(address));\n },\n `getAccountInfo(${address})`\n );\n }\n\n /**\n * Get multiple account infos in parallel (respects rate limiting)\n */\n async getMultipleAccountsInfo(addresses: string[]) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const pubkeys = addresses.map((addr) => new PublicKey(addr));\n return this.connection.getMultipleAccountsInfo(pubkeys);\n },\n `getMultipleAccountsInfo(${addresses.length} addresses)`\n );\n }\n\n /**\n * Check if the RPC connection is healthy\n */\n async healthCheck(): Promise<boolean> {\n try {\n const version = await this.executeWithRetry(\n () => this.connection.getVersion(),\n 'healthCheck'\n );\n return !!version;\n } catch {\n return false;\n }\n }\n}\n", "import type { RPCClient } from '../rpc/client.js';\nimport type { \n ConfirmedSignatureInfo,\n ParsedTransactionWithMeta,\n TokenAccountBalancePair,\n} from '@solana/web3.js';\n\n/**\n * Raw transaction data fetched from RPC\n */\nexport interface RawTransaction {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw wallet data collection result\n */\nexport interface RawWalletData {\n address: string;\n signatures: ConfirmedSignatureInfo[];\n transactions: RawTransaction[];\n tokenAccounts: TokenAccountBalancePair[];\n}\n\n/**\n * Raw transaction scan result\n */\nexport interface RawTransactionData {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw program data collection result\n */\nexport interface RawProgramData {\n programId: string;\n accounts: Array<{\n pubkey: string;\n account: any;\n }>;\n relatedTransactions: RawTransaction[];\n}\n\n/**\n * Options for wallet data collection\n */\nexport interface WalletCollectionOptions {\n /**\n * Maximum number of signatures to fetch\n * @default 100\n */\n maxSignatures?: number;\n\n /**\n * Fetch token accounts\n * @default true\n */\n includeTokenAccounts?: boolean;\n}\n\n/**\n * Options for program data collection\n */\nexport interface ProgramCollectionOptions {\n /**\n * Maximum number of accounts to fetch\n * @default 100\n */\n maxAccounts?: number;\n\n /**\n * Maximum number of related transactions to fetch\n * @default 50\n */\n maxTransactions?: number;\n}\n\n/**\n * Collects raw wallet data from Solana RPC\n */\nexport async function collectWalletData(\n client: RPCClient,\n address: string,\n options: WalletCollectionOptions = {}\n): Promise<RawWalletData> {\n const maxSignatures = options.maxSignatures ?? 100;\n const includeTokenAccounts = options.includeTokenAccounts ?? true;\n\n // Fetch recent signatures (bounded)\n const signatures = await client.getSignaturesForAddress(address, {\n limit: maxSignatures,\n });\n\n // Fetch transactions for all signatures\n const transactions: RawTransaction[] = [];\n \n // Process in batches to avoid overwhelming the RPC\n const BATCH_SIZE = 10;\n for (let i = 0; i < signatures.length; i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n transactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n }\n\n // Fetch token accounts\n let tokenAccounts: TokenAccountBalancePair[] = [];\n if (includeTokenAccounts) {\n try {\n const response = await client.getTokenAccountsByOwner(address);\n tokenAccounts = response.value;\n } catch (error) {\n // Token accounts might not exist or query might fail - that's okay\n console.warn(`Failed to fetch token accounts for ${address}:`, error);\n }\n }\n\n return {\n address,\n signatures,\n transactions,\n tokenAccounts,\n };\n}\n\n/**\n * Collects raw transaction data from Solana RPC\n */\nexport async function collectTransactionData(\n client: RPCClient,\n signature: string\n): Promise<RawTransactionData> {\n // Fetch full transaction with metadata\n const transaction = await client.getTransaction(signature, {\n maxSupportedTransactionVersion: 0,\n });\n\n return {\n signature,\n transaction: transaction as ParsedTransactionWithMeta | null,\n blockTime: transaction?.blockTime ?? null,\n };\n}\n\n/**\n * Collects raw program data from Solana RPC\n */\nexport async function collectProgramData(\n client: RPCClient,\n programId: string,\n options: ProgramCollectionOptions = {}\n): Promise<RawProgramData> {\n const maxAccounts = options.maxAccounts ?? 100;\n const maxTransactions = options.maxTransactions ?? 50;\n\n // Fetch program accounts (limited)\n let accounts: Array<{ pubkey: string; account: any }> = [];\n try {\n const response = await client.getProgramAccounts(programId, {\n encoding: 'jsonParsed',\n dataSlice: { offset: 0, length: 0 }, // Don't fetch full account data\n });\n\n accounts = response.slice(0, maxAccounts).map((acc) => ({\n pubkey: acc.pubkey.toString(),\n account: acc.account,\n }));\n } catch (error) {\n console.warn(`Failed to fetch program accounts for ${programId}:`, error);\n }\n\n // Fetch recent signatures for the program\n let signatures: ConfirmedSignatureInfo[] = [];\n try {\n signatures = await client.getSignaturesForAddress(programId, {\n limit: maxTransactions,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for program ${programId}:`, error);\n }\n\n // Fetch transactions for those signatures\n const relatedTransactions: RawTransaction[] = [];\n const BATCH_SIZE = 10;\n \n for (let i = 0; i < Math.min(signatures.length, maxTransactions); i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n relatedTransactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for program ${programId}:`, error);\n }\n }\n\n return {\n programId,\n accounts,\n relatedTransactions,\n };\n}\n", "import type {\n Transfer,\n NormalizedInstruction,\n InstructionCategory,\n ScanContext,\n LabelProvider,\n} from '../types/index.js';\nimport type {\n RawWalletData,\n RawTransactionData,\n RawProgramData,\n} from '../collectors/index.js';\nimport type { ParsedTransactionWithMeta, PartiallyDecodedInstruction, ParsedInstruction } from '@solana/web3.js';\n\n/**\n * Known program IDs for instruction categorization\n */\nconst PROGRAM_IDS = {\n SYSTEM: '11111111111111111111111111111111',\n TOKEN: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',\n ASSOCIATED_TOKEN: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',\n STAKE: 'Stake11111111111111111111111111111111111111',\n VOTE: 'Vote111111111111111111111111111111111111111',\n MEMO: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',\n MEMO_V1: 'Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo',\n};\n\n/**\n * Extract SOL transfers from a parsed transaction\n */\nfunction extractSOLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.transaction) {\n return transfers;\n }\n\n const preBalances = tx.meta.preBalances;\n const postBalances = tx.meta.postBalances;\n const accountKeys = tx.transaction.message.accountKeys;\n\n // Defensive checks\n if (!accountKeys || !Array.isArray(accountKeys) || accountKeys.length === 0) {\n return transfers;\n }\n\n if (!preBalances || !postBalances) {\n return transfers;\n }\n\n // Compare pre and post balances to find transfers\n for (let i = 0; i < accountKeys.length; i++) {\n const pre = preBalances[i];\n const post = postBalances[i];\n \n // Skip if balances are undefined\n if (pre === undefined || post === undefined) {\n continue;\n }\n \n const diff = post - pre;\n\n // Skip if no change or if it's fee-related (usually account 0)\n if (diff === 0) continue;\n\n const account = accountKeys[i];\n if (!account) continue;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) continue;\n\n // If balance increased, this account received SOL\n if (diff > 0) {\n // Find who sent it (simple heuristic: first account with decreased balance)\n for (let j = 0; j < accountKeys.length; j++) {\n const preSender = preBalances[j];\n const postSender = postBalances[j];\n \n if (preSender === undefined || postSender === undefined) {\n continue;\n }\n \n if (postSender < preSender) {\n const sender = accountKeys[j];\n if (!sender) continue;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) continue;\n \n transfers.push({\n from: senderAddress,\n to: address,\n amount: diff / 1e9, // Convert lamports to SOL\n token: undefined,\n signature,\n blockTime: tx.blockTime,\n });\n break;\n }\n }\n }\n }\n\n return transfers;\n}\n\n/**\n * Extract SPL token transfers from a parsed transaction\n */\nfunction extractSPLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.meta.postTokenBalances || !tx.meta.preTokenBalances) {\n return transfers;\n }\n\n const preTokenBalances = tx.meta.preTokenBalances;\n const postTokenBalances = tx.meta.postTokenBalances;\n\n // Create a map of account index to token balance changes\n const balanceChanges = new Map<number, { mint: string; change: number; decimals: number }>();\n\n // Calculate changes\n for (const post of postTokenBalances) {\n const pre = preTokenBalances.find(\n (p) => p.accountIndex === post.accountIndex && p.mint === post.mint\n );\n\n const preAmount = pre?.uiTokenAmount.uiAmount ?? 0;\n const postAmount = post.uiTokenAmount.uiAmount ?? 0;\n const change = postAmount - preAmount;\n\n if (change !== 0) {\n balanceChanges.set(post.accountIndex, {\n mint: post.mint,\n change,\n decimals: post.uiTokenAmount.decimals,\n });\n }\n }\n\n // Match senders and receivers\n const accountKeys = tx.transaction.message.accountKeys;\n \n if (!accountKeys || !Array.isArray(accountKeys)) {\n return transfers;\n }\n \n balanceChanges.forEach((info, accountIndex) => {\n if (accountIndex >= accountKeys.length) {\n return; // Skip if index out of bounds\n }\n \n const account = accountKeys[accountIndex];\n if (!account) return;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) return;\n\n if (info.change > 0) {\n // This account received tokens - find sender\n balanceChanges.forEach((senderInfo, senderIndex) => {\n if (\n senderInfo.mint === info.mint &&\n senderInfo.change < 0 &&\n senderIndex !== accountIndex &&\n senderIndex < accountKeys.length\n ) {\n const sender = accountKeys[senderIndex];\n if (!sender) return;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) return;\n\n transfers.push({\n from: senderAddress,\n to: address,\n amount: info.change,\n token: info.mint,\n signature,\n blockTime: tx.blockTime,\n });\n }\n });\n }\n });\n\n return transfers;\n}\n\n/**\n * Categorize an instruction based on program ID and instruction type\n */\nfunction categorizeInstruction(\n instruction: ParsedInstruction | PartiallyDecodedInstruction\n): InstructionCategory {\n const programId = instruction.programId.toString();\n\n // System program instructions\n if (programId === PROGRAM_IDS.SYSTEM) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferWithSeed') {\n return 'transfer';\n }\n }\n return 'transfer';\n }\n\n // Token program instructions\n if (programId === PROGRAM_IDS.TOKEN || programId === PROGRAM_IDS.ASSOCIATED_TOKEN) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferChecked') {\n return 'transfer';\n }\n return 'token_operation';\n }\n return 'token_operation';\n }\n\n // Stake program\n if (programId === PROGRAM_IDS.STAKE) {\n return 'stake';\n }\n\n // Vote program\n if (programId === PROGRAM_IDS.VOTE) {\n return 'vote';\n }\n\n // Check for common swap programs (simplified heuristic)\n if (\n programId.includes('Swap') ||\n programId.includes('swap') ||\n programId === 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' || // Jupiter\n programId === 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc' // Orca Whirlpool\n ) {\n return 'swap';\n }\n\n // Default to program interaction\n return 'program_interaction';\n}\n\n/**\n * Extract normalized instructions from a transaction\n */\nfunction extractInstructions(\n tx: ParsedTransactionWithMeta,\n signature: string\n): NormalizedInstruction[] {\n const instructions: NormalizedInstruction[] = [];\n\n if (!tx.transaction || !tx.transaction.message) {\n return instructions;\n }\n\n const message = tx.transaction.message;\n const allInstructions = message.instructions;\n\n // Defensive check\n if (!allInstructions || !Array.isArray(allInstructions)) {\n return instructions;\n }\n\n for (const instruction of allInstructions) {\n // Defensive check for programId\n if (!instruction || !instruction.programId) {\n continue;\n }\n \n const programId = instruction.programId.toString();\n const category = categorizeInstruction(instruction);\n\n let data: Record<string, unknown> | undefined;\n if ('parsed' in instruction) {\n data = instruction.parsed as Record<string, unknown>;\n }\n\n instructions.push({\n programId,\n category,\n signature,\n blockTime: tx.blockTime,\n data,\n });\n }\n\n return instructions;\n}\n\n/**\n * Extract unique counterparties from transfers\n */\nfunction extractCounterparties(transfers: Transfer[], targetAddress: string): Set<string> {\n const counterparties = new Set<string>();\n\n for (const transfer of transfers) {\n // Add the other party in the transfer\n if (transfer.from === targetAddress) {\n counterparties.add(transfer.to);\n } else if (transfer.to === targetAddress) {\n counterparties.add(transfer.from);\n }\n }\n\n return counterparties;\n}\n\n/**\n * Calculate time range from transactions\n */\nfunction calculateTimeRange(transactions: RawTransactionData[]): {\n earliest: number | null;\n latest: number | null;\n} {\n let earliest: number | null = null;\n let latest: number | null = null;\n\n for (const tx of transactions) {\n if (tx.blockTime) {\n if (earliest === null || tx.blockTime < earliest) {\n earliest = tx.blockTime;\n }\n if (latest === null || tx.blockTime > latest) {\n latest = tx.blockTime;\n }\n }\n }\n\n return { earliest, latest };\n}\n\n/**\n * Normalize wallet data into a ScanContext\n */\nexport function normalizeWalletData(\n rawData: RawWalletData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n\n // Process each transaction\n for (const rawTx of rawData.transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n } catch (error) {\n // Skip problematic transactions but continue processing\n console.warn(`Failed to normalize transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Extract counterparties\n const counterparties = extractCounterparties(allTransfers, rawData.address);\n\n // Look up labels for counterparties if provider is available\n const labels = labelProvider \n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n // Calculate time range\n const timeRange = calculateTimeRange(rawData.transactions);\n\n // Normalize token accounts\n const tokenAccounts = rawData.tokenAccounts.map((ta) => {\n try {\n return {\n mint: ta.account.data.parsed.info.mint,\n address: ta.pubkey.toString(),\n balance: ta.account.data.parsed.info.tokenAmount.uiAmount ?? 0,\n };\n } catch (error) {\n // Skip malformed token accounts\n return null;\n }\n }).filter((ta): ta is NonNullable<typeof ta> => ta !== null);\n\n return {\n target: rawData.address,\n targetType: 'wallet',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts,\n timeRange,\n transactionCount: rawData.transactions.length,\n };\n}\n\n/**\n * Normalize transaction data into a ScanContext\n */\nexport function normalizeTransactionData(\n rawData: RawTransactionData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n if (rawData.transaction) {\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawData.transaction, rawData.signature);\n const splTransfers = extractSPLTransfers(rawData.transaction, rawData.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawData.transaction, rawData.signature);\n allInstructions.push(...instructions);\n\n // Extract all unique addresses involved\n const accountKeys = rawData.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize transaction ${rawData.signature}:`, error);\n }\n }\n\n return {\n target: rawData.signature,\n targetType: 'transaction',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange: {\n earliest: rawData.blockTime,\n latest: rawData.blockTime,\n },\n transactionCount: 1,\n };\n}\n\n/**\n * Normalize program data into a ScanContext\n */\nexport function normalizeProgramData(\n rawData: RawProgramData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n // Process related transactions\n for (const rawTx of rawData.relatedTransactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n\n // Extract counterparties\n const accountKeys = rawTx.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize program transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Calculate time range\n const timeRange = calculateTimeRange(rawData.relatedTransactions);\n\n // Look up labels for counterparties\n const labels = labelProvider\n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n return {\n target: rawData.programId,\n targetType: 'program',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange,\n transactionCount: rawData.relatedTransactions.length,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if a wallet frequently interacts with the same counterparties\n * This can enable clustering and linking of addresses\n */\nexport function detectCounterpartyReuse(context: ScanContext): RiskSignal | null {\n // Only applicable to wallet scans\n if (context.targetType !== 'wallet') {\n return null;\n }\n\n if (context.counterparties.size === 0 || context.transfers.length === 0) {\n return null;\n }\n\n // Count interactions per counterparty\n const interactionCounts = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const counterparty = transfer.from === context.target ? transfer.to : transfer.from;\n if (counterparty === context.target) continue;\n \n interactionCounts.set(counterparty, (interactionCounts.get(counterparty) || 0) + 1);\n }\n\n // Find counterparties with multiple interactions\n const reusedCounterparties = Array.from(interactionCounts.entries())\n .filter(([_, count]) => count >= 3)\n .sort((a, b) => b[1] - a[1]);\n\n if (reusedCounterparties.length === 0) {\n return null;\n }\n\n // Calculate severity based on concentration\n const totalInteractions = context.transfers.length;\n const topCounterpartyInteractions = reusedCounterparties[0][1];\n const concentration = topCounterpartyInteractions / totalInteractions;\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if (concentration > 0.5 || reusedCounterparties.length >= 5) {\n severity = 'HIGH';\n } else if (concentration > 0.3 || reusedCounterparties.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = reusedCounterparties.slice(0, 5).map(([addr, count]) => ({\n type: 'address',\n description: `${count} interactions with ${addr.slice(0, 8)}...${addr.slice(-8)}`,\n data: { address: addr, interactionCount: count },\n }));\n\n return {\n id: 'counterparty-reuse',\n name: 'Counterparty Reuse',\n severity,\n reason: `Wallet repeatedly interacts with ${reusedCounterparties.length} address(es)`,\n impact: 'Repeated interactions with the same addresses can be used to cluster wallets and build transaction graphs, enabling surveillance of your activity patterns.',\n evidence,\n mitigation: 'Use different wallets for different counterparties, or use privacy-preserving protocols that obscure transaction graphs.',\n confidence: 0.9,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if the wallet sends or receives suspiciously similar amounts\n * Round numbers or repeated exact amounts can be used for fingerprinting\n */\nexport function detectAmountReuse(context: ScanContext): RiskSignal | null {\n if (context.transfers.length < 3) {\n return null;\n }\n\n // Group amounts (rounded to avoid floating point issues)\n const amountCounts = new Map<string, number>();\n const roundNumbers: number[] = [];\n\n for (const transfer of context.transfers) {\n // Check for round numbers (e.g., 1.0, 10.0, 100.0)\n if (transfer.amount > 0 && Number.isInteger(transfer.amount) && transfer.amount >= 1) {\n roundNumbers.push(transfer.amount);\n }\n\n // Count exact amount reuse\n const amountKey = `${transfer.amount.toFixed(9)}-${transfer.token || 'SOL'}`;\n amountCounts.set(amountKey, (amountCounts.get(amountKey) || 0) + 1);\n }\n\n // Find amounts used multiple times\n const reusedAmounts = Array.from(amountCounts.entries())\n .filter(([_, count]) => count >= 2)\n .sort((a, b) => b[1] - a[1]);\n\n const hasRoundNumbers = roundNumbers.length >= 2;\n const hasReusedAmounts = reusedAmounts.length >= 2;\n\n if (!hasRoundNumbers && !hasReusedAmounts) {\n return null;\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if ((hasRoundNumbers && roundNumbers.length >= 5) || reusedAmounts.length >= 5) {\n severity = 'HIGH';\n } else if ((hasRoundNumbers && roundNumbers.length >= 3) || reusedAmounts.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = [];\n\n if (hasRoundNumbers) {\n evidence.push({\n type: 'amount',\n description: `${roundNumbers.length} round-number transfers detected`,\n data: { roundNumbers: roundNumbers.slice(0, 5) },\n });\n }\n\n if (hasReusedAmounts) {\n const topReused = reusedAmounts.slice(0, 3);\n for (const [amountKey, count] of topReused) {\n const [amount, token] = amountKey.split('-');\n evidence.push({\n type: 'amount',\n description: `Amount ${amount} ${token} used ${count} times`,\n data: { amount: parseFloat(amount), token, count },\n });\n }\n }\n\n return {\n id: 'amount-reuse',\n name: 'Deterministic Amount Patterns',\n severity,\n reason: `Wallet uses ${hasRoundNumbers ? 'round numbers' : 'repeated amounts'} in transactions`,\n impact: 'Using the same amounts repeatedly or sending round numbers creates fingerprints that can be used to link transactions and identify patterns in your activity.',\n evidence,\n mitigation: 'Vary transaction amounts slightly, avoid round numbers, and consider using privacy protocols that obscure amounts.',\n confidence: hasRoundNumbers ? 0.85 : 0.75,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect transaction bursts and timing patterns\n * Concentrated activity creates temporal fingerprints\n */\nexport function detectTimingPatterns(context: ScanContext): RiskSignal | null {\n if (!context.timeRange.earliest || !context.timeRange.latest) {\n return null;\n }\n\n if (context.transactionCount < 3) {\n return null;\n }\n\n // Calculate time span in hours\n const timeSpanSeconds = context.timeRange.latest - context.timeRange.earliest;\n const timeSpanHours = timeSpanSeconds / 3600;\n\n if (timeSpanHours === 0) {\n return null;\n }\n\n // Calculate transaction rate (txs per hour)\n const txRate = context.transactionCount / timeSpanHours;\n\n // Detect burst activity (high tx rate in short time)\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n let isBurst = false;\n\n if (txRate > 10) {\n severity = 'HIGH';\n isBurst = true;\n } else if (txRate > 5) {\n severity = 'MEDIUM';\n isBurst = true;\n } else if (timeSpanHours < 1 && context.transactionCount >= 3) {\n severity = 'MEDIUM';\n isBurst = true;\n }\n\n if (!isBurst) {\n return null;\n }\n\n const evidence: Evidence[] = [\n {\n type: 'timing',\n description: `${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n data: {\n transactionCount: context.transactionCount,\n timeSpanHours: timeSpanHours.toFixed(2),\n transactionRate: txRate.toFixed(2),\n },\n },\n ];\n\n return {\n id: 'timing-correlation',\n name: 'Transaction Burst Pattern',\n severity,\n reason: `Concentrated activity: ${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n impact: 'Concentrated transaction activity creates timing fingerprints that can be used to correlate your transactions and link them to specific events or behaviors.',\n evidence,\n mitigation: 'Spread transactions over longer time periods, use scheduled transactions, or batch operations to reduce timing correlation.',\n confidence: 0.8,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect interactions with known entities (CEXs, bridges, etc.)\n * These entities can link on-chain and off-chain identities\n */\nexport function detectKnownEntityInteraction(context: ScanContext): RiskSignal | null {\n if (context.labels.size === 0) {\n return null;\n }\n\n const entityInteractions: Evidence[] = [];\n\n for (const [address, label] of context.labels.entries()) {\n // Count interactions with this entity\n let interactionCount = 0;\n const relatedTxs: string[] = [];\n\n for (const transfer of context.transfers) {\n if (transfer.from === address || transfer.to === address) {\n interactionCount++;\n if (relatedTxs.length < 3) {\n relatedTxs.push(transfer.signature);\n }\n }\n }\n\n if (interactionCount > 0) {\n entityInteractions.push({\n type: 'label',\n description: `${interactionCount} interaction(s) with ${label.name} (${label.type})`,\n data: {\n entityName: label.name,\n entityType: label.type,\n address,\n interactionCount,\n transactions: relatedTxs,\n },\n reference: address,\n });\n }\n }\n\n if (entityInteractions.length === 0) {\n return null;\n }\n\n // Severity based on entity type and interaction count\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n \n const hasExchangeInteraction = Array.from(context.labels.values())\n .some(label => label.type === 'exchange');\n \n if (hasExchangeInteraction) {\n severity = 'HIGH';\n } else if (entityInteractions.length >= 3) {\n severity = 'HIGH';\n }\n\n return {\n id: 'known-entity-interaction',\n name: 'Known Entity Interaction',\n severity,\n reason: `Wallet interacted with ${entityInteractions.length} known entit${entityInteractions.length === 1 ? 'y' : 'ies'}`,\n impact: 'Interactions with centralized exchanges, bridges, or other known entities can link your on-chain address to your real-world identity through KYC data, IP addresses, and off-chain records.',\n evidence: entityInteractions,\n mitigation: 'Use privacy-preserving bridges, avoid direct CEX interactions from privacy-sensitive wallets, or use intermediate wallets to break the link.',\n confidence: 0.95,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if wallet balances can be easily traced\n * Full balance transfers or predictable balance changes reduce privacy\n */\nexport function detectBalanceTraceability(context: ScanContext): RiskSignal | null {\n if (context.targetType !== 'wallet' || context.transfers.length < 2) {\n return null;\n }\n\n const fullBalanceTransfers: Evidence[] = [];\n const suspiciousPatterns: string[] = [];\n\n // Look for transfers that represent full balance movements\n // (This is simplified - real implementation would need balance tracking)\n \n // Check for transfers where send and receive amounts match closely\n const amountPairs = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const amountKey = transfer.amount.toFixed(6);\n amountPairs.set(amountKey, (amountPairs.get(amountKey) || 0) + 1);\n }\n\n // Find matching send/receive pairs\n const matchingPairs = Array.from(amountPairs.entries())\n .filter(([_, count]) => count >= 2);\n\n if (matchingPairs.length >= 2) {\n suspiciousPatterns.push('Multiple matching send/receive amounts detected');\n }\n\n // Check for sequential transfers of similar amounts\n for (let i = 0; i < context.transfers.length - 1; i++) {\n const current = context.transfers[i];\n const next = context.transfers[i + 1];\n \n if (current.blockTime && next.blockTime) {\n const timeDiff = Math.abs((next.blockTime - current.blockTime));\n \n // If similar amounts within 1 hour\n if (timeDiff < 3600 && Math.abs(current.amount - next.amount) < current.amount * 0.1) {\n suspiciousPatterns.push('Sequential transfers of similar amounts');\n break;\n }\n }\n }\n\n if (suspiciousPatterns.length === 0 && matchingPairs.length === 0) {\n return null;\n }\n\n const evidence: Evidence[] = [];\n\n if (matchingPairs.length > 0) {\n evidence.push({\n type: 'pattern',\n description: `${matchingPairs.length} matching send/receive amount pair(s)`,\n data: { matchingPairs: matchingPairs.length },\n });\n }\n\n for (const pattern of suspiciousPatterns) {\n evidence.push({\n type: 'pattern',\n description: pattern,\n data: {},\n });\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n if (matchingPairs.length >= 3 || suspiciousPatterns.length >= 2) {\n severity = 'HIGH';\n }\n\n return {\n id: 'balance-traceability',\n name: 'Balance Traceability',\n severity,\n reason: 'Wallet shows patterns that enable balance tracking',\n impact: 'Traceable balance movements allow observers to follow funds through the blockchain, linking your transactions and revealing your financial activity.',\n evidence,\n mitigation: 'Split large transfers into multiple smaller ones, introduce timing delays, or use privacy protocols that obscure amounts.',\n confidence: 0.7,\n };\n}\n", "import type { ScanContext, RiskSignal, RiskLevel, PrivacyReport } from '../types/index.js';\nimport {\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n} from '../heuristics/index.js';\n\n/**\n * Current report schema version\n */\nconst REPORT_VERSION = '1.0.0';\n\n/**\n * All available heuristic functions\n */\nconst HEURISTICS = [\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n];\n\n/**\n * Calculate overall risk level from individual signals\n * Uses deterministic scoring based on severity and count\n */\nfunction calculateOverallRisk(signals: RiskSignal[]): RiskLevel {\n if (signals.length === 0) {\n return 'LOW';\n }\n\n // Count signals by severity\n const highCount = signals.filter(s => s.severity === 'HIGH').length;\n const mediumCount = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowCount = signals.filter(s => s.severity === 'LOW').length;\n\n // Deterministic thresholds\n if (highCount >= 2 || (highCount >= 1 && mediumCount >= 2)) {\n return 'HIGH';\n }\n\n if (highCount >= 1 || mediumCount >= 2 || (mediumCount >= 1 && lowCount >= 2)) {\n return 'MEDIUM';\n }\n\n return 'LOW';\n}\n\n/**\n * Generate general mitigation recommendations based on detected signals\n */\nfunction generateMitigations(signals: RiskSignal[]): string[] {\n const mitigations = new Set<string>();\n\n if (signals.length === 0) {\n return ['Continue practicing good privacy hygiene to maintain low exposure.'];\n }\n\n // Add general recommendations\n mitigations.add('Consider using multiple wallets to compartmentalize different activities.');\n \n // Check for specific signal types and add relevant mitigations\n const signalIds = new Set(signals.map(s => s.id));\n\n if (signalIds.has('known-entity-interaction')) {\n mitigations.add('Avoid direct interactions between privacy-sensitive wallets and KYC services.');\n }\n\n if (signalIds.has('counterparty-reuse')) {\n mitigations.add('Use different addresses for different counterparties or contexts.');\n }\n\n if (signalIds.has('timing-correlation') || signalIds.has('balance-traceability')) {\n mitigations.add('Introduce timing delays and vary transaction patterns to reduce correlation.');\n }\n\n if (signalIds.has('amount-reuse')) {\n mitigations.add('Vary transaction amounts to avoid creating fingerprints.');\n }\n\n // Always add this general advice\n mitigations.add('Research and consider privacy-preserving protocols when available.');\n\n return Array.from(mitigations);\n}\n\n/**\n * Evaluate all heuristics against a scan context\n */\nexport function evaluateHeuristics(context: ScanContext): RiskSignal[] {\n const signals: RiskSignal[] = [];\n\n for (const heuristic of HEURISTICS) {\n try {\n const signal = heuristic(context);\n if (signal) {\n signals.push(signal);\n }\n } catch (error) {\n // Log but don't fail if a heuristic errors\n console.warn(`Heuristic evaluation failed:`, error);\n }\n }\n\n // Sort signals by severity (HIGH -> MEDIUM -> LOW) for deterministic ordering\n signals.sort((a, b) => {\n const severityOrder = { HIGH: 0, MEDIUM: 1, LOW: 2 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n\n return signals;\n}\n\n/**\n * Generate a complete privacy report from a scan context\n */\nexport function generateReport(context: ScanContext): PrivacyReport {\n // Evaluate all heuristics\n const signals = evaluateHeuristics(context);\n\n // Calculate overall risk\n const overallRisk = calculateOverallRisk(signals);\n\n // Count signals by severity\n const highRiskSignals = signals.filter(s => s.severity === 'HIGH').length;\n const mediumRiskSignals = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowRiskSignals = signals.filter(s => s.severity === 'LOW').length;\n\n // Generate mitigations\n const mitigations = generateMitigations(signals);\n\n // Extract known entities from context\n const knownEntities = Array.from(context.labels.values());\n\n return {\n version: REPORT_VERSION,\n timestamp: Date.now(),\n targetType: context.targetType,\n target: context.target,\n overallRisk,\n signals,\n summary: {\n totalSignals: signals.length,\n highRiskSignals,\n mediumRiskSignals,\n lowRiskSignals,\n transactionsAnalyzed: context.transactionCount,\n },\n mitigations,\n knownEntities,\n };\n}\n", "import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport type { Label, LabelProvider } from '../types/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Static JSON label provider\n * Loads labels from a curated JSON file\n */\nexport class StaticLabelProvider implements LabelProvider {\n private labels: Map<string, Label>;\n\n constructor(labelsPath?: string) {\n this.labels = new Map();\n this.loadLabels(labelsPath);\n }\n\n /**\n * Load labels from JSON file\n */\n private loadLabels(customPath?: string): void {\n try {\n const path = customPath || join(__dirname, 'known-addresses.json');\n const data = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(data);\n\n if (!parsed.labels || !Array.isArray(parsed.labels)) {\n console.warn('Invalid labels file format');\n return;\n }\n\n for (const label of parsed.labels) {\n if (label.address && label.name && label.type) {\n this.labels.set(label.address, {\n address: label.address,\n name: label.name,\n type: label.type,\n description: label.description,\n relatedAddresses: label.relatedAddresses,\n });\n }\n }\n\n console.debug(`Loaded ${this.labels.size} address labels`);\n } catch (error) {\n console.warn('Failed to load labels file:', error);\n }\n }\n\n /**\n * Look up a label for an address\n */\n lookup(address: string): Label | null {\n return this.labels.get(address) || null;\n }\n\n /**\n * Look up multiple addresses at once\n */\n lookupMany(addresses: string[]): Map<string, Label> {\n const results = new Map<string, Label>();\n\n for (const address of addresses) {\n const label = this.lookup(address);\n if (label) {\n results.set(address, label);\n }\n }\n\n return results;\n }\n\n /**\n * Get all loaded labels\n */\n getAllLabels(): Label[] {\n return Array.from(this.labels.values());\n }\n\n /**\n * Get count of loaded labels\n */\n getCount(): number {\n return this.labels.size;\n }\n}\n\n/**\n * Create a default label provider instance\n */\nexport function createDefaultLabelProvider(): StaticLabelProvider {\n return new StaticLabelProvider();\n}\n", "// Export all types\nexport * from './types/index.js';\n\n// Export RPC client\nexport * from './rpc/index.js';\n\n// Export data collectors\nexport * from './collectors/index.js';\n\n// Export normalizer\nexport * from './normalizer/index.js';\n\n// Export heuristics\nexport * from './heuristics/index.js';\n\n// Export scanner (report generation)\nexport * from './scanner/index.js';\n\n// Export label provider\nexport * from './labels/index.js';\n\n// Export version\nexport const VERSION = '0.1.0';\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Connection, ConnectionConfig } from '@solana/web3.js';\n\n/**\n * Configuration for the RPC client\n */\nexport interface RPCClientConfig {\n /**\n * RPC endpoint URL (Helius, QuickNode, or any Solana-compatible RPC)\n */\n rpcUrl: string;\n\n /**\n * Maximum number of retries for failed requests\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay for exponential backoff (ms)\n * @default 1000\n */\n retryDelay?: number;\n\n /**\n * Request timeout in milliseconds\n * @default 30000\n */\n timeout?: number;\n\n /**\n * Maximum number of concurrent requests\n * @default 10\n */\n maxConcurrency?: number;\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\n/**\n * Rate limiter to control concurrent requests\n */\nclass RateLimiter {\n private activeRequests = 0;\n private queue: Array<() => void> = [];\n\n constructor(private maxConcurrency: number) {}\n\n async acquire(): Promise<void> {\n if (this.activeRequests < this.maxConcurrency) {\n this.activeRequests++;\n return;\n }\n\n return new Promise((resolve) => {\n this.queue.push(() => {\n this.activeRequests++;\n resolve();\n });\n });\n }\n\n release(): void {\n this.activeRequests--;\n const next = this.queue.shift();\n if (next) {\n next();\n }\n }\n\n getActiveCount(): number {\n return this.activeRequests;\n }\n\n getQueueLength(): number {\n return this.queue.length;\n }\n}\n\n/**\n * Sleep utility for retry delays\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * RPC client wrapper with rate limiting and retry logic\n * \n * This class wraps the Solana Connection and provides:\n * - Automatic retries with exponential backoff\n * - Rate limiting for concurrent requests\n * - Centralized error handling\n * - No CLI or UI logic\n */\nexport class RPCClient {\n private connection: Connection;\n private config: Required<RPCClientConfig>;\n private rateLimiter: RateLimiter;\n\n constructor(configOrUrl: RPCClientConfig | string) {\n // Handle both string URL and config object\n const config: RPCClientConfig = typeof configOrUrl === 'string' \n ? { rpcUrl: configOrUrl }\n : configOrUrl;\n \n // Trim the RPC URL to handle trailing/leading whitespace\n const rpcUrl = config.rpcUrl.trim();\n \n this.config = {\n maxRetries: config.maxRetries ?? 3,\n retryDelay: config.retryDelay ?? 1000,\n timeout: config.timeout ?? 30000,\n maxConcurrency: config.maxConcurrency ?? 10,\n debug: config.debug ?? false,\n rpcUrl,\n };\n\n const connectionConfig: ConnectionConfig = {\n commitment: 'confirmed',\n confirmTransactionInitialTimeout: this.config.timeout,\n };\n\n this.connection = new Connection(this.config.rpcUrl, connectionConfig);\n this.rateLimiter = new RateLimiter(this.config.maxConcurrency);\n\n if (this.config.debug) {\n console.log(`[RPCClient] Initialized with URL: ${this.config.rpcUrl}`);\n console.log(`[RPCClient] Max concurrency: ${this.config.maxConcurrency}`);\n console.log(`[RPCClient] Max retries: ${this.config.maxRetries}`);\n }\n }\n\n /**\n * Execute an RPC call with retry logic and rate limiting\n */\n private async executeWithRetry<T>(\n operation: () => Promise<T>,\n operationName: string\n ): Promise<T> {\n await this.rateLimiter.acquire();\n\n let lastError: Error | null = null;\n \n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n if (this.config.debug && attempt > 0) {\n console.log(`[RPCClient] Retry attempt ${attempt} for ${operationName}`);\n }\n\n const result = await operation();\n this.rateLimiter.release();\n return result;\n } catch (error) {\n lastError = error as Error;\n\n if (this.config.debug) {\n console.error(\n `[RPCClient] Error in ${operationName} (attempt ${attempt + 1}/${this.config.maxRetries + 1}):`,\n error\n );\n }\n\n // Don't retry on last attempt\n if (attempt < this.config.maxRetries) {\n // Exponential backoff: delay * 2^attempt\n const delay = this.config.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n this.rateLimiter.release();\n throw new Error(\n `RPC operation ${operationName} failed after ${this.config.maxRetries + 1} attempts: ${lastError?.message}`\n );\n }\n\n /**\n * Get the underlying Solana Connection\n * Use this sparingly - prefer the wrapped methods for automatic retry/rate limiting\n */\n getConnection(): Connection {\n return this.connection;\n }\n\n /**\n * Get current rate limiter stats\n */\n getStats(): { activeRequests: number; queueLength: number } {\n return {\n activeRequests: this.rateLimiter.getActiveCount(),\n queueLength: this.rateLimiter.getQueueLength(),\n };\n }\n\n /**\n * Get signatures for an address with retry and rate limiting\n */\n async getSignaturesForAddress(\n address: string,\n options?: {\n limit?: number;\n before?: string;\n until?: string;\n }\n ) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getSignaturesForAddress(\n new PublicKey(address),\n options\n );\n },\n `getSignaturesForAddress(${address})`\n );\n }\n\n /**\n * Get transaction details with retry and rate limiting\n */\n async getTransaction(signature: string, options?: { maxSupportedTransactionVersion?: number }) {\n return this.executeWithRetry(\n async () => {\n return this.connection.getTransaction(signature, {\n maxSupportedTransactionVersion: options?.maxSupportedTransactionVersion ?? 0,\n });\n },\n `getTransaction(${signature})`\n );\n }\n\n /**\n * Get multiple transactions in parallel (respects rate limiting)\n */\n async getTransactions(signatures: string[], options?: { maxSupportedTransactionVersion?: number }): Promise<Array<any>> {\n const promises = signatures.map((sig) => this.getTransaction(sig, options));\n return Promise.all(promises);\n }\n\n /**\n * Get token accounts by owner with retry and rate limiting\n */\n async getTokenAccountsByOwner(ownerAddress: string, mintAddress?: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const owner = new PublicKey(ownerAddress);\n \n // SPL Token Program ID\n const TOKEN_PROGRAM_ID = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');\n \n if (mintAddress) {\n const mint = new PublicKey(mintAddress);\n return this.connection.getTokenAccountsByOwner(owner, { mint });\n } else {\n return this.connection.getTokenAccountsByOwner(owner, {\n programId: TOKEN_PROGRAM_ID,\n });\n }\n },\n `getTokenAccountsByOwner(${ownerAddress})`\n );\n }\n\n /**\n * Get program accounts with retry and rate limiting\n */\n async getProgramAccounts(programId: string, config?: any) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getProgramAccounts(new PublicKey(programId), config);\n },\n `getProgramAccounts(${programId})`\n );\n }\n\n /**\n * Get account info with retry and rate limiting\n */\n async getAccountInfo(address: string) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n return this.connection.getAccountInfo(new PublicKey(address));\n },\n `getAccountInfo(${address})`\n );\n }\n\n /**\n * Get multiple account infos in parallel (respects rate limiting)\n */\n async getMultipleAccountsInfo(addresses: string[]) {\n return this.executeWithRetry(\n async () => {\n const { PublicKey } = await import('@solana/web3.js');\n const pubkeys = addresses.map((addr) => new PublicKey(addr));\n return this.connection.getMultipleAccountsInfo(pubkeys);\n },\n `getMultipleAccountsInfo(${addresses.length} addresses)`\n );\n }\n\n /**\n * Check if the RPC connection is healthy\n */\n async healthCheck(): Promise<boolean> {\n try {\n const version = await this.executeWithRetry(\n () => this.connection.getVersion(),\n 'healthCheck'\n );\n return !!version;\n } catch {\n return false;\n }\n }\n}\n", "import type { RPCClient } from '../rpc/client.js';\nimport type { \n ConfirmedSignatureInfo,\n ParsedTransactionWithMeta,\n TokenAccountBalancePair,\n} from '@solana/web3.js';\n\n/**\n * Raw transaction data fetched from RPC\n */\nexport interface RawTransaction {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw wallet data collection result\n */\nexport interface RawWalletData {\n address: string;\n signatures: ConfirmedSignatureInfo[];\n transactions: RawTransaction[];\n tokenAccounts: TokenAccountBalancePair[];\n}\n\n/**\n * Raw transaction scan result\n */\nexport interface RawTransactionData {\n signature: string;\n transaction: ParsedTransactionWithMeta | null;\n blockTime: number | null;\n}\n\n/**\n * Raw program data collection result\n */\nexport interface RawProgramData {\n programId: string;\n accounts: Array<{\n pubkey: string;\n account: any;\n }>;\n relatedTransactions: RawTransaction[];\n}\n\n/**\n * Options for wallet data collection\n */\nexport interface WalletCollectionOptions {\n /**\n * Maximum number of signatures to fetch\n * @default 100\n */\n maxSignatures?: number;\n\n /**\n * Fetch token accounts\n * @default true\n */\n includeTokenAccounts?: boolean;\n}\n\n/**\n * Options for program data collection\n */\nexport interface ProgramCollectionOptions {\n /**\n * Maximum number of accounts to fetch\n * @default 100\n */\n maxAccounts?: number;\n\n /**\n * Maximum number of related transactions to fetch\n * @default 50\n */\n maxTransactions?: number;\n}\n\n/**\n * Collects raw wallet data from Solana RPC\n */\nexport async function collectWalletData(\n client: RPCClient,\n address: string,\n options: WalletCollectionOptions = {}\n): Promise<RawWalletData> {\n const maxSignatures = options.maxSignatures ?? 100;\n const includeTokenAccounts = options.includeTokenAccounts ?? true;\n\n // Fetch recent signatures (bounded)\n let signatures = [];\n try {\n signatures = await client.getSignaturesForAddress(address, {\n limit: maxSignatures,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for ${address}:`, error);\n }\n\n // Fetch transactions for all signatures\n const transactions: RawTransaction[] = [];\n \n // Process in batches to avoid overwhelming the RPC\n const BATCH_SIZE = 10;\n for (let i = 0; i < signatures.length; i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n transactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for ${address}:`, error);\n }\n }\n\n // Fetch token accounts\n let tokenAccounts: TokenAccountBalancePair[] = [];\n if (includeTokenAccounts) {\n try {\n const response = await client.getTokenAccountsByOwner(address);\n tokenAccounts = response.value;\n } catch (error) {\n // Token accounts might not exist or query might fail - that's okay\n console.warn(`Failed to fetch token accounts for ${address}:`, error);\n }\n }\n\n return {\n address,\n signatures,\n transactions,\n tokenAccounts,\n };\n}\n\n/**\n * Collects raw transaction data from Solana RPC\n */\nexport async function collectTransactionData(\n client: RPCClient,\n signature: string\n): Promise<RawTransactionData> {\n // Fetch full transaction with metadata\n let transaction = null;\n try {\n transaction = await client.getTransaction(signature, {\n maxSupportedTransactionVersion: 0,\n });\n } catch (error) {\n console.warn(`Failed to fetch transaction ${signature}:`, error);\n }\n\n return {\n signature,\n transaction: transaction as ParsedTransactionWithMeta | null,\n blockTime: transaction?.blockTime ?? null,\n };\n}\n\n/**\n * Collects raw program data from Solana RPC\n */\nexport async function collectProgramData(\n client: RPCClient,\n programId: string,\n options: ProgramCollectionOptions = {}\n): Promise<RawProgramData> {\n const maxAccounts = options.maxAccounts ?? 100;\n const maxTransactions = options.maxTransactions ?? 50;\n\n // Fetch program accounts (limited)\n let accounts: Array<{ pubkey: string; account: any }> = [];\n try {\n const response = await client.getProgramAccounts(programId, {\n encoding: 'jsonParsed',\n dataSlice: { offset: 0, length: 0 }, // Don't fetch full account data\n });\n\n accounts = response.slice(0, maxAccounts).map((acc) => ({\n pubkey: acc.pubkey.toString(),\n account: acc.account,\n }));\n } catch (error) {\n console.warn(`Failed to fetch program accounts for ${programId}:`, error);\n }\n\n // Fetch recent signatures for the program\n let signatures: ConfirmedSignatureInfo[] = [];\n try {\n signatures = await client.getSignaturesForAddress(programId, {\n limit: maxTransactions,\n });\n } catch (error) {\n console.warn(`Failed to fetch signatures for program ${programId}:`, error);\n }\n\n // Fetch transactions for those signatures\n const relatedTransactions: RawTransaction[] = [];\n const BATCH_SIZE = 10;\n \n for (let i = 0; i < Math.min(signatures.length, maxTransactions); i += BATCH_SIZE) {\n const batch = signatures.slice(i, i + BATCH_SIZE);\n const batchSignatures = batch.map((sig) => sig.signature);\n \n try {\n const txs = await client.getTransactions(batchSignatures, {\n maxSupportedTransactionVersion: 0,\n });\n\n for (let j = 0; j < batch.length; j++) {\n relatedTransactions.push({\n signature: batch[j].signature,\n transaction: txs[j] as ParsedTransactionWithMeta | null,\n blockTime: batch[j].blockTime,\n });\n }\n } catch (error) {\n console.warn(`Failed to fetch transaction batch for program ${programId}:`, error);\n }\n }\n\n return {\n programId,\n accounts,\n relatedTransactions,\n };\n}\n", "import type {\n Transfer,\n NormalizedInstruction,\n InstructionCategory,\n ScanContext,\n LabelProvider,\n} from '../types/index.js';\nimport type {\n RawWalletData,\n RawTransactionData,\n RawProgramData,\n} from '../collectors/index.js';\nimport type { ParsedTransactionWithMeta, PartiallyDecodedInstruction, ParsedInstruction } from '@solana/web3.js';\n\n/**\n * Known program IDs for instruction categorization\n */\nconst PROGRAM_IDS = {\n SYSTEM: '11111111111111111111111111111111',\n TOKEN: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',\n ASSOCIATED_TOKEN: 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',\n STAKE: 'Stake11111111111111111111111111111111111111',\n VOTE: 'Vote111111111111111111111111111111111111111',\n MEMO: 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',\n MEMO_V1: 'Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo',\n};\n\n/**\n * Extract SOL transfers from a parsed transaction\n */\nfunction extractSOLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.transaction) {\n return transfers;\n }\n\n const preBalances = tx.meta.preBalances;\n const postBalances = tx.meta.postBalances;\n const accountKeys = tx.transaction.message.accountKeys;\n\n // Defensive checks\n if (!accountKeys || !Array.isArray(accountKeys) || accountKeys.length === 0) {\n return transfers;\n }\n\n if (!preBalances || !postBalances) {\n return transfers;\n }\n\n // Compare pre and post balances to find transfers\n for (let i = 0; i < accountKeys.length; i++) {\n const pre = preBalances[i];\n const post = postBalances[i];\n \n // Skip if balances are undefined\n if (pre === undefined || post === undefined) {\n continue;\n }\n \n const diff = post - pre;\n\n // Skip if no change or if it's fee-related (usually account 0)\n if (diff === 0) continue;\n\n const account = accountKeys[i];\n if (!account) continue;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) continue;\n\n // If balance increased, this account received SOL\n if (diff > 0) {\n // Find who sent it (simple heuristic: first account with decreased balance)\n for (let j = 0; j < accountKeys.length; j++) {\n const preSender = preBalances[j];\n const postSender = postBalances[j];\n \n if (preSender === undefined || postSender === undefined) {\n continue;\n }\n \n if (postSender < preSender) {\n const sender = accountKeys[j];\n if (!sender) continue;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) continue;\n \n transfers.push({\n from: senderAddress,\n to: address,\n amount: diff / 1e9, // Convert lamports to SOL\n token: undefined,\n signature,\n blockTime: tx.blockTime,\n });\n break;\n }\n }\n }\n }\n\n return transfers;\n}\n\n/**\n * Extract SPL token transfers from a parsed transaction\n */\nfunction extractSPLTransfers(\n tx: ParsedTransactionWithMeta,\n signature: string\n): Transfer[] {\n const transfers: Transfer[] = [];\n\n if (!tx.meta || !tx.meta.postTokenBalances || !tx.meta.preTokenBalances) {\n return transfers;\n }\n\n const preTokenBalances = tx.meta.preTokenBalances;\n const postTokenBalances = tx.meta.postTokenBalances;\n\n // Create a map of account index to token balance changes\n const balanceChanges = new Map<number, { mint: string; change: number; decimals: number }>();\n\n // Calculate changes\n for (const post of postTokenBalances) {\n const pre = preTokenBalances.find(\n (p) => p.accountIndex === post.accountIndex && p.mint === post.mint\n );\n\n const preAmount = pre?.uiTokenAmount.uiAmount ?? 0;\n const postAmount = post.uiTokenAmount.uiAmount ?? 0;\n const change = postAmount - preAmount;\n\n if (change !== 0) {\n balanceChanges.set(post.accountIndex, {\n mint: post.mint,\n change,\n decimals: post.uiTokenAmount.decimals,\n });\n }\n }\n\n // Match senders and receivers\n const accountKeys = tx.transaction.message.accountKeys;\n \n if (!accountKeys || !Array.isArray(accountKeys)) {\n return transfers;\n }\n \n balanceChanges.forEach((info, accountIndex) => {\n if (accountIndex >= accountKeys.length) {\n return; // Skip if index out of bounds\n }\n \n const account = accountKeys[accountIndex];\n if (!account) return;\n \n const address = typeof account === 'string' ? account : account.pubkey?.toString();\n if (!address) return;\n\n if (info.change > 0) {\n // This account received tokens - find sender\n balanceChanges.forEach((senderInfo, senderIndex) => {\n if (\n senderInfo.mint === info.mint &&\n senderInfo.change < 0 &&\n senderIndex !== accountIndex &&\n senderIndex < accountKeys.length\n ) {\n const sender = accountKeys[senderIndex];\n if (!sender) return;\n \n const senderAddress = typeof sender === 'string' ? sender : sender.pubkey?.toString();\n if (!senderAddress) return;\n\n transfers.push({\n from: senderAddress,\n to: address,\n amount: info.change,\n token: info.mint,\n signature,\n blockTime: tx.blockTime,\n });\n }\n });\n }\n });\n\n return transfers;\n}\n\n/**\n * Categorize an instruction based on program ID and instruction type\n */\nfunction categorizeInstruction(\n instruction: ParsedInstruction | PartiallyDecodedInstruction\n): InstructionCategory {\n const programId = instruction.programId.toString();\n\n // System program instructions\n if (programId === PROGRAM_IDS.SYSTEM) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferWithSeed') {\n return 'transfer';\n }\n }\n return 'transfer';\n }\n\n // Token program instructions\n if (programId === PROGRAM_IDS.TOKEN || programId === PROGRAM_IDS.ASSOCIATED_TOKEN) {\n if ('parsed' in instruction && instruction.parsed.type) {\n const type = instruction.parsed.type;\n if (type === 'transfer' || type === 'transferChecked') {\n return 'transfer';\n }\n return 'token_operation';\n }\n return 'token_operation';\n }\n\n // Stake program\n if (programId === PROGRAM_IDS.STAKE) {\n return 'stake';\n }\n\n // Vote program\n if (programId === PROGRAM_IDS.VOTE) {\n return 'vote';\n }\n\n // Check for common swap programs (simplified heuristic)\n if (\n programId.includes('Swap') ||\n programId.includes('swap') ||\n programId === 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' || // Jupiter\n programId === 'whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc' // Orca Whirlpool\n ) {\n return 'swap';\n }\n\n // Default to program interaction\n return 'program_interaction';\n}\n\n/**\n * Extract normalized instructions from a transaction\n */\nfunction extractInstructions(\n tx: ParsedTransactionWithMeta,\n signature: string\n): NormalizedInstruction[] {\n const instructions: NormalizedInstruction[] = [];\n\n if (!tx.transaction || !tx.transaction.message) {\n return instructions;\n }\n\n const message = tx.transaction.message;\n const allInstructions = message.instructions;\n\n // Defensive check\n if (!allInstructions || !Array.isArray(allInstructions)) {\n return instructions;\n }\n\n for (const instruction of allInstructions) {\n // Defensive check for programId\n if (!instruction || !instruction.programId) {\n continue;\n }\n \n const programId = instruction.programId.toString();\n const category = categorizeInstruction(instruction);\n\n let data: Record<string, unknown> | undefined;\n if ('parsed' in instruction) {\n data = instruction.parsed as Record<string, unknown>;\n }\n\n instructions.push({\n programId,\n category,\n signature,\n blockTime: tx.blockTime,\n data,\n });\n }\n\n return instructions;\n}\n\n/**\n * Extract unique counterparties from transfers\n */\nfunction extractCounterparties(transfers: Transfer[], targetAddress: string): Set<string> {\n const counterparties = new Set<string>();\n\n for (const transfer of transfers) {\n // Add the other party in the transfer\n if (transfer.from === targetAddress) {\n counterparties.add(transfer.to);\n } else if (transfer.to === targetAddress) {\n counterparties.add(transfer.from);\n }\n }\n\n return counterparties;\n}\n\n/**\n * Calculate time range from transactions\n */\nfunction calculateTimeRange(transactions: RawTransactionData[]): {\n earliest: number | null;\n latest: number | null;\n} {\n let earliest: number | null = null;\n let latest: number | null = null;\n\n for (const tx of transactions) {\n if (tx.blockTime) {\n if (earliest === null || tx.blockTime < earliest) {\n earliest = tx.blockTime;\n }\n if (latest === null || tx.blockTime > latest) {\n latest = tx.blockTime;\n }\n }\n }\n\n return { earliest, latest };\n}\n\n/**\n * Normalize wallet data into a ScanContext\n */\nexport function normalizeWalletData(\n rawData: RawWalletData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n\n // Ensure transactions exists and is an array\n const transactions = rawData.transactions || [];\n\n // Process each transaction\n for (const rawTx of transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n } catch (error) {\n // Skip problematic transactions but continue processing\n console.warn(`Failed to normalize transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Extract counterparties\n const counterparties = extractCounterparties(allTransfers, rawData.address);\n\n // Look up labels for counterparties if provider is available\n const labels = labelProvider \n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n // Calculate time range\n const timeRange = calculateTimeRange(transactions);\n\n // Normalize token accounts\n const tokenAccounts = rawData.tokenAccounts.map((ta) => {\n try {\n return {\n mint: ta.account.data.parsed.info.mint,\n address: ta.pubkey.toString(),\n balance: ta.account.data.parsed.info.tokenAmount.uiAmount ?? 0,\n };\n } catch (error) {\n // Skip malformed token accounts\n return null;\n }\n }).filter((ta): ta is NonNullable<typeof ta> => ta !== null);\n\n return {\n target: rawData.address,\n targetType: 'wallet',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts,\n timeRange,\n transactionCount: transactions.length,\n };\n}\n\n/**\n * Normalize transaction data into a ScanContext\n */\nexport function normalizeTransactionData(\n rawData: RawTransactionData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n if (rawData.transaction) {\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawData.transaction, rawData.signature);\n const splTransfers = extractSPLTransfers(rawData.transaction, rawData.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawData.transaction, rawData.signature);\n allInstructions.push(...instructions);\n\n // Extract all unique addresses involved\n const accountKeys = rawData.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize transaction ${rawData.signature}:`, error);\n }\n }\n\n return {\n target: rawData.signature,\n targetType: 'transaction',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange: {\n earliest: rawData.transaction ? rawData.blockTime : null,\n latest: rawData.transaction ? rawData.blockTime : null,\n },\n transactionCount: rawData.transaction ? 1 : 0,\n };\n}\n\n/**\n * Normalize program data into a ScanContext\n */\nexport function normalizeProgramData(\n rawData: RawProgramData,\n labelProvider?: LabelProvider\n): ScanContext {\n const allTransfers: Transfer[] = [];\n const allInstructions: NormalizedInstruction[] = [];\n const counterparties = new Set<string>();\n\n // Ensure relatedTransactions exists and is an array\n const transactions = rawData.relatedTransactions || [];\n\n // Process related transactions\n for (const rawTx of transactions) {\n if (!rawTx.transaction) continue;\n\n try {\n // Extract transfers\n const solTransfers = extractSOLTransfers(rawTx.transaction, rawTx.signature);\n const splTransfers = extractSPLTransfers(rawTx.transaction, rawTx.signature);\n allTransfers.push(...solTransfers, ...splTransfers);\n\n // Extract instructions\n const instructions = extractInstructions(rawTx.transaction, rawTx.signature);\n allInstructions.push(...instructions);\n\n // Extract counterparties\n const accountKeys = rawTx.transaction.transaction.message.accountKeys;\n if (accountKeys && Array.isArray(accountKeys)) {\n for (const key of accountKeys) {\n const address = typeof key === 'string' ? key : key.pubkey.toString();\n counterparties.add(address);\n }\n }\n } catch (error) {\n console.warn(`Failed to normalize program transaction ${rawTx.signature}:`, error);\n continue;\n }\n }\n\n // Calculate time range\n const timeRange = calculateTimeRange(transactions);\n\n // Look up labels for counterparties\n const labels = labelProvider\n ? labelProvider.lookupMany(Array.from(counterparties))\n : new Map();\n\n return {\n target: rawData.programId,\n targetType: 'program',\n transfers: allTransfers,\n instructions: allInstructions,\n counterparties,\n labels: new Map(),\n tokenAccounts: [],\n timeRange,\n transactionCount: transactions.length,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if a wallet frequently interacts with the same counterparties\n * This can enable clustering and linking of addresses\n */\nexport function detectCounterpartyReuse(context: ScanContext): RiskSignal | null {\n // Only applicable to wallet scans\n if (context.targetType !== 'wallet') {\n return null;\n }\n\n if (context.counterparties.size === 0 || context.transfers.length === 0) {\n return null;\n }\n\n // Count interactions per counterparty\n const interactionCounts = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const counterparty = transfer.from === context.target ? transfer.to : transfer.from;\n if (counterparty === context.target) continue;\n \n interactionCounts.set(counterparty, (interactionCounts.get(counterparty) || 0) + 1);\n }\n\n // Find counterparties with multiple interactions\n const reusedCounterparties = Array.from(interactionCounts.entries())\n .filter(([_, count]) => count >= 3)\n .sort((a, b) => b[1] - a[1]);\n\n if (reusedCounterparties.length === 0) {\n return null;\n }\n\n // Calculate severity based on concentration\n const totalInteractions = context.transfers.length;\n const topCounterpartyInteractions = reusedCounterparties[0][1];\n const concentration = topCounterpartyInteractions / totalInteractions;\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if (concentration > 0.5 || reusedCounterparties.length >= 5) {\n severity = 'HIGH';\n } else if (concentration > 0.3 || reusedCounterparties.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = reusedCounterparties.slice(0, 5).map(([addr, count]) => ({\n type: 'address',\n description: `${count} interactions with ${addr.slice(0, 8)}...${addr.slice(-8)}`,\n data: { address: addr, interactionCount: count },\n }));\n\n return {\n id: 'counterparty-reuse',\n name: 'Counterparty Reuse',\n severity,\n reason: `Wallet repeatedly interacts with ${reusedCounterparties.length} address(es)`,\n impact: 'Repeated interactions with the same addresses can be used to cluster wallets and build transaction graphs, enabling surveillance of your activity patterns.',\n evidence,\n mitigation: 'Use different wallets for different counterparties, or use privacy-preserving protocols that obscure transaction graphs.',\n confidence: 0.9,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if the wallet sends or receives suspiciously similar amounts\n * Round numbers or repeated exact amounts can be used for fingerprinting\n */\nexport function detectAmountReuse(context: ScanContext): RiskSignal | null {\n if (context.transfers.length < 3) {\n return null;\n }\n\n // Group amounts (rounded to avoid floating point issues)\n const amountCounts = new Map<string, number>();\n const roundNumbers: number[] = [];\n\n for (const transfer of context.transfers) {\n // Check for round numbers (e.g., 1.0, 10.0, 100.0)\n if (transfer.amount > 0 && Number.isInteger(transfer.amount) && transfer.amount >= 1) {\n roundNumbers.push(transfer.amount);\n }\n\n // Count exact amount reuse\n const amountKey = `${transfer.amount.toFixed(9)}-${transfer.token || 'SOL'}`;\n amountCounts.set(amountKey, (amountCounts.get(amountKey) || 0) + 1);\n }\n\n // Find amounts used multiple times\n const reusedAmounts = Array.from(amountCounts.entries())\n .filter(([_, count]) => count >= 2)\n .sort((a, b) => b[1] - a[1]);\n\n const hasRoundNumbers = roundNumbers.length >= 2;\n const hasReusedAmounts = reusedAmounts.length >= 2;\n\n if (!hasRoundNumbers && !hasReusedAmounts) {\n return null;\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n if ((hasRoundNumbers && roundNumbers.length >= 5) || reusedAmounts.length >= 5) {\n severity = 'HIGH';\n } else if ((hasRoundNumbers && roundNumbers.length >= 3) || reusedAmounts.length >= 3) {\n severity = 'MEDIUM';\n }\n\n const evidence: Evidence[] = [];\n\n if (hasRoundNumbers) {\n evidence.push({\n type: 'amount',\n description: `${roundNumbers.length} round-number transfers detected`,\n data: { roundNumbers: roundNumbers.slice(0, 5) },\n });\n }\n\n if (hasReusedAmounts) {\n const topReused = reusedAmounts.slice(0, 3);\n for (const [amountKey, count] of topReused) {\n const [amount, token] = amountKey.split('-');\n evidence.push({\n type: 'amount',\n description: `Amount ${amount} ${token} used ${count} times`,\n data: { amount: parseFloat(amount), token, count },\n });\n }\n }\n\n return {\n id: 'amount-reuse',\n name: 'Deterministic Amount Patterns',\n severity,\n reason: `Wallet uses ${hasRoundNumbers ? 'round numbers' : 'repeated amounts'} in transactions`,\n impact: 'Using the same amounts repeatedly or sending round numbers creates fingerprints that can be used to link transactions and identify patterns in your activity.',\n evidence,\n mitigation: 'Vary transaction amounts slightly, avoid round numbers, and consider using privacy protocols that obscure amounts.',\n confidence: hasRoundNumbers ? 0.85 : 0.75,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect transaction bursts and timing patterns\n * Concentrated activity creates temporal fingerprints\n */\nexport function detectTimingPatterns(context: ScanContext): RiskSignal | null {\n if (!context.timeRange.earliest || !context.timeRange.latest) {\n return null;\n }\n\n if (context.transactionCount < 3) {\n return null;\n }\n\n // Calculate time span in hours\n const timeSpanSeconds = context.timeRange.latest - context.timeRange.earliest;\n const timeSpanHours = timeSpanSeconds / 3600;\n\n if (timeSpanHours === 0) {\n return null;\n }\n\n // Calculate transaction rate (txs per hour)\n const txRate = context.transactionCount / timeSpanHours;\n\n // Detect burst activity (high tx rate in short time)\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'LOW';\n let isBurst = false;\n\n if (txRate > 10) {\n severity = 'HIGH';\n isBurst = true;\n } else if (txRate > 5) {\n severity = 'MEDIUM';\n isBurst = true;\n } else if (timeSpanHours < 1 && context.transactionCount >= 3) {\n severity = 'MEDIUM';\n isBurst = true;\n }\n\n if (!isBurst) {\n return null;\n }\n\n const evidence: Evidence[] = [\n {\n type: 'timing',\n description: `${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n data: {\n transactionCount: context.transactionCount,\n timeSpanHours: timeSpanHours.toFixed(2),\n transactionRate: txRate.toFixed(2),\n },\n },\n ];\n\n return {\n id: 'timing-correlation',\n name: 'Transaction Burst Pattern',\n severity,\n reason: `Concentrated activity: ${context.transactionCount} transactions in ${timeSpanHours.toFixed(1)} hours`,\n impact: 'Concentrated transaction activity creates timing fingerprints that can be used to correlate your transactions and link them to specific events or behaviors.',\n evidence,\n mitigation: 'Spread transactions over longer time periods, use scheduled transactions, or batch operations to reduce timing correlation.',\n confidence: 0.8,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect interactions with known entities (CEXs, bridges, etc.)\n * These entities can link on-chain and off-chain identities\n */\nexport function detectKnownEntityInteraction(context: ScanContext): RiskSignal | null {\n if (context.labels.size === 0) {\n return null;\n }\n\n const entityInteractions: Evidence[] = [];\n\n for (const [address, label] of context.labels.entries()) {\n // Count interactions with this entity\n let interactionCount = 0;\n const relatedTxs: string[] = [];\n\n for (const transfer of context.transfers) {\n if (transfer.from === address || transfer.to === address) {\n interactionCount++;\n if (relatedTxs.length < 3) {\n relatedTxs.push(transfer.signature);\n }\n }\n }\n\n if (interactionCount > 0) {\n entityInteractions.push({\n type: 'label',\n description: `${interactionCount} interaction(s) with ${label.name} (${label.type})`,\n data: {\n entityName: label.name,\n entityType: label.type,\n address,\n interactionCount,\n transactions: relatedTxs,\n },\n reference: address,\n });\n }\n }\n\n if (entityInteractions.length === 0) {\n return null;\n }\n\n // Severity based on entity type and interaction count\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n \n const hasExchangeInteraction = Array.from(context.labels.values())\n .some(label => label.type === 'exchange');\n \n if (hasExchangeInteraction) {\n severity = 'HIGH';\n } else if (entityInteractions.length >= 3) {\n severity = 'HIGH';\n }\n\n return {\n id: 'known-entity-interaction',\n name: 'Known Entity Interaction',\n severity,\n reason: `Wallet interacted with ${entityInteractions.length} known entit${entityInteractions.length === 1 ? 'y' : 'ies'}`,\n impact: 'Interactions with centralized exchanges, bridges, or other known entities can link your on-chain address to your real-world identity through KYC data, IP addresses, and off-chain records.',\n evidence: entityInteractions,\n mitigation: 'Use privacy-preserving bridges, avoid direct CEX interactions from privacy-sensitive wallets, or use intermediate wallets to break the link.',\n confidence: 0.95,\n };\n}\n", "import type { ScanContext, RiskSignal, Evidence } from '../types/index.js';\n\n/**\n * Detect if wallet balances can be easily traced\n * Full balance transfers or predictable balance changes reduce privacy\n */\nexport function detectBalanceTraceability(context: ScanContext): RiskSignal | null {\n if (context.targetType !== 'wallet' || context.transfers.length < 2) {\n return null;\n }\n\n const fullBalanceTransfers: Evidence[] = [];\n const suspiciousPatterns: string[] = [];\n\n // Look for transfers that represent full balance movements\n // (This is simplified - real implementation would need balance tracking)\n \n // Check for transfers where send and receive amounts match closely\n const amountPairs = new Map<string, number>();\n \n for (const transfer of context.transfers) {\n const amountKey = transfer.amount.toFixed(6);\n amountPairs.set(amountKey, (amountPairs.get(amountKey) || 0) + 1);\n }\n\n // Find matching send/receive pairs\n const matchingPairs = Array.from(amountPairs.entries())\n .filter(([_, count]) => count >= 2);\n\n if (matchingPairs.length >= 2) {\n suspiciousPatterns.push('Multiple matching send/receive amounts detected');\n }\n\n // Check for sequential transfers of similar amounts\n for (let i = 0; i < context.transfers.length - 1; i++) {\n const current = context.transfers[i];\n const next = context.transfers[i + 1];\n \n if (current.blockTime && next.blockTime) {\n const timeDiff = Math.abs((next.blockTime - current.blockTime));\n \n // If similar amounts within 1 hour\n if (timeDiff < 3600 && Math.abs(current.amount - next.amount) < current.amount * 0.1) {\n suspiciousPatterns.push('Sequential transfers of similar amounts');\n break;\n }\n }\n }\n\n if (suspiciousPatterns.length === 0 && matchingPairs.length === 0) {\n return null;\n }\n\n const evidence: Evidence[] = [];\n\n if (matchingPairs.length > 0) {\n evidence.push({\n type: 'pattern',\n description: `${matchingPairs.length} matching send/receive amount pair(s)`,\n data: { matchingPairs: matchingPairs.length },\n });\n }\n\n for (const pattern of suspiciousPatterns) {\n evidence.push({\n type: 'pattern',\n description: pattern,\n data: {},\n });\n }\n\n let severity: 'LOW' | 'MEDIUM' | 'HIGH' = 'MEDIUM';\n if (matchingPairs.length >= 3 || suspiciousPatterns.length >= 2) {\n severity = 'HIGH';\n }\n\n return {\n id: 'balance-traceability',\n name: 'Balance Traceability',\n severity,\n reason: 'Wallet shows patterns that enable balance tracking',\n impact: 'Traceable balance movements allow observers to follow funds through the blockchain, linking your transactions and revealing your financial activity.',\n evidence,\n mitigation: 'Split large transfers into multiple smaller ones, introduce timing delays, or use privacy protocols that obscure amounts.',\n confidence: 0.7,\n };\n}\n", "import type { ScanContext, RiskSignal, RiskLevel, PrivacyReport } from '../types/index.js';\nimport {\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n} from '../heuristics/index.js';\n\n/**\n * Current report schema version\n */\nconst REPORT_VERSION = '1.0.0';\n\n/**\n * All available heuristic functions\n */\nconst HEURISTICS = [\n detectCounterpartyReuse,\n detectAmountReuse,\n detectTimingPatterns,\n detectKnownEntityInteraction,\n detectBalanceTraceability,\n];\n\n/**\n * Calculate overall risk level from individual signals\n * Uses deterministic scoring based on severity and count\n */\nfunction calculateOverallRisk(signals: RiskSignal[]): RiskLevel {\n if (signals.length === 0) {\n return 'LOW';\n }\n\n // Count signals by severity\n const highCount = signals.filter(s => s.severity === 'HIGH').length;\n const mediumCount = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowCount = signals.filter(s => s.severity === 'LOW').length;\n\n // Deterministic thresholds\n if (highCount >= 2 || (highCount >= 1 && mediumCount >= 2)) {\n return 'HIGH';\n }\n\n if (highCount >= 1 || mediumCount >= 2 || (mediumCount >= 1 && lowCount >= 2)) {\n return 'MEDIUM';\n }\n\n return 'LOW';\n}\n\n/**\n * Generate general mitigation recommendations based on detected signals\n */\nfunction generateMitigations(signals: RiskSignal[]): string[] {\n const mitigations = new Set<string>();\n\n if (signals.length === 0) {\n return ['Continue practicing good privacy hygiene to maintain low exposure.'];\n }\n\n // Add general recommendations\n mitigations.add('Consider using multiple wallets to compartmentalize different activities.');\n \n // Check for specific signal types and add relevant mitigations\n const signalIds = new Set(signals.map(s => s.id));\n\n if (signalIds.has('known-entity-interaction')) {\n mitigations.add('Avoid direct interactions between privacy-sensitive wallets and KYC services.');\n }\n\n if (signalIds.has('counterparty-reuse')) {\n mitigations.add('Use different addresses for different counterparties or contexts.');\n }\n\n if (signalIds.has('timing-correlation') || signalIds.has('balance-traceability')) {\n mitigations.add('Introduce timing delays and vary transaction patterns to reduce correlation.');\n }\n\n if (signalIds.has('amount-reuse')) {\n mitigations.add('Vary transaction amounts to avoid creating fingerprints.');\n }\n\n // Always add this general advice\n mitigations.add('Research and consider privacy-preserving protocols when available.');\n\n return Array.from(mitigations);\n}\n\n/**\n * Evaluate all heuristics against a scan context\n */\nexport function evaluateHeuristics(context: ScanContext): RiskSignal[] {\n const signals: RiskSignal[] = [];\n\n for (const heuristic of HEURISTICS) {\n try {\n const signal = heuristic(context);\n if (signal) {\n signals.push(signal);\n }\n } catch (error) {\n // Log but don't fail if a heuristic errors\n console.warn(`Heuristic evaluation failed:`, error);\n }\n }\n\n // Sort signals by severity (HIGH -> MEDIUM -> LOW) for deterministic ordering\n signals.sort((a, b) => {\n const severityOrder = { HIGH: 0, MEDIUM: 1, LOW: 2 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n\n return signals;\n}\n\n/**\n * Generate a complete privacy report from a scan context\n */\nexport function generateReport(context: ScanContext): PrivacyReport {\n // Evaluate all heuristics\n const signals = evaluateHeuristics(context);\n\n // Calculate overall risk\n const overallRisk = calculateOverallRisk(signals);\n\n // Count signals by severity\n const highRiskSignals = signals.filter(s => s.severity === 'HIGH').length;\n const mediumRiskSignals = signals.filter(s => s.severity === 'MEDIUM').length;\n const lowRiskSignals = signals.filter(s => s.severity === 'LOW').length;\n\n // Generate mitigations\n const mitigations = generateMitigations(signals);\n\n // Extract known entities from context\n const knownEntities = Array.from(context.labels.values());\n\n return {\n version: REPORT_VERSION,\n timestamp: Date.now(),\n targetType: context.targetType,\n target: context.target,\n overallRisk,\n signals,\n summary: {\n totalSignals: signals.length,\n highRiskSignals,\n mediumRiskSignals,\n lowRiskSignals,\n transactionsAnalyzed: context.transactionCount,\n },\n mitigations,\n knownEntities,\n };\n}\n", "import { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport type { Label, LabelProvider } from '../types/index.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Static JSON label provider\n * Loads labels from a curated JSON file\n */\nexport class StaticLabelProvider implements LabelProvider {\n private labels: Map<string, Label>;\n\n constructor(labelsPath?: string) {\n this.labels = new Map();\n this.loadLabels(labelsPath);\n }\n\n /**\n * Load labels from JSON file\n */\n private loadLabels(customPath?: string): void {\n try {\n const path = customPath || join(__dirname, 'known-addresses.json');\n const data = readFileSync(path, 'utf-8');\n const parsed = JSON.parse(data);\n\n if (!parsed.labels || !Array.isArray(parsed.labels)) {\n console.warn('Invalid labels file format');\n return;\n }\n\n for (const label of parsed.labels) {\n if (label.address && label.name && label.type) {\n this.labels.set(label.address, {\n address: label.address,\n name: label.name,\n type: label.type,\n description: label.description,\n relatedAddresses: label.relatedAddresses,\n });\n }\n }\n\n console.debug(`Loaded ${this.labels.size} address labels`);\n } catch (error) {\n console.warn('Failed to load labels file:', error);\n }\n }\n\n /**\n * Look up a label for an address\n */\n lookup(address: string): Label | null {\n return this.labels.get(address) || null;\n }\n\n /**\n * Look up multiple addresses at once\n */\n lookupMany(addresses: string[]): Map<string, Label> {\n const results = new Map<string, Label>();\n\n for (const address of addresses) {\n const label = this.lookup(address);\n if (label) {\n results.set(address, label);\n }\n }\n\n return results;\n }\n\n /**\n * Get all loaded labels\n */\n getAllLabels(): Label[] {\n return Array.from(this.labels.values());\n }\n\n /**\n * Get count of loaded labels\n */\n getCount(): number {\n return this.labels.size;\n }\n}\n\n/**\n * Create a default label provider instance\n */\nexport function createDefaultLabelProvider(): StaticLabelProvider {\n return new StaticLabelProvider();\n}\n", "// Export all types\nexport * from './types/index.js';\n\n// Export RPC client\nexport * from './rpc/index.js';\n\n// Export data collectors\nexport * from './collectors/index.js';\n\n// Export normalizer\nexport * from './normalizer/index.js';\n\n// Export heuristics\nexport * from './heuristics/index.js';\n\n// Export scanner (report generation)\nexport * from './scanner/index.js';\n\n// Export label provider\nexport * from './labels/index.js';\n\n// Export version\nexport const VERSION = '0.1.0';\n"],
5
+ "mappings": ";AAAA,SAAS,kBAAoC;AA6C7C,IAAM,cAAN,MAAkB;AAAA,EAIhB,YAAoB,gBAAwB;AAAxB;AAAA,EAAyB;AAAA,EAHrC,iBAAiB;AAAA,EACjB,QAA2B,CAAC;AAAA,EAIpC,MAAM,UAAyB;AAC7B,QAAI,KAAK,iBAAiB,KAAK,gBAAgB;AAC7C,WAAK;AACL;AAAA,IACF;AAEA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,MAAM,KAAK,MAAM;AACpB,aAAK;AACL,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,UAAgB;AACd,SAAK;AACL,UAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,QAAI,MAAM;AACR,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,iBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,iBAAyB;AACvB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAKA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAWO,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,aAAuC;AAEjD,UAAM,SAA0B,OAAO,gBAAgB,WACnD,EAAE,QAAQ,YAAY,IACtB;AAGJ,UAAM,SAAS,OAAO,OAAO,KAAK;AAElC,SAAK,SAAS;AAAA,MACZ,YAAY,OAAO,cAAc;AAAA,MACjC,YAAY,OAAO,cAAc;AAAA,MACjC,SAAS,OAAO,WAAW;AAAA,MAC3B,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,OAAO,OAAO,SAAS;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,mBAAqC;AAAA,MACzC,YAAY;AAAA,MACZ,kCAAkC,KAAK,OAAO;AAAA,IAChD;AAEA,SAAK,aAAa,IAAI,WAAW,KAAK,OAAO,QAAQ,gBAAgB;AACrE,SAAK,cAAc,IAAI,YAAY,KAAK,OAAO,cAAc;AAE7D,QAAI,KAAK,OAAO,OAAO;AACrB,cAAQ,IAAI,qCAAqC,KAAK,OAAO,MAAM,EAAE;AACrE,cAAQ,IAAI,gCAAgC,KAAK,OAAO,cAAc,EAAE;AACxE,cAAQ,IAAI,4BAA4B,KAAK,OAAO,UAAU,EAAE;AAAA,IAClE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBACZ,WACA,eACY;AACZ,UAAM,KAAK,YAAY,QAAQ;AAE/B,QAAI,YAA0B;AAE9B,aAAS,UAAU,GAAG,WAAW,KAAK,OAAO,YAAY,WAAW;AAClE,UAAI;AACF,YAAI,KAAK,OAAO,SAAS,UAAU,GAAG;AACpC,kBAAQ,IAAI,6BAA6B,OAAO,QAAQ,aAAa,EAAE;AAAA,QACzE;AAEA,cAAM,SAAS,MAAM,UAAU;AAC/B,aAAK,YAAY,QAAQ;AACzB,eAAO;AAAA,MACT,SAAS,OAAO;AACd,oBAAY;AAEZ,YAAI,KAAK,OAAO,OAAO;AACrB,kBAAQ;AAAA,YACN,wBAAwB,aAAa,aAAa,UAAU,CAAC,IAAI,KAAK,OAAO,aAAa,CAAC;AAAA,YAC3F;AAAA,UACF;AAAA,QACF;AAGA,YAAI,UAAU,KAAK,OAAO,YAAY;AAEpC,gBAAM,QAAQ,KAAK,OAAO,aAAa,KAAK,IAAI,GAAG,OAAO;AAC1D,gBAAM,MAAM,KAAK;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAEA,SAAK,YAAY,QAAQ;AACzB,UAAM,IAAI;AAAA,MACR,iBAAiB,aAAa,iBAAiB,KAAK,OAAO,aAAa,CAAC,cAAc,WAAW,OAAO;AAAA,IAC3G;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAA4D;AAC1D,WAAO;AAAA,MACL,gBAAgB,KAAK,YAAY,eAAe;AAAA,MAChD,aAAa,KAAK,YAAY,eAAe;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBACJ,SACA,SAKA;AACA,WAAO,KAAK;AAAA,MACV,YAAY;AACV,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAiB;AACpD,eAAO,KAAK,WAAW;AAAA,UACrB,IAAI,UAAU,OAAO;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,MACA,2BAA2B,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,WAAmB,SAAuD;AAC7F,WAAO,KAAK;AAAA,MACV,YAAY;AACV,eAAO,KAAK,WAAW,eAAe,WAAW;AAAA,UAC/C,gCAAgC,SAAS,kCAAkC;AAAA,QAC7E,CAAC;AAAA,MACH;AAAA,MACA,kBAAkB,SAAS;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,YAAsB,SAA4E;AACtH,UAAM,WAAW,WAAW,IAAI,CAAC,QAAQ,KAAK,eAAe,KAAK,OAAO,CAAC;AAC1E,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,cAAsB,aAAsB;AACxE,WAAO,KAAK;AAAA,MACV,YAAY;AACV,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAiB;AACpD,cAAM,QAAQ,IAAI,UAAU,YAAY;AAGxC,cAAM,mBAAmB,IAAI,UAAU,6CAA6C;AAEpF,YAAI,aAAa;AACf,gBAAM,OAAO,IAAI,UAAU,WAAW;AACtC,iBAAO,KAAK,WAAW,wBAAwB,OAAO,EAAE,KAAK,CAAC;AAAA,QAChE,OAAO;AACL,iBAAO,KAAK,WAAW,wBAAwB,OAAO;AAAA,YACpD,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,MACF;AAAA,MACA,2BAA2B,YAAY;AAAA,IACzC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAmB,WAAmB,QAAc;AACxD,WAAO,KAAK;AAAA,MACV,YAAY;AACV,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAiB;AACpD,eAAO,KAAK,WAAW,mBAAmB,IAAI,UAAU,SAAS,GAAG,MAAM;AAAA,MAC5E;AAAA,MACA,sBAAsB,SAAS;AAAA,IACjC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAAe,SAAiB;AACpC,WAAO,KAAK;AAAA,MACV,YAAY;AACV,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAiB;AACpD,eAAO,KAAK,WAAW,eAAe,IAAI,UAAU,OAAO,CAAC;AAAA,MAC9D;AAAA,MACA,kBAAkB,OAAO;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,wBAAwB,WAAqB;AACjD,WAAO,KAAK;AAAA,MACV,YAAY;AACV,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,iBAAiB;AACpD,cAAM,UAAU,UAAU,IAAI,CAAC,SAAS,IAAI,UAAU,IAAI,CAAC;AAC3D,eAAO,KAAK,WAAW,wBAAwB,OAAO;AAAA,MACxD;AAAA,MACA,2BAA2B,UAAU,MAAM;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB,MAAM,KAAK,WAAW,WAAW;AAAA,QACjC;AAAA,MACF;AACA,aAAO,CAAC,CAAC;AAAA,IACX,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC/OA,eAAsB,kBACpB,QACA,SACA,UAAmC,CAAC,GACZ;AACxB,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,uBAAuB,QAAQ,wBAAwB;AAG7D,MAAI,aAAa,CAAC;AAClB,MAAI;AACF,iBAAa,MAAM,OAAO,wBAAwB,SAAS;AAAA,MACzD,OAAO;AAAA,IACT,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,kCAAkC,OAAO,KAAK,KAAK;AAAA,EAClE;AAGA,QAAM,eAAiC,CAAC;AAGxC,QAAM,aAAa;AACnB,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,YAAY;AACtD,UAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,UAAU;AAChD,UAAM,kBAAkB,MAAM,IAAI,CAAC,QAAQ,IAAI,SAAS;AAExD,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,gBAAgB,iBAAiB;AAAA,QACxD,gCAAgC;AAAA,MAClC,CAAC;AAED,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,qBAAa,KAAK;AAAA,UAChB,WAAW,MAAM,CAAC,EAAE;AAAA,UACpB,aAAa,IAAI,CAAC;AAAA,UAClB,WAAW,MAAM,CAAC,EAAE;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,yCAAyC,OAAO,KAAK,KAAK;AAAA,IACzE;AAAA,EACF;AAGA,MAAI,gBAA2C,CAAC;AAChD,MAAI,sBAAsB;AACxB,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,wBAAwB,OAAO;AAC7D,sBAAgB,SAAS;AAAA,IAC3B,SAAS,OAAO;AAEd,cAAQ,KAAK,sCAAsC,OAAO,KAAK,KAAK;AAAA,IACtE;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKA,eAAsB,uBACpB,QACA,WAC6B;AAE7B,MAAI,cAAc;AAClB,MAAI;AACF,kBAAc,MAAM,OAAO,eAAe,WAAW;AAAA,MACnD,gCAAgC;AAAA,IAClC,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,+BAA+B,SAAS,KAAK,KAAK;AAAA,EACjE;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,aAAa,aAAa;AAAA,EACvC;AACF;AAKA,eAAsB,mBACpB,QACA,WACA,UAAoC,CAAC,GACZ;AACzB,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,kBAAkB,QAAQ,mBAAmB;AAGnD,MAAI,WAAoD,CAAC;AACzD,MAAI;AACF,UAAM,WAAW,MAAM,OAAO,mBAAmB,WAAW;AAAA,MAC1D,UAAU;AAAA,MACV,WAAW,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA;AAAA,IACpC,CAAC;AAED,eAAW,SAAS,MAAM,GAAG,WAAW,EAAE,IAAI,CAAC,SAAS;AAAA,MACtD,QAAQ,IAAI,OAAO,SAAS;AAAA,MAC5B,SAAS,IAAI;AAAA,IACf,EAAE;AAAA,EACJ,SAAS,OAAO;AACd,YAAQ,KAAK,wCAAwC,SAAS,KAAK,KAAK;AAAA,EAC1E;AAGA,MAAI,aAAuC,CAAC;AAC5C,MAAI;AACF,iBAAa,MAAM,OAAO,wBAAwB,WAAW;AAAA,MAC3D,OAAO;AAAA,IACT,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,KAAK,0CAA0C,SAAS,KAAK,KAAK;AAAA,EAC5E;AAGA,QAAM,sBAAwC,CAAC;AAC/C,QAAM,aAAa;AAEnB,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,WAAW,QAAQ,eAAe,GAAG,KAAK,YAAY;AACjF,UAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,UAAU;AAChD,UAAM,kBAAkB,MAAM,IAAI,CAAC,QAAQ,IAAI,SAAS;AAExD,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,gBAAgB,iBAAiB;AAAA,QACxD,gCAAgC;AAAA,MAClC,CAAC;AAED,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,4BAAoB,KAAK;AAAA,UACvB,WAAW,MAAM,CAAC,EAAE;AAAA,UACpB,aAAa,IAAI,CAAC;AAAA,UAClB,WAAW,MAAM,CAAC,EAAE;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,iDAAiD,SAAS,KAAK,KAAK;AAAA,IACnF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC9NA,IAAM,cAAc;AAAA,EAClB,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,kBAAkB;AAAA,EAClB,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,SAAS;AACX;AAKA,SAAS,oBACP,IACA,WACY;AACZ,QAAM,YAAwB,CAAC;AAE/B,MAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,aAAa;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,GAAG,KAAK;AAC5B,QAAM,eAAe,GAAG,KAAK;AAC7B,QAAM,cAAc,GAAG,YAAY,QAAQ;AAG3C,MAAI,CAAC,eAAe,CAAC,MAAM,QAAQ,WAAW,KAAK,YAAY,WAAW,GAAG;AAC3E,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,eAAe,CAAC,cAAc;AACjC,WAAO;AAAA,EACT;AAGA,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,MAAM,YAAY,CAAC;AACzB,UAAM,OAAO,aAAa,CAAC;AAG3B,QAAI,QAAQ,UAAa,SAAS,QAAW;AAC3C;AAAA,IACF;AAEA,UAAM,OAAO,OAAO;AAGpB,QAAI,SAAS,EAAG;AAEhB,UAAM,UAAU,YAAY,CAAC;AAC7B,QAAI,CAAC,QAAS;AAEd,UAAM,UAAU,OAAO,YAAY,WAAW,UAAU,QAAQ,QAAQ,SAAS;AACjF,QAAI,CAAC,QAAS;AAGd,QAAI,OAAO,GAAG;AAEZ,eAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,cAAM,YAAY,YAAY,CAAC;AAC/B,cAAM,aAAa,aAAa,CAAC;AAEjC,YAAI,cAAc,UAAa,eAAe,QAAW;AACvD;AAAA,QACF;AAEA,YAAI,aAAa,WAAW;AAC1B,gBAAM,SAAS,YAAY,CAAC;AAC5B,cAAI,CAAC,OAAQ;AAEb,gBAAM,gBAAgB,OAAO,WAAW,WAAW,SAAS,OAAO,QAAQ,SAAS;AACpF,cAAI,CAAC,cAAe;AAEpB,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,QAAQ,OAAO;AAAA;AAAA,YACf,OAAO;AAAA,YACP;AAAA,YACA,WAAW,GAAG;AAAA,UAChB,CAAC;AACD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBACP,IACA,WACY;AACZ,QAAM,YAAwB,CAAC;AAE/B,MAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,KAAK,qBAAqB,CAAC,GAAG,KAAK,kBAAkB;AACvE,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,GAAG,KAAK;AACjC,QAAM,oBAAoB,GAAG,KAAK;AAGlC,QAAM,iBAAiB,oBAAI,IAAgE;AAG3F,aAAW,QAAQ,mBAAmB;AACpC,UAAM,MAAM,iBAAiB;AAAA,MAC3B,CAAC,MAAM,EAAE,iBAAiB,KAAK,gBAAgB,EAAE,SAAS,KAAK;AAAA,IACjE;AAEA,UAAM,YAAY,KAAK,cAAc,YAAY;AACjD,UAAM,aAAa,KAAK,cAAc,YAAY;AAClD,UAAM,SAAS,aAAa;AAE5B,QAAI,WAAW,GAAG;AAChB,qBAAe,IAAI,KAAK,cAAc;AAAA,QACpC,MAAM,KAAK;AAAA,QACX;AAAA,QACA,UAAU,KAAK,cAAc;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,cAAc,GAAG,YAAY,QAAQ;AAE3C,MAAI,CAAC,eAAe,CAAC,MAAM,QAAQ,WAAW,GAAG;AAC/C,WAAO;AAAA,EACT;AAEA,iBAAe,QAAQ,CAAC,MAAM,iBAAiB;AAC7C,QAAI,gBAAgB,YAAY,QAAQ;AACtC;AAAA,IACF;AAEA,UAAM,UAAU,YAAY,YAAY;AACxC,QAAI,CAAC,QAAS;AAEd,UAAM,UAAU,OAAO,YAAY,WAAW,UAAU,QAAQ,QAAQ,SAAS;AACjF,QAAI,CAAC,QAAS;AAEd,QAAI,KAAK,SAAS,GAAG;AAEnB,qBAAe,QAAQ,CAAC,YAAY,gBAAgB;AAClD,YACE,WAAW,SAAS,KAAK,QACzB,WAAW,SAAS,KACpB,gBAAgB,gBAChB,cAAc,YAAY,QAC1B;AACA,gBAAM,SAAS,YAAY,WAAW;AACtC,cAAI,CAAC,OAAQ;AAEb,gBAAM,gBAAgB,OAAO,WAAW,WAAW,SAAS,OAAO,QAAQ,SAAS;AACpF,cAAI,CAAC,cAAe;AAEpB,oBAAU,KAAK;AAAA,YACb,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,QAAQ,KAAK;AAAA,YACb,OAAO,KAAK;AAAA,YACZ;AAAA,YACA,WAAW,GAAG;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAKA,SAAS,sBACP,aACqB;AACrB,QAAM,YAAY,YAAY,UAAU,SAAS;AAGjD,MAAI,cAAc,YAAY,QAAQ;AACpC,QAAI,YAAY,eAAe,YAAY,OAAO,MAAM;AACtD,YAAM,OAAO,YAAY,OAAO;AAChC,UAAI,SAAS,cAAc,SAAS,oBAAoB;AACtD,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,YAAY,SAAS,cAAc,YAAY,kBAAkB;AACjF,QAAI,YAAY,eAAe,YAAY,OAAO,MAAM;AACtD,YAAM,OAAO,YAAY,OAAO;AAChC,UAAI,SAAS,cAAc,SAAS,mBAAmB;AACrD,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,YAAY,OAAO;AACnC,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,YAAY,MAAM;AAClC,WAAO;AAAA,EACT;AAGA,MACE,UAAU,SAAS,MAAM,KACzB,UAAU,SAAS,MAAM,KACzB,cAAc;AAAA,EACd,cAAc,+CACd;AACA,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAKA,SAAS,oBACP,IACA,WACyB;AACzB,QAAM,eAAwC,CAAC;AAE/C,MAAI,CAAC,GAAG,eAAe,CAAC,GAAG,YAAY,SAAS;AAC9C,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,GAAG,YAAY;AAC/B,QAAM,kBAAkB,QAAQ;AAGhC,MAAI,CAAC,mBAAmB,CAAC,MAAM,QAAQ,eAAe,GAAG;AACvD,WAAO;AAAA,EACT;AAEA,aAAW,eAAe,iBAAiB;AAEzC,QAAI,CAAC,eAAe,CAAC,YAAY,WAAW;AAC1C;AAAA,IACF;AAEA,UAAM,YAAY,YAAY,UAAU,SAAS;AACjD,UAAM,WAAW,sBAAsB,WAAW;AAElD,QAAI;AACJ,QAAI,YAAY,aAAa;AAC3B,aAAO,YAAY;AAAA,IACrB;AAEA,iBAAa,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,GAAG;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,SAAS,sBAAsB,WAAuB,eAAoC;AACxF,QAAM,iBAAiB,oBAAI,IAAY;AAEvC,aAAW,YAAY,WAAW;AAEhC,QAAI,SAAS,SAAS,eAAe;AACnC,qBAAe,IAAI,SAAS,EAAE;AAAA,IAChC,WAAW,SAAS,OAAO,eAAe;AACxC,qBAAe,IAAI,SAAS,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,cAG1B;AACA,MAAI,WAA0B;AAC9B,MAAI,SAAwB;AAE5B,aAAW,MAAM,cAAc;AAC7B,QAAI,GAAG,WAAW;AAChB,UAAI,aAAa,QAAQ,GAAG,YAAY,UAAU;AAChD,mBAAW,GAAG;AAAA,MAChB;AACA,UAAI,WAAW,QAAQ,GAAG,YAAY,QAAQ;AAC5C,iBAAS,GAAG;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,OAAO;AAC5B;AAKO,SAAS,oBACd,SACA,eACa;AACb,QAAM,eAA2B,CAAC;AAClC,QAAM,kBAA2C,CAAC;AAGlD,QAAM,eAAe,QAAQ,gBAAgB,CAAC;AAG9C,aAAW,SAAS,cAAc;AAChC,QAAI,CAAC,MAAM,YAAa;AAExB,QAAI;AAEF,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,mBAAa,KAAK,GAAG,cAAc,GAAG,YAAY;AAGlD,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,sBAAgB,KAAK,GAAG,YAAY;AAAA,IACtC,SAAS,OAAO;AAEd,cAAQ,KAAK,mCAAmC,MAAM,SAAS,KAAK,KAAK;AACzE;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,sBAAsB,cAAc,QAAQ,OAAO;AAG1E,QAAM,SAAS,gBACX,cAAc,WAAW,MAAM,KAAK,cAAc,CAAC,IACnD,oBAAI,IAAI;AAGZ,QAAM,YAAY,mBAAmB,YAAY;AAGjD,QAAM,gBAAgB,QAAQ,cAAc,IAAI,CAAC,OAAO;AACtD,QAAI;AACF,aAAO;AAAA,QACL,MAAM,GAAG,QAAQ,KAAK,OAAO,KAAK;AAAA,QAClC,SAAS,GAAG,OAAO,SAAS;AAAA,QAC5B,SAAS,GAAG,QAAQ,KAAK,OAAO,KAAK,YAAY,YAAY;AAAA,MAC/D;AAAA,IACF,SAAS,OAAO;AAEd,aAAO;AAAA,IACT;AAAA,EACF,CAAC,EAAE,OAAO,CAAC,OAAqC,OAAO,IAAI;AAE3D,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA,QAAQ,oBAAI,IAAI;AAAA,IAChB;AAAA,IACA;AAAA,IACA,kBAAkB,aAAa;AAAA,EACjC;AACF;AAKO,SAAS,yBACd,SACA,eACa;AACb,QAAM,eAA2B,CAAC;AAClC,QAAM,kBAA2C,CAAC;AAClD,QAAM,iBAAiB,oBAAI,IAAY;AAEvC,MAAI,QAAQ,aAAa;AACvB,QAAI;AAEF,YAAM,eAAe,oBAAoB,QAAQ,aAAa,QAAQ,SAAS;AAC/E,YAAM,eAAe,oBAAoB,QAAQ,aAAa,QAAQ,SAAS;AAC/E,mBAAa,KAAK,GAAG,cAAc,GAAG,YAAY;AAGlD,YAAM,eAAe,oBAAoB,QAAQ,aAAa,QAAQ,SAAS;AAC/E,sBAAgB,KAAK,GAAG,YAAY;AAGpC,YAAM,cAAc,QAAQ,YAAY,YAAY,QAAQ;AAC5D,UAAI,eAAe,MAAM,QAAQ,WAAW,GAAG;AAC7C,mBAAW,OAAO,aAAa;AAC7B,gBAAM,UAAU,OAAO,QAAQ,WAAW,MAAM,IAAI,OAAO,SAAS;AACpE,yBAAe,IAAI,OAAO;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,mCAAmC,QAAQ,SAAS,KAAK,KAAK;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA,QAAQ,oBAAI,IAAI;AAAA,IAChB,eAAe,CAAC;AAAA,IAChB,WAAW;AAAA,MACT,UAAU,QAAQ,cAAc,QAAQ,YAAY;AAAA,MACpD,QAAQ,QAAQ,cAAc,QAAQ,YAAY;AAAA,IACpD;AAAA,IACA,kBAAkB,QAAQ,cAAc,IAAI;AAAA,EAC9C;AACF;AAKO,SAAS,qBACd,SACA,eACa;AACb,QAAM,eAA2B,CAAC;AAClC,QAAM,kBAA2C,CAAC;AAClD,QAAM,iBAAiB,oBAAI,IAAY;AAGvC,QAAM,eAAe,QAAQ,uBAAuB,CAAC;AAGrD,aAAW,SAAS,cAAc;AAChC,QAAI,CAAC,MAAM,YAAa;AAExB,QAAI;AAEF,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,mBAAa,KAAK,GAAG,cAAc,GAAG,YAAY;AAGlD,YAAM,eAAe,oBAAoB,MAAM,aAAa,MAAM,SAAS;AAC3E,sBAAgB,KAAK,GAAG,YAAY;AAGpC,YAAM,cAAc,MAAM,YAAY,YAAY,QAAQ;AAC1D,UAAI,eAAe,MAAM,QAAQ,WAAW,GAAG;AAC7C,mBAAW,OAAO,aAAa;AAC7B,gBAAM,UAAU,OAAO,QAAQ,WAAW,MAAM,IAAI,OAAO,SAAS;AACpE,yBAAe,IAAI,OAAO;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,KAAK,2CAA2C,MAAM,SAAS,KAAK,KAAK;AACjF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,mBAAmB,YAAY;AAGjD,QAAM,SAAS,gBACX,cAAc,WAAW,MAAM,KAAK,cAAc,CAAC,IACnD,oBAAI,IAAI;AAEZ,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA,QAAQ,oBAAI,IAAI;AAAA,IAChB,eAAe,CAAC;AAAA,IAChB;AAAA,IACA,kBAAkB,aAAa;AAAA,EACjC;AACF;;;ACrgBO,SAAS,wBAAwB,SAAyC;AAE/E,MAAI,QAAQ,eAAe,UAAU;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,eAAe,SAAS,KAAK,QAAQ,UAAU,WAAW,GAAG;AACvE,WAAO;AAAA,EACT;AAGA,QAAM,oBAAoB,oBAAI,IAAoB;AAElD,aAAW,YAAY,QAAQ,WAAW;AACxC,UAAM,eAAe,SAAS,SAAS,QAAQ,SAAS,SAAS,KAAK,SAAS;AAC/E,QAAI,iBAAiB,QAAQ,OAAQ;AAErC,sBAAkB,IAAI,eAAe,kBAAkB,IAAI,YAAY,KAAK,KAAK,CAAC;AAAA,EACpF;AAGA,QAAM,uBAAuB,MAAM,KAAK,kBAAkB,QAAQ,CAAC,EAChE,OAAO,CAAC,CAAC,GAAG,KAAK,MAAM,SAAS,CAAC,EACjC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAE7B,MAAI,qBAAqB,WAAW,GAAG;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,oBAAoB,QAAQ,UAAU;AAC5C,QAAM,8BAA8B,qBAAqB,CAAC,EAAE,CAAC;AAC7D,QAAM,gBAAgB,8BAA8B;AAEpD,MAAI,WAAsC;AAC1C,MAAI,gBAAgB,OAAO,qBAAqB,UAAU,GAAG;AAC3D,eAAW;AAAA,EACb,WAAW,gBAAgB,OAAO,qBAAqB,UAAU,GAAG;AAClE,eAAW;AAAA,EACb;AAEA,QAAM,WAAuB,qBAAqB,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,IACpF,MAAM;AAAA,IACN,aAAa,GAAG,KAAK,sBAAsB,KAAK,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;AAAA,IAC/E,MAAM,EAAE,SAAS,MAAM,kBAAkB,MAAM;AAAA,EACjD,EAAE;AAEF,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,QAAQ,oCAAoC,qBAAqB,MAAM;AAAA,IACvE,QAAQ;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACF;;;ACzDO,SAAS,kBAAkB,SAAyC;AACzE,MAAI,QAAQ,UAAU,SAAS,GAAG;AAChC,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,oBAAI,IAAoB;AAC7C,QAAM,eAAyB,CAAC;AAEhC,aAAW,YAAY,QAAQ,WAAW;AAExC,QAAI,SAAS,SAAS,KAAK,OAAO,UAAU,SAAS,MAAM,KAAK,SAAS,UAAU,GAAG;AACpF,mBAAa,KAAK,SAAS,MAAM;AAAA,IACnC;AAGA,UAAM,YAAY,GAAG,SAAS,OAAO,QAAQ,CAAC,CAAC,IAAI,SAAS,SAAS,KAAK;AAC1E,iBAAa,IAAI,YAAY,aAAa,IAAI,SAAS,KAAK,KAAK,CAAC;AAAA,EACpE;AAGA,QAAM,gBAAgB,MAAM,KAAK,aAAa,QAAQ,CAAC,EACpD,OAAO,CAAC,CAAC,GAAG,KAAK,MAAM,SAAS,CAAC,EACjC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAE7B,QAAM,kBAAkB,aAAa,UAAU;AAC/C,QAAM,mBAAmB,cAAc,UAAU;AAEjD,MAAI,CAAC,mBAAmB,CAAC,kBAAkB;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,WAAsC;AAC1C,MAAK,mBAAmB,aAAa,UAAU,KAAM,cAAc,UAAU,GAAG;AAC9E,eAAW;AAAA,EACb,WAAY,mBAAmB,aAAa,UAAU,KAAM,cAAc,UAAU,GAAG;AACrF,eAAW;AAAA,EACb;AAEA,QAAM,WAAuB,CAAC;AAE9B,MAAI,iBAAiB;AACnB,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,aAAa,GAAG,aAAa,MAAM;AAAA,MACnC,MAAM,EAAE,cAAc,aAAa,MAAM,GAAG,CAAC,EAAE;AAAA,IACjD,CAAC;AAAA,EACH;AAEA,MAAI,kBAAkB;AACpB,UAAM,YAAY,cAAc,MAAM,GAAG,CAAC;AAC1C,eAAW,CAAC,WAAW,KAAK,KAAK,WAAW;AAC1C,YAAM,CAAC,QAAQ,KAAK,IAAI,UAAU,MAAM,GAAG;AAC3C,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,aAAa,UAAU,MAAM,IAAI,KAAK,SAAS,KAAK;AAAA,QACpD,MAAM,EAAE,QAAQ,WAAW,MAAM,GAAG,OAAO,MAAM;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,QAAQ,eAAe,kBAAkB,kBAAkB,kBAAkB;AAAA,IAC7E,QAAQ;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ,YAAY,kBAAkB,OAAO;AAAA,EACvC;AACF;;;ACvEO,SAAS,qBAAqB,SAAyC;AAC5E,MAAI,CAAC,QAAQ,UAAU,YAAY,CAAC,QAAQ,UAAU,QAAQ;AAC5D,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,mBAAmB,GAAG;AAChC,WAAO;AAAA,EACT;AAGA,QAAM,kBAAkB,QAAQ,UAAU,SAAS,QAAQ,UAAU;AACrE,QAAM,gBAAgB,kBAAkB;AAExC,MAAI,kBAAkB,GAAG;AACvB,WAAO;AAAA,EACT;AAGA,QAAM,SAAS,QAAQ,mBAAmB;AAG1C,MAAI,WAAsC;AAC1C,MAAI,UAAU;AAEd,MAAI,SAAS,IAAI;AACf,eAAW;AACX,cAAU;AAAA,EACZ,WAAW,SAAS,GAAG;AACrB,eAAW;AACX,cAAU;AAAA,EACZ,WAAW,gBAAgB,KAAK,QAAQ,oBAAoB,GAAG;AAC7D,eAAW;AACX,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,QAAM,WAAuB;AAAA,IAC3B;AAAA,MACE,MAAM;AAAA,MACN,aAAa,GAAG,QAAQ,gBAAgB,oBAAoB,cAAc,QAAQ,CAAC,CAAC;AAAA,MACpF,MAAM;AAAA,QACJ,kBAAkB,QAAQ;AAAA,QAC1B,eAAe,cAAc,QAAQ,CAAC;AAAA,QACtC,iBAAiB,OAAO,QAAQ,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,QAAQ,0BAA0B,QAAQ,gBAAgB,oBAAoB,cAAc,QAAQ,CAAC,CAAC;AAAA,IACtG,QAAQ;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACF;;;AC7DO,SAAS,6BAA6B,SAAyC;AACpF,MAAI,QAAQ,OAAO,SAAS,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,qBAAiC,CAAC;AAExC,aAAW,CAAC,SAAS,KAAK,KAAK,QAAQ,OAAO,QAAQ,GAAG;AAEvD,QAAI,mBAAmB;AACvB,UAAM,aAAuB,CAAC;AAE9B,eAAW,YAAY,QAAQ,WAAW;AACxC,UAAI,SAAS,SAAS,WAAW,SAAS,OAAO,SAAS;AACxD;AACA,YAAI,WAAW,SAAS,GAAG;AACzB,qBAAW,KAAK,SAAS,SAAS;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,mBAAmB,GAAG;AACxB,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN,aAAa,GAAG,gBAAgB,wBAAwB,MAAM,IAAI,KAAK,MAAM,IAAI;AAAA,QACjF,MAAM;AAAA,UACJ,YAAY,MAAM;AAAA,UAClB,YAAY,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,UACA,cAAc;AAAA,QAChB;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,mBAAmB,WAAW,GAAG;AACnC,WAAO;AAAA,EACT;AAGA,MAAI,WAAsC;AAE1C,QAAM,yBAAyB,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC,EAC9D,KAAK,WAAS,MAAM,SAAS,UAAU;AAE1C,MAAI,wBAAwB;AAC1B,eAAW;AAAA,EACb,WAAW,mBAAmB,UAAU,GAAG;AACzC,eAAW;AAAA,EACb;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,QAAQ,0BAA0B,mBAAmB,MAAM,eAAe,mBAAmB,WAAW,IAAI,MAAM,KAAK;AAAA,IACvH,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACF;;;AC/DO,SAAS,0BAA0B,SAAyC;AACjF,MAAI,QAAQ,eAAe,YAAY,QAAQ,UAAU,SAAS,GAAG;AACnE,WAAO;AAAA,EACT;AAEA,QAAM,uBAAmC,CAAC;AAC1C,QAAM,qBAA+B,CAAC;AAMtC,QAAM,cAAc,oBAAI,IAAoB;AAE5C,aAAW,YAAY,QAAQ,WAAW;AACxC,UAAM,YAAY,SAAS,OAAO,QAAQ,CAAC;AAC3C,gBAAY,IAAI,YAAY,YAAY,IAAI,SAAS,KAAK,KAAK,CAAC;AAAA,EAClE;AAGA,QAAM,gBAAgB,MAAM,KAAK,YAAY,QAAQ,CAAC,EACnD,OAAO,CAAC,CAAC,GAAG,KAAK,MAAM,SAAS,CAAC;AAEpC,MAAI,cAAc,UAAU,GAAG;AAC7B,uBAAmB,KAAK,iDAAiD;AAAA,EAC3E;AAGA,WAAS,IAAI,GAAG,IAAI,QAAQ,UAAU,SAAS,GAAG,KAAK;AACrD,UAAM,UAAU,QAAQ,UAAU,CAAC;AACnC,UAAM,OAAO,QAAQ,UAAU,IAAI,CAAC;AAEpC,QAAI,QAAQ,aAAa,KAAK,WAAW;AACvC,YAAM,WAAW,KAAK,IAAK,KAAK,YAAY,QAAQ,SAAU;AAG9D,UAAI,WAAW,QAAQ,KAAK,IAAI,QAAQ,SAAS,KAAK,MAAM,IAAI,QAAQ,SAAS,KAAK;AACpF,2BAAmB,KAAK,yCAAyC;AACjE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,mBAAmB,WAAW,KAAK,cAAc,WAAW,GAAG;AACjE,WAAO;AAAA,EACT;AAEA,QAAM,WAAuB,CAAC;AAE9B,MAAI,cAAc,SAAS,GAAG;AAC5B,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,aAAa,GAAG,cAAc,MAAM;AAAA,MACpC,MAAM,EAAE,eAAe,cAAc,OAAO;AAAA,IAC9C,CAAC;AAAA,EACH;AAEA,aAAW,WAAW,oBAAoB;AACxC,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,MAAM,CAAC;AAAA,IACT,CAAC;AAAA,EACH;AAEA,MAAI,WAAsC;AAC1C,MAAI,cAAc,UAAU,KAAK,mBAAmB,UAAU,GAAG;AAC/D,eAAW;AAAA,EACb;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACF;;;AC1EA,IAAM,iBAAiB;AAKvB,IAAM,aAAa;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAMA,SAAS,qBAAqB,SAAkC;AAC9D,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,QAAQ,OAAO,OAAK,EAAE,aAAa,MAAM,EAAE;AAC7D,QAAM,cAAc,QAAQ,OAAO,OAAK,EAAE,aAAa,QAAQ,EAAE;AACjE,QAAM,WAAW,QAAQ,OAAO,OAAK,EAAE,aAAa,KAAK,EAAE;AAG3D,MAAI,aAAa,KAAM,aAAa,KAAK,eAAe,GAAI;AAC1D,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,KAAK,eAAe,KAAM,eAAe,KAAK,YAAY,GAAI;AAC7E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,SAAiC;AAC5D,QAAM,cAAc,oBAAI,IAAY;AAEpC,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,CAAC,oEAAoE;AAAA,EAC9E;AAGA,cAAY,IAAI,2EAA2E;AAG3F,QAAM,YAAY,IAAI,IAAI,QAAQ,IAAI,OAAK,EAAE,EAAE,CAAC;AAEhD,MAAI,UAAU,IAAI,0BAA0B,GAAG;AAC7C,gBAAY,IAAI,+EAA+E;AAAA,EACjG;AAEA,MAAI,UAAU,IAAI,oBAAoB,GAAG;AACvC,gBAAY,IAAI,mEAAmE;AAAA,EACrF;AAEA,MAAI,UAAU,IAAI,oBAAoB,KAAK,UAAU,IAAI,sBAAsB,GAAG;AAChF,gBAAY,IAAI,8EAA8E;AAAA,EAChG;AAEA,MAAI,UAAU,IAAI,cAAc,GAAG;AACjC,gBAAY,IAAI,0DAA0D;AAAA,EAC5E;AAGA,cAAY,IAAI,oEAAoE;AAEpF,SAAO,MAAM,KAAK,WAAW;AAC/B;AAKO,SAAS,mBAAmB,SAAoC;AACrE,QAAM,UAAwB,CAAC;AAE/B,aAAW,aAAa,YAAY;AAClC,QAAI;AACF,YAAM,SAAS,UAAU,OAAO;AAChC,UAAI,QAAQ;AACV,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF,SAAS,OAAO;AAEd,cAAQ,KAAK,gCAAgC,KAAK;AAAA,IACpD;AAAA,EACF;AAGA,UAAQ,KAAK,CAAC,GAAG,MAAM;AACrB,UAAM,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,EAAE;AACnD,WAAO,cAAc,EAAE,QAAQ,IAAI,cAAc,EAAE,QAAQ;AAAA,EAC7D,CAAC;AAED,SAAO;AACT;AAKO,SAAS,eAAe,SAAqC;AAElE,QAAM,UAAU,mBAAmB,OAAO;AAG1C,QAAM,cAAc,qBAAqB,OAAO;AAGhD,QAAM,kBAAkB,QAAQ,OAAO,OAAK,EAAE,aAAa,MAAM,EAAE;AACnE,QAAM,oBAAoB,QAAQ,OAAO,OAAK,EAAE,aAAa,QAAQ,EAAE;AACvE,QAAM,iBAAiB,QAAQ,OAAO,OAAK,EAAE,aAAa,KAAK,EAAE;AAGjE,QAAM,cAAc,oBAAoB,OAAO;AAG/C,QAAM,gBAAgB,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAExD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAW,KAAK,IAAI;AAAA,IACpB,YAAY,QAAQ;AAAA,IACpB,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,cAAc,QAAQ;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA,sBAAsB,QAAQ;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC1JA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAG9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAY,QAAQ,UAAU;AAM7B,IAAM,sBAAN,MAAmD;AAAA,EAChD;AAAA,EAER,YAAY,YAAqB;AAC/B,SAAK,SAAS,oBAAI,IAAI;AACtB,SAAK,WAAW,UAAU;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,YAA2B;AAC5C,QAAI;AACF,YAAM,OAAO,cAAc,KAAK,WAAW,sBAAsB;AACjE,YAAM,OAAO,aAAa,MAAM,OAAO;AACvC,YAAM,SAAS,KAAK,MAAM,IAAI;AAE9B,UAAI,CAAC,OAAO,UAAU,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG;AACnD,gBAAQ,KAAK,4BAA4B;AACzC;AAAA,MACF;AAEA,iBAAW,SAAS,OAAO,QAAQ;AACjC,YAAI,MAAM,WAAW,MAAM,QAAQ,MAAM,MAAM;AAC7C,eAAK,OAAO,IAAI,MAAM,SAAS;AAAA,YAC7B,SAAS,MAAM;AAAA,YACf,MAAM,MAAM;AAAA,YACZ,MAAM,MAAM;AAAA,YACZ,aAAa,MAAM;AAAA,YACnB,kBAAkB,MAAM;AAAA,UAC1B,CAAC;AAAA,QACH;AAAA,MACF;AAEA,cAAQ,MAAM,UAAU,KAAK,OAAO,IAAI,iBAAiB;AAAA,IAC3D,SAAS,OAAO;AACd,cAAQ,KAAK,+BAA+B,KAAK;AAAA,IACnD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,SAA+B;AACpC,WAAO,KAAK,OAAO,IAAI,OAAO,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,WAAyC;AAClD,UAAM,UAAU,oBAAI,IAAmB;AAEvC,eAAW,WAAW,WAAW;AAC/B,YAAM,QAAQ,KAAK,OAAO,OAAO;AACjC,UAAI,OAAO;AACT,gBAAQ,IAAI,SAAS,KAAK;AAAA,MAC5B;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,eAAwB;AACtB,WAAO,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAmB;AACjB,WAAO,KAAK,OAAO;AAAA,EACrB;AACF;AAKO,SAAS,6BAAkD;AAChE,SAAO,IAAI,oBAAoB;AACjC;;;ACzEO,IAAM,UAAU;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-privacy-scanner-core",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Core scanning engine for Solana privacy analysis - analyze on-chain privacy exposure using heuristic-based risk detection",
6
6
  "main": "./dist/index.cjs",