diff --git a/README.md b/README.md index 00146e1..eb3ffb2 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ npm link hardhat-awesome-cli - Run tests (Allow you you to run tests on all files or specific files in test/) - Run scripts (Allow you you to run scripts on specific files in scripts/) - Select scripts and tests to run (Allow you to select a script to execute and all or one test to perform afterward) +- Flatten all your contract or a specific contract - Run coverage tests (Available only if solidity-coverage is installed and available as a task) - Setup chains, RPC and accounts - Add/Remove chains from the chain selection @@ -73,9 +74,16 @@ npm link hardhat-awesome-cli - Ethereum - Mainnet (chainId: 1) - Ethereum - Ropstein (chainId 3) - Ethereum - Rinkeby (chainId 4) +- Ethereum - Goerli (chainId 5) - Ethereum - Kovan (chainId 42) - Polygon - Mainnet (chainId 137) - Polygon - Mumbai (chainId 80001) +- Binance Smart Chain - Mainnet (chainId 56) +- Binance Smart Chain - Tesnet (chainId 97) +- Optimism - Mainnet (chainId 10) +- Optimism - Testnet Kovan (chainId 69) +- Avalanche - Mainnet (chainId 43114) + In 'More settings' you can also add a custom chain, create an issue or pull request to add other chains. @@ -129,6 +137,7 @@ await addressBook.retrieveContract('MockERC20', 'ethereum') - Offer to create test scripts - Tool to log all contracts deploy on each chain (1 unique contractName/chain + full log) and retrieve them (not tested yet) - hre.addressBook.{ saveContract, retrieveContract } +- Flatten your contracts (All contracts, or specific contracts) save in contractsFlatten/ ## 🏗️ To do: @@ -151,5 +160,4 @@ await addressBook.retrieveContract('MockERC20', 'ethereum') - Offer to rename the Mock contract and set all constructor input (or initialize input) via cli - Verify that the input name does not conflict with inheritance - Rename the Mock file, contract name, deployment script, test scripts (and the test values) -- Add a flatten options (All contracts, or specific contracts) save in contractsFlatten/ - Write some test on the package using mocha \ No newline at end of file diff --git a/package.json b/package.json index b9f5010..79f1bca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hardhat-awesome-cli", - "version": "0.0.2", + "version": "0.0.3", "description": "Hardhat made awesome with a flexible CLI to help run test, deploy and more.", "repository": "https://github.com/marc-aurele-besner/hardhat-awesome-cli.git", "author": "Marc-Aurele Besner <82244926+marc-aurele-besner@users.noreply.github.com>", diff --git a/src/config.ts b/src/config.ts index 0c614b4..9557733 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,14 +32,6 @@ export const DefaultChainList: IChain[] = [ currency: 'ETH', defaultBlockExplorer: 'https://rinkeby.etherscan.io/' }, - { - name: 'Ethereum - Kovan', - chainName: 'kovan', - chainId: 42, - gas: 'auto', - currency: 'ETH', - defaultBlockExplorer: 'https://kovan.etherscan.io/' - }, { name: 'Ethereum - Goerli', chainName: 'goerli', @@ -48,6 +40,14 @@ export const DefaultChainList: IChain[] = [ currency: 'ETH', defaultBlockExplorer: 'https://goerli.etherscan.io/' }, + { + name: 'Ethereum - Kovan', + chainName: 'kovan', + chainId: 42, + gas: 'auto', + currency: 'ETH', + defaultBlockExplorer: 'https://kovan.etherscan.io/' + }, { name: 'Polygon - Mainnet', chainName: 'polygon', @@ -96,10 +96,10 @@ export const DefaultChainList: IChain[] = [ { name: 'Optimism - Testnet Kovan', chainName: 'optimismTestnetKovan', - chainId: 56, + chainId: 69, gas: 'auto', currency: 'ETH', - defaultRpcUrl: 'https://bsc-dataseed1.binance.org' + defaultRpcUrl: 'https://kovan.optimism.io' }, { name: 'Avalanche - Mainnet', diff --git a/src/index.ts b/src/index.ts index 1ead6b3..167905b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ import { AwesomeAddressBook } from './AwesomeAddressBook' import { DefaultChainList, DefaultHardhatPluginsList } from './config' import MockContractsList from './mockContracts' import './type-extensions' -import { IChain, IExcludedFiles, IFileSetting, IHardhatPluginAvailableList, IMockContractsList, ITestAndScript } from './types' +import { IChain, IExcludedFiles, IFileList, IFileSetting, IHardhatPluginAvailableList, IInquirerListField, IMockContractsList } from './types' const fileHardhatAwesomeCLI = 'hardhat-awesome-cli.json' const fileEnvHardhatAwesomeCLI = '.env.hardhat-awesome-cli' @@ -22,28 +22,35 @@ const fileContractsAddressDeployedHistory = 'contractsAddressDeployedHistory.jso let contractsAddressDeployed = [] let contractsAddressDeployedHistory = [] -let inquirerRunTests: any = { +let inquirerRunTests: IInquirerListField | string = { name: 'Run tests', disabled: "We can't run tests without a test/ directory" } if (fs.existsSync('test')) { inquirerRunTests = 'Run tests' } -let inquirerRunScripts: any = { +let inquirerRunScripts: IInquirerListField | string = { name: 'Run scripts', disabled: "We can't run scripts without a scripts/ directory" } if (fs.existsSync('scripts')) { inquirerRunScripts = 'Run scripts' } -let inquirerRunMockContractCreator: any = { +let inquirerFlattenContracts: IInquirerListField | string = { + name: 'Flatten contracts', + disabled: "We can't flatten contracts without a contracts/ directory" +} +if (fs.existsSync('contracts')) { + inquirerFlattenContracts = 'Flatten contracts' +} +let inquirerRunMockContractCreator: IInquirerListField | string = { name: 'Create Mock contracts', disabled: "We can't create Mock contracts without a contracts/ directory" } if (fs.existsSync('contracts')) { inquirerRunMockContractCreator = 'Create Mock contracts' } -let inquirerFileContractsAddressDeployed: any = { +let inquirerFileContractsAddressDeployed: IInquirerListField | string = { name: 'Get the previously deployed contracts address', disabled: 'Please deploy the contracts first' } @@ -52,7 +59,7 @@ if (fs.existsSync(fileContractsAddressDeployed)) { contractsAddressDeployed = JSON.parse(rawdata) inquirerFileContractsAddressDeployed = 'Get the previously deployed contracts address' } -let inquirerFileContractsAddressDeployedHistory: any = { +let inquirerFileContractsAddressDeployedHistory: IInquirerListField | string = { name: 'Get all the previously deployed contracts address', disabled: 'Please deploy the contracts first' } @@ -196,7 +203,7 @@ const buildActivatedChainList = async () => { } const buildAllTestsList = async () => { - const testList: ITestAndScript[] = [] + const testList: IFileList[] = [] if (fs.existsSync('test')) { testList.push({ name: 'All tests', @@ -222,7 +229,7 @@ const buildAllTestsList = async () => { } const buildAllScriptsList = async () => { - const scriptsList: ITestAndScript[] = [] + const scriptsList: IFileList[] = [] if (fs.existsSync('scripts')) { const files = fs.readdirSync('scripts') files.map((file) => { @@ -242,8 +249,29 @@ const buildAllScriptsList = async () => { return scriptsList } +const buildAllContractsList = async () => { + const scontractsList: IFileList[] = [] + if (fs.existsSync('contracts')) { + const files = fs.readdirSync('contracts') + files.map((file) => { + let fileName = file.replace(/\.[^/.]+$/, '') + const words = fileName.split(' ') + for (let i = 0; i < words.length; i++) { + words[i] = words[i][0].toUpperCase() + words[i].substr(1) + } + fileName = words.join(' ') + scontractsList.push({ + name: fileName, + type: 'file', + filePath: file + }) + }) + } + return scontractsList +} + const buildTestsList = async () => { - let allTestList: ITestAndScript[] = await buildAllTestsList() + let allTestList: IFileList[] = await buildAllTestsList() let excludedFiles: IExcludedFiles[] = await buildExcludedFile() const buildFilePath: string[] = [] if (excludedFiles && excludedFiles.length > 0) { @@ -252,7 +280,7 @@ const buildTestsList = async () => { excludedFiles.map((file: IExcludedFiles) => { buildFilePath.push(file.filePath) }) - allTestList = allTestList.filter((script: ITestAndScript) => { + allTestList = allTestList.filter((script: IFileList) => { return !buildFilePath.includes(script.filePath) }) return allTestList @@ -263,7 +291,7 @@ const buildTestsList = async () => { } const buildScriptsList = async () => { - let allScriptList: ITestAndScript[] = await buildAllScriptsList() + let allScriptList: IFileList[] = await buildAllScriptsList() let excludedFiles: IExcludedFiles[] = await buildExcludedFile() const buildFilePath: string[] = [] if (excludedFiles && excludedFiles.length > 0) { @@ -272,7 +300,7 @@ const buildScriptsList = async () => { excludedFiles.map((file: any) => { buildFilePath.push(file.filePath) }) - allScriptList = allScriptList.filter((script: ITestAndScript) => { + allScriptList = allScriptList.filter((script: IFileList) => { return !buildFilePath.includes(script.filePath) }) return allScriptList @@ -282,6 +310,26 @@ const buildScriptsList = async () => { } } +const buildContractsList = async () => { + let allContractsList: IFileList[] = await buildAllContractsList() + let excludedFiles: IExcludedFiles[] = await buildExcludedFile() + const buildFilePath: string[] = [] + if (excludedFiles && excludedFiles.length > 0) { + excludedFiles = excludedFiles.filter((test: any) => test.directory === 'contracts') + if (excludedFiles && excludedFiles.length > 0) { + excludedFiles.map((file: any) => { + buildFilePath.push(file.filePath) + }) + allContractsList = allContractsList.filter((script: IFileList) => { + return !buildFilePath.includes(script.filePath) + }) + return allContractsList + } + } else { + return allContractsList + } +} + const buildMockContract = async (contractName: string) => { if (require && require.main) { const packageRootPath = path.join(path.dirname(require.main.filename), '../../../hardhat-awesome-cli/src/mockContracts') @@ -505,8 +553,8 @@ const removeExcludedFiles = async (directory: string, filePath: string) => { }) } else if (directory === 'script') { allFiles = (await buildAllScriptsList()) - .filter((script: ITestAndScript) => script.type === 'file') - .map((file: ITestAndScript) => { + .filter((script: IFileList) => script.type === 'file') + .map((file: IFileList) => { return file.filePath }) } @@ -850,7 +898,7 @@ const serveTestSelector = async (env: any, command: string, firstCommand: string const testFilesObject = await buildTestsList() let testFilesList: string[] = [] if (testFilesObject) { - testFilesList = testFilesObject.map((file: ITestAndScript) => { + testFilesList = testFilesObject.map((file: IFileList) => { return file.name }) if (testFilesList.length > 0) { @@ -864,7 +912,7 @@ const serveTestSelector = async (env: any, command: string, firstCommand: string } ]) .then(async (testSelected: { test: string }) => { - testFilesObject.forEach((file: ITestAndScript) => { + testFilesObject.forEach((file: IFileList) => { if (file.name === testSelected.test) { if (file.type === 'file') { command = command + ' test/' + file.filePath @@ -885,7 +933,7 @@ const serveScriptSelector = async (env: any, ServeTestSelector: any) => { const scriptFilesObject = await buildScriptsList() const scriptFilesList: string[] = [] if (scriptFilesObject) { - scriptFilesObject.map((file: ITestAndScript) => { + scriptFilesObject.map((file: IFileList) => { scriptFilesList.push(file.name) }) if (scriptFilesObject.length > 0) { @@ -900,7 +948,7 @@ const serveScriptSelector = async (env: any, ServeTestSelector: any) => { ]) .then(async (scriptSelected: { script: string }) => { let command = 'npx hardhat run' - scriptFilesObject.forEach((file: ITestAndScript) => { + scriptFilesObject.forEach((file: IFileList) => { if (file.name === scriptSelected.script) { if (file.type === 'file') { command = command + ' scripts/' + file.filePath @@ -918,6 +966,47 @@ const serveScriptSelector = async (env: any, ServeTestSelector: any) => { } } +const serveFlattenContractsSelector = async (env: any, command: string) => { + const contractsFilesObject = await buildContractsList() + const contractsFilesList: string[] = ['Flatten all contracts'] + if (contractsFilesObject) { + contractsFilesObject.map((file: IFileList) => { + contractsFilesList.push(file.name) + }) + if (contractsFilesList.length > 0) { + await inquirer + .prompt([ + { + type: 'list', + name: 'flatten', + message: 'Select a contract to flatten', + choices: contractsFilesList + } + ]) + .then(async (contractsSelected: { flatten: string }) => { + if (!fs.existsSync('contractsFlatten')) { + fs.mkdirSync('contractsFlatten') + } + let contractFlattenName: string = '' + if (contractsSelected.flatten !== 'Flatten all contracts') { + contractsFilesObject.forEach((file: IFileList) => { + if (file.name === contractsSelected.flatten) { + if (file.type === 'file') { + command = command + ' contracts/' + file.filePath + contractFlattenName = file.filePath.replace('.sol', 'Flatten.sol') + } + } + }) + } else { + contractFlattenName = 'AllContractsFlatten.sol' + } + await runCommand(command, '', ' > contractsFlatten/' + contractFlattenName) + await sleep(3000) + }) + } + } +} + const serveEnvBuilder = async (env: any, chainSelected: string) => { const ActivatedChainList = await buildActivatedChainList() if (ActivatedChainList.find((chain: IChain) => chain.name === chainSelected)) { @@ -1038,7 +1127,7 @@ const serveSettingSelector = async (env: any) => { } const serveExcludeFileSelector = async (option: string) => { - let allFiles: ITestAndScript[] = [] + let allFiles: IFileList[] = [] let excludedFiles: IExcludedFiles[] = await buildExcludedFile() const allFilesSelection: string[] = [] let allExcludedSelection: string[] = [] @@ -1048,10 +1137,10 @@ const serveExcludeFileSelector = async (option: string) => { allFiles = await buildAllScriptsList() } if (allFiles && allFiles.length > 0) { - if (allFiles.filter((test: ITestAndScript) => test.type === 'file').length > 0) { + if (allFiles.filter((test: IFileList) => test.type === 'file').length > 0) { allFiles - .filter((test: ITestAndScript) => test.type === 'file') - .map((file: ITestAndScript) => { + .filter((test: IFileList) => test.type === 'file') + .map((file: IFileList) => { allFilesSelection.push(file.filePath) }) } @@ -1075,7 +1164,7 @@ const serveExcludeFileSelector = async (option: string) => { } ]) .then(async (activateFilesSelected: { allFiles: string[] }) => { - allFiles.map(async (file: ITestAndScript) => { + allFiles.map(async (file: IFileList) => { if (activateFilesSelected.allFiles.includes(file.name)) { await addExcludedFiles(option, file.name) } else { @@ -1265,7 +1354,7 @@ d8' '8b 88 I8I 88 88' 88' YP .8P Y8. 88'YbdP'88 88' d8P Y8 88 YP YP '8b8' '8d8' Y88888P '8888Y' 'Y88P' YP YP YP Y88888P 'Y88P' Y88888P Y888888P ` ) - const buildMainOptions = [inquirerRunTests, inquirerRunScripts] + const buildMainOptions: any = [inquirerRunTests, inquirerRunScripts, inquirerFlattenContracts] if (inquirerRunTests === 'Run tests' && inquirerRunScripts === 'Run scripts') buildMainOptions.push('Select scripts and tests to run') const solidityCoverageDetected = await detectPackage('solidity-coverage', false, false) if (solidityCoverageDetected) buildMainOptions.push('Run coverage tests') @@ -1302,6 +1391,10 @@ YP YP '8b8' '8d8' Y88888P '8888Y' 'Y88P' YP YP YP Y88888P 'Y88P' Y8 if (answers.action === 'Run scripts') { await serveScriptSelector(env, '') } + if (answers.action === 'Flatten contracts') { + command = 'npx hardhat flatten' + await serveFlattenContractsSelector(env, command) + } if (answers.action === 'Select scripts and tests to run') { await serveScriptSelector(env, serveTestSelector) } diff --git a/src/types.ts b/src/types.ts index 16e9577..6362898 100644 --- a/src/types.ts +++ b/src/types.ts @@ -13,7 +13,7 @@ export interface IHardhatPluginAvailableList { name: string } -export interface ITestAndScript { +export interface IFileList { name: string type: string filePath: string @@ -39,3 +39,8 @@ export interface IFileSetting { activatedChain?: IChain[] excludedFiles?: IExcludedFiles[] } + +export interface IInquirerListField { + name: string + disabled?: string +}