solana-balance-cli 1.0.2 → 1.1.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/dist/index.js CHANGED
@@ -4,13 +4,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const commander_1 = require("commander");
5
5
  const solana_1 = require("./solana");
6
6
  const price_1 = require("./price");
7
+ const tokens_1 = require("./tokens");
8
+ const watch_1 = require("./watch");
7
9
  const program = new commander_1.Command();
8
10
  program
9
11
  .name('sol-balance')
10
12
  .description('Check Solana wallet balance in SOL and USD')
11
- .version('1.0.0')
13
+ .version('1.0.0');
14
+ // Balance check command
15
+ program
16
+ .command('balance')
17
+ .description('Check Solana wallet balance')
12
18
  .argument('<wallet-address>', 'Solana wallet address to check')
13
- .action(async (walletAddress) => {
19
+ .option('-t, --tokens', 'Show SPL token balances and their USD values')
20
+ .action(async (walletAddress, options) => {
14
21
  try {
15
22
  console.log('\nšŸ” Fetching wallet balance...\n');
16
23
  const [balance, price] = await Promise.all([
@@ -21,9 +28,46 @@ program
21
28
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
22
29
  console.log(`šŸ“ Wallet: ${walletAddress}`);
23
30
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
24
- console.log(`šŸ’° Balance: ${balance.toFixed(4)} SOL`);
25
- console.log(`šŸ’µ SOL Price: $${price.toFixed(2)} USD`);
26
- console.log(`šŸ’ø USD Value: $${usdValue.toFixed(2)} USD`);
31
+ console.log(`šŸ’° SOL Balance: ${balance.toFixed(4)} SOL`);
32
+ console.log(`šŸ’µ SOL Price: ${price.toFixed(2)} USD`);
33
+ console.log(`šŸ’ø SOL Value: ${usdValue.toFixed(2)} USD`);
34
+ if (options.tokens) {
35
+ console.log('\nšŸŖ™ Fetching SPL tokens...\n');
36
+ const tokenAccounts = await (0, tokens_1.getTokenAccounts)(walletAddress);
37
+ if (tokenAccounts.length === 0) {
38
+ console.log('No SPL tokens found in this wallet.');
39
+ }
40
+ else {
41
+ const mintAddresses = tokenAccounts.map(token => token.mint);
42
+ const tokenPrices = await (0, price_1.getTokenPrices)(mintAddresses);
43
+ let totalTokenValue = 0;
44
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
45
+ console.log('SPL TOKENS:');
46
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
47
+ for (const token of tokenAccounts) {
48
+ const tokenPrice = tokenPrices.get(token.mint) || 0;
49
+ const tokenValue = token.uiAmount * tokenPrice;
50
+ totalTokenValue += tokenValue;
51
+ console.log(`\nšŸ”¹ Token: ${token.mint}`);
52
+ console.log(` Balance: ${token.uiAmount.toFixed(token.decimals)} tokens`);
53
+ if (tokenPrice > 0) {
54
+ console.log(` Price: ${tokenPrice.toFixed(6)} USD`);
55
+ console.log(` Value: ${tokenValue.toFixed(2)} USD`);
56
+ }
57
+ else {
58
+ console.log(` Price: Not available`);
59
+ console.log(` Value: $0.00 USD`);
60
+ }
61
+ }
62
+ const totalPortfolioValue = usdValue + totalTokenValue;
63
+ console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
64
+ console.log('PORTFOLIO SUMMARY:');
65
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
66
+ console.log(`šŸ’° Total SOL Value: ${usdValue.toFixed(2)} USD`);
67
+ console.log(`šŸŖ™ Total Token Value: ${totalTokenValue.toFixed(2)} USD`);
68
+ console.log(`šŸ’Ž Total Portfolio Value: ${totalPortfolioValue.toFixed(2)} USD`);
69
+ }
70
+ }
27
71
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
28
72
  }
29
73
  catch (error) {
@@ -36,4 +80,46 @@ program
36
80
  process.exit(1);
37
81
  }
38
82
  });
83
+ // Watch price command
84
+ program
85
+ .command('watch')
86
+ .description('Watch SOL price and send notifications when it crosses target prices')
87
+ .option('--above <price>', 'Send notification when price goes above this value', parseFloat)
88
+ .option('--below <price>', 'Send notification when price goes below this value', parseFloat)
89
+ .option('--interval <seconds>', 'Price check interval in seconds (default: 60)', parseFloat, 60)
90
+ .action(async (options) => {
91
+ try {
92
+ // Validate options
93
+ if (!options.above && !options.below) {
94
+ console.error('\nāŒ Error: You must specify at least one target price (--above or --below)\n');
95
+ process.exit(1);
96
+ }
97
+ if (options.above && isNaN(options.above)) {
98
+ console.error('\nāŒ Error: --above must be a valid number\n');
99
+ process.exit(1);
100
+ }
101
+ if (options.below && isNaN(options.below)) {
102
+ console.error('\nāŒ Error: --below must be a valid number\n');
103
+ process.exit(1);
104
+ }
105
+ if (isNaN(options.interval) || options.interval < 1) {
106
+ console.error('\nāŒ Error: --interval must be a number >= 1\n');
107
+ process.exit(1);
108
+ }
109
+ if (options.above && options.below && options.above <= options.below) {
110
+ console.error('\nāŒ Error: --above price must be greater than --below price\n');
111
+ process.exit(1);
112
+ }
113
+ await (0, watch_1.watchSolanaPrice)(options);
114
+ }
115
+ catch (error) {
116
+ if (error instanceof Error) {
117
+ console.error(`\nāŒ Error: ${error.message}\n`);
118
+ }
119
+ else {
120
+ console.error('\nāŒ An unknown error occurred\n');
121
+ }
122
+ process.exit(1);
123
+ }
124
+ });
39
125
  program.parse();
package/dist/price.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getSolanaPrice = getSolanaPrice;
7
+ exports.getTokenPrices = getTokenPrices;
7
8
  const axios_1 = __importDefault(require("axios"));
8
9
  async function getSolanaPrice() {
9
10
  try {
@@ -21,3 +22,46 @@ async function getSolanaPrice() {
21
22
  throw new Error('Failed to fetch SOL price: Unknown error');
22
23
  }
23
24
  }
25
+ async function getTokenPrices(mintAddresses) {
26
+ try {
27
+ if (mintAddresses.length === 0) {
28
+ return new Map();
29
+ }
30
+ const priceMap = new Map();
31
+ const batchSize = 50; // Limit batch size to avoid URI too long error
32
+ // Process tokens in batches
33
+ for (let i = 0; i < mintAddresses.length; i += batchSize) {
34
+ const batch = mintAddresses.slice(i, i + batchSize);
35
+ const contractAddresses = batch.join(',');
36
+ try {
37
+ const response = await axios_1.default.get(`https://api.coingecko.com/api/v3/simple/token_price/solana?contract_addresses=${contractAddresses}&vs_currencies=usd`, { timeout: 10000 });
38
+ for (const mint of batch) {
39
+ const mintLower = mint.toLowerCase();
40
+ if (response.data[mintLower] && typeof response.data[mintLower].usd === 'number') {
41
+ priceMap.set(mint, response.data[mintLower].usd);
42
+ }
43
+ else {
44
+ priceMap.set(mint, 0);
45
+ }
46
+ }
47
+ }
48
+ catch (batchError) {
49
+ // If batch fails, set all tokens in batch to 0 price
50
+ for (const mint of batch) {
51
+ priceMap.set(mint, 0);
52
+ }
53
+ }
54
+ // Small delay between batches to avoid rate limiting
55
+ if (i + batchSize < mintAddresses.length) {
56
+ await new Promise(resolve => setTimeout(resolve, 200));
57
+ }
58
+ }
59
+ return priceMap;
60
+ }
61
+ catch (error) {
62
+ if (error instanceof Error) {
63
+ throw new Error(`Failed to fetch token prices: ${error.message}`);
64
+ }
65
+ throw new Error('Failed to fetch token prices: Unknown error');
66
+ }
67
+ }
package/dist/tokens.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTokenAccounts = getTokenAccounts;
4
+ const web3_js_1 = require("@solana/web3.js");
5
+ const spl_token_1 = require("@solana/spl-token");
6
+ async function getTokenAccounts(walletAddress) {
7
+ try {
8
+ const connection = new web3_js_1.Connection('https://api.mainnet-beta.solana.com', 'confirmed');
9
+ const publicKey = new web3_js_1.PublicKey(walletAddress);
10
+ const tokenAccounts = await connection.getParsedTokenAccountsByOwner(publicKey, { programId: spl_token_1.TOKEN_PROGRAM_ID });
11
+ const tokens = tokenAccounts.value
12
+ .map((accountInfo) => {
13
+ const parsedInfo = accountInfo.account.data.parsed.info;
14
+ const tokenAmount = parsedInfo.tokenAmount;
15
+ // Only include tokens with non-zero balance
16
+ if (tokenAmount.uiAmount > 0) {
17
+ return {
18
+ mint: parsedInfo.mint,
19
+ balance: parseInt(tokenAmount.amount),
20
+ decimals: tokenAmount.decimals,
21
+ uiAmount: tokenAmount.uiAmount,
22
+ };
23
+ }
24
+ return null;
25
+ })
26
+ .filter((token) => token !== null);
27
+ return tokens;
28
+ }
29
+ catch (error) {
30
+ if (error instanceof Error) {
31
+ throw new Error(`Failed to fetch token accounts: ${error.message}`);
32
+ }
33
+ throw new Error('Failed to fetch token accounts: Unknown error');
34
+ }
35
+ }
package/dist/watch.js ADDED
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.watchSolanaPrice = watchSolanaPrice;
7
+ const node_notifier_1 = __importDefault(require("node-notifier"));
8
+ const price_1 = require("./price");
9
+ async function watchSolanaPrice(options) {
10
+ const { above, below, interval } = options;
11
+ console.log('\nšŸ”” Starting SOL Price Watch Mode\n');
12
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
13
+ console.log('āš™ļø Configuration:');
14
+ if (above) {
15
+ console.log(` šŸ“ˆ Alert when price goes ABOVE: $${above.toFixed(2)}`);
16
+ }
17
+ if (below) {
18
+ console.log(` šŸ“‰ Alert when price goes BELOW: $${below.toFixed(2)}`);
19
+ }
20
+ console.log(` ā±ļø Check interval: ${interval} seconds`);
21
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
22
+ console.log('\nšŸ’” Press Ctrl+C to stop watching\n');
23
+ const state = {
24
+ aboveNotified: false,
25
+ belowNotified: false,
26
+ };
27
+ let isRunning = true;
28
+ // Handle graceful shutdown
29
+ process.on('SIGINT', () => {
30
+ console.log('\n\nšŸ‘‹ Stopping price watch...\n');
31
+ isRunning = false;
32
+ process.exit(0);
33
+ });
34
+ // Main watch loop
35
+ while (isRunning) {
36
+ try {
37
+ const currentPrice = await (0, price_1.getSolanaPrice)();
38
+ const timestamp = new Date().toLocaleTimeString();
39
+ console.log(`[${timestamp}] šŸ’° Current SOL Price: $${currentPrice.toFixed(2)}`);
40
+ // Check above threshold
41
+ if (above && currentPrice >= above && !state.aboveNotified) {
42
+ const message = `SOL crossed $${above.toFixed(2)} (currently $${currentPrice.toFixed(2)})`;
43
+ node_notifier_1.default.notify({
44
+ title: 'šŸš€ SOL Price Alert - Above Target',
45
+ message: message,
46
+ sound: true,
47
+ wait: false,
48
+ });
49
+ console.log(`\nšŸ”” ALERT: ${message}\n`);
50
+ state.aboveNotified = true;
51
+ }
52
+ // Check below threshold
53
+ if (below && currentPrice <= below && !state.belowNotified) {
54
+ const message = `SOL dropped below $${below.toFixed(2)} (currently $${currentPrice.toFixed(2)})`;
55
+ node_notifier_1.default.notify({
56
+ title: 'šŸ“‰ SOL Price Alert - Below Target',
57
+ message: message,
58
+ sound: true,
59
+ wait: false,
60
+ });
61
+ console.log(`\nšŸ”” ALERT: ${message}\n`);
62
+ state.belowNotified = true;
63
+ }
64
+ // Reset notification states when price moves back
65
+ if (above && currentPrice < above) {
66
+ state.aboveNotified = false;
67
+ }
68
+ if (below && currentPrice > below) {
69
+ state.belowNotified = false;
70
+ }
71
+ // Wait for the specified interval
72
+ await new Promise(resolve => setTimeout(resolve, interval * 1000));
73
+ }
74
+ catch (error) {
75
+ if (error instanceof Error) {
76
+ console.error(`āŒ Error fetching price: ${error.message}`);
77
+ }
78
+ else {
79
+ console.error('āŒ Unknown error occurred');
80
+ }
81
+ // Wait before retrying
82
+ await new Promise(resolve => setTimeout(resolve, interval * 1000));
83
+ }
84
+ }
85
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-balance-cli",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool to check Solana wallet balance in SOL and USD",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -40,9 +40,12 @@
40
40
  "url": ""
41
41
  },
42
42
  "dependencies": {
43
+ "@solana/spl-token": "^0.4.14",
43
44
  "@solana/web3.js": "^1.98.4",
45
+ "@types/node-notifier": "^8.0.5",
44
46
  "axios": "^1.13.2",
45
- "commander": "^14.0.2"
47
+ "commander": "^14.0.2",
48
+ "node-notifier": "^10.0.1"
46
49
  },
47
50
  "devDependencies": {
48
51
  "@changesets/cli": "^2.29.8",