diff --git a/bin/index.ts b/bin/index.ts index f240d42..00a026a 100644 --- a/bin/index.ts +++ b/bin/index.ts @@ -9,11 +9,12 @@ import chalk from "chalk"; import { deployCommand } from "../src/commands/deploy.js"; import { verifyCommand } from "../src/commands/verify.js"; import { ReadContract } from "../src/commands/contract.js"; +import { Address } from "viem"; import { bridgeCommand } from "../src/commands/bridge.js"; - interface CommandOptions { testnet?: boolean; - address?: string; + address?: Address; + contract?: Address; value?: string; txid?: string; abi?: string; @@ -56,8 +57,9 @@ program .command("balance") .description("Check the balance of the saved wallet") .option("-t, --testnet", "Check the balance on the testnet") + .option("-a ,--address
", "Token holder address") .action(async (options: CommandOptions) => { - await balanceCommand(!!options.testnet); + await balanceCommand(!!options.testnet, options.address); }); program diff --git a/package.json b/package.json index 2108ddb..657e88d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "scripts": { "build": "tsc", + "dev": "tsc -w", "wallet": "pnpm run build && node dist/bin/index.js wallet", "balance": "pnpm run build && node dist/bin/index.js balance", "transfer": "pnpm run build && node dist/bin/index.js transfer --testnet --address 0xa5f45f5bddefC810C48aCC1D5CdA5e5a4c6BC59E --value 0.001", diff --git a/src/commands/balance.ts b/src/commands/balance.ts index ab6717d..cb5edd9 100644 --- a/src/commands/balance.ts +++ b/src/commands/balance.ts @@ -1,54 +1,124 @@ import ViemProvider from "../utils/viemProvider.js"; -import fs from "fs"; import chalk from "chalk"; -import { walletFilePath } from "../utils/constants.js"; +import inquirer from "inquirer"; +import { + getTokenInfo, + isERC20Contract, + resolveTokenAddress, +} from "../utils/tokenHelper.js"; +import ora from "ora"; +import { + getAddress, + isValidContract, + validateAndFormatAddress, +} from "../utils/index.js"; +import { Address, formatUnits } from "viem"; +import { TOKENS } from "../constants/tokenAdress.js"; + +export async function balanceCommand( + testnet: boolean, + holderAddress?: Address +) { + const spinner = ora(); -export async function balanceCommand(testnet: boolean) { try { - if (!fs.existsSync(walletFilePath)) { - console.log( - chalk.red("🚫 No saved wallet found. Please create a wallet first.") - ); + const targetAddress = getAddress(holderAddress); + + if (!targetAddress) { return; } + const provider = new ViemProvider(testnet); + const client = await provider.getPublicClient(); - const walletsData = JSON.parse(fs.readFileSync(walletFilePath, "utf8")); + const { token } = await inquirer.prompt({ + type: "list", + name: "token", + message: "Select token to check balance:", + choices: ["rBTC", ...Object.keys(TOKENS), "Custom Token"], + }); - if (!walletsData.currentWallet || !walletsData.wallets) { + if (token === "rBTC") { + spinner.start(chalk.white("🔍 Checking balance...")); + const balance = await client.getBalance({ address: targetAddress }); + const rbtcBalance = formatUnits(balance, 18); + + spinner.succeed(chalk.green("Balance retrieved successfully")); + + console.log( + chalk.white(`📄 Wallet Address:`), + chalk.green(targetAddress) + ); + console.log( + chalk.white(`🌐 Network:`), + chalk.green(testnet ? "Rootstock Testnet" : "Rootstock Mainnet") + ); console.log( - chalk.red( - "⚠️ No valid wallet found. Please create or import a wallet first." + chalk.white(`💰 Current Balance:`), + chalk.green(`${rbtcBalance} RBTC`) + ); + console.log( + chalk.blue( + `🔗 Ensure that transactions are being conducted on the correct network.` ) ); - throw new Error(); + return; } - const { currentWallet, wallets } = walletsData; + let tokenAddress: Address; - const wallet = wallets[currentWallet]; - const { address } = wallet; - - if (!address) { - console.log(chalk.red("⚠️ No valid address found in the saved wallet.")); - return; + if (token === "Custom Token") { + spinner.stop(); + const { address } = await inquirer.prompt({ + type: "input", + name: "address", + message: "Enter the token address:", + validate: async (input: string) => { + try { + const address = input as Address; + const formattedContractAddress = validateAndFormatAddress(address); + if (!formattedContractAddress) { + console.log(chalk.red()); + return "🚫 Invalid contract address"; + } + if (!(await isValidContract(client, formattedContractAddress))) { + return "🚫 Invalid contract address or contract not found"; + } + if (!(await isERC20Contract(client, formattedContractAddress))) { + return "🚫 Invalid contract address, only ERC20 tokens are supported"; + } + return true; + } catch { + return false; + } + }, + }); + tokenAddress = address.toLowerCase() as Address; + } else { + tokenAddress = resolveTokenAddress(token, testnet); } - const provider = new ViemProvider(testnet); - const client = await provider.getPublicClient(); + spinner.start(chalk.white("🔍 Checking balance...")); - const balance = await client.getBalance({ address }); + const { balance, decimals, name, symbol } = await getTokenInfo( + client, + tokenAddress, + targetAddress + ); + const formattedBalance = formatUnits(balance, decimals); - const rbtcBalance = Number(balance) / 10 ** 18; + spinner.succeed(chalk.green("Balance retrieved successfully")); - console.log(chalk.white(`📄 Wallet Address:`), chalk.green(address)); console.log( - chalk.white(`🌐 Network:`), - chalk.green(testnet ? "Rootstock Testnet" : "Rootstock Mainnet") - ); - console.log( - chalk.white(`💰 Current Balance:`), - chalk.green(`${rbtcBalance} RBTC`) + chalk.white(`📄 Token Information: + Name: ${chalk.green(name)} + Contract: ${chalk.green(tokenAddress)} + 👤 Holder Address: ${chalk.green(targetAddress)} + 💰 Balance: ${chalk.green(`${formattedBalance} ${symbol}`)} + 🌐 Network: ${chalk.green( + testnet ? "Rootstock Testnet" : "Rootstock Mainnet" + )}`) ); + console.log( chalk.blue( `🔗 Ensure that transactions are being conducted on the correct network.` @@ -63,5 +133,7 @@ export async function balanceCommand(testnet: boolean) { } else { console.error(chalk.red("🚨 An unknown error occurred.")); } + } finally { + spinner.stop(); } } diff --git a/src/constants/tokenAdress.ts b/src/constants/tokenAdress.ts new file mode 100644 index 0000000..d3d803d --- /dev/null +++ b/src/constants/tokenAdress.ts @@ -0,0 +1,16 @@ +import { Address } from "viem"; + +export const TOKENS: Record