UNPKG

@rsksmart/rsk-cli

Version:

CLI tool for Rootstock network using Viem

141 lines (140 loc) 4.81 kB
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); } };