nansen-cli 1.16.1 → 1.17.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 +24 -0
- package/package.json +1 -1
- package/src/api.js +69 -71
- package/src/cli.js +5 -0
- package/src/rpc-urls.js +29 -0
- package/src/schema.json +237 -1447
- package/src/trading.js +26 -12
- package/src/transfer.js +1 -11
- package/src/wallet.js +2 -1
- package/src/x402.js +3 -2
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 { CHAIN_RPCS } from './rpc-urls.js';
|
|
16
17
|
|
|
17
18
|
// ============= Constants =============
|
|
18
19
|
|
|
@@ -63,21 +64,17 @@ export function resolveTokenAddress(symbolOrAddress, chainName) {
|
|
|
63
64
|
return resolved || symbolOrAddress;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
// Default public RPC endpoints (used for nonce fetching)
|
|
67
|
-
const EVM_RPC_URLS = {
|
|
68
|
-
base: process.env.NANSEN_RPC_BASE || 'https://mainnet.base.org',
|
|
69
|
-
};
|
|
70
|
-
|
|
71
67
|
/**
|
|
72
68
|
* Make a JSON-RPC call to an EVM RPC endpoint.
|
|
73
|
-
*
|
|
69
|
+
* RPC URLs come from the shared CHAIN_RPCS registry in rpc-urls.js.
|
|
70
|
+
* @param {string} chain - Chain name (key into CHAIN_RPCS)
|
|
74
71
|
* @param {string} method - JSON-RPC method name
|
|
75
72
|
* @param {Array} params - Method parameters
|
|
76
73
|
* @returns {Promise<*>} Parsed result value
|
|
77
74
|
* @throws {Error} If chain has no configured RPC or the RPC returns an error
|
|
78
75
|
*/
|
|
79
76
|
async function evmRpcCall(chain, method, params = []) {
|
|
80
|
-
const rpcUrl =
|
|
77
|
+
const rpcUrl = CHAIN_RPCS[chain];
|
|
81
78
|
if (!rpcUrl) throw new Error(`No RPC URL configured for chain: ${chain}`);
|
|
82
79
|
const res = await fetch(rpcUrl, {
|
|
83
80
|
method: 'POST',
|
|
@@ -406,7 +403,7 @@ export async function waitForReceipt(chain, txHash, timeoutMs = 30000, pollMs =
|
|
|
406
403
|
* Returns { success: true } or { success: false, reason: string }.
|
|
407
404
|
*/
|
|
408
405
|
export async function simulateEvmCall(chain, { from, to, data, value, gas }) {
|
|
409
|
-
if (!
|
|
406
|
+
if (!CHAIN_RPCS[chain]) return { success: true }; // Can't simulate, skip
|
|
410
407
|
|
|
411
408
|
try {
|
|
412
409
|
const callObj = { from, to, data, value: value || '0x0' };
|
|
@@ -417,7 +414,24 @@ export async function simulateEvmCall(chain, { from, to, data, value, gas }) {
|
|
|
417
414
|
const msg = e.message || 'unknown';
|
|
418
415
|
// Only block on actual contract-level revert errors from the RPC
|
|
419
416
|
if (msg.startsWith('RPC error (eth_call):')) {
|
|
420
|
-
|
|
417
|
+
const rawReason = msg.replace(/^RPC error \(eth_call\): /, '');
|
|
418
|
+
// Convert raw "insufficient funds" RPC errors (amounts in wei) into a human-readable message.
|
|
419
|
+
// EVM nodes emit: "insufficient funds for gas * price + value: address 0x... have X want Y (supplied gas Z)"
|
|
420
|
+
// TODO: full fix would be a pre-flight eth_getBalance check at quote-fetch time so the error
|
|
421
|
+
// surfaces before simulation with an estimated ETH requirement — see PR for this fix.
|
|
422
|
+
const m = rawReason.match(/insufficient funds[\s\S]*?\bhave (\d+)\s+want (\d+)/i);
|
|
423
|
+
if (m) {
|
|
424
|
+
const haveWei = BigInt(m[1]);
|
|
425
|
+
const wantWei = BigInt(m[2]);
|
|
426
|
+
const haveEth = (Number(haveWei) / 1e18).toFixed(6);
|
|
427
|
+
const wantEth = (Number(wantWei) / 1e18).toFixed(6);
|
|
428
|
+
const fundHint = from ? ` Send ETH to ${from} before trading.` : '';
|
|
429
|
+
return {
|
|
430
|
+
success: false,
|
|
431
|
+
reason: `Insufficient ETH: wallet has ${haveEth} ETH but this trade needs ~${wantEth} ETH (amount + gas).${fundHint}`,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return { success: false, reason: rawReason };
|
|
421
435
|
}
|
|
422
436
|
// Network/infrastructure errors (fetch failure, rate limit, non-JSON response) → non-blocking
|
|
423
437
|
return { success: true };
|
|
@@ -429,7 +443,7 @@ export async function simulateEvmCall(chain, { from, to, data, value, gas }) {
|
|
|
429
443
|
* Used to fix under-gassed quotes from aggregators.
|
|
430
444
|
*/
|
|
431
445
|
export async function estimateEvmGas(chain, { from, to, data, value }) {
|
|
432
|
-
if (!
|
|
446
|
+
if (!CHAIN_RPCS[chain]) return null;
|
|
433
447
|
|
|
434
448
|
try {
|
|
435
449
|
const result = await evmRpcCall(chain, 'eth_estimateGas', [{ from, to, data, value: value || '0x0' }]);
|
|
@@ -444,7 +458,7 @@ export async function estimateEvmGas(chain, { from, to, data, value }) {
|
|
|
444
458
|
* Returns the allowance as a BigInt, or 0n on failure.
|
|
445
459
|
*/
|
|
446
460
|
export async function checkErc20Allowance(chain, tokenAddress, ownerAddress, spenderAddress) {
|
|
447
|
-
if (!
|
|
461
|
+
if (!CHAIN_RPCS[chain]) return 0n;
|
|
448
462
|
|
|
449
463
|
try {
|
|
450
464
|
// allowance(address,address) selector = 0xdd62ed3e
|
|
@@ -1123,7 +1137,7 @@ EXAMPLES:
|
|
|
1123
1137
|
let finalGas = apiGas > 0 ? apiGas : txGas;
|
|
1124
1138
|
if (finalGas === 0) {
|
|
1125
1139
|
try {
|
|
1126
|
-
const rpcUrl =
|
|
1140
|
+
const rpcUrl = CHAIN_RPCS[chain];
|
|
1127
1141
|
const estRes = await fetch(rpcUrl, {
|
|
1128
1142
|
method: 'POST',
|
|
1129
1143
|
headers: { 'Content-Type': 'application/json' },
|
package/src/transfer.js
CHANGED
|
@@ -9,26 +9,16 @@ import { base58Encode, exportWallet, getWalletConfig, verifyPassword, showWallet
|
|
|
9
9
|
import { keccak256, signSecp256k1, rlpEncode } from './crypto.js';
|
|
10
10
|
import { getWalletConnectAddress, sendTransactionViaWalletConnect } from './walletconnect-trading.js';
|
|
11
11
|
import { EVM_CHAIN_IDS } from './chain-ids.js';
|
|
12
|
+
import { CHAIN_RPCS } from './rpc-urls.js';
|
|
12
13
|
|
|
13
14
|
// ============= Constants =============
|
|
14
15
|
|
|
15
|
-
const DEFAULT_EVM_RPC = 'https://eth.public-rpc.com';
|
|
16
|
-
const DEFAULT_SOLANA_RPC = 'https://api.mainnet-beta.solana.com';
|
|
17
|
-
|
|
18
16
|
const PRIORITY_FEE_DEFAULTS = { base: 100000000n, ethereum: 1500000000n, evm: 1500000000n };
|
|
19
17
|
|
|
20
18
|
const ERC20_TRANSFER_SELECTOR = 'a9059cbb'; // transfer(address,uint256)
|
|
21
19
|
const SYSTEM_PROGRAM = '11111111111111111111111111111111'; // 32 zero bytes in base58
|
|
22
20
|
const ATA_PROGRAM = 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL';
|
|
23
21
|
|
|
24
|
-
// Chain-specific RPC endpoints
|
|
25
|
-
const CHAIN_RPCS = {
|
|
26
|
-
'ethereum': process.env.NANSEN_EVM_RPC || DEFAULT_EVM_RPC,
|
|
27
|
-
'evm': process.env.NANSEN_EVM_RPC || DEFAULT_EVM_RPC,
|
|
28
|
-
'base': process.env.NANSEN_BASE_RPC || 'https://mainnet.base.org',
|
|
29
|
-
'solana': process.env.NANSEN_SOLANA_RPC || DEFAULT_SOLANA_RPC,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
22
|
// Alias: buildEvmTransaction uses 'evm' as a generic fallback
|
|
33
23
|
const CHAIN_IDS = { ...EVM_CHAIN_IDS, evm: 1 };
|
|
34
24
|
|
package/src/wallet.js
CHANGED
|
@@ -1067,7 +1067,8 @@ ENVIRONMENT:
|
|
|
1067
1067
|
PRIVY_APP_ID Privy application ID (required for --provider privy)
|
|
1068
1068
|
PRIVY_APP_SECRET Privy application secret (required for --provider privy)
|
|
1069
1069
|
NANSEN_WALLET_PROVIDER Default provider for wallet create ("local" or "privy")
|
|
1070
|
-
NANSEN_EVM_RPC Custom
|
|
1070
|
+
NANSEN_EVM_RPC Custom Ethereum RPC endpoint (also generic EVM fallback)
|
|
1071
|
+
NANSEN_BASE_RPC Custom Base RPC endpoint
|
|
1071
1072
|
NANSEN_SOLANA_RPC Custom Solana RPC endpoint
|
|
1072
1073
|
|
|
1073
1074
|
EXAMPLES:
|
package/src/x402.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getSolanaRpcUrl,
|
|
13
13
|
} from './x402-svm.js';
|
|
14
14
|
import { resolvePassword } from './keychain.js';
|
|
15
|
+
import { CHAIN_RPCS } from './rpc-urls.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Parse PaymentRequirements from a 402 response.
|
|
@@ -187,10 +188,10 @@ export async function checkX402Balance(network) {
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
if (network.startsWith('eip155:')) {
|
|
190
|
-
// Base USDC balance check
|
|
191
|
+
// Base USDC balance check — RPC URL from shared registry so NANSEN_BASE_RPC override applies
|
|
191
192
|
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
192
193
|
const addr = walletInfo.evm.replace('0x', '').toLowerCase().padStart(64, '0');
|
|
193
|
-
const resp = await fetch(
|
|
194
|
+
const resp = await fetch(CHAIN_RPCS.base, {
|
|
194
195
|
method: 'POST',
|
|
195
196
|
headers: { 'Content-Type': 'application/json' },
|
|
196
197
|
body: JSON.stringify({
|