nansen-cli 1.5.1 → 1.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.
package/AGENTS.md ADDED
@@ -0,0 +1,222 @@
1
+ # AGENTS.md — Agent Quick Start
2
+
3
+ > **This file is for AI agents (OpenClaw, Claude Code, Cursor, etc.) that need to query Nansen data on behalf of their human.** If you're a human, see [README.md](README.md).
4
+
5
+ ## CLI vs REST API
6
+
7
+ **Prefer the [REST API](https://docs.nansen.ai) for most agent use cases** — no install step, no dependency issues.
8
+
9
+ ```bash
10
+ # Direct API call — no CLI needed
11
+ curl -s -X POST https://api.nansen.ai/api/v1/token-screener \
12
+ -H "apiKey: YOUR_KEY" \
13
+ -H "Content-Type: application/json" \
14
+ -d '{"chain":"solana","pagination":{"page":1,"per_page":5}}' | jq .
15
+ ```
16
+
17
+ Use this CLI guide if the CLI is already installed or your human specifically wants CLI usage. The CLI adds `--pretty`, `--table`, `--fields`, `--stream`, built-in retries, and schema introspection.
18
+
19
+ ## Install & Auth in One Shot
20
+
21
+ ```bash
22
+ # Install
23
+ npm install -g nansen-cli
24
+
25
+ # Auth — pick ONE method:
26
+ # Method 1: Interactive (human needs to paste key)
27
+ nansen login
28
+
29
+ # Method 2: Environment variable (no interaction needed)
30
+ export NANSEN_API_KEY=<key>
31
+
32
+ # Method 3: Config file (write directly — no validation call burned)
33
+ mkdir -p ~/.nansen && echo '{"apiKey":"<key>","baseUrl":"https://api.nansen.ai"}' > ~/.nansen/config.json && chmod 600 ~/.nansen/config.json
34
+ ```
35
+
36
+ **Get an API key:** [app.nansen.ai/api](https://app.nansen.ai/api)
37
+
38
+ ### Auth Priority
39
+
40
+ 1. `NANSEN_API_KEY` env var (highest)
41
+ 2. `~/.nansen/config.json` file
42
+ 3. Prompt (interactive only)
43
+
44
+ ### Common Auth Pitfall
45
+
46
+ `nansen login` validates your key by making a real API call (burns 1 credit). If you already know the key is valid, writing `~/.nansen/config.json` directly is cheaper.
47
+
48
+ ## Verify It Works
49
+
50
+ ```bash
51
+ # This is cheap and fast:
52
+ nansen schema | head -1
53
+ # Expected: {"version":"1.x.x","commands":{...}}
54
+
55
+ # This burns a credit but proves API access:
56
+ nansen token screener --chain solana --limit 1
57
+ ```
58
+
59
+ ## Agent-Optimized Patterns
60
+
61
+ ### 1. Always Use `--fields` to Reduce Token Burn
62
+
63
+ ```bash
64
+ # ❌ Returns everything (huge JSON, wastes agent context)
65
+ nansen smart-money netflow --chain solana
66
+
67
+ # ✅ Only what you need
68
+ nansen smart-money netflow --chain solana --fields token_symbol,net_flow_usd,chain --limit 10
69
+ ```
70
+
71
+ ### 2. Use Schema for Self-Discovery
72
+
73
+ ```bash
74
+ # Don't guess commands — introspect:
75
+ nansen schema --pretty # All commands
76
+ nansen schema smart-money --pretty # One command's options & return fields
77
+ ```
78
+
79
+ ### 3. Parse Errors Programmatically
80
+
81
+ Every response has `success: true/false`. Errors include `code` for routing:
82
+
83
+ | Code | What To Do |
84
+ |------|------------|
85
+ | `CREDITS_EXHAUSTED` | **Stop all calls.** Tell the human. |
86
+ | `RATE_LIMITED` | Wait. Auto-retry handles this. |
87
+ | `UNSUPPORTED_FILTER` | Remove the filter, retry without it. |
88
+ | `UNAUTHORIZED` | Key is wrong. Re-auth. |
89
+ | `INVALID_ADDRESS` | Check address format for the chain. |
90
+
91
+ ### 4. Budget Credits
92
+
93
+ - Most calls cost 1 credit
94
+ - `profiler labels` + `profiler balance` are expensive — batch 3-4 at a time
95
+ - `schema`, `help`, `cache` are free (no API key needed)
96
+ - If you get `CREDITS_EXHAUSTED`, **stop immediately** — don't retry
97
+
98
+ ### 5. Use `--stream` for Large Results
99
+
100
+ ```bash
101
+ # NDJSON mode — process line by line, don't buffer giant arrays
102
+ nansen token dex-trades --chain solana --limit 100 --stream
103
+ ```
104
+
105
+ ## Pagination
106
+
107
+ The CLI exposes `--limit N` which maps to `{page: 1, per_page: N}` in the API request. **There is no `--page` flag** — the CLI always fetches page 1. To access later pages, use the REST API directly.
108
+
109
+ ```bash
110
+ # CLI: page 1 only, up to N results
111
+ nansen smart-money netflow --chain solana --limit 50
112
+
113
+ # REST API: full pagination control
114
+ curl -s -X POST https://api.nansen.ai/api/v1/smart-money/netflow \
115
+ -H "apiKey: $NANSEN_API_KEY" -H "Content-Type: application/json" \
116
+ -d '{"chains":["solana"],"pagination":{"page":2,"per_page":50}}'
117
+ ```
118
+
119
+ **Pagination key inconsistency:** Profiler endpoints (which use the beta API) take `recordsPerPage` instead of `per_page`. The CLI sends the right key automatically based on the command, but if you're calling the REST API directly, check which key each endpoint expects.
120
+
121
+ **Detecting the last page:** The raw API response does not include a `total_pages` or `has_more` field in the CLI-visible output. Reliable heuristic: if the number of results returned is less than your `--limit`, you're on the last page.
122
+
123
+ ```bash
124
+ # Request 50, get 23 back → last page
125
+ nansen token holders --token <addr> --chain solana --limit 50
126
+ # Check: .data.data.length (or .data.results.length) < 50 → done
127
+ ```
128
+
129
+ **`profiler perp-positions` has no pagination** — the API ignores the pagination parameter for this endpoint.
130
+
131
+ ## Output Parsing Gotchas
132
+
133
+ ### Response envelope
134
+
135
+ Every CLI response is wrapped in a standard envelope:
136
+
137
+ ```json
138
+ { "success": true, "data": <raw_api_response> }
139
+ ```
140
+
141
+ Errors follow a different shape:
142
+
143
+ ```json
144
+ { "success": false, "error": "message", "code": "ERROR_CODE", "status": 401, "details": {...} }
145
+ ```
146
+
147
+ ### Raw API response shapes vary by endpoint
148
+
149
+ The `data` field inside the envelope is the raw JSON from the Nansen API. Its internal structure differs across endpoints — there is no single canonical key for the results array:
150
+
151
+ | Shape | Example endpoints |
152
+ |-------|------------------|
153
+ | `data.data` (array) | token screener |
154
+ | `data.results` (array) | entity search |
155
+ | `data.data.results` (array) | most profiler endpoints |
156
+ | `data.netflows` | smart-money netflow |
157
+ | `data.trades` | smart-money dex-trades |
158
+ | `data.holdings` | smart-money holdings |
159
+ | `data.holders` | token holders |
160
+
161
+ When parsing, check for the array at each level. `--table` and `--stream` handle this automatically, but if you're parsing raw JSON with `jq`, probe the shape first:
162
+
163
+ ```bash
164
+ nansen smart-money netflow --chain solana | jq 'keys, .data | keys'
165
+ ```
166
+
167
+ ### `--fields` applies to the entire response tree
168
+
169
+ `--fields token_symbol,net_flow_usd` strips everything except those keys from the **entire** response, including the `success` and `data` wrapper fields. The result will be a bare object containing only the matched keys found anywhere in the tree.
170
+
171
+ ### Client-side vs server-side filtering
172
+
173
+ `token screener --search <term>` is **client-side**: the CLI fetches up to 500 results from the server, then filters locally. Set `--limit` higher than your expected result count when using `--search`.
174
+
175
+ ### Fields absent for some chain/token combinations
176
+
177
+ Some fields are only populated for specific chains or tokens:
178
+ - `smart_money_holders` — absent for tokens without smart money tracking
179
+ - Flow intelligence fields — may all be `0` for illiquid tokens (not an error)
180
+ - Perp endpoints (`--symbol`) only work for Hyperliquid; `--token` is for on-chain tokens
181
+
182
+ ## Chains Quick Reference
183
+
184
+ `ethereum` `solana` `base` `bnb` `arbitrum` `polygon` `optimism` `avalanche` `linea` `scroll` `mantle` `ronin` `sei` `plasma` `sonic` `monad` `hyperevm` `iotaevm`
185
+
186
+ > Run `nansen schema` to get the current chain list (source of truth).
187
+
188
+ ## Troubleshooting
189
+
190
+ ### Quick fixes
191
+
192
+ | Symptom | Fix |
193
+ |---------|-----|
194
+ | `command not found: nansen` | `npm install -g nansen-cli` or `npx nansen-cli` |
195
+ | `UNAUTHORIZED` after login | Check `cat ~/.nansen/config.json` — key may not have saved. Write it directly. |
196
+ | Login hangs or fails | Skip `nansen login`, write config directly (see Install & Auth above) |
197
+ | Huge JSON response | Use `--fields` to select only needed columns |
198
+ | Perp endpoints empty or erroring | Use `--symbol BTC` not `--token`. Perp endpoints are Hyperliquid-only. |
199
+ | JUP DCA returns error | Solana-only endpoint |
200
+ | `UNSUPPORTED_FILTER` on token holders | Not all tokens have smart money data. Remove `--smart-money` and retry. |
201
+ | `CREDITS_EXHAUSTED` | Stop all calls. Check [app.nansen.ai](https://app.nansen.ai). No retry will help. |
202
+
203
+ ### Error codes
204
+
205
+ | Code | What to do |
206
+ |------|------------|
207
+ | `CREDITS_EXHAUSTED` | **Stop all calls.** Tell the human. Check dashboard. |
208
+ | `RATE_LIMITED` | Wait — auto-retry handles this by default. |
209
+ | `UNSUPPORTED_FILTER` | Remove the filter, retry without it. |
210
+ | `UNAUTHORIZED` | Key is wrong or missing. Re-auth. |
211
+ | `INVALID_ADDRESS` | Check address format matches the chain (EVM: `0x...`, Solana: Base58). |
212
+
213
+ ### Known endpoint quirks
214
+
215
+ - **`token holders --smart-money`** — Fails with `UNSUPPORTED_FILTER` for tokens without smart money tracking (e.g., WCT on Optimism). Not all tokens have this data. Do not retry.
216
+ - **`token flow-intelligence`** — May return all-zero flows for tokens without significant smart money activity. Normal, not an error.
217
+ - **`profiler labels` and `profiler balance`** consume credits. Budget ~20 calls per session. Batch calls in groups of 3–4.
218
+ - **`profiler perp-positions`** — No pagination support; the API ignores the pagination parameter.
219
+
220
+ ## For OpenClaw / Skill Users
221
+
222
+ If you installed this as an OpenClaw skill, the `SKILL.md` file has the skill interface. This `AGENTS.md` covers the CLI directly. Both work.
package/README.md CHANGED
@@ -27,7 +27,20 @@ npm link
27
27
 
28
28
  ## Configuration
29
29
 
30
- **Option 1: Interactive login (recommended)**
30
+ ### For AI Agents (Recommended)
31
+
32
+ Use the [AI Agent Setup](https://app.nansen.ai/auth/agent-setup) flow:
33
+
34
+ 1. Your agent will ask you to visit: **[app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup)**
35
+ 2. Sign in with your Nansen account
36
+ 3. Copy the message shown
37
+ 4. Paste it back to your agent
38
+
39
+ Your agent saves the key and handles everything else automatically.
40
+
41
+ ### Manual Setup
42
+
43
+ **Option 1: Interactive login**
31
44
  ```bash
32
45
  nansen login
33
46
  # Enter your API key when prompted
@@ -123,6 +136,21 @@ Deep analytics for any token.
123
136
  |------------|-------------|
124
137
  | `defi` | DeFi holdings across protocols |
125
138
 
139
+ ### `search` - Search
140
+
141
+ Search for tokens and entities across Nansen.
142
+
143
+ ```bash
144
+ nansen search "uniswap" --pretty
145
+ nansen search "uniswap" --type token --chain ethereum
146
+ ```
147
+
148
+ | Option | Description |
149
+ |--------|-------------|
150
+ | `--type` | Filter by result type: `token`, `entity`, or `any` (default) |
151
+ | `--chain` | Filter by chain |
152
+ | `--limit` | Max results, 1-50 (default: 25) |
153
+
126
154
  ### `schema` - Schema Discovery
127
155
 
128
156
  Output JSON schema for agent introspection. No API key required.
@@ -177,6 +205,9 @@ nansen cache clear
177
205
 
178
206
  This CLI is built specifically for AI agents. Every design decision prioritizes agent usability.
179
207
 
208
+ **Getting Started:**
209
+ Direct your users to [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup) for seamless authentication. See [AI Agent Access](https://docs.nansen.ai/reference/ai-agent-access) for full documentation.
210
+
180
211
  **Why agents love it:**
181
212
  - **Structured Output**: All responses are JSON with consistent schema — no parsing HTML or unstructured text
182
213
  - **Predictable Errors**: Errors include status codes and actionable details agents can handle programmatically
@@ -248,6 +279,8 @@ NANSEN_API_KEY=your-key npm run test:live
248
279
 
249
280
  See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
250
281
 
282
+ **AI agents:** See [AGENTS.md](AGENTS.md) for the agent quick-start (install, auth, patterns, troubleshooting).
283
+
251
284
  **AI contributors:** See [CLAUDE.md](CLAUDE.md) for agent-specific guidance on contributing to this repo.
252
285
 
253
286
  ## API Coverage
@@ -258,7 +291,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
258
291
  | Profiler | 11 | 100% |
259
292
  | Token God Mode | 12 | 100% |
260
293
  | Portfolio | 1 | 100% |
261
- | **Total** | **30** | **100%** |
294
+ | Search | 1 | 100% |
295
+ | **Total** | **31** | **100%** |
262
296
 
263
297
  ## License
264
298
 
package/SKILL.md CHANGED
@@ -12,21 +12,40 @@ compatibility: Requires Node.js 18+. Needs NANSEN_API_KEY environment variable o
12
12
 
13
13
  Command-line interface for the [Nansen API](https://docs.nansen.ai) - onchain analytics for crypto investors and AI agents.
14
14
 
15
+ ## CLI vs Direct API
16
+
17
+ If your agent has HTTP access (curl, fetch), you can skip the CLI and hit the [Nansen REST API](https://docs.nansen.ai) directly with `apiKey` header auth. The CLI is most useful for terminal-native agents (Claude Code, Codex, Cursor) that benefit from `--pretty`, `--table`, `--fields`, built-in retries, and schema introspection.
18
+
15
19
  ## Setup
16
20
 
17
21
  ```bash
18
22
  # Install globally
19
23
  npm install -g nansen-cli
20
24
 
21
- # Authenticate (interactive)
22
- nansen login
25
+ # Authenticate — pick the method that works for your context:
26
+
27
+ # Option A: Non-interactive (best for agents — no prompts, no wasted credits)
28
+ mkdir -p ~/.nansen && echo '{"apiKey":"YOUR_KEY","baseUrl":"https://api.nansen.ai"}' > ~/.nansen/config.json && chmod 600 ~/.nansen/config.json
23
29
 
24
- # Or set environment variable
30
+ # Option B: Environment variable (good for CI/scripts)
25
31
  export NANSEN_API_KEY=your-api-key
32
+
33
+ # Option C: Interactive login (burns 1 credit to validate)
34
+ nansen login
26
35
  ```
27
36
 
28
37
  Get your API key at [app.nansen.ai/api](https://app.nansen.ai/api).
29
38
 
39
+ ### Verify Installation
40
+
41
+ ```bash
42
+ # Free check (no API key needed):
43
+ nansen schema | head -1
44
+
45
+ # Full check (uses 1 credit):
46
+ nansen token screener --chain solana --limit 1
47
+ ```
48
+
30
49
  ## Commands
31
50
 
32
51
  ### Smart Money
@@ -82,7 +101,7 @@ nansen portfolio defi --wallet 0x123...
82
101
 
83
102
  ## Supported Chains
84
103
 
85
- ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, zksync, mantle, ronin, sei, sonic, monad, hyperevm
104
+ ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, mantle, ronin, sei, plasma, sonic, monad, hyperevm, iotaevm
86
105
 
87
106
  ## Smart Money Labels
88
107
 
@@ -96,24 +115,9 @@ nansen schema --pretty
96
115
  nansen schema smart-money --pretty
97
116
  ```
98
117
 
99
- ## Known Endpoint Issues
100
-
101
- ### Chain/Token-Specific Limitations
102
- - `token holders --smart-money` — Fails with `UNSUPPORTED_FILTER` for tokens without smart money tracking (e.g., WCT on Optimism). Not all tokens have smart money data. Do not retry.
103
- - `token flow-intelligence` — May return all-zero flows for tokens without significant smart money activity. This is normal, not an error.
104
-
105
- ### Credit Management
106
- - `profiler labels` and `profiler balance` consume credits. Budget ~20 calls per session.
107
- - `Insufficient credits` (403, code `CREDITS_EXHAUSTED`) is a hard stop — no retry will help.
108
- - Check your Nansen dashboard for credit balance: [app.nansen.ai](https://app.nansen.ai).
109
- - Run balance checks in batches of 3-4 to avoid burning credits on rate-limit retries.
118
+ ## Troubleshooting
110
119
 
111
- ### Error Codes to Watch
112
- | Code | Meaning | Action |
113
- |------|---------|--------|
114
- | `UNSUPPORTED_FILTER` | Filter not available for this token/chain | Remove the filter and retry, or skip this token |
115
- | `CREDITS_EXHAUSTED` | API credits depleted | Stop all API calls. Check dashboard. |
116
- | `RATE_LIMITED` | Too many requests (429) | Wait and retry (automatic with default retry) |
120
+ See [AGENTS.md Troubleshooting](AGENTS.md#troubleshooting) for the full troubleshooting guide, including error codes, known endpoint quirks, and pagination gotchas.
117
121
 
118
122
  ## Examples
119
123
 
package/TODO.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  > **Built by agents, for agents.** We prioritize improvements that create the best possible AI agent experience.
4
4
 
5
+ ## P0 - Trading Agent Support
6
+
7
+ ### Wallet Management
8
+ - [ ] `nansen wallet create` — generate a local wallet (per chain), store key securely on disk
9
+ - [ ] `nansen wallet address` — print the wallet address for funding
10
+ - [ ] `nansen wallet balance` — check wallet balance
11
+
12
+ ### Trading Execution
13
+ - [x] `nansen quote` — get a quote for a DEX swap (chain, tokens, amount)
14
+ - [x] `nansen execute` — sign and submit a trade via Nansen API (takes quote-id)
15
+ - [ ] ⚠️ EVM transaction signing — requires thorough security review before production use
16
+
17
+ > These commands are required for `nansen-trading-agent` — the autonomous trading sub-agent.
18
+ > Wallet commands should keep the agent self-contained with zero external dependencies.
19
+
20
+ ---
21
+
5
22
  ## P2 - Nice to Have
6
23
 
7
24
  ### Test Coverage Gaps
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Command-line interface for Nansen API - designed for AI agents",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/api.js CHANGED
@@ -9,6 +9,10 @@ import { fileURLToPath } from 'url';
9
9
 
10
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
 
12
+ const { version: packageVersion } = JSON.parse(
13
+ fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
14
+ );
15
+
12
16
  // ============= Error Codes =============
13
17
 
14
18
  /**
@@ -296,36 +300,40 @@ export function validateTokenAddress(tokenAddress, chain = 'solana') {
296
300
  }
297
301
 
298
302
  function loadConfig() {
299
- // Priority: 1. Environment variables, 2. ~/.nansen/config.json, 3. Local config.json
300
-
301
- // Check environment variables first (highest priority)
302
- if (process.env.NANSEN_API_KEY) {
303
- return {
304
- apiKey: process.env.NANSEN_API_KEY,
305
- baseUrl: process.env.NANSEN_BASE_URL || 'https://api.nansen.ai'
306
- };
307
- }
308
-
309
- // Check ~/.nansen/config.json (from `nansen login`)
303
+ // Base config from files, then env vars override individual fields
304
+ let config = null;
305
+
306
+ // ~/.nansen/config.json (from `nansen login`)
310
307
  if (fs.existsSync(CONFIG_FILE)) {
311
- try {
312
- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
313
- } catch (e) {
314
- // Ignore parse errors, continue to next option
308
+ try { config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch (e) {}
309
+ }
310
+
311
+ // Local config.json (for development)
312
+ if (!config) {
313
+ const localConfig = path.join(__dirname, '..', 'config.json');
314
+ if (fs.existsSync(localConfig)) {
315
+ config = JSON.parse(fs.readFileSync(localConfig, 'utf8'));
315
316
  }
316
317
  }
317
-
318
- // Check local config.json (for development)
319
- const localConfig = path.join(__dirname, '..', 'config.json');
320
- if (fs.existsSync(localConfig)) {
321
- return JSON.parse(fs.readFileSync(localConfig, 'utf8'));
318
+
319
+ if (!config) {
320
+ config = { apiKey: null, baseUrl: 'https://api.nansen.ai' };
322
321
  }
323
-
324
- // No config found
325
- return {
326
- apiKey: null,
327
- baseUrl: 'https://api.nansen.ai'
328
- };
322
+
323
+ // Ensure baseUrl default (config file from older versions may omit it)
324
+ if (!config.baseUrl) {
325
+ config.baseUrl = 'https://api.nansen.ai';
326
+ }
327
+
328
+ // Env vars override individual fields
329
+ if (process.env.NANSEN_API_KEY) {
330
+ config.apiKey = process.env.NANSEN_API_KEY;
331
+ }
332
+ if (process.env.NANSEN_BASE_URL) {
333
+ config.baseUrl = process.env.NANSEN_BASE_URL;
334
+ }
335
+
336
+ return config;
329
337
  }
330
338
 
331
339
  const config = loadConfig();
@@ -388,10 +396,11 @@ export class NansenAPI {
388
396
  this.apiKey = apiKey || null;
389
397
  this.baseUrl = baseUrl;
390
398
  this.retryOptions = { ...DEFAULT_RETRY_OPTIONS, ...options.retry };
391
- this.cacheOptions = {
399
+ this.cacheOptions = {
392
400
  enabled: options.cache?.enabled ?? false,
393
401
  ttl: options.cache?.ttl ?? DEFAULT_CACHE_TTL
394
402
  };
403
+ this.defaultHeaders = options.defaultHeaders || {};
395
404
  }
396
405
 
397
406
  static cleanBody(body) {
@@ -428,7 +437,10 @@ export class NansenAPI {
428
437
  method: 'POST',
429
438
  headers: {
430
439
  'Content-Type': 'application/json',
440
+ 'X-Client-Type': 'nansen-cli',
441
+ 'X-Client-Version': packageVersion,
431
442
  ...(this.apiKey ? { 'apikey': this.apiKey } : {}),
443
+ ...this.defaultHeaders,
432
444
  ...options.headers
433
445
  },
434
446
  body: JSON.stringify(NansenAPI.cleanBody(body))
@@ -482,7 +494,41 @@ export class NansenAPI {
482
494
  } else if (code === ErrorCode.CREDITS_EXHAUSTED) {
483
495
  message = message.replace(/\.+$/, '') + '. No retry will help. Check your Nansen dashboard for credit balance.';
484
496
  } else if (code === ErrorCode.PAYMENT_REQUIRED) {
485
- message = 'Payment required (x402). This endpoint requires on-chain payment.';
497
+ // Try x402 auto-payment with fallback across payment networks
498
+ if (!this.defaultHeaders['Payment-Signature']) {
499
+ try {
500
+ const { createPaymentSignatures } = await import('./x402.js');
501
+ for await (const { signature, network } of createPaymentSignatures(response, url)) {
502
+ const paidResponse = await fetch(url, {
503
+ method: 'POST',
504
+ headers: {
505
+ 'Content-Type': 'application/json',
506
+ 'X-Client-Type': 'nansen-cli',
507
+ 'X-Client-Version': packageVersion,
508
+ 'Payment-Signature': signature,
509
+ ...this.defaultHeaders,
510
+ ...options.headers,
511
+ },
512
+ body: JSON.stringify(NansenAPI.cleanBody(body)),
513
+ });
514
+ if (paidResponse.ok) {
515
+ const chain = network.startsWith('solana:') ? 'Solana' : 'Base';
516
+ console.error(`[x402] Paid via ${chain} USDC`);
517
+ // Check remaining balance and warn if low
518
+ try {
519
+ const { checkX402Balance } = await import('./x402.js');
520
+ const balance = await checkX402Balance(network);
521
+ if (balance !== null && balance < 0.25) {
522
+ console.error(`[x402] Warning: USDC balance low ($${balance.toFixed(2)}). Fund your wallet to avoid interruptions.`);
523
+ }
524
+ } catch { /* balance check is best-effort */ }
525
+ return await paidResponse.json();
526
+ }
527
+ // This payment option was rejected, try next
528
+ }
529
+ } catch { /* x402 auto-pay unavailable, fall through */ }
530
+ }
531
+ message = 'Payment required. To access this endpoint:\n • Set an API key: nansen login --api-key <key> (get one at https://app.nansen.ai/api)\n • Or pay per call: nansen wallet create, fund with USDC on Base or Solana (from $0.01/call, min $0.05 balance)\n • Docs: https://docs.x402.org';
486
532
  const paymentHeader = response.headers.get('payment-required');
487
533
  if (paymentHeader) {
488
534
  try {
@@ -668,6 +714,20 @@ export class NansenAPI {
668
714
  });
669
715
  }
670
716
 
717
+ async generalSearch(params = {}) {
718
+ const { query, resultType = 'any', chain, limit = 25 } = params;
719
+ if (!query) {
720
+ throw new NansenError('Search query is required', ErrorCode.MISSING_PARAM);
721
+ }
722
+ const body = {
723
+ search_query: query,
724
+ result_type: resultType,
725
+ limit
726
+ };
727
+ if (chain) body.chain = chain;
728
+ return this.request('/api/v1/search/general', body);
729
+ }
730
+
671
731
  async addressHistoricalBalances(params = {}) {
672
732
  const { address, chain = 'ethereum', filters = {}, orderBy, pagination, days = 30 } = params;
673
733
  if (address) {
@@ -954,8 +1014,20 @@ export class NansenAPI {
954
1014
  });
955
1015
  }
956
1016
 
1017
+ async tokenIndicators(params = {}) {
1018
+ const { tokenAddress, chain = 'ethereum' } = params;
1019
+ if (tokenAddress) {
1020
+ const validation = validateTokenAddress(tokenAddress, chain);
1021
+ if (!validation.valid) throw new NansenError(validation.error, validation.code);
1022
+ }
1023
+ return this.request('/api/v1/tgm/indicators', {
1024
+ token_address: tokenAddress,
1025
+ chain
1026
+ });
1027
+ }
1028
+
957
1029
  async tokenInformation(params = {}) {
958
- const { tokenAddress, chain = 'solana', timeframe = '24h' } = params;
1030
+ const { tokenAddress, chain = 'solana', timeframe = '1d' } = params;
959
1031
  if (tokenAddress) {
960
1032
  const validation = validateTokenAddress(tokenAddress, chain);
961
1033
  if (!validation.valid) throw new NansenError(validation.error, validation.code);