@rsksmart/rsk-cli
Version:
CLI tool for Rootstock network using Viem
141 lines (140 loc) • 4.81 kB
JavaScript
import { isAddress, keccak256, stringToHex } from "viem";
import chalk from "chalk";
import fs from "fs";
import { ALLOWED_BRIDGE_METHODS, METHOD_TYPES, walletFilePath, } from "./constants.js";
export function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function validateAndFormatAddress(address) {
if (!address)
return undefined;
const formattedAddress = address.toLowerCase();
if (!isAddress(formattedAddress)) {
console.log(chalk.red("🚫 Invalid address"));
return undefined;
}
return formattedAddress;
}
export function getRootstockChainId(testnet) {
return testnet ? 31 : 30;
}
export function validateAndFormatAddressRSK(address, testnet = false) {
if (!address)
return undefined;
const lower = `0x${address.replace(/^0x/, "").toLowerCase()}`;
const hex = lower.replace(/^0x/, "");
if (!/^([a-f0-9]{40})$/.test(hex))
return undefined;
const input = address.startsWith("0x") ? address : `0x${address}`;
const hasUpper = /[A-F]/.test(input);
const hasLower = /[a-f]/.test(input);
const isMixedCase = hasUpper && hasLower;
if (isMixedCase) {
const eip1191 = toEip1191ChecksumAddress(lower, testnet);
const eip55 = toEip55ChecksumAddress(lower);
if (input !== eip1191 && input !== eip55) {
return undefined;
}
}
// Accept all-lowercase or valid checksummed (EIP-55 or EIP-1191). Return lowercase for internal use.
return lower;
}
export function toEip1191ChecksumAddress(address, testnet = false) {
const chainId = getRootstockChainId(testnet);
const clean = address.replace(/^0x/, "");
if (!/^([a-fA-F0-9]{40})$/.test(clean)) {
return (`0x${clean.toLowerCase()}`);
}
const lower = clean.toLowerCase();
const input = `${chainId}0x${lower}`;
const hash = keccak256(stringToHex(input)).slice(2);
let checksummed = "";
for (let i = 0; i < lower.length; i++) {
const h = parseInt(hash[i], 16);
checksummed += h >= 8 ? lower[i].toUpperCase() : lower[i];
}
return (`0x${checksummed}`);
}
export function toEip55ChecksumAddress(address) {
const clean = address.replace(/^0x/, "");
if (!/^([a-fA-F0-9]{40})$/.test(clean)) {
return (`0x${clean.toLowerCase()}`);
}
const lower = clean.toLowerCase();
const hash = keccak256(stringToHex(lower)).slice(2);
let checksummed = "";
for (let i = 0; i < lower.length; i++) {
const h = parseInt(hash[i], 16);
checksummed += h >= 8 ? lower[i].toUpperCase() : lower[i];
}
return (`0x${checksummed}`);
}
export async function isValidContract(client, address) {
try {
const code = await client.getBytecode({ address });
return code !== undefined && code !== "0x";
}
catch (error) {
return false;
}
}
export function getAddress(address) {
if (address) {
return validateAndFormatAddress(address);
}
if (!fs.existsSync(walletFilePath)) {
console.log(chalk.red("🚫 No saved wallet found"));
return undefined;
}
try {
const { currentWallet, wallets } = JSON.parse(fs.readFileSync(walletFilePath, "utf8"));
const savedAddress = wallets[currentWallet].address;
return validateAndFormatAddress(savedAddress);
}
catch (error) {
console.log(chalk.red("⚠️ Invalid wallet data"));
return undefined;
}
}
export function loadWallets() {
if (fs.existsSync(walletFilePath)) {
const walletsData = fs.readFileSync(walletFilePath, "utf8");
if (walletsData) {
return walletsData ?? JSON.stringify({ wallets: {} });
}
}
return JSON.stringify({ wallets: {} });
}
export const formatBridgeFragments = (bridgeAbi) => {
const formatWriteMethod = (fragment) => {
return {
...fragment,
constant: false,
stateMutability: "nonpayable",
};
};
try {
const formattedBridgeAbi = bridgeAbi.map((fragment) => {
if (!fragment || !fragment.name)
throw new Error(`Invalid bridge abi fragment: ${JSON.stringify(fragment)}`);
if (isAllowedMethod(fragment.name, "write")) {
return formatWriteMethod(fragment);
}
return fragment;
});
return formattedBridgeAbi;
}
catch (error) {
console.error("Error while formatting bridge abi fragments");
}
};
export const isAllowedMethod = (name, type) => {
try {
if (!METHOD_TYPES[type])
throw new Error(`Invalid method type "${type}"`);
return ALLOWED_BRIDGE_METHODS[type].includes(name);
}
catch (error) {
console.error(error);
}
};