nansen-cli 1.7.0 → 1.8.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 CHANGED
@@ -1,222 +1,176 @@
1
- # AGENTS.md — Agent Quick Start
1
+ # AGENTS.md — Contributor Guide
2
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).
3
+ Guidance for AI coding agents (Claude Code, Codex, Copilot, etc.) working on this repository. If you're an agent **using** the CLI, see [README.md](README.md).
4
4
 
5
- ## CLI vs REST API
5
+ ## Architecture
6
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
7
  ```
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
8
+ src/
9
+ ├── index.js # Entry point (shebang, calls runCLI)
10
+ ├── cli.js # Command router, arg parsing, schema, help text
11
+ ├── api.js # NansenAPI client (REST, retry, cache, x402 auto-pay)
12
+ ├── wallet.js # Wallet CRUD (create/list/show/export/delete/send)
13
+ ├── trading.js # Quote + execute swaps (OKX router via API)
14
+ ├── transfer.js # Token/native transfers (EVM + Solana)
15
+ ├── x402.js # x402 payment orchestration (picks network, signs)
16
+ ├── x402-evm.js # EVM payment signing (EIP-3009 transferWithAuthorization)
17
+ ├── x402-svm.js # Solana payment signing (SPL transfer)
18
+ ├── crypto.js # Key encryption/decryption (AES-256-GCM or plaintext)
19
+ └── update-check.js # Version upgrade notice
34
20
  ```
35
21
 
36
- **Get an API key:** [app.nansen.ai/api](https://app.nansen.ai/api)
37
-
38
- ### Auth Priority
22
+ ### Command routing
39
23
 
40
- 1. `NANSEN_API_KEY` env var (highest)
41
- 2. `~/.nansen/config.json` file
42
- 3. Prompt (interactive only)
24
+ `src/index.js` `runCLI()` in `src/cli.js`
43
25
 
44
- ### Common Auth Pitfall
26
+ Commands are built by three functions, merged in `runCLI()`:
27
+ - `buildCommands()` in cli.js — analytics commands (smart-money, profiler, token, etc.)
28
+ - `buildWalletCommands()` in wallet.js — wallet subcommands
29
+ - `buildTradingCommands()` in trading.js — quote/execute
45
30
 
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.
31
+ Commands listed in `NO_AUTH_COMMANDS` skip API initialization. Everything else instantiates `NansenAPI` with retry, cache, and x402 config.
47
32
 
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":{...}}
33
+ ### Data flow: trade
54
34
 
55
- # This burns a credit but proves API access:
56
- nansen token screener --chain solana --limit 1
57
35
  ```
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
36
+ CLI args → api.js GET /defi/quote → quote response
37
+ wallet.js decrypt key → trading.js sign tx → api.js POST /defi/execute → broadcast
69
38
  ```
70
39
 
71
- ### 2. Use Schema for Self-Discovery
40
+ ### Data flow: x402 auto-pay
72
41
 
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
42
+ ```
43
+ api.js (any call) 402 response with payment requirements
44
+ x402.js rankRequirements() picks cheapest network (EVM first)
45
+ x402-evm.js or x402-svm.js sign USDC payment
46
+ → api.js retries original request with Payment-Signature header
77
47
  ```
78
48
 
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. |
49
+ If EVM payment fails (insufficient funds), the async generator yields a Solana signature as fallback.
90
50
 
91
- ### 4. Budget Credits
51
+ ### Output convention
92
52
 
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
53
+ Core functions return data objects. The CLI layer formats via `formatOutput()`. Never `console.log` in core functions — use the `log` dependency injection for CLI output.
97
54
 
98
- ### 5. Use `--stream` for Large Results
55
+ ## Development
99
56
 
100
57
  ```bash
101
- # NDJSON mode — process line by line, don't buffer giant arrays
102
- nansen token dex-trades --chain solana --limit 100 --stream
58
+ npm install # Install dependencies
59
+ npm test # Run tests (vitest)
60
+ npm run test:watch # Watch mode
61
+ npm run test:coverage # With coverage
103
62
  ```
104
63
 
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.
64
+ ### Running locally
108
65
 
109
66
  ```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
- ```
67
+ node src/index.js <command> [options]
118
68
 
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
69
+ # Examples
70
+ node src/index.js wallet create my-wallet
71
+ node src/index.js smart-money --chain solana --limit 5
127
72
  ```
128
73
 
129
- **`profiler perp-positions` has no pagination** — the API ignores the pagination parameter for this endpoint.
74
+ ## Testing
130
75
 
131
- ## Output Parsing Gotchas
76
+ - **Framework:** Vitest
77
+ - **Test files:** `src/__tests__/*.test.js`
78
+ - **Current:** 577 tests across 13 test files
79
+ - **All new code must have tests**
80
+ - **Mock all RPC/API calls** — never hit real networks in tests
132
81
 
133
- ### Response envelope
82
+ ### Test structure
134
83
 
135
- Every CLI response is wrapped in a standard envelope:
84
+ ```js
85
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
136
86
 
137
- ```json
138
- { "success": true, "data": <raw_api_response> }
139
- ```
87
+ global.fetch = vi.fn();
140
88
 
141
- Errors follow a different shape:
89
+ describe('featureName', () => {
90
+ beforeEach(() => {
91
+ fetch.mockReset();
92
+ });
142
93
 
143
- ```json
144
- { "success": false, "error": "message", "code": "ERROR_CODE", "status": 401, "details": {...} }
94
+ it('should do the thing', async () => {
95
+ fetch.mockResolvedValueOnce({
96
+ ok: true,
97
+ json: async () => ({ jsonrpc: '2.0', result: '0x...', id: 1 })
98
+ });
99
+ // test logic
100
+ });
101
+ });
145
102
  ```
146
103
 
147
- ### Raw API response shapes vary by endpoint
104
+ ### Required RPC mocks by code path
148
105
 
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:
106
+ **EVM transfers:** `eth_getBalance`, `eth_gasPrice`, `eth_maxPriorityFeePerGas`, `eth_getTransactionCount`, `eth_estimateGas`, `eth_getCode`, `eth_sendRawTransaction`, `eth_getTransactionReceipt`
150
107
 
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
- ```
108
+ **Solana transfers:** `getBalance`, `getLatestBlockhash`, `sendTransaction`, `getSignatureStatuses`
166
109
 
167
- ### `--fields` applies to the entire response tree
110
+ **SPL token transfers** (additionally): `getTokenAccountsByOwner`, `getAccountInfo`
168
111
 
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.
112
+ **Wallet operations:** No RPC mocks needed (file I/O only). Mock `fs` if testing file paths.
170
113
 
171
- ### Client-side vs server-side filtering
114
+ **API calls:** Mock `fetch` to return `{ ok: true, json: () => ({...}) }` or `{ ok: false, status: 402, headers: new Headers({...}) }` for x402 paths.
172
115
 
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`.
116
+ ## Style Guide
174
117
 
175
- ### Fields absent for some chain/token combinations
118
+ - **ESM only** (`import`/`export`). No TypeScript, no transpilation.
119
+ - **No interactive prompts in core functions.** Use env vars: `NANSEN_WALLET_PASSWORD`, `NANSEN_API_KEY`.
120
+ - **Error handling:** `throw new Error('descriptive message')` in core. CLI catches and formats.
121
+ - **Actionable error messages** — tell the user what to do:
122
+ - ❌ `"Authentication failed"`
123
+ - ✅ `"Not logged in. Run: nansen login"`
124
+ - **BigInt for token amounts.** Never use floating point. Parse to BigInt with decimals.
125
+ - **Chain branching:** Use `chain === 'solana'` checks, not inheritance/polymorphism.
126
+ - **Minimal dependencies.** Prefer Node.js built-in APIs (crypto, fs, path, http).
176
127
 
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
128
+ ## PR Checklist
181
129
 
182
- ## Chains Quick Reference
130
+ - [ ] `npm test` passes (all tests)
131
+ - [ ] New code paths have test coverage
132
+ - [ ] No hardcoded secrets, API keys, or private keys
133
+ - [ ] No `console.log` in core functions (use `log` dep injection)
134
+ - [ ] Error messages are actionable (tell user what to do)
135
+ - [ ] CLI help text updated if adding/changing commands
136
+ - [ ] RPC mocks cover all methods in the code path
137
+ - [ ] Wallet flows work both with and without `NANSEN_WALLET_PASSWORD`
138
+ - [ ] Changeset added if changing user-facing behavior (add a `.changeset/<name>.md` file — `npm test` will warn if missing)
183
139
 
184
- `ethereum` `solana` `base` `bnb` `arbitrum` `polygon` `optimism` `avalanche` `linea` `scroll` `mantle` `ronin` `sei` `plasma` `sonic` `monad` `hyperevm` `iotaevm`
140
+ ## Chains & Networks
185
141
 
186
- > Run `nansen schema` to get the current chain list (source of truth).
142
+ **EVM:** Ethereum (chain ID 1), Base (8453). `CHAIN_IDS` in transfer.js only maps these two other EVM chains will fail for transfers.
187
143
 
188
- ## Troubleshooting
144
+ **Solana:** mainnet-beta. Supports native SOL, standard SPL tokens, and Token-2022 (Token Extensions).
189
145
 
190
- ### Quick fixes
146
+ **RPC endpoints:** Hardcoded in `CHAIN_RPCS` (transfer.js). Nansen API handles RPC for trading.
191
147
 
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. |
148
+ ## Key Constants
202
149
 
203
- ### Error codes
150
+ | Constant | Value |
151
+ |----------|-------|
152
+ | USDC (Base) | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
153
+ | USDC (Solana) | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` |
154
+ | x402 payment | $0.05 USDC per API call |
155
+ | Gas buffer | API provides `quote.gas` with 1.5x multiplier — use directly |
204
156
 
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). |
157
+ ## Endpoint Quirks
212
158
 
213
- ### Known endpoint quirks
159
+ These are internal details agents should know when writing or debugging tests:
214
160
 
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.
161
+ - **`token holders --smart-money`** — Returns `UNSUPPORTED_FILTER` for tokens without smart money tracking. Not all tokens have this data.
162
+ - **`token flow-intelligence`** — May return all-zero flows for illiquid tokens. Normal, not an error.
163
+ - **`token screener --search`** Client-side filtering. The CLI fetches up to 500 results, then filters locally.
164
+ - **`--fields`** — Applies to the entire response tree, including the `success`/`data` wrapper.
165
+ - **Profiler beta endpoints** use `recordsPerPage` instead of `per_page`. The CLI handles this automatically.
218
166
  - **`profiler perp-positions`** — No pagination support; the API ignores the pagination parameter.
219
167
 
220
- ## For OpenClaw / Skill Users
168
+ ## Known Gotchas
221
169
 
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.
170
+ 1. **EIP-7702 delegated accounts** on Base have contract code. Always use `eth_estimateGas`, never hardcode 21000 gas.
171
+ 2. **Solana SPL account ordering:** Writable accounts (destATA) must precede readonly (mint) in the transaction message.
172
+ 3. **`getSignatureStatuses`** over `confirmTransaction` — the latter is deprecated and unreliable on public RPCs.
173
+ 4. **`--max` native SOL:** Reserve 5000 lamports for fee. On EVM L2s, reserve 3x estimated gas for L1 data posting fees.
174
+ 5. **Token-2022:** Use `TOKEN_2022_PROGRAM_ID` and `TransferCheckedInstruction` (not plain `Transfer`).
175
+ 6. **CreateATA path:** When recipient doesn't have a token account, the sender creates it. This path in transfer.js has limited test coverage — add tests if modifying.
176
+ 7. **`CHAIN_IDS` is incomplete:** Only ethereum and base are mapped. Adding new EVM chain support requires updating this map.
package/CLAUDE.md CHANGED
@@ -125,7 +125,7 @@ Returns JSON with all commands, subcommands, option types/defaults, return field
125
125
 
126
126
  ### Field Filtering
127
127
  ```bash
128
- nansen smart-money netflow --fields token_symbol,net_flow_usd,chain
128
+ nansen research smart-money netflow --fields token_symbol,net_flow_usd,chain
129
129
  ```
130
130
  Reduces response size by including only specified fields. Works with nested data structures.
131
131
 
@@ -180,41 +180,38 @@ Structured error codes for programmatic handling:
180
180
 
181
181
  ## Publishing (npm)
182
182
 
183
- **⚠️ DO NOT manually run `npm version` or `npm publish`. CI handles everything.**
183
+ **DO NOT manually run `npm version` or `npm publish`. CI handles everything.**
184
184
 
185
- ### How it works:
185
+ 1. **Push to main** — CI runs tests
186
+ 2. **CI creates a "Version Packages" PR** — bumps version + updates CHANGELOG
187
+ 3. **Merge the Version PR** — CI auto-publishes to npm
186
188
 
187
- 1. **Add a changeset** for user-facing changes:
188
- ```bash
189
- npx changeset
190
- # Or manually create .changeset/<name>.md
191
- ```
189
+ If you mess up: accidentally bumped version manually? `git revert` and add a changeset instead. CI publish failed? Check GitHub Actions logs, likely needs `NPM_TOKEN` secret refresh.
192
190
 
193
- 2. **Push to main** — CI runs tests
191
+ ## Changesets
194
192
 
195
- 3. **CI creates a "Version Packages" PR** This bumps version + updates CHANGELOG
193
+ Every PR that changes user-facing behavior **must** include a changeset file. `npm test` will warn if one is missing. The changeset description ends up in CHANGELOG.md (auto-generated by CI), so write it as a user-facing changelog entry.
196
194
 
197
- 4. **Merge the Version PR** CI auto-publishes to npm
195
+ **Needs a changeset:** new features, bug fixes, breaking changes, changed CLI output, new/modified commands.
196
+
197
+ **Does NOT need a changeset:** docs-only, test-only, refactors with no behavior change, CI/tooling.
198
+
199
+ Add a file to `.changeset/` with a descriptive kebab-case name:
198
200
 
199
- ### Changeset format:
200
201
  ```markdown
201
202
  ---
202
203
  "nansen-cli": minor
203
204
  ---
204
205
 
205
- Description of changes (appears in CHANGELOG)
206
+ Short description of the change (appears in CHANGELOG)
206
207
  ```
207
208
 
208
- Choose: `patch` (bug fixes), `minor` (new features), `major` (breaking changes)
209
-
210
- ### If you mess up:
211
- - Accidentally bumped version manually? `git revert` and add a changeset instead
212
- - CI publish failed? Check GitHub Actions logs, likely needs `NPM_TOKEN` secret refresh
209
+ Use `patch` for bug fixes, `minor` for new features, `major` for breaking changes.
213
210
 
214
211
  ## PR Checklist
215
212
 
216
213
  - [ ] Tests pass (`npm test`)
217
214
  - [ ] New endpoints have tests in all 3 test files
218
215
  - [ ] README.md updated if adding user-facing features
219
- - [ ] CHANGELOG.md updated for releases
216
+ - [ ] Changeset added for user-facing changes (see above)
220
217
  - [ ] No new dependencies (keep it lightweight)