@sherwoodagent/cli 0.5.1 → 0.7.0

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.
@@ -0,0 +1,413 @@
1
+ import {
2
+ getAccount
3
+ } from "./chunk-QMWMT6EH.js";
4
+
5
+ // src/providers/research/messari.ts
6
+ import { base, baseSepolia } from "viem/chains";
7
+
8
+ // src/lib/x402.ts
9
+ var _x402Fetch = null;
10
+ async function getX402Fetch() {
11
+ if (_x402Fetch) return _x402Fetch;
12
+ const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
13
+ const { registerExactEvmScheme } = await import("@x402/evm/exact/client");
14
+ const signer = getAccount();
15
+ const client = new x402Client();
16
+ registerExactEvmScheme(client, { signer });
17
+ _x402Fetch = wrapFetchWithPayment(fetch, client);
18
+ return _x402Fetch;
19
+ }
20
+
21
+ // src/providers/research/messari.ts
22
+ var BASE_URL = "https://data.messari.io/api";
23
+ var MESSARI_COST_ESTIMATE = {
24
+ token: "~$0.01",
25
+ market: "~$0.01",
26
+ "smart-money": "~$0.01",
27
+ wallet: "~$0.01"
28
+ };
29
+ var MessariProvider = class {
30
+ info() {
31
+ return {
32
+ name: "messari",
33
+ type: "research",
34
+ capabilities: [
35
+ "research.token",
36
+ "research.market",
37
+ "research.smart-money",
38
+ "research.wallet"
39
+ ],
40
+ supportedChains: [base, baseSepolia]
41
+ };
42
+ }
43
+ async query(params) {
44
+ switch (params.type) {
45
+ case "token":
46
+ return this.tokenReport(params.target);
47
+ case "market":
48
+ return this.marketOverview(params.target);
49
+ case "smart-money":
50
+ return this.smartMoney(params.options?.token ?? params.target);
51
+ case "wallet":
52
+ return this.walletLookup(params.target);
53
+ default:
54
+ throw new Error(`Unsupported query type: ${params.type}`);
55
+ }
56
+ }
57
+ /**
58
+ * Get full asset profile: description, technology, contributors, governance, etc.
59
+ * Endpoint: GET /v2/assets/{assetKey}/profile
60
+ */
61
+ async tokenReport(assetKey) {
62
+ const fetchWithPay = await getX402Fetch();
63
+ const url = `${BASE_URL}/v2/assets/${encodeURIComponent(assetKey)}/profile`;
64
+ const res = await fetchWithPay(url, { method: "GET" });
65
+ if (!res.ok) {
66
+ throw new Error(
67
+ `Messari token query failed: ${res.status} ${res.statusText}`
68
+ );
69
+ }
70
+ const json = await res.json();
71
+ const costUsdc = this.extractCost(res);
72
+ return {
73
+ provider: "messari",
74
+ queryType: "token",
75
+ target: assetKey,
76
+ data: json.data ?? json,
77
+ costUsdc,
78
+ timestamp: Math.floor(Date.now() / 1e3)
79
+ };
80
+ }
81
+ /**
82
+ * Get market metrics: price, volume, market cap, ROI, ATH, etc.
83
+ * Endpoint: GET /v1/assets/{assetKey}/metrics
84
+ */
85
+ async marketOverview(assetKey) {
86
+ const fetchWithPay = await getX402Fetch();
87
+ const url = `${BASE_URL}/v1/assets/${encodeURIComponent(assetKey)}/metrics`;
88
+ const res = await fetchWithPay(url, { method: "GET" });
89
+ if (!res.ok) {
90
+ throw new Error(
91
+ `Messari market query failed: ${res.status} ${res.statusText}`
92
+ );
93
+ }
94
+ const json = await res.json();
95
+ const costUsdc = this.extractCost(res);
96
+ return {
97
+ provider: "messari",
98
+ queryType: "market",
99
+ target: assetKey,
100
+ data: json.data ?? json,
101
+ costUsdc,
102
+ timestamp: Math.floor(Date.now() / 1e3)
103
+ };
104
+ }
105
+ /**
106
+ * Smart money — Messari's on-chain metrics for the asset.
107
+ * Uses the metrics endpoint filtered to on-chain activity data
108
+ * (active addresses, transaction volume, NVT, exchange flows).
109
+ * Endpoint: GET /v1/assets/{assetKey}/metrics
110
+ */
111
+ async smartMoney(assetKey) {
112
+ const fetchWithPay = await getX402Fetch();
113
+ const url = `${BASE_URL}/v1/assets/${encodeURIComponent(assetKey)}/metrics`;
114
+ const res = await fetchWithPay(url, { method: "GET" });
115
+ if (!res.ok) {
116
+ throw new Error(
117
+ `Messari smart-money query failed: ${res.status} ${res.statusText}`
118
+ );
119
+ }
120
+ const json = await res.json();
121
+ const costUsdc = this.extractCost(res);
122
+ const metrics = json.data ?? {};
123
+ const onChainData = {};
124
+ const onChainKeys = [
125
+ "on_chain_data",
126
+ "blockchain_stats_24_hours",
127
+ "exchange_flows",
128
+ "miner_flows",
129
+ "supply",
130
+ "market_data"
131
+ ];
132
+ for (const key of onChainKeys) {
133
+ if (key in metrics) {
134
+ onChainData[key] = metrics[key];
135
+ }
136
+ }
137
+ return {
138
+ provider: "messari",
139
+ queryType: "smart-money",
140
+ target: assetKey,
141
+ data: Object.keys(onChainData).length > 0 ? onChainData : metrics,
142
+ costUsdc,
143
+ timestamp: Math.floor(Date.now() / 1e3)
144
+ };
145
+ }
146
+ /**
147
+ * Wallet lookup — Messari doesn't have per-wallet analytics, but if the
148
+ * address corresponds to a known asset, we can return its profile.
149
+ * Falls back to a search by address.
150
+ * Endpoint: GET /v2/assets/{address}/profile
151
+ */
152
+ async walletLookup(address) {
153
+ const fetchWithPay = await getX402Fetch();
154
+ const url = `${BASE_URL}/v2/assets/${encodeURIComponent(address)}/profile`;
155
+ const res = await fetchWithPay(url, { method: "GET" });
156
+ if (!res.ok) {
157
+ throw new Error(
158
+ `Messari wallet/asset lookup failed: ${res.status} ${res.statusText}. Messari maps addresses to token profiles \u2014 for full wallet analytics, use --provider nansen`
159
+ );
160
+ }
161
+ const json = await res.json();
162
+ const costUsdc = this.extractCost(res);
163
+ return {
164
+ provider: "messari",
165
+ queryType: "wallet",
166
+ target: address,
167
+ data: json.data ?? json,
168
+ costUsdc,
169
+ timestamp: Math.floor(Date.now() / 1e3)
170
+ };
171
+ }
172
+ /**
173
+ * Extract cost from x402 response headers.
174
+ * The PAYMENT-RESPONSE header may contain settlement details including the amount paid.
175
+ */
176
+ extractCost(res) {
177
+ const paymentResponse = res.headers.get("payment-response");
178
+ if (paymentResponse) {
179
+ try {
180
+ const parsed = JSON.parse(paymentResponse);
181
+ if (parsed.amount) {
182
+ const cents = Number(parsed.amount) / 1e6;
183
+ return cents.toFixed(4);
184
+ }
185
+ } catch {
186
+ }
187
+ }
188
+ return "unknown";
189
+ }
190
+ };
191
+
192
+ // src/providers/research/nansen.ts
193
+ import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
194
+ var BASE_URL2 = "https://api.nansen.ai";
195
+ var NANSEN_COST_ESTIMATE = {
196
+ token: "~$0.01",
197
+ market: "~$0.01",
198
+ "smart-money": "~$0.05",
199
+ wallet: "~$0.01"
200
+ };
201
+ var NansenProvider = class {
202
+ info() {
203
+ return {
204
+ name: "nansen",
205
+ type: "research",
206
+ capabilities: [
207
+ "research.token",
208
+ "research.market",
209
+ "research.smart-money",
210
+ "research.wallet"
211
+ ],
212
+ supportedChains: [base2, baseSepolia2]
213
+ };
214
+ }
215
+ async query(params) {
216
+ switch (params.type) {
217
+ case "token":
218
+ return this.tokenScreener(params.target);
219
+ case "market":
220
+ return this.marketScreener(params.target);
221
+ case "smart-money":
222
+ return this.smartMoneyNetflow(params.options?.token ?? params.target);
223
+ case "wallet":
224
+ return this.walletProfile(params.target);
225
+ default:
226
+ throw new Error(`Unsupported query type: ${params.type}`);
227
+ }
228
+ }
229
+ /**
230
+ * Token Screener — comprehensive token discovery with on-chain metrics.
231
+ * Endpoint: POST /api/v1/token-screener
232
+ * Cost: ~$0.01 (basic tier)
233
+ */
234
+ async tokenScreener(target) {
235
+ const fetchWithPay = await getX402Fetch();
236
+ const isAddr = target.startsWith("0x") && target.length === 42;
237
+ const filters = isAddr ? { token_address: [target] } : { token_symbol: [target.toUpperCase()] };
238
+ const body = {
239
+ chains: ["base"],
240
+ timeframe: "24h",
241
+ filters,
242
+ pagination: { page: 1, records_per_page: 10 }
243
+ };
244
+ const res = await fetchWithPay(`${BASE_URL2}/api/v1/token-screener`, {
245
+ method: "POST",
246
+ headers: { "Content-Type": "application/json" },
247
+ body: JSON.stringify(body)
248
+ });
249
+ if (!res.ok) {
250
+ throw new Error(
251
+ `Nansen token query failed: ${res.status} ${res.statusText}`
252
+ );
253
+ }
254
+ const json = await res.json();
255
+ const costUsdc = this.extractCost(res);
256
+ return {
257
+ provider: "nansen",
258
+ queryType: "token",
259
+ target,
260
+ data: { tokens: json.data ?? [], count: (json.data ?? []).length },
261
+ costUsdc,
262
+ timestamp: Math.floor(Date.now() / 1e3)
263
+ };
264
+ }
265
+ /**
266
+ * Market Screener — token screener sorted by volume/market cap for market overview.
267
+ * Endpoint: POST /api/v1/token-screener
268
+ * Cost: ~$0.01 (basic tier)
269
+ */
270
+ async marketScreener(asset) {
271
+ const fetchWithPay = await getX402Fetch();
272
+ const isAddr = asset.startsWith("0x") && asset.length === 42;
273
+ const filters = isAddr ? { token_address: [asset] } : { token_symbol: [asset.toUpperCase()] };
274
+ const body = {
275
+ chains: ["base"],
276
+ timeframe: "24h",
277
+ filters,
278
+ order_by: [{ field: "buy_volume", direction: "desc" }],
279
+ pagination: { page: 1, records_per_page: 10 }
280
+ };
281
+ const res = await fetchWithPay(`${BASE_URL2}/api/v1/token-screener`, {
282
+ method: "POST",
283
+ headers: { "Content-Type": "application/json" },
284
+ body: JSON.stringify(body)
285
+ });
286
+ if (!res.ok) {
287
+ throw new Error(
288
+ `Nansen market query failed: ${res.status} ${res.statusText}`
289
+ );
290
+ }
291
+ const json = await res.json();
292
+ const costUsdc = this.extractCost(res);
293
+ return {
294
+ provider: "nansen",
295
+ queryType: "market",
296
+ target: asset,
297
+ data: { tokens: json.data ?? [], count: (json.data ?? []).length },
298
+ costUsdc,
299
+ timestamp: Math.floor(Date.now() / 1e3)
300
+ };
301
+ }
302
+ /**
303
+ * Smart Money Net Flow — capital flow analysis from labeled wallets.
304
+ * Endpoint: POST /api/v1/smart-money/netflow
305
+ * Cost: ~$0.05 (premium tier)
306
+ */
307
+ async smartMoneyNetflow(tokenSymbol) {
308
+ const fetchWithPay = await getX402Fetch();
309
+ const body = {
310
+ chains: ["base"],
311
+ filters: {
312
+ token_symbol: [tokenSymbol.toUpperCase()]
313
+ },
314
+ pagination: { page: 1, records_per_page: 10 }
315
+ };
316
+ const res = await fetchWithPay(`${BASE_URL2}/api/v1/smart-money/netflow`, {
317
+ method: "POST",
318
+ headers: { "Content-Type": "application/json" },
319
+ body: JSON.stringify(body)
320
+ });
321
+ if (!res.ok) {
322
+ throw new Error(
323
+ `Nansen smart-money query failed: ${res.status} ${res.statusText}`
324
+ );
325
+ }
326
+ const json = await res.json();
327
+ const costUsdc = this.extractCost(res);
328
+ return {
329
+ provider: "nansen",
330
+ queryType: "smart-money",
331
+ target: tokenSymbol,
332
+ data: { flows: json.data ?? [], count: (json.data ?? []).length },
333
+ costUsdc,
334
+ timestamp: Math.floor(Date.now() / 1e3)
335
+ };
336
+ }
337
+ /**
338
+ * Wallet Profile — PnL history, transaction patterns, counterparties.
339
+ * Endpoint: POST /api/v1/profiler/wallet-pnl
340
+ * Cost: ~$0.01 (basic tier)
341
+ */
342
+ async walletProfile(address) {
343
+ const fetchWithPay = await getX402Fetch();
344
+ const body = {
345
+ chains: ["base"],
346
+ address
347
+ };
348
+ const res = await fetchWithPay(`${BASE_URL2}/api/v1/profiler/wallet-pnl`, {
349
+ method: "POST",
350
+ headers: { "Content-Type": "application/json" },
351
+ body: JSON.stringify(body)
352
+ });
353
+ if (!res.ok) {
354
+ throw new Error(
355
+ `Nansen wallet query failed: ${res.status} ${res.statusText}`
356
+ );
357
+ }
358
+ const json = await res.json();
359
+ const costUsdc = this.extractCost(res);
360
+ return {
361
+ provider: "nansen",
362
+ queryType: "wallet",
363
+ target: address,
364
+ data: json.data ?? json,
365
+ costUsdc,
366
+ timestamp: Math.floor(Date.now() / 1e3)
367
+ };
368
+ }
369
+ /**
370
+ * Extract cost from x402 response headers.
371
+ */
372
+ extractCost(res) {
373
+ const creditsUsed = res.headers.get("x-nansen-credits-used");
374
+ if (creditsUsed) {
375
+ return creditsUsed;
376
+ }
377
+ const paymentResponse = res.headers.get("payment-response");
378
+ if (paymentResponse) {
379
+ try {
380
+ const parsed = JSON.parse(paymentResponse);
381
+ if (parsed.amount) {
382
+ const cents = Number(parsed.amount) / 1e6;
383
+ return cents.toFixed(4);
384
+ }
385
+ } catch {
386
+ }
387
+ }
388
+ return "unknown";
389
+ }
390
+ };
391
+
392
+ // src/providers/research/index.ts
393
+ function getResearchProvider(name) {
394
+ switch (name) {
395
+ case "messari":
396
+ return new MessariProvider();
397
+ case "nansen":
398
+ return new NansenProvider();
399
+ default:
400
+ throw new Error(
401
+ `Unknown research provider: ${name}. Valid providers: messari, nansen`
402
+ );
403
+ }
404
+ }
405
+
406
+ export {
407
+ MESSARI_COST_ESTIMATE,
408
+ MessariProvider,
409
+ NANSEN_COST_ESTIMATE,
410
+ NansenProvider,
411
+ getResearchProvider
412
+ };
413
+ //# sourceMappingURL=chunk-L5Y7E7LI.js.map
@@ -0,0 +1 @@
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, protocol data, asset profiles\n *\n * Supports all query types:\n * token → asset profile (description, technology, governance)\n * market → market metrics (price, volume, market cap, ROI, ATH)\n * smart-money → asset metrics filtered to on-chain activity\n * wallet → not natively supported, falls back to asset lookup by address\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://data.messari.io/api\";\n\n/** Known x402 cost per Messari query (approximate). */\nexport const MESSARI_COST_ESTIMATE: Record<string, string> = {\n token: \"~$0.01\",\n market: \"~$0.01\",\n \"smart-money\": \"~$0.01\",\n wallet: \"~$0.01\",\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 \"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.tokenReport(params.target);\n case \"market\":\n return this.marketOverview(params.target);\n case \"smart-money\":\n return this.smartMoney(params.options?.token ?? params.target);\n case \"wallet\":\n return this.walletLookup(params.target);\n default:\n throw new Error(`Unsupported query type: ${params.type}`);\n }\n }\n\n /**\n * Get full asset profile: description, technology, contributors, governance, etc.\n * Endpoint: GET /v2/assets/{assetKey}/profile\n */\n private async tokenReport(assetKey: string): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n const url = `${BASE_URL}/v2/assets/${encodeURIComponent(assetKey)}/profile`;\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 { data?: Record<string, unknown> };\n const costUsdc = this.extractCost(res);\n\n return {\n provider: \"messari\",\n queryType: \"token\",\n target: assetKey,\n data: json.data ?? (json as Record<string, unknown>),\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Get market metrics: price, volume, market cap, ROI, ATH, etc.\n * Endpoint: GET /v1/assets/{assetKey}/metrics\n */\n private async marketOverview(assetKey: string): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n const url = `${BASE_URL}/v1/assets/${encodeURIComponent(assetKey)}/metrics`;\n\n const res = await fetchWithPay(url, { method: \"GET\" });\n if (!res.ok) {\n throw new Error(\n `Messari market 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);\n\n return {\n provider: \"messari\",\n queryType: \"market\",\n target: assetKey,\n data: json.data ?? (json as Record<string, unknown>),\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Smart money — Messari's on-chain metrics for the asset.\n * Uses the metrics endpoint filtered to on-chain activity data\n * (active addresses, transaction volume, NVT, exchange flows).\n * Endpoint: GET /v1/assets/{assetKey}/metrics\n */\n private async smartMoney(assetKey: string): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n const url = `${BASE_URL}/v1/assets/${encodeURIComponent(assetKey)}/metrics`;\n\n const res = await fetchWithPay(url, { method: \"GET\" });\n if (!res.ok) {\n throw new Error(\n `Messari smart-money 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);\n\n // Extract on-chain relevant fields from the full metrics response\n const metrics = json.data ?? {};\n const onChainData: Record<string, unknown> = {};\n const onChainKeys = [\n \"on_chain_data\",\n \"blockchain_stats_24_hours\",\n \"exchange_flows\",\n \"miner_flows\",\n \"supply\",\n \"market_data\",\n ];\n for (const key of onChainKeys) {\n if (key in metrics) {\n onChainData[key] = metrics[key];\n }\n }\n\n return {\n provider: \"messari\",\n queryType: \"smart-money\",\n target: assetKey,\n data: Object.keys(onChainData).length > 0 ? onChainData : metrics,\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Wallet lookup — Messari doesn't have per-wallet analytics, but if the\n * address corresponds to a known asset, we can return its profile.\n * Falls back to a search by address.\n * Endpoint: GET /v2/assets/{address}/profile\n */\n private async walletLookup(address: string): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n\n // Try looking up the address as an asset (works for token contract addresses)\n const url = `${BASE_URL}/v2/assets/${encodeURIComponent(address)}/profile`;\n\n const res = await fetchWithPay(url, { method: \"GET\" });\n if (!res.ok) {\n throw new Error(\n `Messari wallet/asset lookup failed: ${res.status} ${res.statusText}. ` +\n `Messari maps addresses to token profiles — for full wallet analytics, use --provider nansen`,\n );\n }\n\n const json = (await res.json()) as { data?: Record<string, unknown> };\n const costUsdc = this.extractCost(res);\n\n return {\n provider: \"messari\",\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 * Extract cost from x402 response headers.\n * The PAYMENT-RESPONSE header may contain settlement details including the amount paid.\n */\n private extractCost(res: Response): string {\n const paymentResponse = res.headers.get(\"payment-response\");\n if (paymentResponse) {\n try {\n const parsed = JSON.parse(paymentResponse) as {\n amount?: string;\n };\n if (parsed.amount) {\n // x402 amounts are in USDC atomic units (6 decimals)\n const cents = Number(parsed.amount) / 1e6;\n return cents.toFixed(4);\n }\n } catch {\n // Fall through to default\n }\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 * Nansen research provider — x402 micropayments for on-chain analytics.\n *\n * Docs: https://docs.nansen.ai\n * Payment: x402 (USDC on Base) — no API key required\n * Pricing: Basic $0.01/call (Token Screener, Wallet Balances, PnL, DEX Trades)\n * Premium $0.05/call (Smart Money Net Flow, Holdings, PnL Leaderboard)\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 * 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/** 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.05\",\n wallet: \"~$0.01\",\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 getX402Fetch();\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);\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 getX402Fetch();\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);\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.05 (premium tier)\n */\n private async smartMoneyNetflow(\n tokenSymbol: string,\n ): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n\n const body = {\n chains: [\"base\"],\n filters: {\n token_symbol: [tokenSymbol.toUpperCase()],\n },\n pagination: { page: 1, records_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);\n\n return {\n provider: \"nansen\",\n queryType: \"smart-money\",\n target: tokenSymbol,\n data: { flows: json.data ?? [], count: (json.data ?? []).length },\n costUsdc,\n timestamp: Math.floor(Date.now() / 1000),\n };\n }\n\n /**\n * Wallet Profile — PnL history, transaction patterns, counterparties.\n * Endpoint: POST /api/v1/profiler/wallet-pnl\n * Cost: ~$0.01 (basic tier)\n */\n private async walletProfile(address: string): Promise<ResearchResult> {\n const fetchWithPay = await getX402Fetch();\n\n const body = {\n chains: [\"base\"],\n address,\n };\n\n const res = await fetchWithPay(`${BASE_URL}/api/v1/profiler/wallet-pnl`, {\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);\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 * Extract cost from x402 response headers.\n */\n private extractCost(res: Response): 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 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","baseSepolia","BASE_URL","base","baseSepolia"]}