@rsksmart/rsk-cli
Version:
CLI tool for Rootstock network using Viem
244 lines (243 loc) • 10.3 kB
JavaScript
import ViemProvider from "../utils/viemProvider.js";
import chalk from "chalk";
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 { formatUnits } from "viem";
import { TOKENS } from "../constants/tokenAdress.js";
import fs from "fs";
import { walletFilePath } from "../utils/constants.js";
import { getConfig } from "./config.js";
function logMessage(params, message, color = chalk.white) {
if (!params.isExternal) {
console.log(color(message));
}
}
function logError(params, message) {
logMessage(params, `❌ ${message}`, chalk.red);
}
function logSuccess(params, message) {
logMessage(params, message, chalk.green);
}
function logInfo(params, message) {
logMessage(params, message, chalk.blue);
}
function startSpinner(params, spinner, message) {
if (!params.isExternal) {
spinner.start(message);
}
}
function stopSpinner(params, spinner) {
if (!params.isExternal) {
spinner.stop();
}
}
function succeedSpinner(params, spinner, message) {
if (!params.isExternal) {
spinner.succeed(message);
}
}
export async function balanceCommand(params) {
const config = getConfig();
const isTestnet = params.testnet !== undefined ? params.testnet : (config.defaultNetwork === 'testnet');
const spinner = params.isExternal ? ora({ isEnabled: false }) : ora();
try {
let targetAddress;
if (params.address) {
targetAddress = getAddress(params.address);
}
else {
const walletsData = params.isExternal && params.walletsData
? params.walletsData
: JSON.parse(fs.readFileSync(walletFilePath, "utf8"));
if (!walletsData.currentWallet || !walletsData.wallets) {
const errorMessage = "No valid wallet found. Please create or import a wallet first.";
logError(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
const { currentWallet, wallets } = walletsData;
let wallet = wallets[currentWallet];
if (params.walletName) {
if (!wallets[params.walletName]) {
const errorMessage = "Wallet with the provided name does not exist.";
logError(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
else {
wallet = wallets[params.walletName];
}
}
const address = wallet.address;
if (!address) {
const errorMessage = "No valid address found in the saved wallet.";
logError(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
targetAddress = getAddress(address);
}
if (!targetAddress) {
const errorMessage = "Invalid address format.";
logError(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
const provider = new ViemProvider(isTestnet);
const client = await provider.getPublicClient();
let token;
if (params.token) {
token = params.token;
}
else if (params.isExternal) {
return {
error: "Token parameter is required when using external mode.",
success: false,
};
}
else {
try {
const { token: selectedToken } = await inquirer.prompt({
type: "list",
name: "token",
message: "Select token to check balance:",
choices: ["rBTC", ...Object.keys(TOKENS), "Custom Token"],
});
token = selectedToken;
}
catch {
token = "rBTC";
}
}
if (token === "rBTC") {
startSpinner(params, spinner, `⏳ Checking balance...`);
const balance = await client.getBalance({ address: targetAddress });
const rbtcBalance = formatUnits(balance, 18);
succeedSpinner(params, spinner, chalk.white("Balance retrieved successfully"));
if (config.displayPreferences.compactMode) {
logSuccess(params, `${targetAddress}: ${rbtcBalance} RBTC`);
}
else {
logSuccess(params, `📄 Wallet Address: ${targetAddress}`);
logSuccess(params, `🌐 Network: ${isTestnet ? "Rootstock Testnet" : "Rootstock Mainnet"}`);
logSuccess(params, `💰 Current Balance: ${rbtcBalance} RBTC`);
logInfo(params, "🔗 Ensure that transactions are being conducted on the correct network.");
}
return {
success: true,
data: {
walletAddress: targetAddress,
network: isTestnet ? "Rootstock Testnet" : "Rootstock Mainnet",
balance: rbtcBalance,
symbol: "RBTC",
tokenType: "native",
},
};
}
let tokenAddress;
if (token === "Custom Token") {
if (params.isExternal && params.customTokenAddress) {
const formattedContractAddress = validateAndFormatAddress(params.customTokenAddress);
if (!formattedContractAddress) {
return { error: "Invalid custom token address provided.", success: false };
}
if (!(await isValidContract(client, formattedContractAddress))) {
return { error: "Invalid contract address or contract not found.", success: false };
}
if (!(await isERC20Contract(client, formattedContractAddress))) {
return { error: "Invalid contract address, only ERC20 tokens are supported.", success: false };
}
tokenAddress = params.customTokenAddress.toLowerCase();
}
else if (params.isExternal && !params.customTokenAddress) {
return { error: "Custom token address is required when using Custom Token in external mode.", success: false };
}
else {
stopSpinner(params, spinner);
const { address } = await inquirer.prompt({
type: "input",
name: "address",
message: "Enter the token address:",
validate: async (input) => {
try {
const address = input;
const formattedContractAddress = validateAndFormatAddress(address);
if (!formattedContractAddress) {
logError(params, "Invalid contract address");
return "🚫 Invalid contract address";
}
if (!(await isValidContract(client, formattedContractAddress))) {
logError(params, "Invalid contract address or contract not found");
return "🚫 Invalid contract address or contract not found";
}
if (!(await isERC20Contract(client, formattedContractAddress))) {
logError(params, "Invalid contract address, only ERC20 tokens are supported");
return "🚫 Invalid contract address, only ERC20 tokens are supported";
}
return true;
}
catch {
return false;
}
},
});
tokenAddress = address.toLowerCase();
}
}
else {
tokenAddress = resolveTokenAddress(token, isTestnet);
}
startSpinner(params, spinner, `⏳ Checking balance...`);
const { balance, decimals, name, symbol } = await getTokenInfo(client, tokenAddress, targetAddress);
const formattedBalance = formatUnits(balance, decimals);
succeedSpinner(params, spinner, chalk.white("Balance retrieved successfully"));
if (config.displayPreferences.compactMode) {
logSuccess(params, `${targetAddress}: ${formattedBalance} ${symbol}`);
}
else {
logSuccess(params, `📄 Token Information:
Name: ${name}
Contract: ${tokenAddress}
👤 Holder Address: ${targetAddress}
💰 Balance: ${formattedBalance} ${symbol}
🌐 Network: ${isTestnet ? "Rootstock Testnet" : "Rootstock Mainnet"}`);
logInfo(params, "🔗 Ensure that transactions are being conducted on the correct network.");
}
if (params.isExternal) {
return {
success: true,
data: {
walletAddress: targetAddress,
network: isTestnet ? "Rootstock Testnet" : "Rootstock Mainnet",
balance: formattedBalance,
symbol: symbol,
tokenType: "erc20",
tokenName: name,
tokenSymbol: symbol,
tokenContract: tokenAddress,
decimals: decimals,
},
};
}
}
catch (error) {
const errorMessage = "Error checking balance, please check the token address.";
logError(params, errorMessage);
return { error: errorMessage, success: false };
}
finally {
if (!params.isExternal) {
spinner.stop();
}
}
}