nansen-cli 1.10.1 → 1.11.1
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 +20 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/api.js +18 -5
- package/src/cli.js +52 -22
- package/src/index.js +4 -4
- package/src/schema.json +197 -1
- package/src/trading.js +111 -111
- package/src/wallet.js +37 -39
- package/src/walletconnect-x402.js +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.11.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#194](https://github.com/nansen-ai/nansen-cli/pull/194) [`89225f5`](https://github.com/nansen-ai/nansen-cli/commit/89225f5d5b566f7eda77b1876c77545c2feb6a1c) Thanks [@TimNooren](https://github.com/TimNooren)! - fix: --help on trade subcommands and wallet subcommands now shows full help identical to the no-args case
|
|
8
|
+
|
|
9
|
+
- [#199](https://github.com/nansen-ai/nansen-cli/pull/199) [`9ae981e`](https://github.com/nansen-ai/nansen-cli/commit/9ae981e6dcf7fa663e47a3608afab7f12e0a9463) Thanks [@TimNooren](https://github.com/TimNooren)! - fix: replace misleading `walletconnect connect` command reference in x402 payment error with actionable guidance mentioning both local wallet (`nansen wallet create`) and external WalletConnect CLI options
|
|
10
|
+
|
|
11
|
+
## 1.11.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#186](https://github.com/nansen-ai/nansen-cli/pull/186) [`feecc50`](https://github.com/nansen-ai/nansen-cli/commit/feecc5080254b55aaef0addb646279d52a468063) Thanks [@TimNooren](https://github.com/TimNooren)! - Trade commands output to stdout instead of stderr; wallet send prints human-readable text instead of JSON
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#166](https://github.com/nansen-ai/nansen-cli/pull/166) [`c1034db`](https://github.com/nansen-ai/nansen-cli/commit/c1034dbb4bf2fc173f377cbc0adbbbe3e67873aa) Thanks [@0xlaveen](https://github.com/0xlaveen)! - fix: pass --page parameter correctly in smart-money, profiler, token, perp, and points commands
|
|
20
|
+
|
|
21
|
+
- [#137](https://github.com/nansen-ai/nansen-cli/pull/137) [`1214767`](https://github.com/nansen-ai/nansen-cli/commit/12147675aadfd0bd97627cb2f41f1dcc5205b0d7) Thanks [@0xlaveen](https://github.com/0xlaveen)! - Add missing sort/filters options to profiler schema and fix pnl sort/filters forwarding
|
|
22
|
+
|
|
3
23
|
## 1.10.1
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/nansen-cli)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
> **Built by agents, for agents.** Command-line interface for the [Nansen API](https://docs.nansen.ai)
|
|
6
|
+
> **Built by agents, for agents.** Command-line interface for the [Nansen API](https://docs.nansen.ai), designed for AI agents.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -62,7 +62,7 @@ export class NansenError extends Error {
|
|
|
62
62
|
this.name = 'NansenError';
|
|
63
63
|
this.code = code;
|
|
64
64
|
this.status = status;
|
|
65
|
-
this.
|
|
65
|
+
this.details = data;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
toJSON() {
|
|
@@ -70,7 +70,7 @@ export class NansenError extends Error {
|
|
|
70
70
|
error: this.message,
|
|
71
71
|
code: this.code,
|
|
72
72
|
status: this.status,
|
|
73
|
-
details: this.
|
|
73
|
+
details: this.details,
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -578,9 +578,20 @@ export class NansenAPI {
|
|
|
578
578
|
return await paidResponse.json();
|
|
579
579
|
}
|
|
580
580
|
} catch (x402Err) {
|
|
581
|
-
|
|
581
|
+
if (!this.apiKey) {
|
|
582
|
+
// No API key and no payment wallet — guide the user to login rather than
|
|
583
|
+
// showing a confusing x402 payment dump they can't act on.
|
|
584
|
+
// TODO: full fix would skip x402 entirely when no apiKey is set — see PR #<this PR number>
|
|
585
|
+
message = 'No API key configured. Run: nansen login --api-key <key>. Get your key at https://app.nansen.ai/api';
|
|
586
|
+
} else {
|
|
587
|
+
message = `x402 auto-payment failed: ${x402Err.message}`;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Only include raw payment requirements in the error details when the user
|
|
591
|
+
// has an API key — for unauthenticated users they add noise, not signal.
|
|
592
|
+
if (this.apiKey) {
|
|
593
|
+
data.paymentRequirements = paymentRequirements;
|
|
582
594
|
}
|
|
583
|
-
data.paymentRequirements = paymentRequirements;
|
|
584
595
|
}
|
|
585
596
|
}
|
|
586
597
|
}
|
|
@@ -733,7 +744,7 @@ export class NansenAPI {
|
|
|
733
744
|
}
|
|
734
745
|
|
|
735
746
|
async addressPnl(params = {}) {
|
|
736
|
-
const { address, chain = 'ethereum', date, days = 30, pagination } = params;
|
|
747
|
+
const { address, chain = 'ethereum', date, days = 30, filters = {}, orderBy, pagination } = params;
|
|
737
748
|
if (address) {
|
|
738
749
|
const validation = validateAddress(address, chain);
|
|
739
750
|
if (!validation.valid) throw new NansenError(validation.error, validation.code);
|
|
@@ -743,6 +754,8 @@ export class NansenAPI {
|
|
|
743
754
|
address,
|
|
744
755
|
chain,
|
|
745
756
|
date: dateRange,
|
|
757
|
+
filters,
|
|
758
|
+
order_by: orderBy,
|
|
746
759
|
pagination
|
|
747
760
|
});
|
|
748
761
|
}
|
package/src/cli.js
CHANGED
|
@@ -24,6 +24,16 @@ const schemaDefinition = require('./schema.json');
|
|
|
24
24
|
// and should be updated whenever the API changes — do not edit returns arrays here.
|
|
25
25
|
export const SCHEMA = { version: VERSION, ...schemaDefinition };
|
|
26
26
|
|
|
27
|
+
// ============= Pagination =============
|
|
28
|
+
|
|
29
|
+
export function buildPagination(options) {
|
|
30
|
+
if (!options.limit && !options.page) return undefined;
|
|
31
|
+
return {
|
|
32
|
+
page: Math.max(1, parseInt(options.page, 10) || 1),
|
|
33
|
+
per_page: options.limit,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
// ============= Field Filtering =============
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -313,13 +323,17 @@ export function formatOutput(data, { pretty = false, table = false, csv = false
|
|
|
313
323
|
|
|
314
324
|
// Format error data (returns object, does not exit)
|
|
315
325
|
export function formatError(error) {
|
|
316
|
-
|
|
326
|
+
const details = error.details ?? error.data ?? null;
|
|
327
|
+
const result = {
|
|
317
328
|
success: false,
|
|
318
329
|
error: error.message,
|
|
319
330
|
code: error.code || 'UNKNOWN',
|
|
320
331
|
status: error.status || null,
|
|
321
|
-
details: error.data || null
|
|
322
332
|
};
|
|
333
|
+
if (details != null && !(typeof details === 'object' && !Array.isArray(details) && Object.keys(details).length === 0)) {
|
|
334
|
+
result.details = details;
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
323
337
|
}
|
|
324
338
|
|
|
325
339
|
/**
|
|
@@ -606,7 +620,7 @@ export async function compareWallets(api, params = {}) {
|
|
|
606
620
|
|
|
607
621
|
export const BANNER = '';
|
|
608
622
|
|
|
609
|
-
export const HELP = `Nansen CLI v${VERSION} —
|
|
623
|
+
export const HELP = `Nansen CLI v${VERSION} — designed for AI agents.
|
|
610
624
|
|
|
611
625
|
USAGE: nansen <command> [subcommand] [options]
|
|
612
626
|
|
|
@@ -628,9 +642,10 @@ EXAMPLES:
|
|
|
628
642
|
nansen research smart-money netflow --chain solana
|
|
629
643
|
nansen research token screener --chain solana --timeframe 24h
|
|
630
644
|
nansen research profiler balance --address 0x... --chain ethereum
|
|
631
|
-
nansen trade quote --chain
|
|
645
|
+
nansen trade quote --chain base --from ETH --to USDC --amount 1
|
|
632
646
|
|
|
633
|
-
|
|
647
|
+
Research chains: ethereum, solana, base, bnb, arbitrum, polygon, optimism, avalanche, linea, scroll, mantle, ronin, sei, plasma, sonic, monad, hyperevm, iotaevm
|
|
648
|
+
Trade chains: solana, base
|
|
634
649
|
Labels: Fund, Smart Trader, 30D/90D/180D Smart Trader, Smart HL Perps Trader
|
|
635
650
|
|
|
636
651
|
Docs: https://docs.nansen.ai
|
|
@@ -855,7 +870,7 @@ export function buildCommands(deps = {}) {
|
|
|
855
870
|
const chains = options.chains || [chain];
|
|
856
871
|
const filters = options.filters || {};
|
|
857
872
|
const orderBy = parseSort(options.sort, options['order-by']);
|
|
858
|
-
const pagination = options
|
|
873
|
+
const pagination = buildPagination(options);
|
|
859
874
|
|
|
860
875
|
// Add smart money label filter if specified
|
|
861
876
|
if (options.labels) {
|
|
@@ -906,7 +921,7 @@ export function buildCommands(deps = {}) {
|
|
|
906
921
|
}
|
|
907
922
|
const filters = options.filters || {};
|
|
908
923
|
const orderBy = parseSort(options.sort, options['order-by']);
|
|
909
|
-
const pagination = options
|
|
924
|
+
const pagination = buildPagination(options);
|
|
910
925
|
const days = options.days ? parseInt(options.days) : 30;
|
|
911
926
|
|
|
912
927
|
const handlers = {
|
|
@@ -918,7 +933,7 @@ export function buildCommands(deps = {}) {
|
|
|
918
933
|
},
|
|
919
934
|
'pnl': () => {
|
|
920
935
|
const date = parseDateOption(options.date, days);
|
|
921
|
-
return apiInstance.addressPnl({ address, chain, date, days, pagination });
|
|
936
|
+
return apiInstance.addressPnl({ address, chain, date, days, filters, orderBy, pagination });
|
|
922
937
|
},
|
|
923
938
|
'search': () => apiInstance.entitySearch({ query: options.query }),
|
|
924
939
|
'historical-balances': () => apiInstance.addressHistoricalBalances({ address, chain, filters, orderBy, pagination, days }),
|
|
@@ -992,7 +1007,7 @@ export function buildCommands(deps = {}) {
|
|
|
992
1007
|
const timeframe = options.timeframe || '24h';
|
|
993
1008
|
const filters = options.filters || {};
|
|
994
1009
|
const orderBy = parseSort(options.sort, options['order-by']);
|
|
995
|
-
const pagination = options
|
|
1010
|
+
const pagination = buildPagination(options);
|
|
996
1011
|
const days = options.days ? parseInt(options.days) : 30;
|
|
997
1012
|
|
|
998
1013
|
// Convenience filter for smart money only
|
|
@@ -1065,6 +1080,28 @@ export function buildCommands(deps = {}) {
|
|
|
1065
1080
|
|
|
1066
1081
|
let result = await handlers[subcommand]();
|
|
1067
1082
|
|
|
1083
|
+
// Warn when OHLCV price data is null (backend coverage gap)
|
|
1084
|
+
// Volume comes from on-chain DEX data and is always available, but price/market_cap
|
|
1085
|
+
// requires a price oracle — some tokens are not tracked and return all-null price fields.
|
|
1086
|
+
if (subcommand === 'ohlcv') {
|
|
1087
|
+
const candles = Array.isArray(result?.data) ? result.data : [];
|
|
1088
|
+
if (candles.length === 0) {
|
|
1089
|
+
process.stderr.write(`⚠️ No OHLCV data returned for token ${tokenAddress} on ${chain}.\n`);
|
|
1090
|
+
} else {
|
|
1091
|
+
const hasPrice = candles.some(c => c.open !== null || c.close !== null);
|
|
1092
|
+
const hasVolume = candles.some(c => c.volume !== null);
|
|
1093
|
+
if (!hasPrice && hasVolume) {
|
|
1094
|
+
process.stderr.write(
|
|
1095
|
+
`⚠️ Price data unavailable for token ${tokenAddress} on ${chain}.\n` +
|
|
1096
|
+
` open/high/low/close, volume_usd, and market_cap are null.\n` +
|
|
1097
|
+
` Volume (raw token units) is available. This token may not be tracked by Nansen's price oracle.\n`
|
|
1098
|
+
);
|
|
1099
|
+
} else if (!hasPrice && !hasVolume) {
|
|
1100
|
+
process.stderr.write(`⚠️ No OHLCV data available for token ${tokenAddress} on ${chain}.\n`);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1068
1105
|
// Enrich transfers with Nansen labels for from/to addresses
|
|
1069
1106
|
if (subcommand === 'transfers' && (options.enrich || flags.enrich)) {
|
|
1070
1107
|
result = await enrichTransfers(result, apiInstance, chain);
|
|
@@ -1098,7 +1135,7 @@ export function buildCommands(deps = {}) {
|
|
|
1098
1135
|
const subcommand = args[0] || 'help';
|
|
1099
1136
|
const filters = options.filters || {};
|
|
1100
1137
|
const orderBy = parseSort(options.sort, options['order-by']);
|
|
1101
|
-
const pagination = options
|
|
1138
|
+
const pagination = buildPagination(options);
|
|
1102
1139
|
const days = options.days ? parseInt(options.days) : 30;
|
|
1103
1140
|
|
|
1104
1141
|
const handlers = {
|
|
@@ -1130,7 +1167,7 @@ export function buildCommands(deps = {}) {
|
|
|
1130
1167
|
'points': async (args, apiInstance, flags, options) => {
|
|
1131
1168
|
const subcommand = args[0] || 'help';
|
|
1132
1169
|
const tier = options.tier;
|
|
1133
|
-
const pagination = options
|
|
1170
|
+
const pagination = buildPagination(options);
|
|
1134
1171
|
|
|
1135
1172
|
const handlers = {
|
|
1136
1173
|
'leaderboard': () => apiInstance.pointsLeaderboard({ tier, pagination }),
|
|
@@ -1346,17 +1383,9 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1346
1383
|
return { type: 'command-help', command: `research ${category}` };
|
|
1347
1384
|
}
|
|
1348
1385
|
}
|
|
1349
|
-
// Handle 'trade <sub> --help'
|
|
1350
|
-
if (command === 'trade' && subcommand) {
|
|
1351
|
-
const tradeSchema = SCHEMA.commands.trade?.subcommands?.[subcommand];
|
|
1352
|
-
if (tradeSchema) {
|
|
1353
|
-
output(`\ntrade ${subcommand} - ${tradeSchema.description}\n`);
|
|
1354
|
-
notify();
|
|
1355
|
-
return { type: 'subcommand-help', command: 'trade', subcommand };
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
1386
|
// First try subcommand help
|
|
1359
|
-
|
|
1387
|
+
// Skip for 'trade' — its handlers show their own rich usage when required args are missing
|
|
1388
|
+
if (command && subcommand && command !== 'trade') {
|
|
1360
1389
|
const subHelp = generateSubcommandHelp(command, subcommand);
|
|
1361
1390
|
if (subHelp) {
|
|
1362
1391
|
output(subHelp);
|
|
@@ -1365,7 +1394,8 @@ export async function runCLI(rawArgs, deps = {}) {
|
|
|
1365
1394
|
}
|
|
1366
1395
|
}
|
|
1367
1396
|
// Then try command-level help (list subcommands)
|
|
1368
|
-
|
|
1397
|
+
// Skip for 'trade' — let the handler show its own usage
|
|
1398
|
+
const cmdSchemaLookup = command !== 'trade' && (SCHEMA.commands[command] || SCHEMA.commands.research.subcommands[command]);
|
|
1369
1399
|
if (command && cmdSchemaLookup) {
|
|
1370
1400
|
const cmdSchema = cmdSchemaLookup;
|
|
1371
1401
|
const lines = [`${command} — ${cmdSchema.description}`];
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Nansen CLI - Command-line interface for Nansen API
|
|
4
|
-
* Designed for AI agents
|
|
5
|
-
*
|
|
4
|
+
* Designed for AI agents.
|
|
5
|
+
*
|
|
6
6
|
* Usage: nansen <command> [options]
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
*
|
|
8
|
+
* Research commands return JSON; operational commands print human-readable text.
|
|
9
9
|
* Use --pretty for human-readable formatting.
|
|
10
10
|
*
|
|
11
11
|
* Core logic lives in cli.js for testability.
|