@sherwoodagent/cli 0.73.4 → 0.74.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/{abis-KP7M4IPM.js → abis-D7EDCKVZ.js} +2 -2
  2. package/dist/{addresses-DGB3D457.js → addresses-OFFTM6NG.js} +4 -4
  3. package/dist/{agent-UNSW5IPO.js → agent-IJOOONON.js} +8 -8
  4. package/dist/{chat-KT2WVDT2.js → chat-3PM3GP3C.js} +9 -10
  5. package/dist/{chat-KT2WVDT2.js.map → chat-3PM3GP3C.js.map} +1 -1
  6. package/dist/{chunk-ROKE6ZAW.js → chunk-2OHX3O55.js} +6 -20
  7. package/dist/chunk-2OHX3O55.js.map +1 -0
  8. package/dist/{chunk-GSC7PVEO.js → chunk-6MV4ESXM.js} +43 -7
  9. package/dist/chunk-6MV4ESXM.js.map +1 -0
  10. package/dist/chunk-7BMCLMSE.js +173 -0
  11. package/dist/chunk-7BMCLMSE.js.map +1 -0
  12. package/dist/{chunk-NPWYX6NU.js → chunk-BD4ZV7FQ.js} +3 -3
  13. package/dist/{chunk-BKJ7HYX7.js → chunk-DGLLSZ2D.js} +10 -10
  14. package/dist/chunk-DGLLSZ2D.js.map +1 -0
  15. package/dist/chunk-EGJESQXZ.js +1 -0
  16. package/dist/chunk-I2PC6TBZ.js +3887 -0
  17. package/dist/chunk-I2PC6TBZ.js.map +1 -0
  18. package/dist/{chunk-CHWBQJLT.js → chunk-JF7KAVOM.js} +8 -21
  19. package/dist/chunk-JF7KAVOM.js.map +1 -0
  20. package/dist/{chunk-DXJJMRBM.js → chunk-LHVSS27T.js} +2 -2
  21. package/dist/{chunk-2U4TWPWQ.js → chunk-MUINMYCE.js} +5 -5
  22. package/dist/{chunk-GU5VZGXB.js → chunk-N3NVFSDQ.js} +5 -5
  23. package/dist/{chunk-CZRJ5GWE.js → chunk-RAP6MUMB.js} +54 -24
  24. package/dist/chunk-RAP6MUMB.js.map +1 -0
  25. package/dist/{chunk-ZURDCYGU.js → chunk-RE3PPAXZ.js} +9 -5
  26. package/dist/chunk-RE3PPAXZ.js.map +1 -0
  27. package/dist/{chunk-IFL5SREW.js → chunk-UINMHN6S.js} +2 -2
  28. package/dist/chunk-WBQMGZ5X.js +1253 -0
  29. package/dist/chunk-WBQMGZ5X.js.map +1 -0
  30. package/dist/{client-NAL3HVLK.js → client-C3C6IXAH.js} +4 -4
  31. package/dist/{config-FAQO37S7.js → config-7HU4X7EK.js} +4 -4
  32. package/dist/{eas-QAH7XVLK.js → eas-G4TPLJ6S.js} +7 -7
  33. package/dist/{governor-S56M6BV6.js → governor-PYQQ7QRG.js} +7 -7
  34. package/dist/index.js +201 -1286
  35. package/dist/index.js.map +1 -1
  36. package/dist/{network-MKMX2NG6.js → network-WU2QC3L4.js} +3 -3
  37. package/dist/{price-HHDBMKIE.js → price-DGGB6W2G.js} +15 -37
  38. package/dist/price-DGGB6W2G.js.map +1 -0
  39. package/dist/providers-YOOQFBN2.js +31 -0
  40. package/dist/providers-YOOQFBN2.js.map +1 -0
  41. package/dist/{research-IJBOMYL3.js → research-KX3LCAOQ.js} +9 -9
  42. package/dist/{session-L67AVWBY.js → session-GUVSID3Z.js} +19 -174
  43. package/dist/session-GUVSID3Z.js.map +1 -0
  44. package/dist/{trade-LGFQEK3I.js → trade-WDPMFFYB.js} +277 -43
  45. package/dist/trade-WDPMFFYB.js.map +1 -0
  46. package/dist/{vault-XWFPGHRA.js → vault-WDRLJLJ6.js} +14 -9
  47. package/dist/{xmtp-6US7GH3Z.js → xmtp-Q4ZSQV2Q.js} +11 -15
  48. package/dist/xmtp-Q4ZSQV2Q.js.map +1 -0
  49. package/package.json +2 -1
  50. package/dist/chunk-BKJ7HYX7.js.map +0 -1
  51. package/dist/chunk-BQMOE4FJ.js +0 -1629
  52. package/dist/chunk-BQMOE4FJ.js.map +0 -1
  53. package/dist/chunk-CHWBQJLT.js.map +0 -1
  54. package/dist/chunk-CZRJ5GWE.js.map +0 -1
  55. package/dist/chunk-DIZEB2VV.js +0 -325
  56. package/dist/chunk-DIZEB2VV.js.map +0 -1
  57. package/dist/chunk-GSC7PVEO.js.map +0 -1
  58. package/dist/chunk-ROKE6ZAW.js.map +0 -1
  59. package/dist/chunk-ZURDCYGU.js.map +0 -1
  60. package/dist/price-HHDBMKIE.js.map +0 -1
  61. package/dist/research-4TBGYEVI.js +0 -14
  62. package/dist/session-L67AVWBY.js.map +0 -1
  63. package/dist/trade-LGFQEK3I.js.map +0 -1
  64. package/dist/xmtp-6US7GH3Z.js.map +0 -1
  65. /package/dist/{abis-KP7M4IPM.js.map → abis-D7EDCKVZ.js.map} +0 -0
  66. /package/dist/{addresses-DGB3D457.js.map → addresses-OFFTM6NG.js.map} +0 -0
  67. /package/dist/{agent-UNSW5IPO.js.map → agent-IJOOONON.js.map} +0 -0
  68. /package/dist/{chunk-NPWYX6NU.js.map → chunk-BD4ZV7FQ.js.map} +0 -0
  69. /package/dist/{client-NAL3HVLK.js.map → chunk-EGJESQXZ.js.map} +0 -0
  70. /package/dist/{chunk-DXJJMRBM.js.map → chunk-LHVSS27T.js.map} +0 -0
  71. /package/dist/{chunk-2U4TWPWQ.js.map → chunk-MUINMYCE.js.map} +0 -0
  72. /package/dist/{chunk-GU5VZGXB.js.map → chunk-N3NVFSDQ.js.map} +0 -0
  73. /package/dist/{chunk-IFL5SREW.js.map → chunk-UINMHN6S.js.map} +0 -0
  74. /package/dist/{config-FAQO37S7.js.map → client-C3C6IXAH.js.map} +0 -0
  75. /package/dist/{eas-QAH7XVLK.js.map → config-7HU4X7EK.js.map} +0 -0
  76. /package/dist/{governor-S56M6BV6.js.map → eas-G4TPLJ6S.js.map} +0 -0
  77. /package/dist/{network-MKMX2NG6.js.map → governor-PYQQ7QRG.js.map} +0 -0
  78. /package/dist/{research-4TBGYEVI.js.map → network-WU2QC3L4.js.map} +0 -0
  79. /package/dist/{research-IJBOMYL3.js.map → research-KX3LCAOQ.js.map} +0 -0
  80. /package/dist/{vault-XWFPGHRA.js.map → vault-WDRLJLJ6.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/trade.ts","../src/providers/uniswap.ts","../src/lib/signals.ts","../src/lib/exit-strategy.ts","../src/lib/positions.ts"],"sourcesContent":["/**\n * Trade commands — sherwood trade <subcommand>\n *\n * Memecoin trading via the Uniswap Trading API on Base, driven by signal-based\n * analysis (on-chain flows, social sentiment, fundamentals).\n *\n * Trades execute from the agent's own EOA wallet using USDC as quote currency.\n * Requires a Uniswap API key: `sherwood config set --uniswap-api-key <key>`\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { confirm } from \"@inquirer/prompts\";\nimport type { Address } from \"viem\";\nimport { isAddress, parseUnits, formatUnits } from \"viem\";\n\nimport { getPublicClient, getAccount, formatContractError } from \"../lib/client.js\";\nimport { getExplorerUrl, getNetwork } from \"../lib/network.js\";\nimport { TOKENS } from \"../lib/addresses.js\";\nimport { ERC20_ABI } from \"../lib/abis.js\";\nimport { UniswapProvider } from \"../providers/uniswap.js\";\nimport { analyzeToken } from \"../lib/signals.js\";\nimport type { SignalResult } from \"../lib/signals.js\";\nimport { checkExit, DEFAULT_EXIT_CONFIG } from \"../lib/exit-strategy.js\";\nimport type { ExitConfig } from \"../lib/exit-strategy.js\";\nimport {\n getOpenPositions,\n addPosition,\n closePosition,\n updateHighWater,\n getCurrentPrice,\n} from \"../lib/positions.js\";\nimport type { MessageType } from \"../lib/types.js\";\n\nconst uniswap = new UniswapProvider();\n\n// Lazy-load XMTP to avoid breaking non-chat environments\nasync function loadXmtp() {\n return import(\"../lib/xmtp.js\");\n}\n\n// ── Token resolution ──\n\nconst ETH_ADDRESS = \"0x0000000000000000000000000000000000000000\" as Address;\n\nconst KNOWN_MEMECOINS: Record<string, Address> = {\n DEGEN: \"0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed\" as Address,\n TOSHI: \"0xAC1Bd2486aAf3B5C0fc3Fd868558b082a531B2B4\" as Address,\n BRETT: \"0x532f27101965dd16442E59d40670FaF5eBB142E4\" as Address,\n HIGHER: \"0x0578d8A44db98B23BF096A382e016e29a5Ce0ffe\" as Address,\n};\n\nconst WETH_ABI = [\n {\n name: \"deposit\",\n type: \"function\",\n stateMutability: \"payable\",\n inputs: [],\n outputs: [],\n },\n] as const;\n\nasync function resolveToken(\n symbolOrAddress: string,\n): Promise<{ address: Address; symbol: string; decimals: number }> {\n const upper = symbolOrAddress.toUpperCase();\n\n // ETH is native — not an ERC-20, handle specially\n if (upper === \"ETH\") {\n return { address: ETH_ADDRESS, symbol: \"ETH\", decimals: 18 };\n }\n\n let address: Address;\n let symbol: string;\n\n if (isAddress(symbolOrAddress)) {\n address = symbolOrAddress as Address;\n const client = getPublicClient();\n try {\n symbol = (await client.readContract({\n address,\n abi: ERC20_ABI,\n functionName: \"symbol\",\n })) as string;\n } catch {\n symbol = address.slice(0, 8);\n }\n } else {\n const tokens = TOKENS();\n const tokenMap: Record<string, Address> = {\n USDC: tokens.USDC,\n WETH: tokens.WETH,\n ...KNOWN_MEMECOINS,\n };\n address = tokenMap[upper];\n if (!address) {\n throw new Error(\n `Unknown token: ${symbolOrAddress}. Use a contract address or known symbol (${Object.keys(tokenMap).join(\", \")}).`,\n );\n }\n symbol = upper;\n }\n\n const client = getPublicClient();\n const decimals = (await client.readContract({\n address,\n abi: ERC20_ABI,\n functionName: \"decimals\",\n })) as number;\n\n return { address, symbol, decimals };\n}\n\n// ── Chat posting ──\n\nasync function postToChat(\n fund: string,\n type: MessageType,\n text: string,\n data: Record<string, unknown>,\n): Promise<void> {\n try {\n const xmtp = await loadXmtp();\n const group = await xmtp.getGroup(\"\", fund);\n await xmtp.sendEnvelope(group, {\n type,\n from: getAccount().address,\n text,\n data,\n timestamp: Math.floor(Date.now() / 1000),\n });\n } catch {\n // Chat posting is best-effort\n }\n}\n\n// ── Formatting ──\n\nfunction formatSignalTable(results: { symbol: string; address: Address; result: SignalResult }[]): void {\n console.log();\n console.log(chalk.bold(\" Token Score Action Conf. On-chain Social Fundmtl\"));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n\n for (const { symbol, address, result } of results) {\n const scoreColor = result.compositeScore > 0 ? chalk.green : result.compositeScore < 0 ? chalk.red : chalk.dim;\n const actionColor =\n result.action === \"buy\" ? chalk.green.bold :\n result.action === \"sell\" ? chalk.red.bold :\n chalk.dim;\n\n const onChain = result.signals.find((s) => s.source === \"onchain\");\n const social = result.signals.find((s) => s.source === \"social\");\n const fundamental = result.signals.find((s) => s.source === \"fundamental\");\n\n const shortAddr = `${address.slice(0, 6)}..`;\n const label = `${symbol.padEnd(6)} (${shortAddr})`;\n\n console.log(\n ` ${label.padEnd(16)} ` +\n `${scoreColor(result.compositeScore.toFixed(2).padStart(6))} ` +\n `${actionColor(result.action.toUpperCase().padEnd(6))} ` +\n `${(result.confidence * 100).toFixed(0).padStart(4)}% ` +\n `${formatSignalValue(onChain?.value).padStart(8)} ` +\n `${formatSignalValue(social?.value).padStart(6)} ` +\n `${formatSignalValue(fundamental?.value).padStart(7)}`,\n );\n }\n console.log();\n}\n\nfunction formatSignalValue(v: number | undefined): string {\n if (v === undefined) return chalk.dim(\"n/a\");\n const s = (v >= 0 ? \"+\" : \"\") + v.toFixed(2);\n return v > 0 ? chalk.green(s) : v < 0 ? chalk.red(s) : chalk.dim(s);\n}\n\n// ── Commands ──\n\n// The Uniswap Trading API (and the KNOWN_MEMECOINS universe) covers Base only.\n// No currently registered network qualifies — Sherwood currently deploys on\n// Robinhood testnet (chain 46630) — so every trade subcommand fails fast\n// instead of advertising a capability that would throw mid-flow.\nconst TRADING_API_NETWORKS = new Set<string>([\"base\", \"base-sepolia\"]);\n\nexport function registerTradeCommands(program: Command): void {\n const trade = program\n .command(\"trade\")\n .description(\"Memecoin trading — scan, buy, sell, monitor positions (Uniswap Trading API on Base)\");\n\n trade.hook(\"preAction\", () => {\n const network = getNetwork();\n if (!TRADING_API_NETWORKS.has(network)) {\n console.error(chalk.red(`trade commands are not supported on ${network}.`));\n console.error(\n chalk.dim(\n \"They require the Uniswap Trading API, which covers Base only. \" +\n \"Sherwood currently deploys on Robinhood testnet (chain 46630).\",\n ),\n );\n process.exit(1);\n }\n });\n\n // ── trade scan ──\n\n trade\n .command(\"scan\")\n .description(\"Analyze token(s) using on-chain, social, and fundamental signals\")\n .option(\"--token <addr|symbol>\", \"Specific token to analyze (otherwise scans known memecoins)\")\n .option(\"--fund <name>\", \"Post results to fund chat\")\n .option(\"--yes\", \"Skip cost confirmation\", false)\n .action(async (opts: { token?: string; fund?: string; yes: boolean }) => {\n const targets: { symbol: string; address: Address }[] = [];\n\n if (opts.token) {\n const resolved = await resolveToken(opts.token);\n targets.push({ symbol: resolved.symbol, address: resolved.address });\n } else {\n for (const [symbol, address] of Object.entries(KNOWN_MEMECOINS)) {\n targets.push({ symbol, address });\n }\n }\n\n const estCost = targets.length * 0.26;\n console.log();\n console.log(chalk.bold(\"Memecoin Alpha Scan\"));\n console.log(chalk.dim(\"─\".repeat(40)));\n console.log(` Tokens: ${targets.map((t) => t.symbol).join(\", \")}`);\n console.log(` Est. cost: ${chalk.yellow(`~$${estCost.toFixed(2)} USDC`)} (x402 research) + Venice inference`);\n console.log();\n\n if (!opts.yes) {\n const ok = await confirm({\n message: `Proceed with signal analysis?`,\n default: true,\n });\n if (!ok) {\n console.log(chalk.dim(\"Cancelled.\"));\n return;\n }\n }\n\n const spinner = ora(\"Analyzing signals...\").start();\n const results: { symbol: string; address: Address; result: SignalResult }[] = [];\n\n for (const target of targets) {\n try {\n spinner.text = `Analyzing ${target.symbol}...`;\n const result = await analyzeToken(target.address, target.symbol);\n results.push({ ...target, result });\n } catch {\n results.push({\n ...target,\n result: {\n action: \"hold\",\n confidence: 0,\n compositeScore: 0,\n signals: [],\n costUsdc: \"0\",\n timestamp: Math.floor(Date.now() / 1000),\n },\n });\n }\n }\n\n spinner.succeed(\"Scan complete\");\n formatSignalTable(results);\n\n const totalCost = results.reduce((sum, r) => sum + Number(r.result.costUsdc), 0);\n console.log(chalk.dim(` Total research cost: $${totalCost.toFixed(4)} USDC`));\n console.log();\n\n if (opts.fund) {\n await postToChat(opts.fund, \"TRADE_SIGNAL\" as MessageType,\n `Scanned ${results.length} tokens: ${results.filter((r) => r.result.action === \"buy\").map((r) => r.symbol).join(\", \") || \"no buys\"}`,\n { results: results.map((r) => ({ symbol: r.symbol, action: r.result.action, score: r.result.compositeScore })) },\n );\n }\n });\n\n // ── trade buy ──\n\n trade\n .command(\"buy\")\n .description(\"Buy a token via Uniswap Trading API\")\n .requiredOption(\"--token <addr|symbol>\", \"Token to buy\")\n .requiredOption(\"--amount <n>\", \"Amount of input token to spend\")\n .option(\"--with <token>\", \"Input token to spend (default: USDC). Use ETH for native ETH (auto-wraps to WETH).\", \"USDC\")\n .option(\"--slippage <pct>\", \"Slippage tolerance % (default: 0.5)\", \"0.5\")\n .option(\"--stop-loss <pct>\", \"Stop loss percentage (default: 10)\", \"10\")\n .option(\"--trailing-stop <pct>\", \"Trailing stop percentage (0 = disabled)\", \"0\")\n .option(\"--deadline <hours>\", \"Force exit after N hours (0 = none)\", \"0\")\n .option(\"--fund <name>\", \"Post trade to fund chat\")\n .action(async (opts) => {\n const { address: tokenAddr, symbol, decimals } = await resolveToken(opts.token as string);\n const slippage = Number(opts.slippage);\n\n // Resolve input token\n const inputSpec = (opts.with as string).toUpperCase();\n const isEthInput = inputSpec === \"ETH\";\n // ETH input: wrap to WETH first, then swap WETH → target\n const {\n address: inputAddr,\n symbol: inputSymbol,\n decimals: inputDecimals,\n } = isEthInput\n ? { address: TOKENS().WETH, symbol: \"ETH\", decimals: 18 }\n : await resolveToken(opts.with as string);\n const inputAmount = parseUnits(opts.amount as string, inputDecimals);\n\n // Check balance\n const client = getPublicClient();\n const account = getAccount();\n\n if (isEthInput) {\n const ethBalance = await client.getBalance({ address: account.address });\n if (ethBalance < inputAmount) {\n console.error(chalk.red(\n `Insufficient ETH. Have ${formatUnits(ethBalance, 18)}, need ${opts.amount}`,\n ));\n process.exit(1);\n }\n\n // Wrap ETH → WETH\n const wrapSpinner = ora(\"Wrapping ETH → WETH...\").start();\n try {\n const { writeContractWithRetry, waitForReceipt: waitForReceiptFn } = await import(\"../lib/client.js\");\n const wethAddr = TOKENS().WETH;\n const wrapHash = await writeContractWithRetry({\n address: wethAddr,\n abi: WETH_ABI,\n functionName: \"deposit\",\n args: [],\n value: inputAmount,\n account,\n chain: (await import(\"../lib/network.js\")).getChain(),\n });\n await waitForReceiptFn(wrapHash);\n wrapSpinner.succeed(`Wrapped ${opts.amount} ETH → WETH`);\n } catch (err) {\n wrapSpinner.fail(\"ETH wrap failed\");\n console.error(chalk.red(err instanceof Error ? err.message : String(err)));\n process.exit(1);\n }\n } else {\n const balance = (await client.readContract({\n address: inputAddr,\n abi: ERC20_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n if (balance < inputAmount) {\n console.error(chalk.red(\n `Insufficient ${inputSymbol}. Have ${formatUnits(balance, inputDecimals)}, need ${opts.amount}`,\n ));\n process.exit(1);\n }\n }\n\n // Get quote via Uniswap Trading API (handles routing automatically)\n const quoteSpinner = ora(\"Getting quote from Uniswap API...\").start();\n let expectedOut: bigint;\n\n try {\n const result = await uniswap.fullQuote({\n tokenIn: inputAddr,\n tokenOut: tokenAddr,\n amountIn: inputAmount,\n slippageTolerance: slippage,\n });\n expectedOut = result.amountOut;\n const routing = result.routing;\n quoteSpinner.succeed(\n `Quote: ${formatUnits(result.amountOut, decimals)} ${symbol} (${routing} route)`,\n );\n } catch (err) {\n quoteSpinner.fail(\"Quote failed\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n\n // Execute swap via Uniswap Trading API (check_approval + quote + swap + sign + broadcast)\n const swapSpinner = ora(\"Executing swap via Uniswap API...\").start();\n let txHash: string;\n try {\n const result = await uniswap.swap({\n tokenIn: inputAddr,\n tokenOut: tokenAddr,\n amountIn: inputAmount,\n amountOutMinimum: 0n, // slippage handled by API\n fee: 3000,\n slippageTolerance: slippage,\n });\n txHash = result.hash;\n\n if (!result.success) {\n swapSpinner.fail(\"Swap reverted\");\n console.error(chalk.dim(` Tx: ${getExplorerUrl(result.hash)}`));\n process.exit(1);\n }\n swapSpinner.succeed(\"Swap executed\");\n } catch (err) {\n swapSpinner.fail(\"Swap failed\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n\n // Read actual token balance received\n const tokenBalance = (await client.readContract({\n address: tokenAddr,\n abi: ERC20_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n\n const tokensReceived = tokenBalance > 0n ? tokenBalance : expectedOut;\n const entryPrice = Number(opts.amount) / Number(formatUnits(tokensReceived, decimals));\n\n // Build exit config\n const exitConfig: ExitConfig = {\n stopLossPct: Number(opts.stopLoss) || DEFAULT_EXIT_CONFIG.stopLossPct,\n trailingStopPct: Number(opts.trailingStop) || DEFAULT_EXIT_CONFIG.trailingStopPct,\n takeProfitPct: DEFAULT_EXIT_CONFIG.takeProfitPct,\n deadlineUnix: Number(opts.deadline) > 0\n ? Math.floor(Date.now() / 1000) + Number(opts.deadline) * 3600\n : 0,\n signalExitEnabled: true,\n };\n\n // Save position\n addPosition({\n tokenAddress: tokenAddr,\n tokenSymbol: symbol,\n tokenDecimals: decimals,\n amountIn: opts.amount as string,\n amountOut: tokensReceived.toString(),\n entryPrice,\n highWaterPrice: entryPrice,\n feeTier: 3000, // stored for price lookups via QuoterV2\n openedAt: Math.floor(Date.now() / 1000),\n txHash,\n exitConfig,\n inputTokenAddress: inputAddr,\n inputTokenSymbol: inputSymbol,\n inputTokenDecimals: inputDecimals,\n });\n\n console.log();\n console.log(chalk.bold(\"Trade Executed\"));\n console.log(chalk.dim(\"─\".repeat(40)));\n console.log(` Token: ${symbol} (${tokenAddr})`);\n console.log(` Spent: ${opts.amount} ${inputSymbol}`);\n console.log(` Received: ${formatUnits(tokensReceived, decimals)} ${symbol}`);\n console.log(` Entry: ${entryPrice.toFixed(8)} ${inputSymbol}/${symbol}`);\n console.log(` Stop: -${exitConfig.stopLossPct}%`);\n if (exitConfig.trailingStopPct > 0) {\n console.log(` Trailing: ${exitConfig.trailingStopPct}%`);\n }\n if (exitConfig.deadlineUnix > 0) {\n console.log(` Deadline: ${new Date(exitConfig.deadlineUnix * 1000).toISOString()}`);\n }\n console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash as `0x${string}`))}`);\n\n // EAS attestation (best-effort) — recipient is vault so dashboard can display it\n try {\n const { createTradeAttestation, getEasScanUrl } = await import(\"../lib/eas.js\");\n const { getChainContracts } = await import(\"../lib/config.js\");\n const { getChain: getActiveChain } = await import(\"../lib/network.js\");\n const vaultRecipient = getChainContracts(getActiveChain().id).vault as `0x${string}` | undefined;\n const { uid } = await createTradeAttestation(\n inputAddr, tokenAddr, inputAmount,\n formatUnits(tokensReceived, decimals),\n txHash, \"BUY\", vaultRecipient,\n );\n if (uid !== \"0x0000000000000000000000000000000000000000000000000000000000000000\") {\n console.log(` Attested: ${chalk.dim(getEasScanUrl(uid))}`);\n }\n } catch {\n // Attestation is best-effort\n }\n\n console.log();\n\n if (opts.fund) {\n await postToChat(opts.fund, \"TRADE_EXECUTED\" as MessageType,\n `Bought ${formatUnits(tokensReceived, decimals)} ${symbol} for ${opts.amount} ${inputSymbol} via Uniswap`,\n { token: symbol, address: tokenAddr, amountIn: opts.amount, inputToken: inputSymbol, txHash },\n );\n }\n });\n\n // ── trade sell ──\n\n trade\n .command(\"sell\")\n .description(\"Sell a token position via Uniswap Trading API\")\n .requiredOption(\"--token <addr|symbol>\", \"Token to sell\")\n .option(\"--amount <n>\", \"Token amount to sell (default: entire position)\")\n .option(\"--for <token>\", \"Output token to receive (default: USDC)\", \"USDC\")\n .option(\"--slippage <pct>\", \"Slippage tolerance % (default: 0.5)\", \"0.5\")\n .option(\"--fund <name>\", \"Post trade to fund chat\")\n .action(async (opts) => {\n const { address: tokenAddr, symbol, decimals } = await resolveToken(opts.token as string);\n const {\n address: outputAddr,\n symbol: outputSymbol,\n decimals: outputDecimals,\n } = await resolveToken(opts.for as string);\n const slippage = Number(opts.slippage);\n\n // Find open position\n const positions = getOpenPositions();\n const pos = positions.find(\n (p) => p.tokenAddress.toLowerCase() === tokenAddr.toLowerCase(),\n );\n\n // Determine sell amount\n const client = getPublicClient();\n const account = getAccount();\n let sellAmount: bigint;\n\n if (opts.amount) {\n sellAmount = parseUnits(opts.amount as string, decimals);\n } else {\n sellAmount = (await client.readContract({\n address: tokenAddr,\n abi: ERC20_ABI,\n functionName: \"balanceOf\",\n args: [account.address],\n })) as bigint;\n }\n\n if (sellAmount === 0n) {\n console.error(chalk.red(\"No tokens to sell.\"));\n process.exit(1);\n }\n\n // Quote via Uniswap Trading API\n const quoteSpinner = ora(\"Getting quote from Uniswap API...\").start();\n let expectedOut: bigint;\n\n try {\n const result = await uniswap.fullQuote({\n tokenIn: tokenAddr,\n tokenOut: outputAddr,\n amountIn: sellAmount,\n slippageTolerance: slippage,\n });\n expectedOut = result.amountOut;\n const routing = result.routing;\n quoteSpinner.succeed(\n `Quote: ${formatUnits(result.amountOut, outputDecimals)} ${outputSymbol} (${routing} route)`,\n );\n } catch (err) {\n quoteSpinner.fail(\"Quote failed\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n\n // Execute sell via Uniswap Trading API\n const swapSpinner = ora(\"Executing sell via Uniswap API...\").start();\n let txHash: string;\n try {\n const result = await uniswap.swap({\n tokenIn: tokenAddr,\n tokenOut: outputAddr,\n amountIn: sellAmount,\n amountOutMinimum: 0n,\n fee: 3000,\n slippageTolerance: slippage,\n });\n txHash = result.hash;\n\n if (!result.success) {\n swapSpinner.fail(\"Sell reverted\");\n process.exit(1);\n }\n swapSpinner.succeed(\"Sell executed\");\n } catch (err) {\n swapSpinner.fail(\"Sell failed\");\n console.error(chalk.red(formatContractError(err)));\n process.exit(1);\n }\n\n // Calculate P&L (meaningful when output matches the original input token)\n const outputReceived = Number(formatUnits(expectedOut, outputDecimals));\n const costBasis = pos ? Number(pos.amountIn) : 0;\n const inputMatchesOutput = pos\n ? (pos.inputTokenSymbol ?? \"USDC\").toUpperCase() === outputSymbol.toUpperCase()\n : outputSymbol.toUpperCase() === \"USDC\";\n const pnl = costBasis > 0 && inputMatchesOutput ? outputReceived - costBasis : 0;\n const pnlPct = costBasis > 0 && inputMatchesOutput ? (pnl / costBasis) * 100 : 0;\n const exitPrice = sellAmount > 0n\n ? outputReceived / Number(formatUnits(sellAmount, decimals))\n : 0;\n\n if (pos) {\n closePosition(tokenAddr, {\n exitPrice,\n closedAt: Math.floor(Date.now() / 1000),\n exitTxHash: txHash,\n exitReason: \"manual\",\n pnlUsdc: pnl,\n pnlPct,\n });\n }\n\n const pnlColor = pnl >= 0 ? chalk.green : chalk.red;\n\n console.log();\n console.log(chalk.bold(\"Position Closed\"));\n console.log(chalk.dim(\"─\".repeat(40)));\n console.log(` Token: ${symbol}`);\n console.log(` Sold: ${formatUnits(sellAmount, decimals)} ${symbol}`);\n console.log(` Received: ~${outputReceived.toFixed(outputDecimals <= 6 ? 2 : 6)} ${outputSymbol}`);\n if (costBasis > 0 && inputMatchesOutput) {\n const posInputSymbol = pos?.inputTokenSymbol ?? \"USDC\";\n console.log(` Cost: ${costBasis.toFixed(2)} ${posInputSymbol}`);\n console.log(` P&L: ${pnlColor(`${pnl >= 0 ? \"+\" : \"\"}${pnl.toFixed(2)} ${outputSymbol} (${pnlPct >= 0 ? \"+\" : \"\"}${pnlPct.toFixed(1)}%)`)}`);\n }\n console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash as `0x${string}`))}`);\n\n // EAS attestation (best-effort) — recipient is vault so dashboard can display it\n try {\n const { createTradeAttestation, getEasScanUrl } = await import(\"../lib/eas.js\");\n const { getChainContracts } = await import(\"../lib/config.js\");\n const { getChain: getActiveChain } = await import(\"../lib/network.js\");\n const vaultRecipient = getChainContracts(getActiveChain().id).vault as `0x${string}` | undefined;\n const { uid } = await createTradeAttestation(\n tokenAddr, outputAddr, sellAmount,\n formatUnits(expectedOut, outputDecimals),\n txHash, \"SELL\", vaultRecipient,\n );\n if (uid !== \"0x0000000000000000000000000000000000000000000000000000000000000000\") {\n console.log(` Attested: ${chalk.dim(getEasScanUrl(uid))}`);\n }\n } catch {\n // Attestation is best-effort\n }\n\n console.log();\n\n if (opts.fund) {\n await postToChat(opts.fund, \"TRADE_EXECUTED\" as MessageType,\n `Sold ${formatUnits(sellAmount, decimals)} ${symbol} for ~${outputReceived.toFixed(2)} ${outputSymbol}${inputMatchesOutput && costBasis > 0 ? ` (P&L: ${pnl >= 0 ? \"+\" : \"\"}${pnl.toFixed(2)})` : \"\"}`,\n { token: symbol, address: tokenAddr, outputReceived, outputToken: outputSymbol, pnl, pnlPct, txHash },\n );\n }\n });\n\n // ── trade positions ──\n\n trade\n .command(\"positions\")\n .description(\"Show open positions with current prices and unrealized P&L\")\n .action(async () => {\n const positions = getOpenPositions();\n if (positions.length === 0) {\n console.log(chalk.dim(\"\\n No open positions.\\n\"));\n return;\n }\n\n console.log();\n console.log(chalk.bold(\"Open Positions\"));\n console.log(chalk.dim(\"─\".repeat(80)));\n console.log(chalk.dim(\n \" Token Entry Current Qty Cost Value P&L\",\n ));\n\n for (const pos of positions) {\n try {\n const current = await getCurrentPrice(pos.tokenAddress, pos.tokenDecimals, pos.feeTier);\n const qty = Number(formatUnits(BigInt(pos.amountOut), pos.tokenDecimals));\n const cost = Number(pos.amountIn);\n const value = qty * current;\n const pnl = value - cost;\n const pnlPct = cost > 0 ? (pnl / cost) * 100 : 0;\n const pnlColor = pnl >= 0 ? chalk.green : chalk.red;\n\n console.log(\n ` ${pos.tokenSymbol.padEnd(14)} ` +\n `$${pos.entryPrice.toFixed(6).padStart(10)} ` +\n `$${current.toFixed(6).padStart(10)} ` +\n `${qty.toFixed(2).padStart(15)} ` +\n `$${cost.toFixed(2).padStart(8)} ` +\n `$${value.toFixed(2).padStart(8)} ` +\n pnlColor(`${pnl >= 0 ? \"+\" : \"\"}${pnl.toFixed(2)} (${pnlPct >= 0 ? \"+\" : \"\"}${pnlPct.toFixed(1)}%)`),\n );\n\n if (current > pos.highWaterPrice) {\n updateHighWater(pos.tokenAddress, current);\n }\n } catch {\n console.log(\n ` ${pos.tokenSymbol.padEnd(14)} ` +\n `$${pos.entryPrice.toFixed(6).padStart(10)} ` +\n `${chalk.dim(\"price unavailable\")}`,\n );\n }\n }\n console.log();\n });\n\n // ── trade monitor ──\n\n trade\n .command(\"monitor\")\n .description(\"Monitor positions and auto-exit on signal triggers\")\n .option(\"--interval <seconds>\", \"Check interval in seconds (default: 300)\", \"300\")\n .option(\"--fund <name>\", \"Post updates to fund chat\")\n .action(async (opts) => {\n const interval = Number(opts.interval) * 1000;\n\n console.log();\n console.log(chalk.bold(\"Position Monitor\"));\n console.log(chalk.dim(`Checking every ${opts.interval}s. Press Ctrl-C to stop.`));\n console.log();\n\n const running = { value: true };\n process.on(\"SIGINT\", () => {\n running.value = false;\n console.log(chalk.dim(\"\\nStopping monitor...\"));\n });\n\n while (running.value) {\n const positions = getOpenPositions();\n if (positions.length === 0) {\n console.log(chalk.dim(\" No open positions. Waiting...\"));\n await sleep(interval);\n continue;\n }\n\n for (const pos of positions) {\n if (!running.value) break;\n\n try {\n const current = await getCurrentPrice(pos.tokenAddress, pos.tokenDecimals, pos.feeTier);\n const hwPrice = Math.max(current, pos.highWaterPrice);\n if (current > pos.highWaterPrice) {\n updateHighWater(pos.tokenAddress, current);\n }\n\n // Run signal analysis for exit check\n let signalResult: SignalResult | undefined;\n if (pos.exitConfig.signalExitEnabled) {\n try {\n signalResult = await analyzeToken(pos.tokenAddress, pos.tokenSymbol);\n } catch {\n // Signal analysis can fail — continue with price-only checks\n }\n }\n\n const exit = checkExit(pos.entryPrice, current, hwPrice, pos.exitConfig, signalResult);\n const pnlPct = exit.currentPnlPct;\n const pnlColor = pnlPct >= 0 ? chalk.green : chalk.red;\n const ts = new Date().toLocaleTimeString();\n\n console.log(\n ` [${ts}] ${pos.tokenSymbol}: ` +\n `$${current.toFixed(6)} ` +\n pnlColor(`(${pnlPct >= 0 ? \"+\" : \"\"}${pnlPct.toFixed(1)}%)`) +\n (exit.shouldExit ? chalk.red.bold(` → EXIT (${exit.reason})`) : \"\"),\n );\n\n if (exit.shouldExit) {\n console.log(chalk.yellow(` Executing exit for ${pos.tokenSymbol}: ${exit.reason}`));\n\n if (opts.fund) {\n await postToChat(opts.fund, \"RISK_ALERT\" as MessageType,\n `Exit triggered for ${pos.tokenSymbol}: ${exit.reason} (${pnlPct >= 0 ? \"+\" : \"\"}${pnlPct.toFixed(1)}%)`,\n { token: pos.tokenSymbol, reason: exit.reason, pnlPct },\n );\n }\n\n try {\n const sellAmount = BigInt(pos.amountOut);\n const usdc = TOKENS().USDC;\n\n // Get quote for P&L calculation\n const { amountOut: sellQuote } = await uniswap.fullQuote({\n tokenIn: pos.tokenAddress,\n tokenOut: usdc,\n amountIn: sellAmount,\n slippageTolerance: 0.5,\n });\n\n // Execute sell via Trading API\n const result = await uniswap.swap({\n tokenIn: pos.tokenAddress,\n tokenOut: usdc,\n amountIn: sellAmount,\n amountOutMinimum: 0n,\n fee: 3000,\n slippageTolerance: 1.0, // 1% for auto-sell\n });\n\n const usdcReceived = Number(formatUnits(sellQuote, 6));\n const costBasis = Number(pos.amountIn);\n const pnlUsdc = usdcReceived - costBasis;\n\n closePosition(pos.tokenAddress, {\n exitPrice: current,\n closedAt: Math.floor(Date.now() / 1000),\n exitTxHash: result.hash,\n exitReason: exit.reason,\n pnlUsdc,\n pnlPct,\n });\n\n const pnlStr = `${pnlUsdc >= 0 ? \"+\" : \"\"}${pnlUsdc.toFixed(2)} USDC`;\n console.log(chalk.green(` Sold ${pos.tokenSymbol}: ${pnlStr}`));\n\n if (opts.fund) {\n await postToChat(opts.fund, \"TRADE_EXECUTED\" as MessageType,\n `Auto-sold ${pos.tokenSymbol}: ${pnlStr} (${exit.reason})`,\n { token: pos.tokenSymbol, reason: exit.reason, pnlUsdc, pnlPct, txHash: result.hash },\n );\n }\n } catch (err) {\n console.error(chalk.red(\n ` Failed to sell ${pos.tokenSymbol}: ${formatContractError(err)}`,\n ));\n }\n }\n } catch {\n console.error(chalk.dim(\n ` [${new Date().toLocaleTimeString()}] ${pos.tokenSymbol}: price check failed`,\n ));\n }\n }\n\n if (running.value) {\n await sleep(interval);\n }\n }\n\n console.log(chalk.dim(\"Monitor stopped.\"));\n });\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * PARKED — Base-only Uniswap Trading API adapter (inactive on the current\n * deployment target).\n *\n * The hosted Trading API covers Base / Base Sepolia only; the CLI's sole\n * registered chain is Robinhood testnet (chain 46630), so this adapter cannot\n * execute there and is NOT advertised by `sherwood providers` (see\n * providers/index.ts). Its only consumer is `sherwood trade`, which fails fast\n * on unsupported networks (commands/trade.ts). Swaps on the live chain go\n * through lib/swap-router.ts (Synthra). Retained for a future Base deployment.\n *\n * Uniswap Trading API provider — swaps on Base via the hosted API.\n *\n * Uses the 3-step flow:\n * 1. POST /check_approval → check/submit token approval\n * 2. POST /quote → get optimal route + executable quote\n * 3. POST /swap → get transaction calldata to sign\n *\n * API key required — set via `sherwood config set --uniswap-api-key <key>`\n * or UNISWAP_API_KEY env var.\n *\n * Docs: https://docs.uniswap.org/api/trading-api\n * Key: https://developers.uniswap.org/\n */\n\nimport type { Address, Hex } from \"viem\";\nimport { isAddress, isHex, formatUnits } from \"viem\";\nimport type { SwapParams, SwapQuoteParams, TxResult, SwapQuote } from \"../types.js\";\nimport { getPublicClient, getAccount, writeContractWithRetry, sendTxWithRetry, waitForReceipt } from \"../lib/client.js\";\nimport { getChain } from \"../lib/network.js\";\nimport { getUniswapApiKey } from \"../lib/config.js\";\n\nconst API_BASE = \"https://trade-api.gateway.uniswap.org/v1\";\n\n// ── API Types ──\n\ninterface ApprovalResponse {\n approval: {\n to: Address;\n from: Address;\n data: Hex;\n value: string;\n chainId: number;\n } | null;\n}\n\n// CLASSIC route quote\ninterface ClassicQuote {\n routing: \"CLASSIC\" | \"WRAP\" | \"UNWRAP\";\n quote: {\n input: { token: string; amount: string };\n output: { token: string; amount: string };\n slippage: number;\n route: unknown[];\n gasFee: string;\n gasFeeUSD: string;\n gasUseEstimate: string;\n };\n permitData: Record<string, unknown> | null;\n}\n\n// UniswapX route quote (PRIORITY on Base)\ninterface UniswapXQuote {\n routing: \"DUTCH_V2\" | \"DUTCH_V3\" | \"PRIORITY\";\n quote: {\n orderInfo: {\n outputs: Array<{\n token: string;\n startAmount: string;\n endAmount: string;\n recipient: string;\n }>;\n input: { token: string; startAmount: string; endAmount: string };\n deadline: number;\n nonce: string;\n };\n encodedOrder: string;\n orderHash: string;\n };\n permitData: Record<string, unknown> | null;\n}\n\ntype QuoteResponse = ClassicQuote | UniswapXQuote;\n\ninterface SwapResponse {\n swap: {\n to: Address;\n from: Address;\n data: Hex;\n value: string;\n chainId: number;\n gasLimit?: string;\n };\n}\n\n// ── Helpers ──\n\nfunction getApiKey(): string {\n const key = getUniswapApiKey();\n if (!key) {\n throw new Error(\n \"Uniswap API key not configured. Run 'sherwood config set --uniswap-api-key <key>' \" +\n \"or set UNISWAP_API_KEY env var.\\n\" +\n \"Get your key at https://developers.uniswap.org/\",\n );\n }\n return key;\n}\n\nfunction apiHeaders(): Record<string, string> {\n return {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": getApiKey(),\n \"x-universal-router-version\": \"2.0\",\n };\n}\n\nfunction isUniswapXRouting(routing: string): boolean {\n return routing === \"DUTCH_V2\" || routing === \"DUTCH_V3\" || routing === \"PRIORITY\";\n}\n\nfunction getOutputAmount(q: QuoteResponse): string {\n if (isUniswapXRouting(q.routing)) {\n const ux = q as UniswapXQuote;\n const firstOutput = ux.quote.orderInfo.outputs[0];\n if (!firstOutput) throw new Error(\"UniswapX quote has no outputs\");\n return firstOutput.startAmount; // best-case fill\n }\n return (q as ClassicQuote).quote.output.amount;\n}\n\nasync function fetchWithRetry(\n url: string,\n init: RequestInit,\n maxRetries = 3,\n): Promise<Response> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const response = await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(30_000),\n });\n if (response.status !== 429 && response.status < 500) return response;\n if (attempt === maxRetries) return response;\n\n const delay = Math.min(200 * Math.pow(2, attempt) + Math.random() * 100, 10000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n throw new Error(\"Unreachable\");\n}\n\n// ── Provider ──\n\nexport class UniswapProvider {\n /**\n * Get a quote from the Uniswap Trading API.\n * Returns the optimal route (CLASSIC, PRIORITY, or UniswapX).\n */\n async quote(params: SwapQuoteParams): Promise<SwapQuote> {\n const account = getAccount();\n const chainId = getChain().id;\n\n const body = {\n swapper: account.address,\n tokenIn: params.tokenIn,\n tokenOut: params.tokenOut,\n tokenInChainId: String(chainId),\n tokenOutChainId: String(chainId),\n amount: params.amountIn.toString(),\n type: \"EXACT_INPUT\",\n slippageTolerance: 0.5,\n routingPreference: \"BEST_PRICE\",\n };\n\n const res = await fetchWithRetry(`${API_BASE}/quote`, {\n method: \"POST\",\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Uniswap quote failed (${res.status}): ${err}`);\n }\n\n const data = (await res.json()) as QuoteResponse;\n const amountOut = BigInt(getOutputAmount(data));\n\n return {\n amountOut,\n priceImpact: 0,\n route: `${data.routing}: ${params.tokenIn.slice(0, 8)}→${params.tokenOut.slice(0, 8)}`,\n };\n }\n\n /**\n * Get a full quote response (preserving the raw API response for swap execution).\n */\n async fullQuote(params: {\n tokenIn: Address;\n tokenOut: Address;\n amountIn: bigint;\n slippageTolerance?: number;\n }): Promise<{ quoteResponse: QuoteResponse; amountOut: bigint; routing: string }> {\n const account = getAccount();\n const chainId = getChain().id;\n\n const body = {\n swapper: account.address,\n tokenIn: params.tokenIn,\n tokenOut: params.tokenOut,\n tokenInChainId: String(chainId),\n tokenOutChainId: String(chainId),\n amount: params.amountIn.toString(),\n type: \"EXACT_INPUT\",\n slippageTolerance: params.slippageTolerance ?? 0.5,\n routingPreference: \"BEST_PRICE\",\n };\n\n const res = await fetchWithRetry(`${API_BASE}/quote`, {\n method: \"POST\",\n headers: apiHeaders(),\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Uniswap quote failed (${res.status}): ${err}`);\n }\n\n const quoteResponse = (await res.json()) as QuoteResponse;\n const amountOut = BigInt(getOutputAmount(quoteResponse));\n return { quoteResponse, amountOut, routing: quoteResponse.routing };\n }\n\n /**\n * Check and handle token approval for Uniswap.\n * Uses the Trading API's /check_approval endpoint.\n */\n async checkApproval(params: {\n token: Address;\n amount: bigint;\n }): Promise<void> {\n const account = getAccount();\n const chainId = getChain().id;\n const client = getPublicClient();\n\n const res = await fetchWithRetry(`${API_BASE}/check_approval`, {\n method: \"POST\",\n headers: apiHeaders(),\n body: JSON.stringify({\n walletAddress: account.address,\n token: params.token,\n amount: params.amount.toString(),\n chainId,\n }),\n });\n\n if (!res.ok) {\n const err = await res.text();\n throw new Error(`Approval check failed (${res.status}): ${err}`);\n }\n\n const data = (await res.json()) as ApprovalResponse;\n\n if (data.approval) {\n // API returns a ready-to-send transaction — use sendTxWithRetry for nonce safety\n const approvalHash = await sendTxWithRetry({\n to: data.approval.to,\n data: data.approval.data,\n value: BigInt(data.approval.value || \"0\"),\n account,\n chain: getChain(),\n });\n await waitForReceipt(approvalHash);\n }\n }\n\n /**\n * Execute a swap via the Uniswap Trading API.\n *\n * Full flow: check_approval → quote → swap → sign & broadcast.\n */\n async swap(params: SwapParams): Promise<TxResult> {\n const account = getAccount();\n const client = getPublicClient();\n\n // 1. Check approval (skip for native ETH)\n const ETH_ADDRESS = \"0x0000000000000000000000000000000000000000\" as Address;\n if (params.tokenIn !== ETH_ADDRESS) {\n await this.checkApproval({\n token: params.tokenIn,\n amount: params.amountIn,\n });\n }\n\n // 2. Get quote\n const { quoteResponse } = await this.fullQuote({\n tokenIn: params.tokenIn,\n tokenOut: params.tokenOut,\n amountIn: params.amountIn,\n slippageTolerance: params.slippageTolerance ?? 0.5,\n });\n\n // 3. Prepare swap request — routing-aware permitData handling\n const quoteRaw = quoteResponse as unknown as Record<string, unknown>;\n const { permitData, ...cleanQuote } = quoteRaw;\n const swapRequest: Record<string, unknown> = { ...cleanQuote };\n\n // Sign permitData if present (required for both CLASSIC and UniswapX).\n // For CLASSIC routes using Permit2 (PermitSingle): include BOTH permitData + signature.\n // For UniswapX routes: include signature only (order-level signing, no permitData in body).\n if (permitData && typeof permitData === \"object\") {\n const typedData = permitData as {\n domain: Record<string, unknown>;\n types: Record<string, unknown>;\n values: Record<string, unknown>;\n };\n const primaryType =\n Object.keys(typedData.types).find((k) => k !== \"EIP712Domain\") ?? \"PermitSingle\";\n const signature = await account.signTypedData({\n domain: typedData.domain as Record<string, unknown>,\n types: typedData.types as Record<string, Array<{ name: string; type: string }>>,\n primaryType,\n message: typedData.values,\n });\n swapRequest.signature = signature;\n // CLASSIC routes: /swap requires both permitData and signature\n // UniswapX routes: /swap requires signature only (order is in quote body)\n if (!isUniswapXRouting(quoteResponse.routing)) {\n swapRequest.permitData = permitData;\n }\n }\n\n // 4. Get swap transaction\n const swapRes = await fetchWithRetry(`${API_BASE}/swap`, {\n method: \"POST\",\n headers: apiHeaders(),\n body: JSON.stringify(swapRequest),\n });\n\n if (!swapRes.ok) {\n const err = await swapRes.text();\n throw new Error(`Uniswap swap failed (${swapRes.status}): ${err}`);\n }\n\n const swapData = (await swapRes.json()) as SwapResponse;\n\n // 5. Validate before broadcasting\n if (!swapData.swap?.data || swapData.swap.data === (\"\" as Hex) || swapData.swap.data === \"0x\") {\n throw new Error(\"Empty swap data — quote may have expired. Try again.\");\n }\n if (!isAddress(swapData.swap.to) || !isAddress(swapData.swap.from)) {\n throw new Error(\"Invalid address in swap response\");\n }\n\n // 6. Sign and broadcast with retry-aware sender\n // Apply a 1.5x buffer to the API's gasLimit: the Uniswap API under-estimates for WETH\n // and other non-USDC inputs where PermitSingle+transfer adds overhead.\n const gasLimit = swapData.swap.gasLimit\n ? (BigInt(swapData.swap.gasLimit) * 150n) / 100n\n : undefined;\n const hash = await sendTxWithRetry({\n to: swapData.swap.to,\n data: swapData.swap.data,\n value: BigInt(swapData.swap.value || \"0\"),\n gas: gasLimit,\n account,\n chain: getChain(),\n });\n\n const receipt = await waitForReceipt(hash);\n return {\n hash,\n success: receipt.status === \"success\",\n gasUsed: receipt.gasUsed,\n };\n }\n}\n","/**\n * Signal engine — aggregates on-chain, social, and fundamental data\n * into a composite buy/sell/hold recommendation.\n *\n * Sources:\n * On-chain: Nansen smart-money net flow (x402, ~$0.06)\n * Social: Venice inference with web search (X/Twitter sentiment)\n * Fundamental: Messari market data (x402, ~$0.20)\n *\n * No technical analysis — purely signal-driven.\n */\n\nimport type { Address } from \"viem\";\nimport { getResearchProvider } from \"../providers/research/index.js\";\nimport type { ResearchResult } from \"../providers/research/index.js\";\nimport { chatCompletion } from \"./venice.js\";\n\n// ── Types ──\n\nexport type SignalAction = \"buy\" | \"sell\" | \"hold\";\nexport type SignalSource = \"onchain\" | \"social\" | \"fundamental\";\n\nexport interface Signal {\n source: SignalSource;\n name: string;\n value: number; // -1.0 (bearish) to +1.0 (bullish)\n weight: number; // contribution to composite score\n raw: Record<string, unknown>;\n}\n\nexport interface SignalResult {\n action: SignalAction;\n confidence: number; // 0.0 to 1.0\n compositeScore: number; // -1.0 to +1.0\n signals: Signal[];\n costUsdc: string; // total x402 cost\n timestamp: number;\n}\n\nexport interface SignalConfig {\n buyThreshold: number; // composite score above this → buy (default 0.3)\n sellThreshold: number; // composite score below this → sell (default -0.2)\n}\n\nconst DEFAULT_CONFIG: SignalConfig = {\n buyThreshold: 0.3,\n sellThreshold: -0.2,\n};\n\n// ── Main Entry Point ──\n\n/**\n * Analyze a token using on-chain, social, and fundamental signals.\n * Runs all three data sources in parallel for speed.\n */\nexport async function analyzeToken(\n tokenAddress: Address,\n tokenSymbol: string,\n config?: Partial<SignalConfig>,\n): Promise<SignalResult> {\n const cfg = { ...DEFAULT_CONFIG, ...config };\n let totalCostUsdc = 0;\n\n // Run all three signals in parallel — each returns a Signal + cost\n const [onChain, social, fundamental] = await Promise.allSettled([\n getOnChainSignal(tokenSymbol),\n getSocialSignal(tokenAddress, tokenSymbol),\n getFundamentalSignal(tokenSymbol),\n ]);\n\n const signals: Signal[] = [];\n\n if (onChain.status === \"fulfilled\") {\n signals.push(onChain.value.signal);\n totalCostUsdc += onChain.value.costUsdc;\n }\n\n if (social.status === \"fulfilled\") {\n signals.push(social.value.signal);\n totalCostUsdc += social.value.costUsdc;\n }\n\n if (fundamental.status === \"fulfilled\") {\n signals.push(fundamental.value.signal);\n totalCostUsdc += fundamental.value.costUsdc;\n }\n\n // Compute composite score\n let weightedSum = 0;\n let totalWeight = 0;\n for (const s of signals) {\n weightedSum += s.value * s.weight;\n totalWeight += s.weight;\n }\n const compositeScore = totalWeight > 0 ? weightedSum / totalWeight : 0;\n\n // Confidence = average absolute signal strength\n const confidence =\n signals.length > 0\n ? signals.reduce((sum, s) => sum + Math.abs(s.value), 0) / signals.length\n : 0;\n\n // Decision\n let action: SignalAction = \"hold\";\n if (compositeScore >= cfg.buyThreshold && confidence >= 0.5) {\n action = \"buy\";\n } else if (compositeScore <= cfg.sellThreshold) {\n action = \"sell\";\n }\n\n return {\n action,\n confidence: Math.min(confidence, 1),\n compositeScore,\n signals,\n costUsdc: totalCostUsdc.toFixed(4),\n timestamp: Math.floor(Date.now() / 1000),\n };\n}\n\n// ── Individual Signal Sources ──\n\ninterface SignalWithCost {\n signal: Signal;\n costUsdc: number;\n}\n\n/**\n * On-chain signal: Nansen smart-money net flow.\n * Positive net flow (whales buying) → bullish.\n */\nasync function getOnChainSignal(tokenSymbol: string): Promise<SignalWithCost> {\n const nansen = getResearchProvider(\"nansen\");\n let result: ResearchResult;\n try {\n result = await nansen.query({ type: \"smart-money\", target: tokenSymbol });\n } catch {\n return {\n signal: {\n source: \"onchain\",\n name: \"smart_money_net_flow\",\n value: 0,\n weight: 0.40,\n raw: { error: \"nansen query failed\" },\n },\n costUsdc: 0,\n };\n }\n\n const data = result.data as Record<string, unknown>;\n\n // Extract net flow value — Nansen returns flow data in various formats\n const netFlow = extractNetFlow(data);\n // Normalize to -1..+1 range (clamp large values)\n const value = Math.max(-1, Math.min(1, netFlow));\n\n return {\n signal: {\n source: \"onchain\",\n name: \"smart_money_net_flow\",\n value,\n weight: 0.40,\n raw: data,\n },\n costUsdc: Number(result.costUsdc) || 0.06,\n };\n}\n\n/**\n * Social signal: Venice inference with web search for X/Twitter sentiment.\n */\nasync function getSocialSignal(\n tokenAddress: Address,\n tokenSymbol: string,\n): Promise<SignalWithCost> {\n try {\n const result = await chatCompletion({\n model: \"llama-3.3-70b\",\n messages: [\n {\n role: \"system\",\n content: `You are a crypto market sentiment analyst. Analyze current Twitter/X discourse about the given token. Return ONLY valid JSON with no markdown: {\"sentiment\": <number from -1.0 to 1.0>, \"reasoning\": \"<brief explanation>\", \"tweetCount\": <estimated tweets in last 24h>, \"keyTopics\": [\"topic1\", \"topic2\"]}. Positive sentiment means bullish discussion, influencer endorsements, positive news. Negative means FUD, rug pull warnings, community exodus. If you cannot find data, return {\"sentiment\": 0, \"reasoning\": \"insufficient data\", \"tweetCount\": 0, \"keyTopics\": []}.`,\n },\n {\n role: \"user\",\n content: `Analyze current X/Twitter sentiment for token ${tokenSymbol} (contract: ${tokenAddress} on Base chain). What is the social consensus in the last 24 hours?`,\n },\n ],\n temperature: 0.3,\n maxTokens: 500,\n enableWebSearch: true,\n });\n\n // Parse JSON from response\n const parsed = parseJsonResponse(result.content);\n const sentiment = typeof parsed.sentiment === \"number\"\n ? Math.max(-1, Math.min(1, parsed.sentiment))\n : 0;\n\n return {\n signal: {\n source: \"social\",\n name: \"x_sentiment\",\n value: sentiment,\n weight: 0.30,\n raw: parsed as Record<string, unknown>,\n },\n costUsdc: 0, // Venice inference is prepaid via sVVV, no per-call cost\n };\n } catch {\n return {\n signal: {\n source: \"social\",\n name: \"x_sentiment\",\n value: 0,\n weight: 0.30,\n raw: { error: \"venice inference failed\" },\n },\n costUsdc: 0,\n };\n }\n}\n\n/**\n * Fundamental signal: Messari market data — volume spike, market cap, ATH distance.\n */\nasync function getFundamentalSignal(\n tokenSymbol: string,\n): Promise<SignalWithCost> {\n const messari = getResearchProvider(\"messari\");\n let result: ResearchResult;\n try {\n result = await messari.query({ type: \"market\", target: tokenSymbol });\n } catch {\n return {\n signal: {\n source: \"fundamental\",\n name: \"market_fundamentals\",\n value: 0,\n weight: 0.30,\n raw: { error: \"messari query failed\" },\n },\n costUsdc: 0,\n };\n }\n\n const data = result.data as Record<string, unknown>;\n const value = scoreFundamentals(data);\n\n return {\n signal: {\n source: \"fundamental\",\n name: \"market_fundamentals\",\n value,\n weight: 0.30,\n raw: data,\n },\n costUsdc: Number(result.costUsdc) || 0.20,\n };\n}\n\n// ── Scoring Helpers ──\n\n/**\n * Extract net flow from Nansen smart-money response.\n * Normalizes to -1..+1 range.\n */\nfunction extractNetFlow(data: Record<string, unknown>): number {\n // Nansen returns various formats — try common paths\n const netFlow =\n (data.netFlow as number) ??\n (data.net_flow as number) ??\n ((data.inflow as number ?? 0) - (data.outflow as number ?? 0));\n\n if (typeof netFlow !== \"number\" || isNaN(netFlow)) return 0;\n\n // Normalize: scale by rough heuristic (> $1M net flow = strong signal)\n const normalized = netFlow / 1_000_000;\n return Math.max(-1, Math.min(1, normalized));\n}\n\n/**\n * Score fundamental data from Messari.\n *\n * Factors:\n * - Volume spike (24h vs 7d avg): > 3x = +0.5, > 2x = +0.25\n * - Market cap: < $10M with volume = +0.3 (high upside)\n * - ATH distance: > 80% down = +0.2 (recovery play), < 10% = -0.3 (possible top)\n */\nfunction scoreFundamentals(data: Record<string, unknown>): number {\n let score = 0;\n\n // Try to extract market data from nested Messari response\n const marketData = (data.marketData ?? data.market_data ?? data) as Record<string, unknown>;\n const athData = (data.allTimeHigh ?? data.ath ?? {}) as Record<string, unknown>;\n\n // Volume spike\n const vol24h = toNumber(marketData.volume_last_24_hours ?? marketData.volume24h);\n const vol7d = toNumber(marketData.volume_last_7_days ?? marketData.volume7d);\n if (vol24h > 0 && vol7d > 0) {\n const dailyAvg7d = vol7d / 7;\n const ratio = vol24h / dailyAvg7d;\n if (ratio > 3) score += 0.5;\n else if (ratio > 2) score += 0.25;\n else if (ratio < 0.5) score -= 0.25;\n }\n\n // Market cap\n const mcap = toNumber(marketData.current_marketcap_usd ?? marketData.marketCap);\n if (mcap > 0 && mcap < 10_000_000 && vol24h > 0) {\n score += 0.3; // small cap with volume = high upside potential\n } else if (mcap > 1_000_000_000) {\n score -= 0.15; // large cap = less upside for memecoins\n }\n\n // ATH distance\n const currentPrice = toNumber(marketData.price_usd ?? marketData.currentPrice);\n const athPrice = toNumber(athData.price ?? athData.athPrice);\n if (currentPrice > 0 && athPrice > 0) {\n const athDistance = ((athPrice - currentPrice) / athPrice) * 100;\n if (athDistance > 80) score += 0.2; // deep discount recovery play\n else if (athDistance < 10) score -= 0.3; // near ATH, potential top\n }\n\n return Math.max(-1, Math.min(1, score));\n}\n\nfunction toNumber(v: unknown): number {\n if (typeof v === \"number\") return v;\n if (typeof v === \"string\") return Number(v) || 0;\n return 0;\n}\n\n/**\n * Parse a JSON response from Venice, handling markdown fences and extra text.\n */\nfunction parseJsonResponse(content: string): Record<string, unknown> {\n // Strip markdown code fences if present\n let cleaned = content.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\n?/, \"\").replace(/\\n?```$/, \"\");\n }\n // Find the first { ... } block\n const start = cleaned.indexOf(\"{\");\n const end = cleaned.lastIndexOf(\"}\");\n if (start >= 0 && end > start) {\n try {\n return JSON.parse(cleaned.slice(start, end + 1));\n } catch {\n // Fall through\n }\n }\n return { sentiment: 0, reasoning: \"failed to parse response\", raw: content };\n}\n","/**\n * Signal-based exit strategy for memecoin trades.\n *\n * Pure computation — no network calls. Evaluates a priority-ordered\n * set of exit conditions against the current position state.\n */\n\nimport type { SignalResult } from \"./signals.js\";\n\nexport interface ExitConfig {\n stopLossPct: number; // exit if PnL drops below this % (default 10)\n trailingStopPct: number; // exit if drawdown from high-water exceeds this % (0 = disabled)\n takeProfitPct: number; // exit if PnL reaches this % (0 = disabled, use signal exit)\n deadlineUnix: number; // force exit before this unix timestamp (0 = none)\n signalExitEnabled: boolean; // exit when signals flip bearish (default true)\n}\n\nexport type ExitReason =\n | \"stop_loss\"\n | \"trailing_stop\"\n | \"take_profit\"\n | \"deadline\"\n | \"signal_bearish\"\n | \"hold\";\n\nexport interface ExitCheck {\n shouldExit: boolean;\n reason: ExitReason;\n currentPnlPct: number;\n highWaterPnlPct: number;\n}\n\nexport const DEFAULT_EXIT_CONFIG: ExitConfig = {\n stopLossPct: 10,\n trailingStopPct: 0,\n takeProfitPct: 0,\n deadlineUnix: 0,\n signalExitEnabled: true,\n};\n\n/**\n * Check whether a position should be exited.\n *\n * Priority order (first match wins):\n * 1. Deadline passed\n * 2. Stop loss\n * 3. Trailing stop (drawdown from high-water mark)\n * 4. Take profit\n * 5. Signal-based (bearish signals with sufficient confidence)\n * 6. Hold\n */\nexport function checkExit(\n entryPrice: number,\n currentPrice: number,\n highWaterPrice: number,\n config: ExitConfig,\n signalResult?: SignalResult,\n): ExitCheck {\n if (entryPrice <= 0) {\n return { shouldExit: false, reason: \"hold\", currentPnlPct: 0, highWaterPnlPct: 0 };\n }\n\n const currentPnlPct = ((currentPrice - entryPrice) / entryPrice) * 100;\n const highWaterPnlPct = ((highWaterPrice - entryPrice) / entryPrice) * 100;\n\n // 1. Deadline\n if (config.deadlineUnix > 0 && Date.now() / 1000 > config.deadlineUnix) {\n return { shouldExit: true, reason: \"deadline\", currentPnlPct, highWaterPnlPct };\n }\n\n // 2. Stop loss\n if (currentPnlPct <= -config.stopLossPct) {\n return { shouldExit: true, reason: \"stop_loss\", currentPnlPct, highWaterPnlPct };\n }\n\n // 3. Trailing stop\n if (config.trailingStopPct > 0 && highWaterPrice > 0) {\n const drawdownPct = ((highWaterPrice - currentPrice) / highWaterPrice) * 100;\n if (drawdownPct >= config.trailingStopPct) {\n return { shouldExit: true, reason: \"trailing_stop\", currentPnlPct, highWaterPnlPct };\n }\n }\n\n // 4. Take profit\n if (config.takeProfitPct > 0 && currentPnlPct >= config.takeProfitPct) {\n return { shouldExit: true, reason: \"take_profit\", currentPnlPct, highWaterPnlPct };\n }\n\n // 5. Signal-based exit\n if (\n config.signalExitEnabled &&\n signalResult &&\n signalResult.action === \"sell\" &&\n signalResult.confidence > 0.4\n ) {\n return { shouldExit: true, reason: \"signal_bearish\", currentPnlPct, highWaterPnlPct };\n }\n\n // 6. Hold\n return { shouldExit: false, reason: \"hold\", currentPnlPct, highWaterPnlPct };\n}\n","/**\n * Position tracking for memecoin trades.\n *\n * Persists open and closed positions to ~/.sherwood/config.json.\n * Uses Uniswap QuoterV2 for current price lookups.\n */\n\nimport type { Address } from \"viem\";\nimport { formatUnits, parseUnits } from \"viem\";\nimport { loadConfig, saveConfig } from \"./config.js\";\nimport { getQuote } from \"./quote.js\";\nimport { TOKENS } from \"./addresses.js\";\nimport type { ExitConfig } from \"./exit-strategy.js\";\n\nexport interface Position {\n tokenAddress: Address;\n tokenSymbol: string;\n tokenDecimals: number;\n amountIn: string; // amount spent (human-readable, e.g. \"500\")\n amountOut: string; // tokens received (raw bigint as string)\n entryPrice: number; // input token per target token at entry\n highWaterPrice: number; // highest observed price since entry\n feeTier: number; // Uniswap fee tier used\n openedAt: number; // unix timestamp\n txHash: string; // buy tx hash\n exitConfig: ExitConfig; // per-position exit parameters\n inputTokenAddress?: Address; // token used to buy (default: USDC for backwards compat)\n inputTokenSymbol?: string; // symbol of input token (default: \"USDC\")\n inputTokenDecimals?: number; // decimals of input token (default: 6)\n}\n\nexport interface ClosedPosition extends Position {\n exitPrice: number;\n closedAt: number;\n exitTxHash: string;\n exitReason: string;\n pnlUsdc: number;\n pnlPct: number;\n}\n\n// ── Reads ──\n\nexport function getOpenPositions(): Position[] {\n const config = loadConfig();\n return (config.positions as Position[] | undefined) ?? [];\n}\n\nexport function getClosedPositions(): ClosedPosition[] {\n const config = loadConfig();\n return (config.closedPositions as ClosedPosition[] | undefined) ?? [];\n}\n\n// ── Writes ──\n\nexport function addPosition(pos: Position): void {\n const config = loadConfig();\n const positions = (config.positions as Position[] | undefined) ?? [];\n positions.push(pos);\n config.positions = positions;\n saveConfig(config);\n}\n\nexport function closePosition(\n tokenAddress: Address,\n exitData: {\n exitPrice: number;\n closedAt: number;\n exitTxHash: string;\n exitReason: string;\n pnlUsdc: number;\n pnlPct: number;\n },\n): void {\n const config = loadConfig();\n const positions = (config.positions as Position[] | undefined) ?? [];\n const idx = positions.findIndex(\n (p) => p.tokenAddress.toLowerCase() === tokenAddress.toLowerCase(),\n );\n if (idx === -1) {\n throw new Error(`No open position for ${tokenAddress}`);\n }\n\n const [pos] = positions.splice(idx, 1);\n const closed: ClosedPosition = { ...pos, ...exitData };\n\n const closedPositions = (config.closedPositions as ClosedPosition[] | undefined) ?? [];\n closedPositions.push(closed);\n\n config.positions = positions;\n config.closedPositions = closedPositions;\n saveConfig(config);\n}\n\nexport function updateHighWater(tokenAddress: Address, price: number): void {\n const config = loadConfig();\n const positions = (config.positions as Position[] | undefined) ?? [];\n const pos = positions.find(\n (p) => p.tokenAddress.toLowerCase() === tokenAddress.toLowerCase(),\n );\n if (pos && price > pos.highWaterPrice) {\n pos.highWaterPrice = price;\n config.positions = positions;\n saveConfig(config);\n }\n}\n\n// ── Price Lookup ──\n\n/**\n * Get the current USDC price of one token via Uniswap QuoterV2.\n * Quotes 1 full token unit against USDC.\n */\nexport async function getCurrentPrice(\n tokenAddress: Address,\n tokenDecimals: number,\n feeTier: number,\n): Promise<number> {\n const oneToken = parseUnits(\"1\", tokenDecimals);\n const usdc = TOKENS().USDC;\n\n const { amountOut } = await getQuote({\n tokenIn: tokenAddress,\n tokenOut: usdc,\n amountIn: oneToken,\n fee: feeTier,\n });\n\n // USDC has 6 decimals\n return Number(formatUnits(amountOut, 6));\n}\n"],"mappings":"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","names":["isAddress","parseUnits","formatUnits","ETH_ADDRESS","formatUnits","formatUnits","isAddress","client","parseUnits","formatUnits","writeContractWithRetry"]}
@@ -3,11 +3,12 @@ import {
3
3
  approveDepositors,
4
4
  deposit,
5
5
  depositWithEthWrap,
6
- executeBatch,
6
+ getAgentAddresses,
7
7
  getAgentFeeBps,
8
8
  getAssetAddress,
9
9
  getAssetDecimals,
10
10
  getBalance,
11
+ getNetDeposited,
11
12
  getShareBalance,
12
13
  getVaultAddress,
13
14
  getVaultInfo,
@@ -21,22 +22,26 @@ import {
21
22
  removeDepositor,
22
23
  setAgentFeeBps,
23
24
  setVaultAddress
24
- } from "./chunk-CZRJ5GWE.js";
25
- import "./chunk-IFL5SREW.js";
26
- import "./chunk-ROKE6ZAW.js";
27
- import "./chunk-DXJJMRBM.js";
28
- import "./chunk-GSC7PVEO.js";
29
- import "./chunk-BQMOE4FJ.js";
25
+ } from "./chunk-RAP6MUMB.js";
26
+ import "./chunk-7BMCLMSE.js";
27
+ import "./chunk-RE3PPAXZ.js";
28
+ import "./chunk-UINMHN6S.js";
29
+ import "./chunk-2OHX3O55.js";
30
+ import "./chunk-LHVSS27T.js";
31
+ import "./chunk-6MV4ESXM.js";
32
+ import "./chunk-I2PC6TBZ.js";
33
+ import "./chunk-XDGCHURW.js";
30
34
  export {
31
35
  approveDepositor,
32
36
  approveDepositors,
33
37
  deposit,
34
38
  depositWithEthWrap,
35
- executeBatch,
39
+ getAgentAddresses,
36
40
  getAgentFeeBps,
37
41
  getAssetAddress,
38
42
  getAssetDecimals,
39
43
  getBalance,
44
+ getNetDeposited,
40
45
  getShareBalance,
41
46
  getVaultAddress,
42
47
  getVaultInfo,
@@ -51,4 +56,4 @@ export {
51
56
  setAgentFeeBps,
52
57
  setVaultAddress
53
58
  };
54
- //# sourceMappingURL=vault-XWFPGHRA.js.map
59
+ //# sourceMappingURL=vault-WDRLJLJ6.js.map
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  getTextRecord
3
- } from "./chunk-CHWBQJLT.js";
4
- import "./chunk-CZRJ5GWE.js";
5
- import "./chunk-IFL5SREW.js";
3
+ } from "./chunk-JF7KAVOM.js";
4
+ import "./chunk-UINMHN6S.js";
6
5
  import {
7
6
  getAccount
8
- } from "./chunk-ROKE6ZAW.js";
7
+ } from "./chunk-2OHX3O55.js";
9
8
  import {
10
9
  getChainConfig
11
- } from "./chunk-DXJJMRBM.js";
10
+ } from "./chunk-LHVSS27T.js";
12
11
  import {
13
12
  cacheGroupId,
14
13
  getCachedGroupId,
15
14
  invalidateCachedGroupId,
16
15
  loadConfig,
16
+ requirePrivateKey,
17
17
  saveConfig
18
- } from "./chunk-GSC7PVEO.js";
19
- import "./chunk-BQMOE4FJ.js";
18
+ } from "./chunk-6MV4ESXM.js";
19
+ import "./chunk-I2PC6TBZ.js";
20
20
 
21
21
  // src/lib/xmtp.ts
22
22
  import fs from "fs";
@@ -54,12 +54,7 @@ function ethIdentifier(address) {
54
54
  }
55
55
  async function getOrCreateClient() {
56
56
  if (_client) return _client;
57
- const config = loadConfig();
58
- if (!config.privateKey) {
59
- throw new Error(
60
- 'No private key configured. Run "sherwood config set --private-key 0x..."'
61
- );
62
- }
57
+ const privateKey = requirePrivateKey();
63
58
  const account = getAccount();
64
59
  const signer = {
65
60
  type: "EOA",
@@ -77,13 +72,14 @@ async function getOrCreateClient() {
77
72
  };
78
73
  _client = await Client.create(signer, {
79
74
  ...clientOptions,
80
- dbEncryptionKey: getDbEncryptionKey(config.privateKey),
75
+ dbEncryptionKey: getDbEncryptionKey(privateKey),
81
76
  dbPath: getDbPath()
82
77
  });
83
78
  await _client.conversations.syncAll([
84
79
  ConsentState.Allowed,
85
80
  ConsentState.Unknown
86
81
  ]);
82
+ const config = loadConfig();
87
83
  if (!config._xmtpMigrated) {
88
84
  try {
89
85
  await _client.revokeAllOtherInstallations();
@@ -319,4 +315,4 @@ export {
319
315
  sendReaction,
320
316
  streamMessages
321
317
  };
322
- //# sourceMappingURL=xmtp-6US7GH3Z.js.map
318
+ //# sourceMappingURL=xmtp-Q4ZSQV2Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/xmtp.ts"],"sourcesContent":["/**\n * XMTP client and group operations for fund chat.\n *\n * Uses @xmtp/node-sdk directly with a singleton Client instance.\n * Deterministic DB path (~/.sherwood/xmtp/) and encryption key derived\n * from the sherwood private key via keccak256.\n *\n * This replaces the previous @xmtp/cli subprocess architecture which\n * caused stale MLS installations (issue #110).\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { homedir } from \"node:os\";\nimport {\n Client,\n ConsentState,\n Group,\n GroupPermissionsOptions,\n IdentifierKind,\n type CreateGroupOptions,\n type DecodedMessage,\n type Identifier,\n type NetworkOptions,\n type Signer,\n type XmtpEnv,\n} from \"@xmtp/node-sdk\";\nimport { keccak256, toBytes } from \"viem\";\nimport {\n loadConfig,\n saveConfig,\n requirePrivateKey,\n cacheGroupId,\n getCachedGroupId,\n invalidateCachedGroupId,\n} from \"./config.js\";\nimport { getTextRecord } from \"./ens.js\";\nimport { getChainConfig } from \"./network.js\";\nimport type { ChatEnvelope } from \"./types.js\";\nimport { getAccount } from \"./client.js\";\n\n// ── Types ──\n\nexport interface XmtpMessage {\n id: string;\n conversationId: string;\n senderInboxId: string;\n contentType: string;\n content: string;\n sentAt: Date;\n}\n\nexport interface XmtpMember {\n inboxId: string;\n permissionLevel: string;\n}\n\n// ── Internal helpers ──\n\nlet _client: Client | null = null;\n\n/**\n * Canonical Sherwood dashboard spectator address (XMTP production network,\n * inbox prefix `744cfb`). Used to enable the dashboard live feed by adding\n * the spectator service as a member of a fund group. The actual\n * service runs at spectator.sherwood.sh and pushes messages over websocket\n * to the dashboard's LiveFeed component, which filters by this exact inbox.\n *\n * Overridable via `DASHBOARD_SPECTATOR_ADDRESS` for testing, but the env\n * var should generally not be set in production — the literal IS the\n * production spectator. Verify against\n * curl -s https://spectator.sherwood.sh/health\n * which returns `.address` matching this value.\n */\nexport const DEFAULT_DASHBOARD_SPECTATOR_ADDRESS =\n \"0x9f6518e69a62c526ead155ad2661f5957b6b2fc3\";\n\n/**\n * Resolve the dashboard spectator address for `chat init --public` /\n * `chat <name> public on`. Falls back to the canonical production\n * spectator when `DASHBOARD_SPECTATOR_ADDRESS` is unset — both call\n * sites MUST share this default, otherwise a cron with a typo for the\n * env var silently adds nothing (or worse, the agent's own wallet) and\n * the dashboard never goes LIVE.\n */\nexport function resolveSpectatorAddress(): string {\n return (\n process.env.DASHBOARD_SPECTATOR_ADDRESS ||\n DEFAULT_DASHBOARD_SPECTATOR_ADDRESS\n );\n}\n\nfunction getXmtpEnv(): string {\n return getChainConfig().xmtpEnv;\n}\n\nfunction getDbEncryptionKey(privateKey: string): Uint8Array {\n const hash = keccak256(toBytes(privateKey + \"xmtp-db-key\"));\n return toBytes(hash);\n}\n\nfunction getDbPath(): string {\n const dir = path.join(homedir(), \".sherwood\", \"xmtp\");\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n return path.join(dir, \"xmtp.db3\");\n}\n\nfunction ethIdentifier(address: string): Identifier {\n return {\n identifier: address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n };\n}\n\nasync function getOrCreateClient(): Promise<Client> {\n if (_client) return _client;\n\n // Same resolution as the signer below (getAccount) — config → env. A\n // divergent key here would derive a different XMTP inbox than the one\n // the CLI signs with.\n // Env-only footgun: getDbPath() is fixed (not keyed by address), so\n // rotating PRIVATE_KEY across shells points a different dbEncryptionKey\n // at the same xmtp.db3 → SQLCipher decrypt failure. Persist the key with\n // `sherwood config set --private-key` for a stable identity.\n const privateKey = requirePrivateKey();\n\n const account = getAccount();\n\n const signer: Signer = {\n type: \"EOA\",\n getIdentifier: () => ({\n identifier: account.address.toLowerCase(),\n identifierKind: IdentifierKind.Ethereum,\n }),\n signMessage: async (message: string) => {\n const signature = await account.signMessage({ message });\n return toBytes(signature);\n },\n };\n\n const clientOptions: NetworkOptions = {\n env: getXmtpEnv() as XmtpEnv,\n };\n _client = await Client.create(signer, {\n ...clientOptions,\n dbEncryptionKey: getDbEncryptionKey(privateKey),\n dbPath: getDbPath(),\n });\n\n // Sync all conversations on first connect (processes MLS welcome messages).\n // Includes Unknown — fresh welcomes default to Unknown consent and are missed\n // if we filter to Allowed only (agent-side group invisibility bug, 2026-05-07).\n await _client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n // One-time migration: revoke stale installations from the old ~/.xmtp/ era\n const config = loadConfig();\n if (!config._xmtpMigrated) {\n try {\n await _client.revokeAllOtherInstallations();\n } catch {\n // Non-fatal — stale installations are a UX issue, not a blocker\n }\n config._xmtpMigrated = true;\n saveConfig(config);\n }\n\n return _client;\n}\n\n// ── Client ──\n\nexport async function getXmtpClient(): Promise<string> {\n const client = await getOrCreateClient();\n\n // Cache inbox ID\n const config = loadConfig();\n if (!config.xmtpInboxId && client.inboxId) {\n config.xmtpInboxId = client.inboxId;\n saveConfig(config);\n }\n\n return client.inboxId;\n}\n\n// ── Group Creation ──\n\nexport async function createFundGroup(\n _client: string,\n subdomain: string,\n isPublic: boolean = false,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n const group = await client.conversations.createGroup([], {\n name: subdomain,\n description: `Sherwood fund: ${subdomain}.sherwoodagent.eth`,\n permissionLevel: GroupPermissionsOptions.AdminOnly,\n } as CreateGroupOptions);\n\n const groupId = group.id;\n if (!groupId) {\n throw new Error(\"Failed to create XMTP group — no group ID returned\");\n }\n\n // Commit name/description as MLS messages so they propagate to welcome\n // recipients. Passing them in CreateGroupOptions sets only creator-local\n // metadata; without an explicit update*, member installations see empty\n // values (spectator's \"groups: 0\" symptom, 2026-05-07).\n await group.updateName(subdomain);\n await group.updateDescription(\n `Sherwood fund: ${subdomain}.sherwoodagent.eth`,\n );\n\n // Add spectator if requested — enables dashboard live feed\n if (isPublic) {\n await addMember(groupId, resolveSpectatorAddress());\n }\n\n // Cache locally\n cacheGroupId(subdomain, groupId);\n\n return groupId;\n}\n\n// ── Group Lookup ──\n\nexport async function getGroup(\n _client: string,\n subdomain: string,\n): Promise<string> {\n const client = await getOrCreateClient();\n\n // Sync to get latest conversations (including MLS welcome messages).\n // Includes Unknown so a fresh agent-side welcome (defaults to Unknown) lands.\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n // Try local cache — validate it exists in the local DB before trusting.\n // Stale cache entries (e.g. after `init --force` recreated the group) would\n // otherwise short-circuit every downstream call with a nonexistent group.\n let groupId: string | undefined = getCachedGroupId(subdomain);\n if (groupId) {\n const conv = await client.conversations.getConversationById(groupId);\n if (conv) {\n return groupId;\n }\n // Stale cache — clear and fall through to ENS / name search.\n invalidateCachedGroupId(subdomain);\n groupId = undefined;\n }\n\n // Fall back to on-chain ENS text record (may not exist on all chains)\n if (!groupId) {\n try {\n const ensId = await getTextRecord(subdomain, \"xmtpGroupId\");\n if (ensId) {\n const conv = await client.conversations.getConversationById(ensId);\n if (conv) {\n groupId = ensId;\n cacheGroupId(subdomain, groupId);\n }\n }\n } catch {\n // ENS not available on this chain (e.g. HyperEVM) — skip\n }\n }\n\n // Last resort: search synced conversations by name\n if (!groupId) {\n const groups = client.conversations.listGroups();\n const match = groups.find(\n (g) =>\n g.name === subdomain ||\n g.description?.includes(`${subdomain}.sherwoodagent.eth`),\n );\n if (match) {\n groupId = match.id;\n cacheGroupId(subdomain, groupId);\n }\n }\n\n if (!groupId) {\n throw new Error(\n `No XMTP group found for fund \"${subdomain}\". Run \"sherwood chat ${subdomain} init\" to create one.`,\n );\n }\n\n return groupId;\n}\n\n// ── Member Management ──\n\nexport async function addMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n\n // Verify the target has an active XMTP identity before adding\n const reachable = await client.canMessage([ethIdentifier(address)]);\n // Try both original and lowercased as map key\n const isReachable =\n reachable.get(address) ?? reachable.get(address.toLowerCase()) ?? false;\n if (!isReachable) {\n throw new Error(\n `${address} is not reachable on XMTP. They need to initialize their client first.`,\n );\n }\n\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know fund conversations are always groups\n const group = conv as Group;\n await group.addMembersByIdentifiers([ethIdentifier(address)]);\n\n // Set consent to \"allowed\" so subsequent operations don't skip this conversation\n try {\n group.updateConsentState(ConsentState.Allowed);\n } catch {\n // Non-fatal — consent state is a filtering concern, not a blocker\n }\n}\n\nexport async function removeMember(\n groupId: string,\n address: string,\n): Promise<void> {\n const client = await getOrCreateClient();\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Cast to Group — we know fund conversations are always groups\n const group = conv as Group;\n await group.removeMembersByIdentifiers([ethIdentifier(address)]);\n}\n\n/**\n * Update a group's name and/or description as MLS commits so the change\n * propagates to all members' installations (including welcome recipients\n * that joined before metadata was committed).\n */\nexport async function renameGroup(\n groupId: string,\n name?: string,\n description?: string,\n): Promise<void> {\n if (name === undefined && description === undefined) {\n throw new Error(\"renameGroup: at least one of name or description required\");\n }\n const client = await getOrCreateClient();\n await client.conversations.syncAll([\n ConsentState.Allowed,\n ConsentState.Unknown,\n ]);\n\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n const group = conv as Group;\n\n if (name !== undefined) await group.updateName(name);\n if (description !== undefined) await group.updateDescription(description);\n}\n\n// ── Messaging ──\n\nexport async function sendEnvelope(\n groupId: string,\n envelope: ChatEnvelope,\n): Promise<void> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const text = JSON.stringify(envelope);\n await conv.sendText(text);\n}\n\nexport async function sendMarkdown(\n groupId: string,\n markdown: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"MESSAGE\",\n from: getAccount().address,\n text: markdown,\n data: { format: \"markdown\" },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\nexport async function sendReaction(\n groupId: string,\n messageId: string,\n emoji: string,\n): Promise<void> {\n const envelope: ChatEnvelope = {\n type: \"REACTION\",\n from: getAccount().address,\n data: { reference: messageId, emoji },\n timestamp: Math.floor(Date.now() / 1000),\n };\n await sendEnvelope(groupId, envelope);\n}\n\n// ── Streaming ──\n\nexport async function streamMessages(\n groupId: string,\n onMessage: (msg: XmtpMessage) => void,\n): Promise<() => void> {\n const client = await getOrCreateClient();\n\n const stream = await client.conversations.streamAllMessages({\n consentStates: [ConsentState.Allowed],\n });\n\n let stopped = false;\n\n // Consume stream in background\n (async () => {\n try {\n for await (const message of stream) {\n if (stopped) break;\n // Filter to our group\n if (message.conversationId !== groupId) continue;\n\n onMessage({\n id: message.id || \"\",\n conversationId: message.conversationId || \"\",\n senderInboxId: message.senderInboxId || \"\",\n contentType:\n typeof message.contentType === \"object\"\n ? (message.contentType as { typeId?: string })?.typeId || \"text\"\n : String(message.contentType || \"text\"),\n content:\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content),\n sentAt: message.sentAt || new Date(),\n });\n }\n } catch {\n // Stream ended or errored — expected on cleanup\n }\n })();\n\n // Return cleanup function\n return () => {\n stopped = true;\n try {\n // AsyncStreamProxy exposes end() to terminate the stream\n if (typeof (stream as any).end === \"function\") {\n (stream as any).end();\n } else if (typeof (stream as any).return === \"function\") {\n (stream as any).return();\n }\n } catch {\n // Best-effort cleanup\n }\n };\n}\n\n// ── Message History ──\n\nexport async function getRecentMessages(\n groupId: string,\n limit: number = 20,\n): Promise<XmtpMessage[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n // Sync to get latest messages\n await conv.sync();\n\n const rawMessages = await conv.messages({ limit });\n\n return rawMessages.map((m) => ({\n id: m.id || \"\",\n conversationId: m.conversationId || \"\",\n senderInboxId: m.senderInboxId || \"\",\n contentType:\n typeof m.contentType === \"object\"\n ? (m.contentType as { typeId?: string })?.typeId || \"text\"\n : String(m.contentType || \"text\"),\n content:\n typeof m.content === \"string\"\n ? m.content\n : JSON.stringify(m.content),\n sentAt: m.sentAt || new Date(),\n }));\n}\n\n// ── Members ──\n\nexport async function getMembers(\n groupId: string,\n): Promise<XmtpMember[]> {\n const client = await getOrCreateClient();\n const conv = await client.conversations.getConversationById(groupId);\n if (!conv) throw new Error(`Conversation ${groupId} not found`);\n\n const members = await conv.members();\n\n return (Array.isArray(members) ? members : []).map((m: any) => ({\n inboxId: String(m.inboxId || \"\"),\n permissionLevel:\n m.permissionLevel === 2\n ? \"super_admin\"\n : m.permissionLevel === 1\n ? \"admin\"\n : \"member\",\n }));\n}\n"],"mappings":"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","names":["_client"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sherwoodagent/cli",
3
- "version": "0.73.4",
3
+ "version": "0.74.2",
4
4
  "description": "CLI for agent-managed investment funds — onchain DeFi funds with XMTP chat",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,7 @@
22
22
  "scripts": {
23
23
  "prepublishOnly": "npm run build",
24
24
  "build": "tsup",
25
+ "codegen": "tsx scripts/codegen-contracts.ts",
25
26
  "dev": "tsx src/index.ts",
26
27
  "lint": "eslint src/",
27
28
  "typecheck": "tsc --noEmit",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/providers/research/messari.ts","../src/lib/x402.ts","../src/providers/research/nansen.ts","../src/providers/research/index.ts"],"sourcesContent":["/**\n * Messari research provider — x402 micropayments for crypto market data.\n *\n * Docs: https://docs.messari.io\n * Payment: x402 (USDC on Base) — no API key required\n * Data: 34,000+ assets, market metrics, asset profiles\n * Pricing: $0.10/call for asset details, ROI, ATH. $0.15–$0.35 for timeseries.\n * $0.55 for news/signals. Full pricing: https://docs.messari.io/api-reference/x402-payments\n *\n * Supports:\n * token → asset details (description, category, sector, links, market snapshot)\n * market → market metrics + ATH (price, volume, market cap, ROI, ATH, cycle low)\n *\n * Not supported (use --provider nansen instead):\n * smart-money → on-chain analytics not available in Messari x402 API\n * wallet → wallet profiling not available in Messari x402 API\n */\n\nimport { base, baseSepolia } from \"viem/chains\";\nimport type { ProviderInfo } from \"../../types.js\";\nimport type { ResearchProvider, ResearchQuery, ResearchResult } from \"./index.js\";\nimport { getX402Fetch } from \"../../lib/x402.js\";\n\nconst BASE_URL = \"https://api.messari.io\";\n\n/** Known x402 cost per Messari query (approximate). */\nexport const MESSARI_COST_ESTIMATE: Record<string, string> = {\n token: \"~$0.10\",\n market: \"~$0.20\",\n};\n\nexport class MessariProvider implements ResearchProvider {\n info(): ProviderInfo {\n return {\n name: \"messari\",\n type: \"research\",\n capabilities: [\n \"research.token\",\n \"research.market\",\n ],\n supportedChains: [base, baseSepolia],\n };\n }\n\n async query(params: ResearchQuery): Promise<ResearchResult> {\n switch (params.type) {\n case \"token\":\n return this.tokenReport(params.target);\n case \"market\":\n return this.marketOverview(params.target);\n case \"smart-money\":\n throw new Error(\n \"Messari x402 API does not support on-chain analytics. Use --provider nansen for smart money data.\",\n );\n case \"wallet\":\n throw new Error(\n \"Messari does not support wallet analytics. Use --provider nansen for wallet profiling.\",\n );\n default:\n throw new Error(`Unsupported query type: ${params.type}`);\n }\n }\n\n /**\n * Resolve any user input (symbol, name, slug, address) to a Messari asset slug.\n * Uses the free /metrics/v2/assets endpoint ($0.00) to search.\n */\n private async resolveSlug(target: string): Promise<string> {\n const fetchWithPay = await getX402Fetch();\n const params = new URLSearchParams({\n search: target,\n limit: \"1\",\n });\n const url = `${BASE_URL}/metrics/v2/assets?${params}`;\n\n const res = await fetchWithPay(url, { method: \"GET\" });\n if (!res.ok) {\n throw new Error(\n `Messari asset lookup failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as {\n data?: Array<{ slug?: string }>;\n };\n\n const slug = json.data?.[0]?.slug;\n if (!slug) {\n throw new Error(\n `No Messari asset found for \"${target}\". Try a different name, symbol, or slug.`,\n );\n }\n return slug;\n }\n\n /**\n * Get asset details: description, category, sector, links, market snapshot.\n * Endpoint: GET /metrics/v2/assets/details?assetIDs={slug} ($0.10)\n */\n private async tokenReport(target: string): Promise<ResearchResult> {\n const slug = await this.resolveSlug(target);\n const fetchWithPay = await getX402Fetch();\n const url = `${BASE_URL}/metrics/v2/assets/details?assetIDs=${encodeURIComponent(slug)}`;\n\n const res = await fetchWithPay(url, { method: \"GET\" });\n if (!res.ok) {\n throw new Error(\n `Messari token query failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as {\n data?: Array<Record<string, unknown>>;\n };\n const costUsdc = this.extractCost(res, \"token\");\n const asset = json.data?.[0] ?? {};\n\n return {\n provider: \"messari\",\n queryType: \"token\",\n target,\n data: asset,\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Get market metrics + ATH: price, volume, market cap, ROI, all-time high, cycle low.\n * Makes two parallel calls:\n * GET /metrics/v2/assets/details?assetIDs={slug} ($0.10)\n * GET /metrics/v2/assets/ath?assetIDs={slug} ($0.10)\n * Total: ~$0.20\n */\n private async marketOverview(target: string): Promise<ResearchResult> {\n const slug = await this.resolveSlug(target);\n const fetchWithPay = await getX402Fetch();\n const assetParam = encodeURIComponent(slug);\n\n const [detailsRes, athRes] = await Promise.all([\n fetchWithPay(`${BASE_URL}/metrics/v2/assets/details?assetIDs=${assetParam}`, { method: \"GET\" }),\n fetchWithPay(`${BASE_URL}/metrics/v2/assets/ath?assetIDs=${assetParam}`, { method: \"GET\" }),\n ]);\n\n if (!detailsRes.ok) {\n throw new Error(\n `Messari market query failed: ${detailsRes.status} ${detailsRes.statusText}`,\n );\n }\n if (!athRes.ok) {\n throw new Error(\n `Messari ATH query failed: ${athRes.status} ${athRes.statusText}`,\n );\n }\n\n const detailsJson = (await detailsRes.json()) as {\n data?: Array<Record<string, unknown>>;\n };\n const athJson = (await athRes.json()) as {\n data?: Array<Record<string, unknown>>;\n };\n\n // Sum costs from both responses\n const cost1 = this.extractCostRaw(detailsRes);\n const cost2 = this.extractCostRaw(athRes);\n const totalCost = cost1 + cost2;\n const costUsdc = totalCost > 0\n ? totalCost.toFixed(4)\n : MESSARI_COST_ESTIMATE[\"market\"]?.replace(\"~$\", \"\") ?? \"unknown\";\n\n const details = detailsJson.data?.[0] ?? {};\n const ath = athJson.data?.[0] ?? {};\n\n return {\n provider: \"messari\",\n queryType: \"market\",\n target,\n data: {\n ...details,\n allTimeHigh: ath.allTimeHigh ?? null,\n },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Extract cost from x402 response headers as a number (USDC).\n * Returns 0 if no payment info found.\n */\n private extractCostRaw(res: Response): number {\n const paymentResponse = res.headers.get(\"payment-response\");\n if (paymentResponse) {\n try {\n const parsed = JSON.parse(paymentResponse) as { amount?: string };\n if (parsed.amount) {\n return Number(parsed.amount) / 1e6;\n }\n } catch {\n // Fall through\n }\n }\n return 0;\n }\n\n /**\n * Extract cost from x402 response headers as a formatted string.\n * Falls back to the known estimate for the query type when headers are absent.\n */\n private extractCost(res: Response, queryType: string = \"token\"): string {\n const cost = this.extractCostRaw(res);\n if (cost > 0) return cost.toFixed(4);\n\n // Fall back to known estimate (strip \"~$\" prefix)\n const est = MESSARI_COST_ESTIMATE[queryType];\n if (est) return est.replace(\"~$\", \"\");\n\n return \"unknown\";\n }\n}\n","/**\n * x402 fetch wrapper — wraps native fetch with automatic USDC micropayments.\n *\n * Uses the Coinbase x402 protocol: when a server responds 402 Payment Required,\n * the wrapper automatically signs a USDC payment on Base and retries the request.\n * The agent pays from its own wallet — no vault interaction needed.\n *\n * Singleton pattern matching client.ts — cached after first creation.\n */\n\nimport { getAccount } from \"./client.js\";\n\nlet _x402Fetch: typeof fetch | null = null;\n\n/**\n * Returns a fetch function that automatically handles x402 (402 Payment Required)\n * responses by signing USDC micropayments on Base.\n *\n * Lazily initializes the x402 client on first call and caches it.\n * Uses dynamic imports so @x402 packages are only loaded when research commands run.\n */\nexport async function getX402Fetch(): Promise<typeof fetch> {\n if (_x402Fetch) return _x402Fetch;\n\n const { x402Client, wrapFetchWithPayment } = await import(\"@x402/fetch\");\n const { registerExactEvmScheme } = await import(\"@x402/evm/exact/client\");\n\n const signer = getAccount();\n const client = new x402Client();\n registerExactEvmScheme(client, { signer });\n\n _x402Fetch = wrapFetchWithPayment(fetch, client) as typeof fetch;\n return _x402Fetch;\n}\n\n/**\n * Reset cached x402 fetch. Required for tests that change accounts.\n */\nexport function resetX402Fetch(): void {\n _x402Fetch = null;\n}\n\n// ── x402 USDC balance check ──\n\n/** USDC on Base mainnet. */\nconst BASE_USDC_ADDRESS = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\" as const;\n\n/** Minimum USDC balance (in 6-decimal units) to consider x402 available. $0.50 */\nconst MIN_USDC_BALANCE = 500_000n; // 0.50 USDC (6 decimals)\n\n/** Cache: { balance, timestamp }. Refreshed at most once per scan cycle. */\nlet _balanceCache: { available: boolean; timestamp: number } | null = null;\nconst BALANCE_CACHE_TTL_MS = 60_000; // 1 minute — covers a full scan cycle\n\n/**\n * Check whether the cron wallet has enough USDC on Base to pay for x402 requests.\n * Result is cached for 60s so we only make one RPC call per scan cycle.\n * On RPC error, returns true (assume available — don't break scoring).\n */\nexport async function isX402WalletFunded(): Promise<boolean> {\n // Return cached result if fresh\n if (_balanceCache && Date.now() - _balanceCache.timestamp < BALANCE_CACHE_TTL_MS) {\n return _balanceCache.available;\n }\n\n try {\n const { createPublicClient, http } = await import(\"viem\");\n const { base } = await import(\"viem/chains\");\n\n const account = getAccount();\n const client = createPublicClient({\n chain: base,\n transport: http(\"https://base-rpc.publicnode.com\"),\n });\n\n // ERC-20 balanceOf(address) — minimal ABI\n const balance = await client.readContract({\n address: BASE_USDC_ADDRESS,\n abi: [\n {\n name: \"balanceOf\",\n type: \"function\",\n stateMutability: \"view\",\n inputs: [{ name: \"account\", type: \"address\" }],\n outputs: [{ name: \"\", type: \"uint256\" }],\n },\n ] as const,\n functionName: \"balanceOf\",\n args: [account.address],\n });\n\n const available = (balance as bigint) >= MIN_USDC_BALANCE;\n _balanceCache = { available, timestamp: Date.now() };\n return available;\n } catch (err) {\n // RPC failure — assume funded so we don't break scoring\n console.error(`x402 balance check failed (assuming available): ${(err as Error).message}`);\n _balanceCache = { available: true, timestamp: Date.now() };\n return true;\n }\n}\n\n/** Reset balance cache — for tests. */\nexport function resetBalanceCache(): void {\n _balanceCache = null;\n}\n","/**\n * Nansen research provider — supports both API key auth (Pro subscription,\n * ~5× cheaper) and x402 micropayments (pay-per-request, no subscription).\n *\n * Auth priority:\n * 1. NANSEN_API_KEY env var → standard fetch with apiKey header (Pro plan)\n * 2. x402 fetch wrapper → automatic USDC micropayments on Base\n *\n * Docs: https://docs.nansen.ai\n * Pro pricing: 5 credits/call for Smart Money endpoints (~$0.005/call)\n * x402 pricing: ~$0.06/call for Smart Money endpoints\n *\n * Supports all query types:\n * token → token screener (on-chain metrics, holder quality)\n * market → token screener sorted by market cap / volume\n * smart-money → smart money net flow from labeled wallets\n * hl-perp-trades → Hyperliquid smart-money perp trades\n * wallet → wallet profiler (PnL, tx patterns, counterparties)\n */\n\nimport { base, baseSepolia } from \"viem/chains\";\nimport type { ProviderInfo } from \"../../types.js\";\nimport type { ResearchProvider, ResearchQuery, ResearchResult } from \"./index.js\";\nimport { getX402Fetch } from \"../../lib/x402.js\";\n\nconst BASE_URL = \"https://api.nansen.ai\";\n\n/**\n * Returns a fetch function configured for Nansen auth. Cached after first\n * creation — same pattern as getX402Fetch() to avoid allocating a new\n * closure on every call.\n *\n * If NANSEN_API_KEY is set → standard fetch with apiKey header (Pro plan).\n * Otherwise → x402 micropayment fetch.\n */\nlet _nansenFetch: typeof fetch | null = null;\nlet _nansenFetchKey: string | undefined;\n\nasync function getNansenFetch(): Promise<typeof fetch> {\n const apiKey = process.env.NANSEN_API_KEY;\n\n // Cache hit — return if key hasn't changed\n if (_nansenFetch && _nansenFetchKey === apiKey) return _nansenFetch;\n\n if (apiKey) {\n const baseFetch = globalThis.fetch;\n _nansenFetch = ((url: string | URL | Request, init?: RequestInit) => {\n const headers = new Headers(init?.headers);\n headers.set(\"apiKey\", apiKey);\n return baseFetch(url, { ...init, headers });\n }) as typeof fetch;\n _nansenFetchKey = apiKey;\n } else {\n _nansenFetch = await getX402Fetch();\n _nansenFetchKey = undefined;\n }\n\n return _nansenFetch;\n}\n\n/** Known x402 cost per Nansen query type. */\nexport const NANSEN_COST_ESTIMATE: Record<string, string> = {\n token: \"~$0.01\",\n market: \"~$0.01\",\n \"smart-money\": \"~$0.06\",\n \"hl-perp-trades\": \"~$0.06\",\n wallet: \"~$0.01\",\n \"flow-intelligence\": \"~$0.005\", // 1 credit — cheapest premium endpoint\n};\n\nexport class NansenProvider implements ResearchProvider {\n info(): ProviderInfo {\n return {\n name: \"nansen\",\n type: \"research\",\n capabilities: [\n \"research.token\",\n \"research.market\",\n \"research.smart-money\",\n \"research.wallet\",\n ],\n supportedChains: [base, baseSepolia],\n };\n }\n\n async query(params: ResearchQuery): Promise<ResearchResult> {\n switch (params.type) {\n case \"token\":\n return this.tokenScreener(params.target);\n case \"market\":\n return this.marketScreener(params.target);\n case \"smart-money\":\n return this.smartMoneyNetflow(params.options?.token ?? params.target);\n case \"wallet\":\n return this.walletProfile(params.target);\n default:\n throw new Error(`Unsupported query type: ${params.type}`);\n }\n }\n\n /**\n * Token Screener — comprehensive token discovery with on-chain metrics.\n * Endpoint: POST /api/v1/token-screener\n * Cost: ~$0.01 (basic tier)\n */\n private async tokenScreener(target: string): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n // Determine if target is an address or symbol\n const isAddr = target.startsWith(\"0x\") && target.length === 42;\n const filters: Record<string, unknown> = isAddr\n ? { token_address: [target] }\n : { token_symbol: [target.toUpperCase()] };\n\n const body = {\n chains: [\"base\"],\n timeframe: \"24h\",\n filters,\n pagination: { page: 1, records_per_page: 10 },\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/token-screener`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(\n `Nansen token query failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as { data?: unknown[] };\n const costUsdc = this.extractCost(res, \"token\");\n\n return {\n provider: \"nansen\",\n queryType: \"token\",\n target,\n data: { tokens: json.data ?? [], count: (json.data ?? []).length },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Market Screener — token screener sorted by volume/market cap for market overview.\n * Endpoint: POST /api/v1/token-screener\n * Cost: ~$0.01 (basic tier)\n */\n private async marketScreener(asset: string): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n const isAddr = asset.startsWith(\"0x\") && asset.length === 42;\n const filters: Record<string, unknown> = isAddr\n ? { token_address: [asset] }\n : { token_symbol: [asset.toUpperCase()] };\n\n const body = {\n chains: [\"base\"],\n timeframe: \"24h\",\n filters,\n order_by: [{ field: \"buy_volume\", direction: \"desc\" }],\n pagination: { page: 1, records_per_page: 10 },\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/token-screener`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(\n `Nansen market query failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as { data?: unknown[] };\n const costUsdc = this.extractCost(res, \"market\");\n\n return {\n provider: \"nansen\",\n queryType: \"market\",\n target: asset,\n data: { tokens: json.data ?? [], count: (json.data ?? []).length },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Smart Money Net Flow — capital flow analysis from labeled wallets.\n * Endpoint: POST /api/v1/smart-money/netflow\n * Cost: ~$0.06 (premium tier)\n *\n * Queries across all major chains (ethereum, solana, base, arbitrum, etc.).\n * The API returns per-chain rows so we get flows across ethereum, solana,\n * base, L2s, etc.\n *\n * The netflow endpoint filters by `token_address` (string or array), NOT by\n * `token_symbol` — passing `token_symbol` yields 422 \"Unknown field\". So we\n * first resolve the symbol to its per-chain contract address(es) via the\n * token screener, then filter netflow by those addresses.\n *\n * Response includes net_flow_1h/24h/7d/30d in USD per token+chain,\n * plus trader_count (active smart money wallets in 30d window).\n */\n private async smartMoneyNetflow(\n target: string,\n ): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n // Map CoinGecko token IDs to symbols for the lookup\n const symbolMap: Record<string, string> = {\n bitcoin: \"BTC\", ethereum: \"ETH\", solana: \"SOL\",\n arbitrum: \"ARB\", aave: \"AAVE\", uniswap: \"UNI\",\n chainlink: \"LINK\", dogecoin: \"DOGE\", ripple: \"XRP\",\n polkadot: \"DOT\", avalanche: \"AVAX\", near: \"NEAR\",\n sui: \"SUI\", aptos: \"APT\", hyperliquid: \"HYPE\",\n worldcoin: \"WLD\", \"worldcoin-wld\": \"WLD\",\n bittensor: \"TAO\", zcash: \"ZEC\", fartcoin: \"FARTCOIN\",\n pepe: \"PEPE\", pendle: \"PENDLE\", jupiter: \"JUP\",\n };\n const symbol = symbolMap[target.toLowerCase()] ?? target.toUpperCase();\n\n // Resolve symbol → per-chain contract address(es). The netflow filter is\n // token_address-only. If the target is already an address, use it directly.\n const isAddr = target.startsWith(\"0x\") && target.length === 42;\n const tokenAddresses = isAddr\n ? [target]\n : await this.resolveTokenAddresses(symbol);\n\n if (tokenAddresses.length === 0) {\n // No on-chain address found for this symbol — return an empty result\n // rather than firing an unfiltered (and expensive) all-token query.\n return {\n provider: \"nansen\",\n queryType: \"smart-money\",\n target,\n data: { flows: [], count: 0 },\n costUsdc: \"0\",\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n const body: Record<string, unknown> = {\n // Query all supported smart-money chains — the API returns per-chain rows.\n chains: [\"ethereum\", \"solana\", \"base\", \"arbitrum\", \"optimism\", \"polygon\", \"avalanche\", \"bnb\"],\n filters: {\n token_address: tokenAddresses,\n },\n order_by: [{ field: \"net_flow_24h_usd\", direction: \"DESC\" }],\n pagination: { page: 1, per_page: 10 },\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/smart-money/netflow`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(\n `Nansen smart-money query failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as { data?: unknown[] };\n const costUsdc = this.extractCost(res, \"smart-money\");\n\n return {\n provider: \"nansen\",\n queryType: \"smart-money\",\n target,\n data: { flows: json.data ?? [], count: (json.data ?? []).length },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Hyperliquid Smart Money Perp Trades — what smart wallets are trading\n * on Hyperliquid perps right now.\n * Endpoint: POST /api/v1/smart-money/perp-trades\n * Cost: ~$0.06 (premium tier, estimated)\n *\n * Returns granular trade data: direction (Long/Short), size, price, action\n * (Buy - Add Long, Sell - Open Short, etc.), trader labels (Fund, Smart Trader).\n * Directly relevant — same venue + instrument we trade.\n */\n async queryHyperliquidSmartMoney(\n tokenSymbol: string,\n ): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n const body = {\n filters: {\n include_smart_money_labels: [\"Fund\", \"Smart Trader\", \"Smart HL Perps Trader\"],\n token_symbol: tokenSymbol,\n value_usd: { min: 50000 }, // only meaningful-size trades\n },\n only_new_positions: false,\n pagination: { page: 1, per_page: 20 },\n order_by: [{ field: \"block_timestamp\", direction: \"DESC\" }],\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/smart-money/perp-trades`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(`Nansen HL perp-trades query failed: ${res.status} ${res.statusText}`);\n }\n\n const json = (await res.json()) as { data?: unknown[] };\n const costUsdc = this.extractCost(res, \"smart-money\");\n\n return {\n provider: \"nansen\",\n queryType: \"hl-perp-trades\",\n target: tokenSymbol,\n data: { trades: json.data ?? [], count: (json.data ?? []).length },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Flow Intelligence — net inflows/outflows by investor category.\n * Endpoint: POST /api/v1/tgm/flow-intelligence\n * Cost: 1 credit (~$0.005 at Pro pricing)\n *\n * Returns aggregated flow data by investor type (Smart Traders, Whales,\n * Exchanges, Top PnL). More stable than individual trade data — shows\n * accumulation/distribution trends rather than noisy per-trade signals.\n */\n async queryFlowIntelligence(\n tokenSymbol: string,\n ): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n const body = {\n token_symbol: tokenSymbol,\n timeframe: \"24h\",\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/tgm/flow-intelligence`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(`Nansen flow-intelligence query failed: ${res.status} ${res.statusText}`);\n }\n\n const json = (await res.json()) as { data?: unknown };\n const costUsdc = this.extractCost(res, \"flow-intelligence\");\n\n return {\n provider: \"nansen\",\n queryType: \"flow-intelligence\",\n target: tokenSymbol,\n data: (json.data ?? {}) as Record<string, unknown>,\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Wallet Profile — PnL summary, realized/unrealized gains, token breakdown.\n * Endpoint: POST /api/v1/profiler/address/pnl-summary\n * Cost: ~$0.01 (basic tier)\n *\n * Note: /api/v1/profiler/wallet-pnl requires an API key and does not support x402.\n * The correct x402 endpoint is /api/v1/profiler/address/pnl-summary.\n */\n private async walletProfile(address: string): Promise<ResearchResult> {\n const fetchWithPay = await getNansenFetch();\n\n // pnl-summary uses `chain` (singular) + a date range, not `chains` array\n const now = new Date();\n const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n const body = {\n address,\n chain: \"base\",\n date: {\n from: thirtyDaysAgo.toISOString().replace(/\\.\\d{3}Z$/, \"Z\"),\n to: now.toISOString().replace(/\\.\\d{3}Z$/, \"Z\"),\n },\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/profiler/address/pnl-summary`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n throw new Error(\n `Nansen wallet query failed: ${res.status} ${res.statusText}`,\n );\n }\n\n const json = (await res.json()) as { data?: Record<string, unknown> };\n const costUsdc = this.extractCost(res, \"wallet\");\n\n return {\n provider: \"nansen\",\n queryType: \"wallet\",\n target: address,\n data: json.data ?? (json as Record<string, unknown>),\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Resolve a token symbol to its per-chain contract address(es) via the\n * token screener endpoint (~$0.01). Returns a deduped list across the major\n * smart-money chains so the netflow filter (token_address only) can match\n * the token wherever it trades.\n *\n * The screener caps `chains` at 5 items, so we query the highest-liquidity\n * smart-money chains. Returns an empty array if the symbol resolves to\n * nothing (unknown token) or the lookup fails.\n */\n private async resolveTokenAddresses(symbol: string): Promise<string[]> {\n const fetchWithPay = await getNansenFetch();\n const body = {\n // Screener allows at most 5 chains per request.\n chains: [\"ethereum\", \"base\", \"arbitrum\", \"solana\", \"bnb\"],\n timeframe: \"24h\",\n filters: { token_symbol: [symbol.toUpperCase()] },\n pagination: { page: 1, records_per_page: 25 },\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/token-screener`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) return [];\n\n const json = (await res.json()) as {\n data?: Array<{ token_address?: string }>;\n };\n const addresses = (json.data ?? [])\n .map((row) => row.token_address)\n .filter((addr): addr is string => typeof addr === \"string\" && addr.length > 0);\n return [...new Set(addresses)];\n }\n\n /**\n * Extract cost from x402 response headers.\n * Falls back to the known estimate for the query type when headers are absent.\n */\n private extractCost(res: Response, queryType: string): string {\n // Check Nansen-specific credit header\n const creditsUsed = res.headers.get(\"x-nansen-credits-used\");\n if (creditsUsed) {\n return creditsUsed;\n }\n\n // Fall back to x402 payment-response header\n const paymentResponse = res.headers.get(\"payment-response\");\n if (paymentResponse) {\n try {\n const parsed = JSON.parse(paymentResponse) as { amount?: string };\n if (parsed.amount) {\n const cents = Number(parsed.amount) / 1e6;\n return cents.toFixed(4);\n }\n } catch {\n // Fall through\n }\n }\n\n // Fall back to known estimate (strip \"~$\" prefix)\n const est = NANSEN_COST_ESTIMATE[queryType];\n if (est) {\n return est.replace(\"~$\", \"\");\n }\n\n return \"unknown\";\n }\n}\n","/**\n * Research providers — pluggable interface for DeFi research data via x402 micropayments.\n *\n * Same pattern as DeFi providers (Moonwell, Uniswap). Each research provider\n * implements ResearchProvider and uses x402-wrapped fetch for automatic USDC payments.\n */\n\nimport type { Provider } from \"../../types.js\";\nimport { MessariProvider } from \"./messari.js\";\nimport { NansenProvider } from \"./nansen.js\";\n\n// ── Types ──\n\nexport interface ResearchQuery {\n /** Query type determines which API endpoint is called */\n type: \"token\" | \"smart-money\" | \"market\" | \"wallet\";\n /** Token address, asset symbol, or wallet address depending on type */\n target: string;\n /** Additional query params (e.g. token symbol for smart-money queries) */\n options?: Record<string, string>;\n}\n\nexport interface ResearchResult {\n provider: string;\n queryType: string;\n target: string;\n /** Structured response data from the provider */\n data: Record<string, unknown>;\n /** Cost paid in USDC (human-readable, e.g. \"0.05\") */\n costUsdc: string;\n timestamp: number;\n}\n\nexport interface ResearchProvider extends Provider {\n /** Query the research provider. Payment is handled automatically via x402. */\n query(params: ResearchQuery): Promise<ResearchResult>;\n}\n\n// ── Factory ──\n\nexport function getResearchProvider(name: string): ResearchProvider {\n switch (name) {\n case \"messari\":\n return new MessariProvider();\n case \"nansen\":\n return new NansenProvider();\n default:\n throw new Error(\n `Unknown research provider: ${name}. Valid providers: messari, nansen`,\n );\n }\n}\n\n// ── Re-exports ──\n\nexport { MessariProvider, NansenProvider };\n"],"mappings":"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","names":["base","base","baseSepolia","BASE_URL","base","baseSepolia"]}