{"version":3,"file":"defi-swap.mjs","names":[],"sources":["../../../src/tools/defi-swap.ts"],"sourcesContent":["/**\n * DeFi Swap Tool — token swaps via 0x aggregator\n * \n * Builds swap transactions, presents quotes, and submits via ClawnchConnect.\n * Transactions above spending policy thresholds go to the user's phone.\n */\n\nimport { Type } from '@sinclair/typebox';\nimport { stringEnum, jsonResult, errorResult, readStringParam, readNumberParam } from '../lib/tool-helpers.js';\nimport {\n  getWalletState,\n  requireWalletClient,\n  requirePublicClient,\n  getMevWalletClient,\n  isBankrMode,\n} from '../services/walletconnect-service.js';\nimport { validateSwap, type SafetyCheckResult } from '../services/safety-service.js';\nimport { getPrice } from '../services/price-service.js';\nimport { hasBankrApi } from '../services/bankr-api.js';\nimport { guardedFetch } from '../services/endpoint-allowlist.js';\nimport { resolveTokenDecimals, isNativeEth } from '../lib/token-decimals.js';\n\nconst ACTIONS = ['quote', 'execute'] as const;\n\nconst DefiSwapSchema = Type.Object({\n  action: stringEnum(ACTIONS, {\n    description: 'quote: get a swap quote with price impact. execute: execute the swap.',\n  }),\n  token_in: Type.String({\n    description: 'Token to sell — symbol (e.g. \"ETH\", \"USDC\") or contract address (0x...)',\n  }),\n  token_out: Type.String({\n    description: 'Token to buy — symbol (e.g. \"ETH\", \"USDC\") or contract address (0x...)',\n  }),\n  amount: Type.String({\n    description: 'Amount to sell (in human-readable units, e.g. \"0.1\" for 0.1 ETH)',\n  }),\n  slippage: Type.Optional(Type.Number({\n    description: 'Max slippage percentage (default: 1.0 for 1%)',\n  })),\n  chain: Type.Optional(Type.String({\n    description: 'Chain for the swap (default: \"base\"). Bankr mode supports: base, ethereum, polygon, unichain, solana',\n  })),\n});\n\n// Well-known token addresses on Base\nconst BASE_TOKENS: Record<string, string> = {\n  ETH: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',\n  WETH: '0x4200000000000000000000000000000000000006',\n  USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n  USDT: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n  DAI: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',\n  CLAWNCH: '0xa1F72459dfA10BAD200Ac160eCd78C6b77a747be',\n};\n\nfunction resolveToken(input: string): string {\n  if (input.startsWith('0x') && input.length === 42) return input;\n  const upper = input.toUpperCase();\n  return BASE_TOKENS[upper] ?? input;\n}\n\nexport function createDefiSwapTool() {\n  return {\n    name: 'defi_swap',\n    label: 'DeFi Swap',\n    ownerOnly: true,\n    description:\n      'Swap tokens via DEX aggregator (Base) or Bankr Agent API (all chains). ' +\n      'Get quotes with price impact and gas estimates, then execute swaps. ' +\n      'In Bankr mode, supports Base, Ethereum, Polygon, Unichain, and Solana. ' +\n      'Supports ETH, WETH, USDC, USDT, DAI, CLAWNCH, and any token by address or symbol.',\n    parameters: DefiSwapSchema,\n    execute: async (_toolCallId: string, args: unknown) => {\n      const params = args as Record<string, unknown>;\n      const action = readStringParam(params, 'action', { required: true })!;\n      const chain = readStringParam(params, 'chain') || 'base';\n\n      const state = getWalletState();\n      if (!state.connected) {\n        return errorResult('No wallet connected. Use clawnchconnect tool to connect first.');\n      }\n\n      // Bankr routing: all chains go through Bankr when in bankr mode,\n      // non-Base chains go through Bankr when API key is available\n      const useBankr = isBankrMode() || (chain !== 'base' && hasBankrApi());\n\n      if (useBankr) {\n        if (!hasBankrApi()) {\n          return errorResult(\n            `Swaps on ${chain} require Bankr wallet. Connect via /connect_bankr first.`\n          );\n        }\n        return action === 'quote'\n          ? handleBankrQuote(params, chain)\n          : handleBankrSwap(params, chain);\n      }\n\n      // Local path (Base only)\n      if (chain !== 'base') {\n        return errorResult(\n          `Swaps on ${chain} are not supported without Bankr wallet. ` +\n          'Connect via /connect_bankr to access multi-chain swaps.'\n        );\n      }\n\n      switch (action) {\n        case 'quote':\n          return handleQuote(params);\n        case 'execute':\n          return handleExecute(params);\n        default:\n          return errorResult(`Unknown action: ${action}`);\n      }\n    },\n  };\n}\n\n// ─── Bankr Swap Handlers ─────────────────────────────────────────────────\n\n// ─── Input Sanitization (C3: prevent prompt injection in Bankr NL prompts) ──\nconst SAFE_TOKEN_RE = /^[a-zA-Z0-9_.\\-\\/]{1,60}$/;\nconst SAFE_ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;\nconst SAFE_AMOUNT_RE = /^[0-9][0-9,._]*$/;\nconst SAFE_CHAIN_RE = /^[a-zA-Z0-9\\-]{1,20}$/;\n\nfunction sanitizeBankrToken(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_ADDRESS_RE.test(trimmed) || SAFE_TOKEN_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid token: \"${trimmed.slice(0, 30)}\". Use a symbol (e.g. \"ETH\") or address (0x...).`);\n}\nfunction sanitizeBankrAmount(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_AMOUNT_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid amount: \"${trimmed.slice(0, 30)}\". Use a number like \"0.1\" or \"100\".`);\n}\nfunction sanitizeBankrChain(input: string): string {\n  const trimmed = input.trim();\n  if (SAFE_CHAIN_RE.test(trimmed)) return trimmed;\n  throw new Error(`Invalid chain: \"${trimmed.slice(0, 20)}\".`);\n}\n\nasync function handleBankrQuote(params: Record<string, unknown>, chain: string) {\n  const tokenIn = sanitizeBankrToken(readStringParam(params, 'token_in', { required: true })!);\n  const tokenOut = sanitizeBankrToken(readStringParam(params, 'token_out', { required: true })!);\n  const amount = sanitizeBankrAmount(readStringParam(params, 'amount', { required: true })!);\n  const safeChain = sanitizeBankrChain(chain);\n\n  try {\n    const { bankrPromptAndPoll } = await import('../services/bankr-api.js');\n\n    const prompt = `quote swapping ${amount} ${tokenIn} to ${tokenOut} on ${safeChain}`;\n    const result = await bankrPromptAndPoll(prompt, { timeoutMs: 30_000 });\n\n    if (result.status === 'failed') {\n      return errorResult(`Quote failed: ${result.error ?? 'Unknown error'}`);\n    }\n\n    return jsonResult({\n      source: 'bankr',\n      chain,\n      tokenIn,\n      tokenOut,\n      amount,\n      response: result.response,\n      richData: result.richData,\n      note: 'Use action \"execute\" to proceed with this swap.',\n    });\n  } catch (err) {\n    return errorResult(`Bankr quote failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\nasync function handleBankrSwap(params: Record<string, unknown>, chain: string) {\n  const tokenIn = sanitizeBankrToken(readStringParam(params, 'token_in', { required: true })!);\n  const tokenOut = sanitizeBankrToken(readStringParam(params, 'token_out', { required: true })!);\n  const amount = sanitizeBankrAmount(readStringParam(params, 'amount', { required: true })!);\n  const safeChain = sanitizeBankrChain(chain);\n\n  try {\n    const { bankrPromptAndPoll } = await import('../services/bankr-api.js');\n\n    const prompt = `swap ${amount} ${tokenIn} to ${tokenOut} on ${safeChain}`;\n    const result = await bankrPromptAndPoll(prompt, { timeoutMs: 120_000 });\n\n    if (result.status === 'failed') {\n      return errorResult(`Swap failed: ${result.error ?? 'Unknown error'}`);\n    }\n\n    // Parse transaction from result\n    const txData = result.transactions?.find(t => t.type === 'swap');\n\n    return jsonResult({\n      status: 'success',\n      source: 'bankr',\n      chain,\n      tokenIn,\n      tokenOut,\n      amountIn: amount,\n      txHash: txData?.hash ?? (txData?.metadata as any)?.transaction?.hash,\n      response: result.response,\n      richData: result.richData,\n    });\n  } catch (err) {\n    return errorResult(`Bankr swap failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\n// ─── Local Swap Handlers ─────────────────────────────────────────────────\n\nasync function handleQuote(params: Record<string, unknown>) {\n  const tokenIn = resolveToken(readStringParam(params, 'token_in', { required: true })!);\n  const tokenOut = resolveToken(readStringParam(params, 'token_out', { required: true })!);\n  const amount = readStringParam(params, 'amount', { required: true })!;\n  // Validate amount format early — prevents opaque errors in parseEther/parseUnits\n  if (!/^\\d+(\\.\\d+)?$/.test(amount.trim())) {\n    return errorResult(`Invalid amount: \"${amount}\". Must be a positive number (e.g. \"1.5\" or \"100\").`);\n  }\n  const slippage = readNumberParam(params, 'slippage') ?? 1.0;\n\n  try {\n    const { parseEther, parseUnits, formatUnits } = await import('viem');\n    const state = getWalletState();\n\n    // Detect actual decimals instead of assuming 18 for all tokens\n    const isEth = isNativeEth(tokenIn);\n    const publicClientForDecimals = (() => { try { return requirePublicClient(); } catch { return null; } })();\n    const tokenDecimals = await resolveTokenDecimals(tokenIn, publicClientForDecimals);\n    const amountWei = isEth\n      ? parseEther(amount)\n      : parseUnits(amount, tokenDecimals);\n\n    // Query multiple DEX aggregators in parallel for best price\n    const { getDexAggregator } = await import('../services/dex-aggregator.js');\n    const aggregator = getDexAggregator({\n      slippageBps: Math.round(slippage * 100),\n    });\n\n    let allQuotes: import('../services/dex-aggregator.js').SwapQuote[] = [];\n    let bestQuote: import('../services/dex-aggregator.js').SwapQuote | null = null;\n\n    try {\n      allQuotes = await aggregator.getQuotes(tokenIn, tokenOut, amountWei.toString());\n      const valid = allQuotes.filter((q) => !q.error && q.buyAmount !== '0');\n      bestQuote = valid[0] ?? null;\n    } catch {\n      // All aggregators failed — fall back to Clawnch API (which proxies 0x)\n    }\n\n    // Fallback: Clawnch API swap quote if no aggregator returned a quote\n    if (!bestQuote) {\n      const apiUrl = process.env.CLAWNCHER_API_URL || 'https://clawn.ch';\n      const quoteParams = new URLSearchParams({\n        sellToken: tokenIn,\n        buyToken: tokenOut,\n        sellAmount: amountWei.toString(),\n        slippagePercentage: (slippage / 100).toString(),\n        takerAddress: state.address!,\n      });\n\n      const response = await guardedFetch(`${apiUrl}/api/swap/quote?${quoteParams}`, {\n        headers: { Accept: 'application/json' },\n      });\n\n      if (!response.ok) {\n        const err = await response.text();\n        return errorResult(`Quote failed: ${err}`);\n      }\n\n      const clawnchQuote = await response.json() as any;\n      bestQuote = {\n        aggregator: 'Clawnch (0x)',\n        sellToken: tokenIn,\n        buyToken: tokenOut,\n        sellAmount: amountWei.toString(),\n        buyAmount: clawnchQuote.buyAmount ?? '0',\n        price: parseFloat(clawnchQuote.price ?? '0'),\n        gasEstimate: clawnchQuote.estimatedGas ?? '0',\n        gasPrice: clawnchQuote.gasPrice,\n        route: clawnchQuote.sources?.filter((s: any) => s.proportion !== '0')\n          .map((s: any) => s.name).join(' → ') ?? 'Clawnch',\n        data: clawnchQuote,\n      };\n    }\n\n    // Run safety check (non-blocking for quotes, just informational)\n    let safety: SafetyCheckResult | null = null;\n    try {\n      safety = await validateSwap({\n        tokenIn,\n        tokenOut,\n        amountEth: parseFloat(amount),\n      });\n    } catch {\n      // Safety check failure shouldn't block quotes\n    }\n\n    // Build comparison table from all aggregator quotes\n    // Detect output token decimals for correct display\n    const outDecimals = await resolveTokenDecimals(tokenOut, publicClientForDecimals);\n    const comparison = allQuotes\n      .filter((q) => !q.error)\n      .map((q) => ({\n        aggregator: q.aggregator,\n        buyAmount: formatUnits(BigInt(q.buyAmount || '0'), outDecimals),\n        gasEstimate: q.gasEstimate,\n        gasCostUsd: q.gasCostUsd,\n        route: q.route,\n        isBest: q.aggregator === bestQuote!.aggregator,\n      }));\n\n    return jsonResult({\n      bestAggregator: bestQuote.aggregator,\n      tokenIn: {\n        address: tokenIn,\n        amount,\n        amountWei: amountWei.toString(),\n      },\n      tokenOut: {\n        address: tokenOut,\n        estimatedAmount: bestQuote.buyAmount\n          ? formatUnits(BigInt(bestQuote.buyAmount), outDecimals)\n          : 'unknown',\n      },\n      price: bestQuote.price,\n      gas: bestQuote.gasEstimate,\n      gasPrice: bestQuote.gasPrice,\n      gasCostUsd: bestQuote.gasCostUsd,\n      route: bestQuote.route,\n      slippage: `${slippage}%`,\n      comparison: comparison.length > 1 ? comparison : undefined,\n      safety: safety ? {\n        safe: safety.safe,\n        warnings: safety.warnings,\n        blockers: safety.blockers,\n      } : undefined,\n      note: safety?.blockers.length\n        ? 'SAFETY BLOCKERS FOUND — review before executing.'\n        : 'Use action \"execute\" to proceed with this swap.',\n    });\n  } catch (err) {\n    return errorResult(`Quote failed: ${err instanceof Error ? err.message : String(err)}`);\n  }\n}\n\nasync function handleExecute(params: Record<string, unknown>) {\n  const tokenIn = resolveToken(readStringParam(params, 'token_in', { required: true })!);\n  const tokenOut = resolveToken(readStringParam(params, 'token_out', { required: true })!);\n  const amount = readStringParam(params, 'amount', { required: true })!;\n  // Validate amount format early — prevents opaque errors in parseEther/parseUnits\n  if (!/^\\d+(\\.\\d+)?$/.test(amount.trim())) {\n    return errorResult(`Invalid amount: \"${amount}\". Must be a positive number (e.g. \"1.5\" or \"100\").`);\n  }\n  const slippage = readNumberParam(params, 'slippage') ?? 1.0;\n\n  // Pre-flight safety checks (blocking for execute)\n  try {\n    const safety = await validateSwap({\n      tokenIn,\n      tokenOut,\n      amountEth: parseFloat(amount),\n    });\n\n    if (!safety.safe) {\n      return errorResult(\n        `Swap blocked by safety checks:\\n` +\n        safety.blockers.map(b => `  ✗ ${b}`).join('\\n') +\n        (safety.warnings.length\n          ? '\\n\\nWarnings:\\n' + safety.warnings.map(w => `  ⚠ ${w}`).join('\\n')\n          : '')\n      );\n    }\n\n    // Log warnings but proceed\n    if (safety.warnings.length > 0) {\n      // Warnings are included in the successful result below\n    }\n  } catch (err) {\n    // Safety check infrastructure failure — log but don't block the swap.\n    // The user explicitly requested this trade; blocking on infra failure\n    // would be worse than proceeding without the safety overlay.\n    console.warn('[defi-swap] Safety check failed, proceeding without validation:', err instanceof Error ? err.message : String(err));\n  }\n\n  try {\n    const { ClawnchSwapper } = await import('@clawnch/clawncher-sdk');\n    const wallet = await getMevWalletClient();\n    const publicClient = requirePublicClient();\n\n    const swapper = new ClawnchSwapper({\n      wallet,\n      publicClient,\n      apiBaseUrl: process.env.CLAWNCHER_API_URL || 'https://clawn.ch',\n    });\n\n    const { parseEther, parseUnits } = await import('viem');\n\n    // Detect actual decimals for input token (critical: USDC=6, not 18)\n    const isEthIn = isNativeEth(tokenIn);\n    const sellDecimals = await resolveTokenDecimals(tokenIn, publicClient);\n    const sellAmountWei = isEthIn\n      ? parseEther(amount)\n      : parseUnits(amount, sellDecimals);\n\n    // Wrap swap in a timeout to prevent hanging when WC/wallet doesn't respond.\n    // The WC signer has a 180s timeout internally, but we add our own 120s timeout\n    // so the tool returns an error to the LLM instead of hanging indefinitely.\n    const SWAP_TIMEOUT_MS = 120_000; // 2 minutes\n\n    const swapPromise = swapper.swap({\n      sellToken: tokenIn as `0x${string}`,\n      buyToken: tokenOut as `0x${string}`,\n      sellAmount: sellAmountWei,\n      slippageBps: Math.round(slippage * 100),\n    });\n\n    const timeoutPromise = new Promise<never>((_, reject) =>\n      setTimeout(() => reject(new Error(\n        'Swap timed out after 2 minutes. The transaction may still be pending in your wallet — check Rainbow/MetaMask. ' +\n        'If you see a pending approval or swap, you can approve or reject it there.'\n      )), SWAP_TIMEOUT_MS)\n    );\n\n    const result = await Promise.race([swapPromise, timeoutPromise]);\n\n    return jsonResult({\n      status: 'success',\n      txHash: result.txHash,\n      tokenIn,\n      tokenOut,\n      amountIn: amount,\n      amountOut: result.buyAmount ? result.buyAmount.toString() : undefined,\n      sellAmount: result.sellAmount?.toString(),\n      gasUsed: result.gasUsed?.toString(),\n    });\n  } catch (err) {\n    const msg = err instanceof Error ? err.message : String(err);\n    // Provide actionable error messages for common failure modes\n    if (msg.includes('reverted')) {\n      return errorResult(\n        `Swap transaction reverted on-chain. This usually means:\\n` +\n        `  - Insufficient token balance or allowance\\n` +\n        `  - Price moved beyond slippage tolerance\\n` +\n        `  - Token has transfer restrictions\\n` +\n        `  - No liquidity for this pair on supported DEXes\\n\\n` +\n        `Try checking your balance with defi_balance, or try a smaller amount.\\n\\nError: ${msg}`\n      );\n    }\n    if (msg.includes('rejected') || msg.includes('declined') || msg.includes('denied')) {\n      return errorResult(`Swap cancelled — you rejected the transaction in your wallet.`);\n    }\n    if (msg.includes('timed out') || msg.includes('Swap timed out')) {\n      return errorResult(msg);\n    }\n    return errorResult(`Swap failed: ${msg}`);\n  }\n}\n"],"mappings":"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"}