{"version":3,"file":"allowance-manager.mjs","names":[],"sources":["../../../src/services/allowance-manager.ts"],"sourcesContent":["/**\n * Token Allowance Manager — batch ERC-20 allowance checking and tracking.\n *\n * Provides:\n * - Batch allowance checks across multiple tokens and spenders\n * - Permit2 allowance aggregation\n * - Unlimited/excessive allowance detection\n * - Revocation recommendations\n * - Common spender identification (Uniswap Router, 0x Exchange Proxy, etc.)\n *\n * Security concern: ERC-20 approvals are a major attack vector — unlimited\n * approvals to compromised contracts can drain tokens. This service helps\n * users audit and manage their exposure.\n */\n\nimport { formatUnits } from 'viem';\nimport { getRpcManager } from './rpc-provider.js';\n\n// Well-known token decimals (lowercase address → decimals)\nconst KNOWN_TOKEN_DECIMALS: Record<string, number> = {\n  // Base\n  '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 6,  // USDC\n  '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2': 6,  // USDT\n  // Ethereum\n  '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': 6,  // USDC\n  '0xdac17f958d2ee523a2206206994597c13d831ec7': 6,  // USDT\n  '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599': 8,  // WBTC\n};\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface AllowanceInfo {\n  token: string;\n  tokenAddress: string;\n  spender: string;\n  spenderName: string;\n  spenderAddress: string;\n  allowance: string;       // raw wei/units string\n  allowanceHuman: string;  // human-readable with decimals\n  isUnlimited: boolean;\n  riskLevel: 'safe' | 'moderate' | 'high' | 'critical';\n  chain: string;\n  chainId: number;\n}\n\nexport interface AllowanceReport {\n  owner: string;\n  chainId: number;\n  chain: string;\n  totalChecked: number;\n  unlimited: number;\n  highRisk: number;\n  allowances: AllowanceInfo[];\n  recommendations: string[];\n  timestamp: number;\n}\n\nexport interface AllowanceManagerConfig {\n  /** Threshold (in human-readable units) above which an allowance is flagged. Default: 1e12 */\n  unlimitedThreshold?: number;\n}\n\n// ── ERC-20 ABI (minimal) ────────────────────────────────────────────────────\n\nconst ERC20_ABI = [\n  {\n    inputs: [\n      { name: 'owner', type: 'address' },\n      { name: 'spender', type: 'address' },\n    ],\n    name: 'allowance',\n    outputs: [{ name: '', type: 'uint256' }],\n    stateMutability: 'view',\n    type: 'function',\n  },\n  {\n    inputs: [],\n    name: 'decimals',\n    outputs: [{ name: '', type: 'uint8' }],\n    stateMutability: 'view',\n    type: 'function',\n  },\n  {\n    inputs: [],\n    name: 'symbol',\n    outputs: [{ name: '', type: 'string' }],\n    stateMutability: 'view',\n    type: 'function',\n  },\n] as const;\n\n// ── Known Spenders ──────────────────────────────────────────────────────────\n\n/** Common spender contracts by chain. */\nconst KNOWN_SPENDERS: Record<number, Record<string, string>> = {\n  8453: { // Base\n    '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD': 'Uniswap Universal Router',\n    '0x000000000022D473030F116dDEE9F6B43aC78BA3': 'Permit2',\n    '0xDef1C0ded9bec7F1a1670819833240f027b25EfF': '0x Exchange Proxy',\n    '0x1111111254EEB25477B68fb85Ed929f73A960582': '1inch Router v5',\n    '0x6131B5fae19EA4f9D964eAc0408E4408b66337b5': 'KyberSwap Router',\n    '0xCf5540fFFCdC3d510B18bFcA6d2b9987b0772559': 'Odos Router',\n  },\n  1: { // Ethereum\n    '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD': 'Uniswap Universal Router',\n    '0x000000000022D473030F116dDEE9F6B43aC78BA3': 'Permit2',\n    '0xDef1C0ded9bec7F1a1670819833240f027b25EfF': '0x Exchange Proxy',\n    '0x1111111254EEB25477B68fb85Ed929f73A960582': '1inch Router v5',\n    '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D': 'Uniswap V2 Router',\n    '0xE592427A0AEce92De3Edee1F18E0157C05861564': 'Uniswap V3 Router',\n    '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F': 'SushiSwap Router',\n  },\n  42161: { // Arbitrum\n    '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD': 'Uniswap Universal Router',\n    '0x000000000022D473030F116dDEE9F6B43aC78BA3': 'Permit2',\n    '0xDef1C0ded9bec7F1a1670819833240f027b25EfF': '0x Exchange Proxy',\n    '0x1111111254EEB25477B68fb85Ed929f73A960582': '1inch Router v5',\n  },\n  10: { // Optimism\n    '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD': 'Uniswap Universal Router',\n    '0x000000000022D473030F116dDEE9F6B43aC78BA3': 'Permit2',\n    '0xDef1C0ded9bec7F1a1670819833240f027b25EfF': '0x Exchange Proxy',\n  },\n  137: { // Polygon\n    '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD': 'Uniswap Universal Router',\n    '0x000000000022D473030F116dDEE9F6B43aC78BA3': 'Permit2',\n    '0xDef1C0ded9bec7F1a1670819833240f027b25EfF': '0x Exchange Proxy',\n    '0x1111111254EEB25477B68fb85Ed929f73A960582': '1inch Router v5',\n  },\n};\n\n// ── Well-Known Tokens ───────────────────────────────────────────────────────\n\nconst WELL_KNOWN_TOKENS: Record<number, Record<string, string>> = {\n  8453: {\n    USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n    WETH: '0x4200000000000000000000000000000000000006',\n    DAI: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',\n    USDT: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n  },\n  1: {\n    USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',\n    WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',\n    USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7',\n    DAI: '0x6B175474E89094C44Da98b954EedeAC495271d0F',\n    WBTC: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',\n    LINK: '0x514910771AF9Ca656af840dff83E8264EcF986CA',\n  },\n};\n\nconst CHAIN_NAMES: Record<number, string> = {\n  1: 'ethereum', 8453: 'base', 42161: 'arbitrum', 10: 'optimism', 137: 'polygon',\n};\n\n// ── Service ─────────────────────────────────────────────────────────────────\n\nexport class AllowanceManager {\n  private config: Required<AllowanceManagerConfig>;\n\n  constructor(config: AllowanceManagerConfig = {}) {\n    this.config = {\n      unlimitedThreshold: config.unlimitedThreshold ?? 1e12,\n    };\n  }\n\n  /**\n   * Check allowances for a wallet against known spenders.\n   * Scans common tokens × common spenders on a given chain.\n   */\n   async auditAllowances(\n     ownerAddress: string,\n     chainId = 8453,\n     tokenAddresses?: string[],\n   ): Promise<AllowanceReport> {\n     // Resolve tokens to check\n     const tokens = tokenAddresses\n       ? tokenAddresses.map((addr) => ({ symbol: '???', address: addr }))\n       : Object.entries(WELL_KNOWN_TOKENS[chainId] ?? {}).map(([symbol, address]) => ({ symbol, address }));\n\n     // Resolve spenders\n     const spenders = KNOWN_SPENDERS[chainId] ?? {};\n     const spenderEntries = Object.entries(spenders);\n\n     if (tokens.length === 0 || spenderEntries.length === 0) {\n       return {\n         owner: ownerAddress,\n         chainId,\n         chain: CHAIN_NAMES[chainId] ?? String(chainId),\n         totalChecked: 0,\n         unlimited: 0,\n         highRisk: 0,\n         allowances: [],\n         recommendations: ['No known tokens or spenders configured for this chain.'],\n         timestamp: Date.now(),\n       };\n     }\n\n     const rpcManager = getRpcManager();\n     const client = await rpcManager.getClient(chainId);\n\n    // Build batch of allowance checks\n    const checks: Array<{ token: { symbol: string; address: string }; spenderAddr: string; spenderName: string }> = [];\n    for (const token of tokens) {\n      for (const [spenderAddr, spenderName] of spenderEntries) {\n        checks.push({ token, spenderAddr, spenderName });\n      }\n    }\n\n    // Execute all checks in parallel (batched) with overall 30s timeout\n    const allChecks = Promise.all(\n      checks.map(async ({ token, spenderAddr, spenderName }): Promise<AllowanceInfo | null> => {\n        try {\n          const [allowance, decimals, symbol] = await Promise.all([\n            client.readContract({\n              address: token.address as `0x${string}`,\n              abi: ERC20_ABI,\n              functionName: 'allowance',\n              args: [ownerAddress as `0x${string}`, spenderAddr as `0x${string}`],\n            }),\n            token.symbol === '???'\n              ? client.readContract({ address: token.address as `0x${string}`, abi: ERC20_ABI, functionName: 'decimals' })\n              : Promise.resolve(KNOWN_TOKEN_DECIMALS[token.address.toLowerCase()] ?? 18),\n            token.symbol === '???'\n              ? client.readContract({ address: token.address as `0x${string}`, abi: ERC20_ABI, functionName: 'symbol' }).catch(() => '???')\n              : Promise.resolve(token.symbol),\n          ]);\n\n          const raw = (allowance as bigint).toString();\n          if (raw === '0') return null; // Skip zero allowances\n\n          const dec = Number(decimals);\n          // Use formatUnits to avoid Number(bigint) overflow for large allowances\n          const humanStr = formatUnits(allowance as bigint, dec);\n          const human = parseFloat(humanStr);\n          const isUnlimited = human > this.config.unlimitedThreshold;\n          const riskLevel = this.assessRisk(human, spenderName, isUnlimited);\n\n          return {\n            token: String(symbol),\n            tokenAddress: token.address,\n            spender: spenderName,\n            spenderName,\n            spenderAddress: spenderAddr,\n            allowance: raw,\n            allowanceHuman: isUnlimited ? 'unlimited' : human.toLocaleString(),\n            isUnlimited,\n            riskLevel,\n            chain: CHAIN_NAMES[chainId] ?? String(chainId),\n            chainId,\n          };\n        } catch {\n          return null; // Skip tokens that fail (might not be ERC-20)\n        }\n      }),\n    );\n    const timeoutPromise = new Promise<(AllowanceInfo | null)[]>((_, reject) =>\n      setTimeout(() => reject(new Error('Allowance audit timed out after 30s')), 30_000),\n    );\n    const results = await Promise.race([allChecks, timeoutPromise]);\n\n    const allowances = results.filter((r): r is AllowanceInfo => r !== null);\n    const unlimited = allowances.filter((a) => a.isUnlimited).length;\n    const highRisk = allowances.filter((a) => a.riskLevel === 'high' || a.riskLevel === 'critical').length;\n\n    // Generate recommendations\n    const recommendations: string[] = [];\n    if (unlimited > 0) {\n      recommendations.push(\n        `${unlimited} unlimited approval${unlimited > 1 ? 's' : ''} found. Consider revoking unused ones with the permit2 tool.`,\n      );\n    }\n    const unknownSpenders = allowances.filter((a) => a.spenderName === 'Unknown');\n    if (unknownSpenders.length > 0) {\n      recommendations.push(\n        `${unknownSpenders.length} approval${unknownSpenders.length > 1 ? 's' : ''} to unrecognized contracts. Review and revoke if not needed.`,\n      );\n    }\n    if (allowances.length === 0) {\n      recommendations.push('No active approvals found — your wallet has minimal token approval exposure.');\n    }\n\n    return {\n      owner: ownerAddress,\n      chainId,\n      chain: CHAIN_NAMES[chainId] ?? String(chainId),\n      totalChecked: checks.length,\n      unlimited,\n      highRisk,\n      allowances,\n      recommendations,\n      timestamp: Date.now(),\n    };\n  }\n\n  /**\n   * Check a single token's allowance for a specific spender.\n   */\n  async checkAllowance(\n    ownerAddress: string,\n    tokenAddress: string,\n    spenderAddress: string,\n    chainId = 8453,\n  ): Promise<{ allowance: string; decimals: number; isUnlimited: boolean }> {\n    const rpcManager = getRpcManager();\n    const client = await rpcManager.getClient(chainId);\n\n    const [allowance, decimals] = await Promise.all([\n      client.readContract({\n        address: tokenAddress as `0x${string}`,\n        abi: ERC20_ABI,\n        functionName: 'allowance',\n        args: [ownerAddress as `0x${string}`, spenderAddress as `0x${string}`],\n      }),\n      client.readContract({\n        address: tokenAddress as `0x${string}`,\n        abi: ERC20_ABI,\n        functionName: 'decimals',\n      }),\n    ]);\n\n    const dec = Number(decimals);\n    const humanStr = formatUnits(allowance as bigint, dec);\n    const human = parseFloat(humanStr);\n\n    return {\n      allowance: (allowance as bigint).toString(),\n      decimals: dec,\n      isUnlimited: human > this.config.unlimitedThreshold,\n    };\n  }\n\n  /**\n   * Get known spender names for a chain.\n   */\n  getKnownSpenders(chainId = 8453): Record<string, string> {\n    return { ...KNOWN_SPENDERS[chainId] };\n  }\n\n  /**\n   * Resolve a spender address to a human-readable name.\n   */\n  resolveSpenderName(address: string, chainId = 8453): string {\n    return KNOWN_SPENDERS[chainId]?.[address] ?? 'Unknown';\n  }\n\n  // ── Private ─────────────────────────────────────────────────────────\n\n  private assessRisk(\n    humanAmount: number,\n    spenderName: string,\n    isUnlimited: boolean,\n  ): 'safe' | 'moderate' | 'high' | 'critical' {\n    if (!isUnlimited && humanAmount < 1000) return 'safe';\n    if (spenderName === 'Unknown') {\n      return isUnlimited ? 'critical' : 'high';\n    }\n    if (isUnlimited) return 'moderate'; // unlimited to known protocol is moderate\n    return 'safe';\n  }\n}\n\n// ── Singleton ───────────────────────────────────────────────────────────────\n\nlet _instance: AllowanceManager | null = null;\n\nexport function getAllowanceManager(config?: AllowanceManagerConfig): AllowanceManager {\n  if (!_instance) {\n    _instance = new AllowanceManager(config);\n  }\n  return _instance;\n}\n\nexport function resetAllowanceManager(): void {\n  _instance = null;\n}\n"],"mappings":"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"}