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 +39 -23
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +39 -23
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
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
|
-
|
|
280
|
-
|
|
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
|
-
|
|
288
|
-
|
|
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
|
-
|
|
316
|
-
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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:
|
|
715
|
+
transactionCount: transactions.length
|
|
700
716
|
};
|
|
701
717
|
}
|
|
702
718
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
-
|
|
228
|
-
|
|
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
|
-
|
|
236
|
-
|
|
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
|
-
|
|
264
|
-
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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": "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",
|
|
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.
|
|
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",
|