nansen-cli 1.38.0 → 1.40.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/CHANGELOG.md +35 -0
- package/README.md +56 -1
- package/package.json +1 -1
- package/skills/nansen-wallet-batch/SKILL.md +1 -1
- package/skills/nansen-wallet-profiler/SKILL.md +1 -1
- package/src/api.js +4 -3
- package/src/cli.js +8 -3
- package/src/limit-order.js +30 -7
- package/src/response-meta.js +2 -2
- package/src/rpc-urls.js +67 -0
- package/src/schema.json +20 -3
- package/src/swap-simulation.js +477 -0
- package/src/trade-validation.js +237 -6
- package/src/trading.js +566 -70
- package/src/transfer.js +25 -3
- package/src/walletconnect-trading.js +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.40.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#509](https://github.com/nansen-ai/nansen-cli/pull/509) [`430c300`](https://github.com/nansen-ai/nansen-cli/commit/430c3003d28a44bd1bfb123eef9290a7350a7e1e) Thanks [@kome12](https://github.com/kome12)! - `trade execute` now revokes an existing on-chain ERC-20 allowance before
|
|
8
|
+
re-approving when it is more than 10x the current trade's scoped amount, such
|
|
9
|
+
as a legacy unlimited approval or an allowance granted by another app. Most
|
|
10
|
+
trades are unaffected. Opt out with `--no-revoke-excessive-allowance`.
|
|
11
|
+
|
|
12
|
+
After each revoke or reapproval, the CLI reads the resulting allowance back
|
|
13
|
+
on-chain and fails closed (instead of proceeding to the swap) if it doesn't
|
|
14
|
+
match what was expected or can't be read.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [#498](https://github.com/nansen-ai/nansen-cli/pull/498) [`a964dd1`](https://github.com/nansen-ai/nansen-cli/commit/a964dd19c826f5231d2651547212d8169a74e7fe) Thanks [@crazywriter1](https://github.com/crazywriter1)! - Use `pending` nonce block tag for EVM sends: back-to-back transfers no longer risk reusing the same nonce when mempool transactions are queued.
|
|
19
|
+
|
|
20
|
+
- [#493](https://github.com/nansen-ai/nansen-cli/pull/493) [`bc89fef`](https://github.com/nansen-ai/nansen-cli/commit/bc89fef74489da3df32a12079effbdfa899373fd) Thanks [@crazywriter1](https://github.com/crazywriter1)! - Validate `--slippage-bps` on `limit-order create`: values outside 0-10000 now fail with a clear error before any auth/API call.
|
|
21
|
+
|
|
22
|
+
## 1.39.0
|
|
23
|
+
|
|
24
|
+
### Minor Changes
|
|
25
|
+
|
|
26
|
+
- [#495](https://github.com/nansen-ai/nansen-cli/pull/495) [`3306897`](https://github.com/nansen-ai/nansen-cli/commit/3306897c1aaae594f4401fd4656b2451ab375d78) Thanks [@kome12](https://github.com/kome12)! - Add EVM swap-outcome verification to `trade execute`. Before broadcasting a swap on an EVM chain (Base), the CLI now simulates the transaction and confirms the wallet's balance changes match the quote — the input is spent within your maximum, at least the expected output is received, and no other token or NFT leaves the wallet — refusing to sign when they don't. This runs on top of the existing pre-broadcast checks and needs a simulation-capable RPC (`NANSEN_BASE_SIM_RPC`); when none is available it degrades with a warning rather than blocking the trade. Skip it with `--no-verify-outcome`. Solana is unaffected.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- [#495](https://github.com/nansen-ai/nansen-cli/pull/495) [`e8cf217`](https://github.com/nansen-ai/nansen-cli/commit/e8cf217feaa9e7c8f68f4f3c3c2a49adcda07101) Thanks [@kome12](https://github.com/kome12)! - Harden swap-outcome verification error handling: a revert reported by the simulation endpoint as a top-level JSON-RPC error (rather than a per-call status) now fails closed (blocks the swap) instead of degrading, and a non-2xx simulation response (e.g. HTTP 401 "Invalid API key") now degrades with the real status and message instead of a misleading "returned no call result" warning.
|
|
31
|
+
|
|
32
|
+
- [#499](https://github.com/nansen-ai/nansen-cli/pull/499) [`de0bcc5`](https://github.com/nansen-ai/nansen-cli/commit/de0bcc562bcd20a80edd3ab2f486870b80629c83) Thanks [@gulshngill](https://github.com/gulshngill)! - Fix `profiler labels`: call `/api/v1/profiler/address/labels` with its v1 request body — the beta endpoint previously used was removed from the Nansen API. `profiler batch --include labels` now returns the label array itself instead of the raw `{pagination, data}` envelope.
|
|
33
|
+
|
|
34
|
+
- [#506](https://github.com/nansen-ai/nansen-cli/pull/506) [`f407edb`](https://github.com/nansen-ai/nansen-cli/commit/f407edb19444d6b5a1a631d29b4c4fb9bd280708) Thanks [@gulshngill](https://github.com/gulshngill)! - Add a canonical MCP setup section to the README — endpoint `https://mcp.nansen.ai/ra/mcp`, `NANSEN-API-KEY` auth, per-client setup paths for Claude Code, Claude Tag, and generic or stdio-only clients, plus a pointer to the connection docs for Claude Desktop and Cursor — and point the out-of-credits and low-credit warnings at the credits tab of the billing page, `app.nansen.ai/api?tab=api`, instead of the bare `app.nansen.ai/api`.
|
|
35
|
+
|
|
36
|
+
- [#500](https://github.com/nansen-ai/nansen-cli/pull/500) [`9ccf8a2`](https://github.com/nansen-ai/nansen-cli/commit/9ccf8a20841a9ca01a2627ccf5de2575bf016a46) Thanks [@gulshngill](https://github.com/gulshngill)! - Document global pagination options in `nansen schema`.
|
|
37
|
+
|
|
3
38
|
## 1.38.0
|
|
4
39
|
|
|
5
40
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -51,6 +51,61 @@ nansen schema [command] [--pretty] # full command reference (no API key neede
|
|
|
51
51
|
|
|
52
52
|
Run `nansen schema --pretty` for the full subcommand and field reference.
|
|
53
53
|
|
|
54
|
+
## MCP
|
|
55
|
+
|
|
56
|
+
Connect any MCP client to Nansen's streamable HTTP server:
|
|
57
|
+
|
|
58
|
+
- **Endpoint:** `https://mcp.nansen.ai/ra/mcp`
|
|
59
|
+
- **Authentication:** `NANSEN-API-KEY` header
|
|
60
|
+
- **API key:** [app.nansen.ai/auth/agent-setup](https://app.nansen.ai/auth/agent-setup)
|
|
61
|
+
|
|
62
|
+
**Claude Desktop and Cursor:** setup instructions for both — the Claude Desktop `.dxt` bundle and the Cursor install deep link — are in the connection docs: [docs.nansen.ai/mcp/connecting](https://docs.nansen.ai/mcp/connecting).
|
|
63
|
+
|
|
64
|
+
**One-command (Claude Code):**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
claude mcp add --transport http nansen https://mcp.nansen.ai/ra/mcp --header "NANSEN-API-KEY: <your-key>"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Manual (any streamable-HTTP client):** for example, add this to Cursor's `~/.cursor/mcp.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"nansen": {
|
|
76
|
+
"url": "https://mcp.nansen.ai/ra/mcp",
|
|
77
|
+
"headers": {
|
|
78
|
+
"NANSEN-API-KEY": "<your-key>"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Manual (stdio-only clients):** use `mcp-remote` as a bridge. Keep the header as one argument with no space after the colon:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"nansen": {
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": [
|
|
93
|
+
"-y",
|
|
94
|
+
"mcp-remote@latest",
|
|
95
|
+
"https://mcp.nansen.ai/ra/mcp",
|
|
96
|
+
"--header",
|
|
97
|
+
"NANSEN-API-KEY:${NANSEN_API_KEY}"
|
|
98
|
+
],
|
|
99
|
+
"env": {
|
|
100
|
+
"NANSEN_API_KEY": "<your-key>"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Claude Tag (Claude in Slack):** an admin must attach a plugin whose `.mcp.json` points at `https://mcp.nansen.ai/ra/mcp` and add a custom credential allowing the host `mcp.nansen.ai`. See the [Claude Tag custom-connections documentation](https://claude.com/docs/claude-tag/admins/connections/custom). Per-user fallback: use Claude Code or Claude Desktop.
|
|
108
|
+
|
|
54
109
|
## Trading
|
|
55
110
|
|
|
56
111
|
DEX swaps on `solana` and `base`. Two-step: quote then execute.
|
|
@@ -216,7 +271,7 @@ nansen research smart-money netflow --chain solana --fields token_symbol,net_flo
|
|
|
216
271
|
|
|
217
272
|
| Code | Action |
|
|
218
273
|
|------|--------|
|
|
219
|
-
| `CREDITS_EXHAUSTED` | Stop all API calls immediately. `details.credits.remaining` is your actual balance. Top up at [app.nansen.ai/api](https://app.nansen.ai/api). |
|
|
274
|
+
| `CREDITS_EXHAUSTED` | Stop all API calls immediately. `details.credits.remaining` is your actual balance. Top up at [app.nansen.ai/api?tab=api](https://app.nansen.ai/api?tab=api). |
|
|
220
275
|
| `UNAUTHORIZED` | Wrong or missing key. Re-auth. |
|
|
221
276
|
| `RATE_LIMITED` | Auto-retried by CLI. `details.rateLimit.resetSeconds` is how long the window needs to drain. |
|
|
222
277
|
| `UNSUPPORTED_FILTER` | Remove the filter and retry. |
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ allowed-tools: Bash(nansen:*)
|
|
|
19
19
|
ADDRESSES="0xaddr1,0xaddr2,0xaddr3,..." CHAIN=ethereum
|
|
20
20
|
nansen research profiler batch --addresses "$ADDRESSES" --chain $CHAIN --include labels,balance
|
|
21
21
|
# → .data.{total, completed, results[]: {address, chain, labels[], balance, error}}
|
|
22
|
-
# labels[]: {label, category ("smart_money","fund","social","behavioral","others"),
|
|
22
|
+
# labels[]: {label, category ("smart_money","fund","social","behavioral","others"), kind[]}
|
|
23
23
|
# balance: {data[]: {token_symbol, token_amount, price_usd, value_usd}}
|
|
24
24
|
```
|
|
25
25
|
Check .error per result — invalid addresses return an error message, not a crash. Skip those.
|
|
@@ -92,7 +92,7 @@ nansen research profiler compare --addresses "0xabc,0xdef" --chain ethereum
|
|
|
92
92
|
|
|
93
93
|
- `pnl-summary` has no pagination support (returns aggregate stats, not a list).
|
|
94
94
|
- `perp-positions` has no pagination support.
|
|
95
|
-
- `labels`
|
|
95
|
+
- `labels` supports pagination — `--limit`/`--page` are honoured and the response is `{pagination: {page, per_page, is_last_page}, data[]: {label, category, kind[]}}`.
|
|
96
96
|
- `transactions` caps at per_page=100 (API limit).
|
|
97
97
|
- `trace` makes many API calls — use `--width` conservatively.
|
|
98
98
|
- `batch` accepts `--file <path>` with one address per line as alternative to `--addresses`.
|
package/src/api.js
CHANGED
|
@@ -393,7 +393,7 @@ function requireValidToken(tokenAddress, chain) {
|
|
|
393
393
|
if (!v.valid) throw new NansenError(v.error, v.code);
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
function loadConfig() {
|
|
396
|
+
export function loadConfig() {
|
|
397
397
|
// Base config from files, then env vars override individual fields
|
|
398
398
|
let config = null;
|
|
399
399
|
|
|
@@ -908,8 +908,9 @@ export class NansenAPI {
|
|
|
908
908
|
async addressLabels(params = {}) {
|
|
909
909
|
const { address, chain = 'ethereum', pagination = { page: 1, per_page: 100 } } = params;
|
|
910
910
|
if (address) requireValidAddress(address, chain);
|
|
911
|
-
return this.request('/api/
|
|
912
|
-
|
|
911
|
+
return this.request('/api/v1/profiler/address/labels', {
|
|
912
|
+
address,
|
|
913
|
+
chain,
|
|
913
914
|
pagination
|
|
914
915
|
});
|
|
915
916
|
}
|
package/src/cli.js
CHANGED
|
@@ -190,7 +190,7 @@ export function parseArgs(args) {
|
|
|
190
190
|
const key = arg.slice(2);
|
|
191
191
|
const next = args[i + 1];
|
|
192
192
|
|
|
193
|
-
if (key === 'pretty' || key === 'help' || key === 'version' || key === 'table' || key === 'no-retry' || key === 'cache' || key === 'no-cache' || key === 'stream' || key === 'enrich' || key === 'full' || key === 'human' || key === 'enabled' || key === 'disabled' || key === 'expert' || key === 'json' || key === 'offline') {
|
|
193
|
+
if (key === 'pretty' || key === 'help' || key === 'version' || key === 'table' || key === 'no-retry' || key === 'cache' || key === 'no-cache' || key === 'stream' || key === 'enrich' || key === 'full' || key === 'human' || key === 'enabled' || key === 'disabled' || key === 'expert' || key === 'json' || key === 'offline' || key === 'no-simulate' || key === 'no-verify-outcome' || key === 'no-revoke-excessive-allowance') {
|
|
194
194
|
result.flags[key] = true;
|
|
195
195
|
} else if (next && (!next.startsWith('-') || /^-\d/.test(next))) {
|
|
196
196
|
// Try to parse as JSON first (for objects/arrays/booleans),
|
|
@@ -489,7 +489,9 @@ async function enrichTransfers(result, apiInstance, chain) {
|
|
|
489
489
|
for (const addr of addrs) {
|
|
490
490
|
try {
|
|
491
491
|
const labelsResult = await apiInstance.addressLabels({ address: addr, chain });
|
|
492
|
-
labelMap[addr] = labelsResult?.
|
|
492
|
+
labelMap[addr] = Array.isArray(labelsResult?.data)
|
|
493
|
+
? labelsResult.data.map(item => item.label)
|
|
494
|
+
: labelsResult?.labels || [];
|
|
493
495
|
} catch {
|
|
494
496
|
labelMap[addr] = [];
|
|
495
497
|
}
|
|
@@ -568,7 +570,10 @@ export async function batchProfile(api, params = {}) {
|
|
|
568
570
|
}
|
|
569
571
|
try {
|
|
570
572
|
if (include.includes('labels')) {
|
|
571
|
-
|
|
573
|
+
const labelsResult = await api.addressLabels({ address, chain });
|
|
574
|
+
entry.labels = Array.isArray(labelsResult?.data)
|
|
575
|
+
? labelsResult.data
|
|
576
|
+
: labelsResult?.labels || [];
|
|
572
577
|
}
|
|
573
578
|
if (include.includes('balance')) {
|
|
574
579
|
entry.balance = await api.addressBalance({ address, chain });
|
package/src/limit-order.js
CHANGED
|
@@ -401,6 +401,17 @@ export function parseExpiry(expiryStr) {
|
|
|
401
401
|
throw new Error(`Invalid expiry format: "${expiryStr}". Use "24h", "7d", "30d", or epoch ms.`);
|
|
402
402
|
}
|
|
403
403
|
|
|
404
|
+
// Whole integer bps in [0, 10000], matching bridge parseSlippageBps.
|
|
405
|
+
// Number() would accept "1.5", "1e2", "0x10", and boolean true.
|
|
406
|
+
function parseSlippageBps(raw) {
|
|
407
|
+
const s = String(raw).trim();
|
|
408
|
+
const bad = 'Error: --slippage-bps must be a whole integer between 0 and 10000 basis points.';
|
|
409
|
+
if (!/^\d+$/.test(s)) throw new Error(bad);
|
|
410
|
+
const n = parseInt(s, 10);
|
|
411
|
+
if (!Number.isInteger(n) || n < 0 || n > 10000) throw new Error(bad);
|
|
412
|
+
return n;
|
|
413
|
+
}
|
|
414
|
+
|
|
404
415
|
// ============= Order Formatting =============
|
|
405
416
|
|
|
406
417
|
function formatOrderStatus(status) {
|
|
@@ -501,7 +512,7 @@ export function buildLimitOrderCommands(deps = {}) {
|
|
|
501
512
|
const triggerPrice = options['trigger-price'];
|
|
502
513
|
const triggerCondition = options['trigger-condition'];
|
|
503
514
|
const triggerMintRaw = options['trigger-mint'];
|
|
504
|
-
const
|
|
515
|
+
const slippageBpsRaw = options['slippage-bps'];
|
|
505
516
|
const expiresStr = options.expires || '30d';
|
|
506
517
|
const walletName = options.wallet;
|
|
507
518
|
|
|
@@ -516,7 +527,7 @@ OPTIONS:
|
|
|
516
527
|
--trigger-mint <symbol|addr> Token whose price triggers the order (e.g. SOL)
|
|
517
528
|
--trigger-condition <cond> "above" or "below"
|
|
518
529
|
--trigger-price <usd> Trigger price in USD (must be a positive number)
|
|
519
|
-
--slippage-bps <bps>
|
|
530
|
+
--slippage-bps <bps> Whole integer bps, 0-10000 (100 = 1%), omit for auto
|
|
520
531
|
--expires <duration> Expiry duration: "24h", "7d", "30d" (default: 30d)
|
|
521
532
|
--wallet <name> Wallet name (or "walletconnect"/"wc")
|
|
522
533
|
|
|
@@ -581,6 +592,18 @@ EXAMPLES:
|
|
|
581
592
|
return;
|
|
582
593
|
}
|
|
583
594
|
|
|
595
|
+
// Same bounds as update / bridge: whole-integer bps in 0–10000.
|
|
596
|
+
let slippageBps;
|
|
597
|
+
if (slippageBpsRaw != null) {
|
|
598
|
+
try {
|
|
599
|
+
slippageBps = parseSlippageBps(slippageBpsRaw);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
log(err.message);
|
|
602
|
+
exit(1);
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
584
607
|
let expiresAt;
|
|
585
608
|
try {
|
|
586
609
|
expiresAt = parseExpiry(expiresStr);
|
|
@@ -820,7 +843,7 @@ Usage: nansen trade limit-order update --order <orderId> [--trigger-price <usd>]
|
|
|
820
843
|
OPTIONS:
|
|
821
844
|
--order <id> Order ID to update
|
|
822
845
|
--trigger-price <usd> New trigger price in USD
|
|
823
|
-
--slippage-bps <bps>
|
|
846
|
+
--slippage-bps <bps> Whole integer bps, 0-10000 (100 = 1%)
|
|
824
847
|
--wallet <name> Wallet name (or "walletconnect"/"wc")
|
|
825
848
|
|
|
826
849
|
NOTE: Only provided fields are updated. Auto slippage can only be set at creation time
|
|
@@ -850,13 +873,13 @@ EXAMPLES:
|
|
|
850
873
|
updateBody.triggerPriceUsd = price;
|
|
851
874
|
}
|
|
852
875
|
if (slippageBps != null) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
876
|
+
try {
|
|
877
|
+
updateBody.slippageBps = parseSlippageBps(slippageBps);
|
|
878
|
+
} catch (err) {
|
|
879
|
+
log(err.message);
|
|
856
880
|
exit(1);
|
|
857
881
|
return;
|
|
858
882
|
}
|
|
859
|
-
updateBody.slippageBps = bps;
|
|
860
883
|
}
|
|
861
884
|
|
|
862
885
|
try {
|
package/src/response-meta.js
CHANGED
|
@@ -118,12 +118,12 @@ export function creditWarning(meta) {
|
|
|
118
118
|
const { used, remaining, cost } = credits;
|
|
119
119
|
if (remaining === null) return null;
|
|
120
120
|
if (remaining === 0) {
|
|
121
|
-
return '⚠️ Out of API credits. Top up at https://app.nansen.ai/api';
|
|
121
|
+
return '⚠️ Out of API credits. Top up at https://app.nansen.ai/api?tab=api';
|
|
122
122
|
}
|
|
123
123
|
// The cost header is the authoritative charge; used is the fallback.
|
|
124
124
|
const charged = cost ?? used;
|
|
125
125
|
if (charged !== null && charged > 0 && remaining < charged) {
|
|
126
|
-
return `⚠️ ${remaining} API credit${remaining === 1 ? '' : 's'} left — less than this call cost (${charged}). Top up at https://app.nansen.ai/api`;
|
|
126
|
+
return `⚠️ ${remaining} API credit${remaining === 1 ? '' : 's'} left — less than this call cost (${charged}). Top up at https://app.nansen.ai/api?tab=api`;
|
|
127
127
|
}
|
|
128
128
|
return null;
|
|
129
129
|
}
|
package/src/rpc-urls.js
CHANGED
|
@@ -11,6 +11,23 @@
|
|
|
11
11
|
* NANSEN_BSC_RPC Custom BNB Smart Chain RPC
|
|
12
12
|
* NANSEN_XLAYER_RPC Custom X Layer RPC
|
|
13
13
|
* NANSEN_SOLANA_RPC Custom Solana RPC
|
|
14
|
+
* NANSEN_BASE_SIM_RPC Custom Base simulation RPC (see SIMULATION_RPCS below)
|
|
15
|
+
*
|
|
16
|
+
* Simulation RPCs (SIMULATION_RPCS) are a SEPARATE registry from the cheap
|
|
17
|
+
* defaults above. Swap-outcome verification (src/swap-simulation.js) needs an
|
|
18
|
+
* endpoint that supports state-changing simulation with asset-transfer tracing
|
|
19
|
+
* (`eth_simulateV1` / `debug_traceCall`), which the free public defaults in
|
|
20
|
+
* CHAIN_RPCS deliberately DISABLE. Keeping the two registries apart means only
|
|
21
|
+
* the (pricey) simulation calls hit the trace-capable endpoint; ordinary reads
|
|
22
|
+
* (nonce, balance, allowance, eth_call revert check) stay on the cheap default.
|
|
23
|
+
*
|
|
24
|
+
* The shipped simulation endpoint is a Nansen-hosted service authenticated with
|
|
25
|
+
* the user's existing Nansen API key (no secret in this public package): the
|
|
26
|
+
* trace-capable upstream is reached server-side, so the baked default carries no
|
|
27
|
+
* credential. With no NANSEN_BASE_SIM_RPC override, swap-outcome verification
|
|
28
|
+
* uses this default; if the service is ever unreachable it degrades with a
|
|
29
|
+
* warning rather than blocking the trade. To use your own endpoint (or for local
|
|
30
|
+
* dev/e2e), point NANSEN_BASE_SIM_RPC at any trace-capable RPC in a gitignored .env.
|
|
14
31
|
*
|
|
15
32
|
* Backward-compat aliases (deprecated — prefer the forms above):
|
|
16
33
|
* NANSEN_RPC_BASE Old name for NANSEN_BASE_RPC; trading.js previously read this
|
|
@@ -40,3 +57,53 @@ export const CHAIN_RPCS = {
|
|
|
40
57
|
polygon: process.env.NANSEN_POLYGON_RPC || DEFAULT_POLYGON_RPC,
|
|
41
58
|
bnb: process.env.NANSEN_BNB_RPC || DEFAULT_BNB_RPC,
|
|
42
59
|
};
|
|
60
|
+
|
|
61
|
+
// Zero-config default for the shipped Nansen-hosted simulation endpoint. It
|
|
62
|
+
// authenticates with the user's existing Nansen API key (attached automatically
|
|
63
|
+
// by swap-simulation.js), and the trace-capable upstream is reached server-side —
|
|
64
|
+
// so this URL carries no secret and is safe to bake into a public package. Never
|
|
65
|
+
// embed an RPC URL that carries an inline token here; any embedded secret would
|
|
66
|
+
// leak on publish.
|
|
67
|
+
const DEFAULT_BASE_SIM_RPC = 'https://api.nansen.ai/api/v1/trade/simulate-swap';
|
|
68
|
+
|
|
69
|
+
// Separate registry for swap-outcome simulation (src/swap-simulation.js). These
|
|
70
|
+
// endpoints must support state-changing simulation with asset-transfer tracing
|
|
71
|
+
// (`eth_simulateV1` / `debug_traceCall`), which the CHAIN_RPCS public defaults
|
|
72
|
+
// disable. Only outcome verification reads this registry; every other RPC call
|
|
73
|
+
// stays on the cheap CHAIN_RPCS default. A null entry (no baked default and no
|
|
74
|
+
// override) signals "no sim-capable endpoint" to the caller, which degrades.
|
|
75
|
+
//
|
|
76
|
+
// Intentionally a mutable export: unit tests override an entry in-place (e.g.
|
|
77
|
+
// `SIMULATION_RPCS.base = ...`) to point at a mock or to null out the endpoint,
|
|
78
|
+
// restoring it in afterEach. Runtime code only ever reads it.
|
|
79
|
+
export const SIMULATION_RPCS = {
|
|
80
|
+
base: process.env.NANSEN_BASE_SIM_RPC || DEFAULT_BASE_SIM_RPC,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Nansen hosts the API key may be forwarded to. Kept to an explicit allowlist
|
|
84
|
+
// (not a `*.nansen.ai` wildcard): the key only ever authenticates the sim proxy
|
|
85
|
+
// on api.nansen.ai, and a wildcard would forward it to any subdomain that
|
|
86
|
+
// resolves — including a misconfigured or compromised one. Add new sim hosts
|
|
87
|
+
// here deliberately if one is ever introduced.
|
|
88
|
+
const NANSEN_HOSTED_SIM_HOSTS = new Set(['api.nansen.ai']);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Whether a simulation URL is a Nansen-hosted endpoint that may receive the
|
|
92
|
+
* user's Nansen API key. The key authenticates the shipped default proxy
|
|
93
|
+
* (DEFAULT_BASE_SIM_RPC); a NANSEN_BASE_SIM_RPC override can point at ANY host
|
|
94
|
+
* (dev node, third-party trace RPC), and forwarding the credential there would
|
|
95
|
+
* leak it. So the key is attached ONLY when this returns true — every other
|
|
96
|
+
* endpoint is called anonymously.
|
|
97
|
+
*
|
|
98
|
+
* Trust is: https + hostname is one of NANSEN_HOSTED_SIM_HOSTS. Anything else
|
|
99
|
+
* (http, other host, unparseable) is untrusted and gets no key.
|
|
100
|
+
*/
|
|
101
|
+
export function isNansenHostedUrl(url) {
|
|
102
|
+
try {
|
|
103
|
+
const u = new URL(url);
|
|
104
|
+
if (u.protocol !== 'https:') return false;
|
|
105
|
+
return NANSEN_HOSTED_SIM_HOSTS.has(u.hostname.toLowerCase());
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
package/src/schema.json
CHANGED
|
@@ -1620,7 +1620,15 @@
|
|
|
1620
1620
|
},
|
|
1621
1621
|
"no-simulate": {
|
|
1622
1622
|
"type": "boolean",
|
|
1623
|
-
"description": "Skip the pre-broadcast simulation."
|
|
1623
|
+
"description": "Skip the pre-broadcast simulation (the eth_call revert check)."
|
|
1624
|
+
},
|
|
1625
|
+
"no-verify-outcome": {
|
|
1626
|
+
"type": "boolean",
|
|
1627
|
+
"description": "Skip EVM swap-outcome verification. That check simulates the swap and confirms the wallet's balance changes match the quote (input spent within your max, expected output received, no other token moved) before broadcasting; it needs a simulation-capable endpoint (NANSEN_BASE_SIM_RPC) and degrades with a warning when none is available. No effect on Solana."
|
|
1628
|
+
},
|
|
1629
|
+
"no-revoke-excessive-allowance": {
|
|
1630
|
+
"type": "boolean",
|
|
1631
|
+
"description": "Skip revoking an existing on-chain ERC-20 allowance before re-approving when it exceeds 10x this trade's scoped amount. By default, an oversized or legacy allowance is revoked to zero and a fresh trade-scoped allowance is granted. WalletConnect users will see separate wallet prompts for the revoke and re-approval."
|
|
1624
1632
|
}
|
|
1625
1633
|
}
|
|
1626
1634
|
},
|
|
@@ -1686,7 +1694,7 @@
|
|
|
1686
1694
|
},
|
|
1687
1695
|
"slippage-bps": {
|
|
1688
1696
|
"type": "number",
|
|
1689
|
-
"description": "Slippage
|
|
1697
|
+
"description": "Slippage as a whole integer in basis points, 0-10000 (50 = 0.5%), omit for auto"
|
|
1690
1698
|
},
|
|
1691
1699
|
"expires": {
|
|
1692
1700
|
"type": "string",
|
|
@@ -1775,7 +1783,7 @@
|
|
|
1775
1783
|
},
|
|
1776
1784
|
"slippage-bps": {
|
|
1777
1785
|
"type": "number",
|
|
1778
|
-
"description": "New slippage in basis points (0-10000)"
|
|
1786
|
+
"description": "New slippage as a whole integer in basis points (0-10000)"
|
|
1779
1787
|
},
|
|
1780
1788
|
"wallet": {
|
|
1781
1789
|
"type": "string",
|
|
@@ -1968,6 +1976,15 @@
|
|
|
1968
1976
|
"type": "string",
|
|
1969
1977
|
"description": "Comma-separated list of fields to include in output"
|
|
1970
1978
|
},
|
|
1979
|
+
"limit": {
|
|
1980
|
+
"type": "number",
|
|
1981
|
+
"description": "Maximum results per page for list-returning research commands; maps to pagination.per_page. General endpoints default to 10 (max 1000), while profiler address endpoints default to 20 (max 100). Supported by smart-money, profiler, token, perp, points, prediction-market, and supported research historical-* commands. Commands that declare their own limit option (search, token top-tokens, trade limit-order list) use those command-specific semantics instead; token ohlcv, profiler perp-positions, and historical-token-flow-summary do not support pagination. profiler labels defaults to 100 when omitted."
|
|
1982
|
+
},
|
|
1983
|
+
"page": {
|
|
1984
|
+
"type": "number",
|
|
1985
|
+
"default": 1,
|
|
1986
|
+
"description": "1-based page number for list-returning research commands; maps to pagination.page. Supported by smart-money, profiler, token, perp, points, prediction-market, and supported research historical-* commands. Trade, wallet, and operational commands ignore it; token ohlcv, profiler perp-positions, and historical-token-flow-summary do not support pagination. profiler labels defaults to page 1 when omitted."
|
|
1987
|
+
},
|
|
1971
1988
|
"no-retry": {
|
|
1972
1989
|
"type": "boolean",
|
|
1973
1990
|
"description": "Disable automatic retry on rate limits/errors"
|