nansen-cli 1.23.0 → 1.23.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.23.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#361](https://github.com/nansen-ai/nansen-cli/pull/361) [`ff22da3`](https://github.com/nansen-ai/nansen-cli/commit/ff22da376c1072ef06e84054f6ee31c058e6e08c) Thanks [@TimNooren](https://github.com/TimNooren)! - fix(alerts): error when --webhook-secret is passed without --webhook
8
+
9
+ Previously, passing --webhook-secret with a non-webhook channel (e.g. --telegram)
10
+ silently discarded the secret with no warning. The alert was created successfully
11
+ but without any signing, giving the false impression that the secret was active.
12
+
13
+ Now throws an actionable error: "--webhook-secret requires --webhook".
14
+
15
+ - [#358](https://github.com/nansen-ai/nansen-cli/pull/358) [`70ee712`](https://github.com/nansen-ai/nansen-cli/commit/70ee71205343fb2d003eb5f50144e266bdc6109e) Thanks [@TimNooren](https://github.com/TimNooren)! - Add pre-quote trade input validation: rejects same-token swaps, invalid address formats, and non-positive amounts before any network call.
16
+
3
17
  ## 1.23.0
4
18
 
5
19
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nansen-cli",
3
- "version": "1.23.0",
3
+ "version": "1.23.1",
4
4
  "description": "Command-line interface for Nansen API - designed for AI agents",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -560,8 +560,11 @@ USAGE:
560
560
  return;
561
561
  }
562
562
 
563
- // Build channels array from --telegram/--slack/--discord flags
563
+ // Build channels array from --telegram/--slack/--discord/--webhook flags
564
564
  function buildChannels() {
565
+ if (options["webhook-secret"] && !options.webhook) {
566
+ throw new NansenError('--webhook-secret requires --webhook', ErrorCode.INVALID_PARAMS);
567
+ }
565
568
  const channels = [];
566
569
  if (options.telegram) channels.push({ type: 'telegram', data: { chatId: String(options.telegram) } });
567
570
  if (options.slack) channels.push({ type: 'slack', data: { webhookUrl: options.slack } });
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Trade input validation for the Nansen CLI.
3
+ * Catches common agent errors (wrong addresses, same-token swaps,
4
+ * bad amounts) before any network call.
5
+ */
6
+
7
+ import { validateAddress } from './api.js';
8
+
9
+ const SUPPORTED_CHAINS = ['solana', 'base'];
10
+
11
+ /**
12
+ * Validate quote inputs before any network call.
13
+ * Throws on validation failure with an actionable error message.
14
+ */
15
+ export function validateQuoteInput({ chain, from, to, amount }) {
16
+ // 1. Chain must be supported
17
+ const normalizedChain = chain?.toLowerCase();
18
+ if (!SUPPORTED_CHAINS.includes(normalizedChain)) {
19
+ throw new Error(
20
+ `Unsupported chain "${chain}". Supported chains: ${SUPPORTED_CHAINS.join(', ')}.`
21
+ );
22
+ }
23
+
24
+ // 2. Amount must be a positive finite number
25
+ const numAmount = Number(amount);
26
+ if (!Number.isFinite(numAmount) || numAmount <= 0) {
27
+ throw new Error(
28
+ `Invalid amount "${amount}". Must be a positive number.`
29
+ );
30
+ }
31
+
32
+ // 3. Token address format must match the chain (reuses api.js validateAddress)
33
+ const fromResult = validateAddress(from, normalizedChain);
34
+ if (!fromResult.valid) {
35
+ throw new Error(
36
+ `Invalid sell token address for ${normalizedChain}. ${fromResult.error}`
37
+ );
38
+ }
39
+ const toResult = validateAddress(to, normalizedChain);
40
+ if (!toResult.valid) {
41
+ throw new Error(
42
+ `Invalid buy token address for ${normalizedChain}. ${toResult.error}`
43
+ );
44
+ }
45
+
46
+ // 4. Sell and buy tokens must be different
47
+ const fromNorm = normalizedChain === 'solana' ? from : from.toLowerCase();
48
+ const toNorm = normalizedChain === 'solana' ? to : to.toLowerCase();
49
+ if (fromNorm === toNorm) {
50
+ throw new Error(
51
+ `Cannot swap ${from} for itself. Sell and buy tokens must be different.`
52
+ );
53
+ }
54
+ }
package/src/trading.js CHANGED
@@ -13,6 +13,7 @@ import { base58Decode } from './transfer.js';
13
13
  import { keccak256, signSecp256k1, rlpEncode } from './crypto.js';
14
14
  import { getWalletConnectAddress, sendTransactionViaWalletConnect, sendSolanaTransactionViaWalletConnect, sendApprovalViaWalletConnect } from './walletconnect-trading.js';
15
15
  import { retrievePassword } from './keychain.js';
16
+ import { validateQuoteInput } from './trade-validation.js';
16
17
  import { CHAIN_RPCS } from './rpc-urls.js';
17
18
 
18
19
  // ============= Constants =============
@@ -877,6 +878,16 @@ EXAMPLES:
877
878
  return;
878
879
  }
879
880
 
881
+ // Static input validation — catches common agent errors (wrong addresses,
882
+ // same-token swaps, bad amounts) before any network or wallet call.
883
+ try {
884
+ validateQuoteInput({ chain, from, to, amount });
885
+ } catch (validationErr) {
886
+ log(`Error: ${validationErr.message}`);
887
+ exit(1);
888
+ return;
889
+ }
890
+
880
891
  // When --amount-unit token is used, resolve decimals and convert to base units.
881
892
  // Otherwise, validate that the amount is already in base units (integer).
882
893
  let resolvedAmount = amount;