diff --git a/bin/index.ts b/bin/index.ts index facdc33..08615ef 100644 --- a/bin/index.ts +++ b/bin/index.ts @@ -13,6 +13,7 @@ import { Address } from "viem"; import { bridgeCommand } from "../src/commands/bridge.js"; import { batchTransferCommand } from "../src/commands/batchTransfer.js"; import { historyCommand } from "../src/commands/history.js"; +import { selectAddress } from "../src/commands/selectAddress.js"; interface CommandOptions { testnet?: boolean; @@ -50,7 +51,7 @@ const program = new Command(); program .name("rsk-cli") .description("CLI tool for interacting with Rootstock blockchain") - .version("1.0.8", "-v, --version", "Display the current version"); + .version("1.0.9", "-v, --version", "Display the current version"); program .command("wallet") @@ -76,10 +77,30 @@ program .description("Transfer rBTC to the provided address") .option("-t, --testnet", "Transfer on the testnet") .option("--wallet ", "Name of the wallet") - .requiredOption("-a, --address
", "Recipient address") + .option("-a, --address
", "Recipient address") .requiredOption("--value ", "Amount to transfer in rBTC") .action(async (options: CommandOptions) => { try { + if (!options.value) { + throw new Error("Value is required for the transfer."); + } + + const value = parseFloat(options.value); + + if (isNaN(value) || value <= 0) { + throw new Error("Invalid value specified for transfer."); + } + + const address = options.address + ? (`0x${options.address.replace(/^0x/, "")}` as `0x${string}`) + : await selectAddress(); + + await transferCommand(!!options.testnet, address, value); + } catch (error: any) { + console.error( + chalk.red("Error during transfer:"), + error.message || error + ); const address = `0x${options.address!.replace( /^0x/, "" @@ -90,8 +111,6 @@ program parseFloat(options.value!), options.wallet! ); - } catch (error) { - console.error(chalk.red("Error during transfer:"), error); } }); diff --git a/package.json b/package.json index acdea1b..7278f90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rsksmart/rsk-cli", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for Rootstock network using Viem", "repository": { "type": "git", diff --git a/src/commands/addressbook.ts b/src/commands/addressbook.ts new file mode 100644 index 0000000..abbddf3 --- /dev/null +++ b/src/commands/addressbook.ts @@ -0,0 +1,117 @@ +import inquirer from "inquirer"; +import chalk from "chalk"; +import { loadWallets } from "../utils/index.js"; +import { walletFilePath } from "../utils/constants.js"; +import { writeWalletData } from "./wallet.js"; + +export async function addressBookCommand() { + try { + const walletsDataString = loadWallets(); + const walletsData = JSON.parse(walletsDataString); + walletsData.addressBook = walletsData.addressBook || {}; + + const actions = [ + "➕ Add Address", + "📖 View Address Book", + "✏️ Update Address", + "🗑️ Delete Address", + ]; + + const { action } = await inquirer.prompt([ + { + type: "list", + name: "action", + message: "What would you like to do with your address book?", + choices: actions, + }, + ]); + + if (action === "➕ Add Address") { + const { label, address } = await inquirer.prompt([ + { + type: "input", + name: "label", + message: "Enter a label for the address:", + }, + { + type: "input", + name: "address", + message: "Enter the address:", + }, + ]); + + if (walletsData.addressBook[label]) { + console.log(chalk.red(`❌ Label "${label}" already exists.`)); + return; + } + + walletsData.addressBook[label] = address; + console.log(chalk.green(`✅ Address added under label "${label}".`)); + } + + if (action === "📖 View Address Book") { + const addressBook = walletsData.addressBook; + if (Object.keys(addressBook).length === 0) { + console.log(chalk.red("❌ Address book is empty.")); + return; + } + + console.log(chalk.green("📖 Address Book:")); + Object.entries(addressBook).forEach(([label, address]) => { + console.log(chalk.blue(`- ${label}: ${address}`)); + }); + } + + if (action === "✏️ Update Address") { + const labels = Object.keys(walletsData.addressBook); + if (labels.length === 0) { + console.log(chalk.red("❌ Address book is empty.")); + return; + } + + const { label } = await inquirer.prompt([ + { + type: "list", + name: "label", + message: "Select the address you want to update:", + choices: labels, + }, + ]); + + const { newAddress } = await inquirer.prompt([ + { + type: "input", + name: "newAddress", + message: `Enter the new address for "${label}":`, + }, + ]); + + walletsData.addressBook[label] = newAddress; + console.log(chalk.green(`✅ Address for "${label}" updated.`)); + } + + if (action === "🗑️ Delete Address") { + const labels = Object.keys(walletsData.addressBook); + if (labels.length === 0) { + console.log(chalk.red("❌ Address book is empty.")); + return; + } + + const { label } = await inquirer.prompt([ + { + type: "list", + name: "label", + message: "Select the address you want to delete:", + choices: labels, + }, + ]); + + delete walletsData.addressBook[label]; + console.log(chalk.red(`🗑️ Address with label "${label}" deleted.`)); + } + + writeWalletData(walletFilePath, walletsData); + } catch (error: any) { + console.error(chalk.red("❌ Error managing address book:"), error.message); + } +} diff --git a/src/commands/selectAddress.ts b/src/commands/selectAddress.ts new file mode 100644 index 0000000..019a4f3 --- /dev/null +++ b/src/commands/selectAddress.ts @@ -0,0 +1,51 @@ +import { loadWallets } from "../utils/index.js"; +import inquirer from "inquirer"; +import chalk from "chalk"; +import { Address } from "viem"; + +export async function selectAddress(): Promise
{ + try { + const walletsDataString = loadWallets(); + const walletsData = JSON.parse(walletsDataString); + walletsData.addressBook = walletsData.addressBook || {}; + + const addressBook = walletsData.addressBook; + const addressBookLabels = Object.keys(addressBook); + + const addressChoices = [ + ...addressBookLabels.map((label) => ({ + name: `${label} (${addressBook[label]})`, + value: addressBook[label], + })), + { name: "Enter a custom address", value: "custom" }, + ]; + + const { selectedAddress } = await inquirer.prompt([ + { + type: "list", + name: "selectedAddress", + message: "Select an address:", + choices: addressChoices, + }, + ]); + + if (selectedAddress === "custom") { + const { customAddress } = await inquirer.prompt([ + { + type: "input", + name: "customAddress", + message: "Enter the address:", + }, + ]); + return customAddress; + } + + return selectedAddress; + } catch (error: any) { + console.error( + chalk.red("❌ Error selecting address:"), + chalk.yellow(error.message || error) + ); + throw error; + } +} diff --git a/src/commands/wallet.ts b/src/commands/wallet.ts index 4c44c10..f0b0553 100644 --- a/src/commands/wallet.ts +++ b/src/commands/wallet.ts @@ -6,6 +6,7 @@ import crypto from "crypto"; import { loadWallets } from "../utils/index.js"; import { walletFilePath } from "../utils/constants.js"; import path from "path"; +import { addressBookCommand } from "./addressbook.js"; type InquirerAnswers = { action?: string; @@ -51,6 +52,7 @@ export async function walletCommand() { "📝 Update wallet name", "📂 Backup wallet data", "❌ Delete wallet", + "📖 Address Book", ], }, ]; @@ -505,6 +507,10 @@ export async function walletCommand() { await backupCommand(backupPath); } + + if (action === "📖 Address Book") { + await addressBookCommand(); + } } catch (error: any) { console.error( chalk.red("❌ Error creating or managing wallets:"), @@ -513,7 +519,7 @@ export async function walletCommand() { } } -async function writeWalletData(filePath: string, data: any) { +export async function writeWalletData(filePath: string, data: any) { try { fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8"); console.log(chalk.green(`💾 Changes saved at ${filePath}`));