@rsksmart/rsk-cli
Version:
CLI tool for Rootstock network using Viem
204 lines (203 loc) • 7.61 kB
JavaScript
#!/usr/bin/env node
import { Command } from "commander";
import { walletCommand } from "../src/commands/wallet.js";
import { balanceCommand } from "../src/commands/balance.js";
import { transferCommand } from "../src/commands/transfer.js";
import { txCommand } from "../src/commands/tx.js";
import figlet from "figlet";
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 { 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";
import { parseEther } from "viem";
const orange = chalk.rgb(255, 165, 0);
console.log(orange(figlet.textSync("Rootstock", {
font: "3D-ASCII",
horizontalLayout: "fitted",
verticalLayout: "fitted",
})));
const program = new Command();
program
.name("rsk-cli")
.description("CLI tool for interacting with Rootstock blockchain")
.version("1.2.1", "-v, --version", "Display the current version");
program
.command("wallet")
.description("Manage your wallet: create a new one, use an existing wallet, or import a custom wallet")
.action(async () => {
await walletCommand();
});
program
.command("balance")
.description("Check the balance of the saved wallet")
.option("-t, --testnet", "Check the balance on the testnet")
.option("--wallet <wallet>", "Name of the wallet")
.action(async (options) => {
await balanceCommand({
testnet: !!options.testnet,
walletName: options.wallet,
});
});
program
.command("transfer")
.description("Transfer RBTC or ERC20 tokens to the provided address")
.option("-t, --testnet", "Transfer on the testnet")
.option("--wallet <wallet>", "Name of the wallet")
.option("-a, --address <address>", "Recipient address")
.option("--token <address>", "ERC20 token contract address (optional, for token transfers)")
.option("--value <value>", "Amount to transfer")
.option("-i, --interactive", "Execute interactively and input transactions")
.option("--gas-limit <limit>", "Custom gas limit")
.option("--gas-price <price>", "Custom gas price in RBTC")
.option("--data <data>", "Custom transaction data (hex)")
.action(async (options) => {
try {
if (options.interactive) {
await batchTransferCommand({
testnet: !!options.testnet,
interactive: true,
});
return;
}
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/, "")}`
: await selectAddress();
const txOptions = {
...(options.gasLimit && { gasLimit: BigInt(options.gasLimit) }),
...(options.gasPrice && { gasPrice: parseEther(options.gasPrice.toString()) }),
...(options.data && { data: options.data })
};
await transferCommand({
testnet: !!options.testnet,
toAddress: address,
value: value,
name: options.wallet,
tokenAddress: options.token,
});
}
catch (error) {
console.error(chalk.red("Error during transfer:"), error.message || error);
}
});
program
.command("tx")
.description("Check the status of a transaction")
.option("-t, --testnet", "Check the transaction status on the testnet")
.requiredOption("-i, --txid <txid>", "Transaction ID")
.action(async (options) => {
const formattedTxId = options.txid.startsWith("0x")
? options.txid
: `0x${options.txid}`;
await txCommand({
testnet: !!options.testnet,
txid: formattedTxId,
});
});
program
.command("deploy")
.description("Deploy a contract")
.requiredOption("--abi <path>", "Path to the ABI file")
.requiredOption("--bytecode <path>", "Path to the bytecode file")
.option("--wallet <wallet>", "Name of the wallet")
.option("--args <args...>", "Constructor arguments (space-separated)")
.option("-t, --testnet", "Deploy on the testnet")
.action(async (options) => {
const args = options.args || [];
await deployCommand({
abiPath: options.abi,
bytecodePath: options.bytecode,
testnet: !!options.testnet,
args: args,
name: options.wallet,
});
});
program
.command("verify")
.description("Verify a contract")
.requiredOption("--json <path>", "Path to the JSON Standard Input")
.requiredOption("--name <name>", "Name of the contract")
.requiredOption("-a, --address <address>", "Address of the deployed contract")
.option("-t, --testnet", "Deploy on the testnet")
.option("--decodedArgs <args...>", "Decoded Constructor arguments (space-separated)")
.action(async (options) => {
const args = options.decodedArgs || [];
await verifyCommand({
jsonPath: options.json,
address: options.address,
name: options.name,
testnet: !!options.testnet,
args: args,
});
});
program
.command("contract")
.description("Interact with a contract")
.requiredOption("-a, --address <address>", "Address of a verified contract")
.option("-t, --testnet", "Deploy on the testnet")
.action(async (options) => {
await ReadContract({
address: options.address,
testnet: !!options.testnet,
});
});
program
.command("bridge")
.description("Interact with RSK bridge")
.option("-t, --testnet", "Deploy on the testnet")
.option("--wallet <wallet>", "Name of the wallet")
.action(async (options) => {
await bridgeCommand({
testnet: !!options.testnet,
name: options.wallet,
});
});
program
.command("history")
.description("Fetch history for current wallet")
.option("--apiKey <apiKey", "Alchemy API key")
.option("--number <number>", "Number of transactions to fetch")
.option("-t, --testnet", "History of wallet on the testnet")
.action(async (options) => {
await historyCommand({
testnet: !!options.testnet,
apiKey: options.apiKey,
number: options.number,
});
});
program
.command("batch-transfer")
.description("Execute batch transactions interactively or from stdin")
.option("-i, --interactive", "Execute interactively and input transactions")
.option("-t, --testnet", "Execute on the testnet")
.option("-f, --file <path>", "Execute transactions from a file")
.action(async (options) => {
try {
const interactive = !!options.interactive;
const testnet = !!options.testnet;
const file = options.file;
if (interactive && file) {
console.error(chalk.red("🚨 Cannot use both interactive mode and file input simultaneously."));
return;
}
await batchTransferCommand({
filePath: file,
testnet: testnet,
interactive: interactive,
});
}
catch (error) {
console.error(chalk.red("🚨 Error during batch transfer:"), chalk.yellow(error.message || "Unknown error"));
}
});
program.parse(process.argv);