@rsksmart/rsk-cli
Version:
CLI tool for Rootstock network using Viem
190 lines (189 loc) • 7.24 kB
JavaScript
import chalk from "chalk";
import ora from "ora";
import inquirer from "inquirer";
import ViemProvider from "../utils/viemProvider.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 logWarning(params, message) {
logMessage(params, message, chalk.yellow);
}
function startSpinner(params, spinner, message) {
if (!params.isExternal) {
spinner.start(message);
}
}
function stopSpinner(params, spinner) {
if (!params.isExternal) {
spinner.stop();
}
}
function failSpinner(params, spinner, message) {
if (!params.isExternal) {
spinner.fail(message);
}
}
function isValidAddress(address) {
const regex = /^0x[a-fA-F0-9]{40}$/;
return regex.test(address);
}
export async function ReadContract(params) {
const address = params.address.toLowerCase();
if (!isValidAddress(address)) {
const errorMessage = "Invalid address format. Please provide a valid address.";
logError(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
logInfo(params, `🔧 Initializing interaction on ${params.testnet ? "testnet" : "mainnet"}...`);
const baseUrl = params.testnet
? "https://be.explorer.testnet.rootstock.io"
: "https://be.explorer.rootstock.io";
logInfo(params, `🔎 Checking if contract ${address} is verified...`);
const spinner = params.isExternal ? ora({ isEnabled: false }) : ora();
startSpinner(params, spinner, "⏳ Checking contract verification...");
try {
const response = await fetch(`${baseUrl}/api?module=verificationResults&action=getVerification&address=${address}`);
if (!response.ok) {
const errorMessage = "Error during verification check.";
failSpinner(params, spinner, errorMessage);
return {
error: errorMessage,
success: false,
};
}
const resData = await response.json();
if (!resData.data) {
const errorMessage = "Contract verification not found.";
failSpinner(params, spinner, errorMessage);
return {
error: errorMessage,
success: false,
};
}
const { abi } = resData.data;
const readFunctions = abi.filter((item) => item.type === "function" &&
(item.stateMutability === "view" || item.stateMutability === "pure"));
if (readFunctions.length === 0) {
const errorMessage = "No read functions found in the contract.";
stopSpinner(params, spinner);
logWarning(params, errorMessage);
return {
error: errorMessage,
success: false,
};
}
stopSpinner(params, spinner);
let selectedFunction;
let selectedAbiFunction;
if (params.isExternal) {
if (!params.functionName) {
return {
error: "Function name is required when using external mode.",
success: false,
};
}
selectedFunction = params.functionName;
selectedAbiFunction = readFunctions.find((item) => item.name === selectedFunction);
if (!selectedAbiFunction) {
return {
error: `Function '${selectedFunction}' not found in contract.`,
success: false,
};
}
}
else {
const questions = [
{
type: "list",
name: "selectedFunction",
message: "Select a read function to call:",
choices: [...readFunctions.map((item) => item.name)],
},
];
const answers = await inquirer.prompt(questions);
selectedFunction = answers.selectedFunction;
logSuccess(params, `📜 You selected: ${selectedFunction}`);
selectedAbiFunction = readFunctions.find((item) => item.name === selectedFunction);
}
let args = [];
if (selectedAbiFunction.inputs && selectedAbiFunction.inputs.length > 0) {
if (params.isExternal) {
if (!params.args ||
params.args.length !== selectedAbiFunction.inputs.length) {
return {
error: `Function '${selectedFunction}' requires ${selectedAbiFunction.inputs.length} arguments.`,
success: false,
};
}
args = params.args;
}
else {
const argQuestions = selectedAbiFunction.inputs.map((input) => ({
type: "input",
name: input.name,
message: `Enter the value for argument ${input.name} (${input.type}):`,
}));
const answers = await inquirer.prompt(argQuestions);
args = selectedAbiFunction.inputs.map((input) => answers[input.name]);
}
}
startSpinner(params, spinner, "⏳ Calling read function...");
const provider = new ViemProvider(params.testnet);
const publicClient = await provider.getPublicClient();
try {
const data = await publicClient.readContract({
address,
abi,
functionName: selectedFunction,
args,
});
const explorerUrl = params.testnet
? `https://explorer.testnet.rootstock.io/address/${address}`
: `https://explorer.rootstock.io/address/${address}`;
stopSpinner(params, spinner);
logSuccess(params, `✅ Function ${selectedFunction} called successfully!`);
logSuccess(params, `🔧 Result: ${data}`);
logInfo(params, `🔗 View on Explorer: ${explorerUrl}`);
return {
success: true,
data: {
contractAddress: address,
network: params.testnet ? "Rootstock Testnet" : "Rootstock Mainnet",
functionName: selectedFunction,
result: data,
explorerUrl: explorerUrl,
},
};
}
catch (error) {
const errorMessage = `Error while calling function ${selectedFunction}.`;
failSpinner(params, spinner, errorMessage);
return {
error: errorMessage,
success: false,
};
}
}
catch (error) {
const errorMessage = "Error during contract interaction.";
failSpinner(params, spinner, errorMessage);
return {
error: errorMessage,
success: false,
};
}
}