nansen-cli 1.6.0 → 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 +222 -0
- package/README.md +19 -1
- package/SKILL.md +25 -21
- package/TODO.md +17 -0
- package/package.json +1 -1
- package/src/api.js +40 -1
- package/src/cli.js +23 -10
- package/src/crypto.js +215 -0
- package/src/trading.js +1081 -0
- package/src/transfer.js +723 -0
- package/src/wallet.js +764 -0
- package/src/x402-evm.js +207 -0
- package/src/x402-svm.js +474 -0
- package/src/x402.js +205 -0
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
|
-
|
|
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
|
|
@@ -192,6 +205,9 @@ nansen cache clear
|
|
|
192
205
|
|
|
193
206
|
This CLI is built specifically for AI agents. Every design decision prioritizes agent usability.
|
|
194
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
|
+
|
|
195
211
|
**Why agents love it:**
|
|
196
212
|
- **Structured Output**: All responses are JSON with consistent schema — no parsing HTML or unstructured text
|
|
197
213
|
- **Predictable Errors**: Errors include status codes and actionable details agents can handle programmatically
|
|
@@ -263,6 +279,8 @@ NANSEN_API_KEY=your-key npm run test:live
|
|
|
263
279
|
|
|
264
280
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines.
|
|
265
281
|
|
|
282
|
+
**AI agents:** See [AGENTS.md](AGENTS.md) for the agent quick-start (install, auth, patterns, troubleshooting).
|
|
283
|
+
|
|
266
284
|
**AI contributors:** See [CLAUDE.md](CLAUDE.md) for agent-specific guidance on contributing to this repo.
|
|
267
285
|
|
|
268
286
|
## API Coverage
|
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
|
|
22
|
-
|
|
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
|
-
#
|
|
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,
|
|
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
|
-
##
|
|
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
|
-
|
|
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
package/src/api.js
CHANGED
|
@@ -320,6 +320,11 @@ function loadConfig() {
|
|
|
320
320
|
config = { apiKey: null, baseUrl: 'https://api.nansen.ai' };
|
|
321
321
|
}
|
|
322
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
|
+
|
|
323
328
|
// Env vars override individual fields
|
|
324
329
|
if (process.env.NANSEN_API_KEY) {
|
|
325
330
|
config.apiKey = process.env.NANSEN_API_KEY;
|
|
@@ -489,7 +494,41 @@ export class NansenAPI {
|
|
|
489
494
|
} else if (code === ErrorCode.CREDITS_EXHAUSTED) {
|
|
490
495
|
message = message.replace(/\.+$/, '') + '. No retry will help. Check your Nansen dashboard for credit balance.';
|
|
491
496
|
} else if (code === ErrorCode.PAYMENT_REQUIRED) {
|
|
492
|
-
|
|
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';
|
|
493
532
|
const paymentHeader = response.headers.get('payment-required');
|
|
494
533
|
if (paymentHeader) {
|
|
495
534
|
try {
|
package/src/cli.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { NansenAPI, NansenError, ErrorCode, saveConfig, deleteConfig, getConfigFile, clearCache, getCacheDir, validateAddress, sleep } from './api.js';
|
|
7
|
+
import { buildWalletCommands } from './wallet.js';
|
|
8
|
+
import { buildTradingCommands } from './trading.js';
|
|
7
9
|
import fs from 'fs';
|
|
8
10
|
import { getUpdateNotification, scheduleUpdateCheck } from './update-check.js';
|
|
9
11
|
import { createRequire } from 'module';
|
|
@@ -839,6 +841,9 @@ COMMANDS:
|
|
|
839
841
|
login Save your API key (interactive)
|
|
840
842
|
logout Remove saved API key
|
|
841
843
|
schema Output JSON schema for all commands (for agent introspection)
|
|
844
|
+
wallet Local wallet management (create, list, show, export, default, delete)
|
|
845
|
+
quote Get a DEX swap quote (chain, tokens, amount)
|
|
846
|
+
execute Sign and broadcast a quoted trade
|
|
842
847
|
cache Cache management (clear)
|
|
843
848
|
smart-money Smart Money analytics (netflow, dex-trades, perp-trades, holdings, dcas, historical-holdings)
|
|
844
849
|
profiler Wallet profiling (balance, labels, transactions, pnl, pnl-summary, search,
|
|
@@ -968,10 +973,16 @@ export function buildCommands(deps = {}) {
|
|
|
968
973
|
|
|
969
974
|
return {
|
|
970
975
|
'login': async (args, apiInstance, flags, options) => {
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
976
|
+
// Support non-interactive: nansen login --api-key <key>
|
|
977
|
+
let apiKey = options['api-key'] || options.apiKey;
|
|
978
|
+
|
|
979
|
+
if (!apiKey) {
|
|
980
|
+
log('Nansen CLI Login\n');
|
|
981
|
+
log('Get your API key at: https://app.nansen.ai/api\n');
|
|
982
|
+
log('Tip: For non-interactive use: nansen login --api-key <key>\n');
|
|
983
|
+
|
|
984
|
+
apiKey = await promptFn('Enter your API key: ', true);
|
|
985
|
+
}
|
|
975
986
|
|
|
976
987
|
if (!apiKey || apiKey.trim().length === 0) {
|
|
977
988
|
log('\n❌ No API key provided');
|
|
@@ -1346,7 +1357,7 @@ export function buildCommands(deps = {}) {
|
|
|
1346
1357
|
}
|
|
1347
1358
|
|
|
1348
1359
|
// Commands that don't require API authentication
|
|
1349
|
-
export const NO_AUTH_COMMANDS = ['login', 'logout', 'help', 'schema', 'cache'];
|
|
1360
|
+
export const NO_AUTH_COMMANDS = ['login', 'logout', 'help', 'schema', 'cache', 'wallet', 'quote', 'execute'];
|
|
1350
1361
|
|
|
1351
1362
|
// Command aliases for convenience
|
|
1352
1363
|
export const COMMAND_ALIASES = {
|
|
@@ -1489,7 +1500,7 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1489
1500
|
scheduleUpdateCheck();
|
|
1490
1501
|
const notify = () => { if (updateNotification) errorOutput(updateNotification); };
|
|
1491
1502
|
|
|
1492
|
-
const commands = { ...buildCommands(deps), ...commandOverrides };
|
|
1503
|
+
const commands = { ...buildCommands(deps), ...buildWalletCommands(deps), ...buildTradingCommands(deps), ...commandOverrides };
|
|
1493
1504
|
|
|
1494
1505
|
if (flags.version || flags.v) {
|
|
1495
1506
|
output(VERSION);
|
|
@@ -1522,10 +1533,12 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1522
1533
|
return { type: 'command-help', command };
|
|
1523
1534
|
}
|
|
1524
1535
|
}
|
|
1525
|
-
//
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1536
|
+
// Commands with handlers (e.g. quote, execute) show their own usage
|
|
1537
|
+
if (command === 'help' || !commands[command]) {
|
|
1538
|
+
output(BANNER + HELP);
|
|
1539
|
+
notify();
|
|
1540
|
+
return { type: 'help' };
|
|
1541
|
+
}
|
|
1529
1542
|
}
|
|
1530
1543
|
|
|
1531
1544
|
if (!commands[command]) {
|