const fs = require('fs').promises; const path = require('path'); const { utils } = require('ethers'); // Capture the command line arguments const [_, __, DeploymentName, ChainId, ChainName] = process.argv; if (!DeploymentName || !ChainId || !ChainName) { console.error('Please provide DeploymentName, ChainId and ChainName as command line arguments.'); process.exit(1); } async function findDeploymentDirectory(basePath, prefix, chainId) { const dirs = await fs.readdir(basePath); const filtered = dirs.filter((dir) => dir.startsWith(prefix)); if (filtered.length !== 1) { for (const dir of filtered) { console.error(dir); } throw new Error(`Expected to find one directory, but found ${filtered.length} dirs.`); } if (chainId) { return path.join(basePath, filtered[0], ChainId); } else { return path.join(basePath, filtered[0]); } } async function findApiDirectory(basePath, prefix) { const dirs = await fs.readdir(basePath); const filteredDirs = dirs.filter((dir) => dir.startsWith(prefix) && !dir.includes('.t.')); const foundDirs = []; for (const dir of filteredDirs) { const contents = await fs.readdir(path.join(basePath, dir)); if (contents.includes(`${prefix}.json`)) { foundDirs.push(dir); } } if (foundDirs.length !== 1) { for (const dir of foundDirs) { console.error(dir); } throw new Error( `Expected to find one directory, but found the above ${foundDirs.length} dirs.` ); } return path.join(basePath, foundDirs[0]); } function transformArgsForEncoding(args, inputs) { if (typeof args === 'string' && args.startsWith('(') && args.endsWith(')')) { // Convert the tuple string into an array args = args .slice(1, -1) .split(',') .map((item) => item.trim()); } return args.map((arg, index) => { if (inputs[index].type === 'tuple') { // If the type is a tuple, recursively transform its arguments return transformArgsForEncoding(arg, inputs[index].components); } else { return arg; } }); } async function encodeConstructorArgs(contractName, args) { const contractDirectory = await findApiDirectory('out', contractName); const filePath = path.join(contractDirectory, `${contractName}.json`); const fileContents = await fs.readFile(filePath, 'utf-8'); const data = JSON.parse(fileContents); const abi = data.abi; // Example usage const constructorAbi = abi.find((item) => item.type === 'constructor'); // console.log('\n\nABI tuple definition:', constructorAbi.inputs); if (!constructorAbi) { throw new Error('Constructor ABI not found for the contract.'); } // Convert tuple string representation to arrays const transformedArgs = transformArgsForEncoding(args, constructorAbi.inputs); const encodedArgs = utils.defaultAbiCoder.encode(constructorAbi.inputs, transformedArgs); return encodedArgs; } async function outputLibrariesMessage(data) { if (data.libraries && data.libraries.length > 0) { console.log( '\n\nPlease make sure you have the following libraries added into your foundry.toml file:\n' ); console.log('libraries = ['); data.libraries.forEach((lib) => { console.log(`\t"${lib}",`); }); console.log(']'); } else { console.log('No libraries found in the broadcast JSON.'); } } async function generateBashScript() { const deploymentPath = await findDeploymentDirectory('broadcast', DeploymentName, ChainId); const files = await fs.readdir(deploymentPath); const targetFile = files.find((file) => file.endsWith('-latest.json')); if (!targetFile) { throw new Error('No matching JSON file found.'); } const fileContents = await fs.readFile(path.join(deploymentPath, targetFile), 'utf-8'); const data = JSON.parse(fileContents); const transactions = data.transactions.filter( (transaction) => transaction.transactionType === 'CREATE' ); const bashCommands = await Promise.all( transactions.map(async (transaction) => { let baseCmd = `forge verify-contract ${transaction.contractAddress} ${transaction.contractName} --chain ${ChainName} --watch`; if (transaction.arguments && transaction.arguments.length > 0) { // Placeholder for abi encoding const abiEncodedArgs = await encodeConstructorArgs( transaction.contractName, transaction.arguments ); baseCmd += ` --constructor-args ${abiEncodedArgs}`; } return baseCmd; }) ); await fs.writeFile('verify.sh', bashCommands.join('\n')); console.log('verify.sh has been generated successfully.'); // Call the function outputLibrariesMessage(data); } generateBashScript().catch((error) => { console.error('An error occurred:', error); });