From 3c939bdbe1e5f2acb16428a92f84e0d7f77a6422 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 11 Nov 2022 01:08:56 -0500 Subject: [PATCH 1/5] fix(pg): refactor remote bundling logic --- .changeset/good-maps-run.md | 5 + packages/executor/.gitignore | 1 + packages/executor/src/index.ts | 1 - packages/executor/src/utils/compile.ts | 2 +- packages/executor/src/utils/etherscan.ts | 10 +- packages/plugins/src/hardhat/artifacts.ts | 78 +++--- packages/plugins/src/hardhat/deployments.ts | 5 +- packages/plugins/src/hardhat/tasks.ts | 289 ++++++++++---------- 8 files changed, 197 insertions(+), 194 deletions(-) create mode 100644 .changeset/good-maps-run.md diff --git a/.changeset/good-maps-run.md b/.changeset/good-maps-run.md new file mode 100644 index 000000000..d8a43fbbc --- /dev/null +++ b/.changeset/good-maps-run.md @@ -0,0 +1,5 @@ +--- +'@chugsplash/plugins': patch +--- + +Refactor remote bundling logic diff --git a/packages/executor/.gitignore b/packages/executor/.gitignore index 51eb69f27..9deb5f74e 100644 --- a/packages/executor/.gitignore +++ b/packages/executor/.gitignore @@ -1,2 +1,3 @@ dist/ .env +cache \ No newline at end of file diff --git a/packages/executor/src/index.ts b/packages/executor/src/index.ts index 3eff637d3..d9b875bef 100644 --- a/packages/executor/src/index.ts +++ b/packages/executor/src/index.ts @@ -112,7 +112,6 @@ export class ChugSplashExecutor extends BaseServiceV2 { chugSplashManager: manager, bundleState, bundle, - deployerAddress: await signer.getAddress(), parsedConfig: canonicalConfig, deployer: signer, hide: false, diff --git a/packages/executor/src/utils/compile.ts b/packages/executor/src/utils/compile.ts index fc4263e0c..937900f5e 100644 --- a/packages/executor/src/utils/compile.ts +++ b/packages/executor/src/utils/compile.ts @@ -32,7 +32,7 @@ export const compileRemoteBundle = async ( }> => { const canonicalConfig = await fetchChugSplashConfig(configUri) const bundle = await hre.run('chugsplash-bundle-remote', { - deployConfig: canonicalConfig, + canonicalConfig, }) return { bundle, canonicalConfig } } diff --git a/packages/executor/src/utils/etherscan.ts b/packages/executor/src/utils/etherscan.ts index 03e9954c9..ec3d7824a 100644 --- a/packages/executor/src/utils/etherscan.ts +++ b/packages/executor/src/utils/etherscan.ts @@ -57,14 +57,14 @@ export const verifyChugSplashConfig = async (hre: any, configUri: string) => { canonicalConfig.contracts )) { const artifact = artifacts[contractConfig.contract] - const { abi, contractName, sourceName, sources, immutableReferences } = - artifact - const { constructorArgValues } = await getConstructorArgs( + const { abi, contractName, sourceName, fileOutput } = artifact + const { constructorArgValues } = getConstructorArgs( canonicalConfig, referenceName, abi, - sources, - immutableReferences + fileOutput, + sourceName, + contractName ) const contractAddress = await ChugSplashManager.implementations( diff --git a/packages/plugins/src/hardhat/artifacts.ts b/packages/plugins/src/hardhat/artifacts.ts index 18234f648..8430a8696 100644 --- a/packages/plugins/src/hardhat/artifacts.ts +++ b/packages/plugins/src/hardhat/artifacts.ts @@ -88,32 +88,23 @@ export const getStorageLayout = async ( return (output as any).storageLayout } -export const getCreationCode = async ( +export const getCreationCode = ( + bytecode: string, parsedConfig: ChugSplashConfig, - referenceName: string -): Promise => { - const contractConfig = parsedConfig.contracts[referenceName] - - const { abi, sourceName, contractName, bytecode } = getContractArtifact( - contractConfig.contract + referenceName: string, + abi: any, + compilerOutput: any, + sourceName: string, + contractName: string +): string => { + const { constructorArgTypes, constructorArgValues } = getConstructorArgs( + parsedConfig, + referenceName, + abi, + compilerOutput, + sourceName, + contractName ) - const buildInfo = await getBuildInfo(sourceName, contractName) - const output = buildInfo.output.contracts[sourceName][contractName] - const immutableReferences: { - [astId: number]: { - length: number - start: number - }[] - } = output.evm.deployedBytecode.immutableReferences - - const { constructorArgTypes, constructorArgValues } = - await getConstructorArgs( - parsedConfig, - referenceName, - abi, - buildInfo.output.sources, - immutableReferences - ) const creationCodeWithConstructorArgs = bytecode.concat( remove0x( @@ -126,18 +117,18 @@ export const getCreationCode = async ( // TODO: I think this should go in /core now that we don't rely on hardhat as a dependency. // We could potentially move other contracts in here to core too. -export const getConstructorArgs = async ( +export const getConstructorArgs = ( parsedConfig: ChugSplashConfig, referenceName: string, abi: any, - sources: any, - immutableReferences: { - [astId: number]: { - length: number - start: number - }[] - } -): Promise<{ constructorArgTypes: any[]; constructorArgValues: any[] }> => { + compilerOutput: any, + sourceName: string, + contractName: string +): { constructorArgTypes: any[]; constructorArgValues: any[] } => { + const immutableReferences = + compilerOutput.contracts[sourceName][contractName].evm.deployedBytecode + .immutableReferences + const contractConfig = parsedConfig.contracts[referenceName] const constructorFragment = abi.find( @@ -152,7 +143,7 @@ export const getConstructorArgs = async ( // Maps a constructor argument name to the corresponding variable name in the ChugSplash config const constructorArgNamesToImmutableNames = {} - for (const source of Object.values(sources)) { + for (const source of Object.values(compilerOutput.sources)) { for (const contractNode of (source as any).ast.nodes) { if ( contractNode.nodeType === 'ContractDefinition' && @@ -265,20 +256,19 @@ export const getConstructorArgNameForImmutableVariable = ( ) } -export const getImmutableVariables = async ( - contractConfig -): Promise => { - const { sourceName, contractName } = getContractArtifact( - contractConfig.contract - ) - const buildInfo = await getBuildInfo(sourceName, contractName) - const output = buildInfo.output.contracts[sourceName][contractName] +export const getImmutableVariables = ( + compilerOutput: any, + sourceName: string, + contractName: string +): string[] => { const immutableReferences: { [astId: number]: { length: number start: number }[] - } = output.evm.deployedBytecode.immutableReferences + } = + compilerOutput.contracts[sourceName][contractName].evm.deployedBytecode + .immutableReferences if ( immutableReferences === undefined || @@ -288,7 +278,7 @@ export const getImmutableVariables = async ( } const immutableVariables: string[] = [] - for (const source of Object.values(buildInfo.output.sources)) { + for (const source of Object.values(compilerOutput.sources)) { for (const contractNode of (source as any).ast.nodes) { if (contractNode.nodeType === 'ContractDefinition') { for (const node of contractNode.nodes) { diff --git a/packages/plugins/src/hardhat/deployments.ts b/packages/plugins/src/hardhat/deployments.ts index d1c504651..2937662a1 100644 --- a/packages/plugins/src/hardhat/deployments.ts +++ b/packages/plugins/src/hardhat/deployments.ts @@ -77,7 +77,7 @@ export const deployChugSplashConfig = async ( const { bundleId } = await hre.run('chugsplash-commit', { deployConfig: configRelativePath, - local: false, + local, log: verbose, }) @@ -113,7 +113,7 @@ export const deployChugSplashConfig = async ( const { bundle } = await hre.run('chugsplash-propose', { deployConfig: configRelativePath, - local: false, + local, log: verbose, }) @@ -148,7 +148,6 @@ export const deployChugSplashConfig = async ( chugSplashManager: ChugSplashManager, bundleState, bundle, - deployerAddress, parsedConfig, deployer, hide, diff --git a/packages/plugins/src/hardhat/tasks.ts b/packages/plugins/src/hardhat/tasks.ts index 4cdf3dced..f25fde480 100644 --- a/packages/plugins/src/hardhat/tasks.ts +++ b/packages/plugins/src/hardhat/tasks.ts @@ -107,8 +107,28 @@ subtask(TASK_CHUGSPLASH_BUNDLE_LOCAL) parsed.contracts )) { const storageLayout = await getStorageLayout(contractConfig.contract) - const creationCode = await getCreationCode(parsed, referenceName) - const immutableVariables = await getImmutableVariables(contractConfig) + + const { abi, sourceName, contractName, bytecode } = getContractArtifact( + contractConfig.contract + ) + const { output: compilerOutput } = await getBuildInfo( + sourceName, + contractName + ) + const creationCode = getCreationCode( + bytecode, + parsed, + referenceName, + abi, + compilerOutput, + sourceName, + contractName + ) + const immutableVariables = getImmutableVariables( + compilerOutput, + sourceName, + contractName + ) artifacts[referenceName] = { creationCode, storageLayout, @@ -121,97 +141,90 @@ subtask(TASK_CHUGSPLASH_BUNDLE_LOCAL) ) subtask(TASK_CHUGSPLASH_BUNDLE_REMOTE) - .addParam('deployConfig', undefined, undefined, types.any) + .addParam('canonicalConfig', undefined, undefined, types.any) .setAction( async ( - args: { deployConfig: CanonicalChugSplashConfig }, + args: { canonicalConfig: CanonicalChugSplashConfig }, hre ): Promise => { - const artifacts = {} - for (const [referenceName] of Object.entries( - args.deployConfig.contracts - )) { - for (const contract of args.deployConfig.inputs) { - const solcBuild: SolcBuild = await hre.run( - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - { - quiet: true, - solcVersion: contract.solcVersion, - } - ) - - let output: any // TODO: Compiler output - if (solcBuild.isSolcJs) { - output = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { - input: contract.input, - solcJsPath: solcBuild.compilerPath, - }) - } else { - output = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { - input: contract.input, - solcPath: solcBuild.compilerPath, - }) + const parsedCanonicalConfig = parseChugSplashConfig( + args.canonicalConfig + ) as CanonicalChugSplashConfig + + const compilerOutputs: any[] = [] + // Get the compiler output for each compiler input. + for (const compilerInput of parsedCanonicalConfig.inputs) { + const solcBuild: SolcBuild = await hre.run( + TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, + { + quiet: true, + solcVersion: compilerInput.solcVersion, } + ) + + let compilerOutput: any // TODO: Compiler output type + if (solcBuild.isSolcJs) { + compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { + input: compilerInput.input, + solcJsPath: solcBuild.compilerPath, + }) + } else { + compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { + input: compilerInput.input, + solcPath: solcBuild.compilerPath, + }) + } + compilerOutputs.push(compilerOutput) + } - for (const [sourceName, fileOutput] of Object.entries( - output.contracts + const artifacts = {} + // Generate an artifact for each contract in the ChugSplash config. + for (const [referenceName, contractConfig] of Object.entries( + parsedCanonicalConfig.contracts + )) { + let compilerOutputIndex = 0 + while (artifacts[referenceName] === undefined) { + // Iterate through the sources in the current compiler output to find the one that + // contains this contract. + const compilerOutput = compilerOutputs[compilerOutputIndex] + for (const [sourceName, sourceOutput] of Object.entries( + compilerOutput.contracts )) { - for (const [contractName, contractOutput] of Object.entries( - fileOutput - )) { - const bytecode = add0x(contractOutput.evm.bytecode.object) - const { constructorArgTypes, constructorArgValues } = - await getConstructorArgs( - args.deployConfig, - referenceName, - contractOutput.abi, - output.sources, - output.contracts[sourceName][contractName].evm - .deployedBytecode.immutableReferences - ) - - const creationCode = bytecode.concat( - remove0x( - utils.defaultAbiCoder.encode( - constructorArgTypes, - constructorArgValues - ) - ) + // Check if the current source contains the contract. + if (sourceOutput.hasOwnProperty(contractConfig.contract)) { + const contractOutput = sourceOutput[contractConfig.contract] + + const creationCode = getCreationCode( + add0x(contractOutput.evm.bytecode.object), + parsedCanonicalConfig, + referenceName, + contractOutput.abi, + compilerOutput, + sourceName, + contractConfig.contract + ) + const immutableVariables = getImmutableVariables( + compilerOutput, + sourceName, + contractConfig.contract ) - - const immutableVariables: string[] = [] - for (const source of Object.values(output.sources)) { - for (const contractNode of (source as any).ast.nodes) { - if (contractNode.nodeType === 'ContractDefinition') { - for (const node of contractNode.nodes) { - if (node.mutability === 'immutable') { - immutableVariables.push(node.name) - } - } - } - } - } artifacts[referenceName] = { - bytecode: add0x(contractOutput.evm.bytecode.object), creationCode, storageLayout: contractOutput.storageLayout, - contractName, - sourceName, - abi: contractOutput.abi, - sources: output.sources, immutableVariables, - immutableReferences: - output.contracts[sourceName][contractName].evm - .deployedBytecode.immutableReferences, } + // We can exit the loop at this point since each contract only has a single artifact + // associated with it. + break } } + compilerOutputIndex += 1 } } return makeActionBundleFromConfig( - args.deployConfig, + parsedCanonicalConfig, artifacts, process.env ) @@ -453,7 +466,6 @@ subtask(TASK_CHUGSPLASH_EXECUTE) types.any ) .addParam('bundle', 'The bundle to be executed', undefined, types.any) - .addParam('deployerAddress', 'Address of the user deploying the bundle') .addParam( 'parsedConfig', 'Parsed ChugSplash configuration', @@ -468,7 +480,6 @@ subtask(TASK_CHUGSPLASH_EXECUTE) chugSplashManager: Contract bundleState: ChugSplashBundleState bundle: any // todo - figure out a type for this - deployerAddress: any // todo - figure out a type for this parsedConfig: ChugSplashConfig deployer: any // todo - figure out a type for this hide: boolean @@ -479,7 +490,6 @@ subtask(TASK_CHUGSPLASH_EXECUTE) chugSplashManager, bundleState, bundle, - deployerAddress, parsedConfig, deployer, hide, @@ -515,8 +525,6 @@ subtask(TASK_CHUGSPLASH_EXECUTE) // If the bundle hasn't already been completed in an earlier call, complete the bundle by // executing all the SetImplementation actions in a single transaction. - let finalDeploymentTxnHash: string - let finalDeploymentReceipt: any if (bundleState.status !== ChugSplashBundleStatus.COMPLETED) { const setImplActions = bundle.actions.slice( firstSetImplementationActionIndex @@ -527,8 +535,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) setImplActions.map((action) => action.proof.actionIndex), setImplActions.map((action) => action.proof.siblings) ) - finalDeploymentReceipt = await finalDeploymentTxn.wait() - finalDeploymentTxnHash = finalDeploymentTxn.hash + await finalDeploymentTxn.wait() } // Withdraw all available funds from the chugSplashManager. @@ -539,7 +546,9 @@ subtask(TASK_CHUGSPLASH_EXECUTE) if (chugsplashManagerBalance.sub(totalDebt).gt(0)) { await (await chugSplashManager.withdrawOwnerETH()).wait() } - const deployerDebt = await chugSplashManager.debt(deployerAddress) + const deployerDebt = await chugSplashManager.debt( + await deployer.getAddress() + ) if (deployerDebt.gt(0)) { await (await chugSplashManager.claimExecutorPayment()).wait() } @@ -582,64 +591,64 @@ subtask(TASK_CHUGSPLASH_EXECUTE) } } - if ((await getChainId(hre.ethers.provider)) !== 31337) { - createDeploymentFolderForNetwork( - hre.network.name, - hre.config.paths.deployed - ) - - for (const [referenceName, contractConfig] of Object.entries( - parsedConfig.contracts - )) { - const artifact = getContractArtifact(contractConfig.contract) - const { sourceName, contractName, bytecode, abi } = artifact - - const buildInfo = await getBuildInfo(sourceName, contractName) - const output = buildInfo.output.contracts[sourceName][contractName] - const immutableReferences: { - [astId: number]: { - length: number - start: number - }[] - } = output.evm.deployedBytecode.immutableReferences - - const metadata = - buildInfo.output.contracts[sourceName][contractName].metadata - const { devdoc, userdoc } = JSON.parse(metadata).output - const { constructorArgValues } = await getConstructorArgs( - parsedConfig, - referenceName, - abi, - buildInfo.output.sources, - immutableReferences - ) - const deploymentArtifact = { - contractName, - address: contractConfig.address, - abi, - transactionHash: finalDeploymentTxnHash, - solcInputHash: buildInfo.id, - receipt: finalDeploymentReceipt, - numDeployments: 1, - metadata, - args: constructorArgValues, - bytecode, - deployedBytecode: await hre.ethers.provider.getCode( - contractConfig.address - ), - devdoc, - userdoc, - storageLayout: await getStorageLayout(contractConfig.contract), - } - - writeDeploymentArtifact( - hre.network.name, - hre.config.paths.deployed, - deploymentArtifact, - referenceName - ) - } - } + // if ((await getChainId(hre.ethers.provider)) !== 31337) { + // createDeploymentFolderForNetwork( + // hre.network.name, + // hre.config.paths.deployed + // ) + + // for (const [referenceName, contractConfig] of Object.entries( + // parsedConfig.contracts + // )) { + // const artifact = getContractArtifact(contractConfig.contract) + // const { sourceName, contractName, bytecode, abi } = artifact + + // const buildInfo = await getBuildInfo(sourceName, contractName) + // const output = buildInfo.output.contracts[sourceName][contractName] + // const immutableReferences: { + // [astId: number]: { + // length: number + // start: number + // }[] + // } = output.evm.deployedBytecode.immutableReferences + + // const metadata = + // buildInfo.output.contracts[sourceName][contractName].metadata + // const { devdoc, userdoc } = JSON.parse(metadata).output + // const { constructorArgValues } = getConstructorArgs( + // parsedConfig,outdated + // referenceName, + // abi, + // buildInfo.output.sources, + // immutableReferences + // ) + // const deploymentArtifact = { + // contractName, + // address: contractConfig.address, + // abi, + // transactionHash: finalDeploymentTxnHash, + // solcInputHash: buildInfo.id, + // receipt: finalDeploymentReceipt, + // numDeployments: 1, + // metadata, + // args: constructorArgValues, + // bytecode, + // deployedBytecode: await hre.ethers.provider.getCode( + // contractConfig.address + // ), + // devdoc, + // userdoc, + // storageLayout: await getStorageLayout(contractConfig.contract), + // } + + // writeDeploymentArtifact( + // hre.network.name, + // hre.config.paths.deployed, + // deploymentArtifact, + // referenceName + // ) + // } + // } if (!hide) { const deployments = {} @@ -995,7 +1004,7 @@ task(TASK_CHUGSPLASH_CHECK_BUNDLE) const bundle: ChugSplashActionBundle = await hre.run( TASK_CHUGSPLASH_BUNDLE_REMOTE, { - deployConfig: config, + canonicalConfig: config, } ) spinner.succeed('Built artifact bundle') From 7b3379162063972ebe078631abe60d32a624bf02 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 11 Nov 2022 13:00:29 -0500 Subject: [PATCH 2/5] fix: integrate etherscan verification into executor --- .changeset/honest-shrimps-bake.md | 6 + packages/core/src/utils.ts | 5 +- packages/executor/src/index.ts | 7 +- packages/executor/src/utils/compile.ts | 99 ++------------- packages/executor/src/utils/etherscan.ts | 46 ++++--- packages/executor/src/utils/initialize.ts | 4 +- packages/plugins/src/hardhat/artifacts.ts | 96 +++++++++++++- packages/plugins/src/hardhat/deployments.ts | 6 +- packages/plugins/src/hardhat/predeploys.ts | 2 +- packages/plugins/src/hardhat/tasks.ts | 131 +++++--------------- 10 files changed, 180 insertions(+), 222 deletions(-) create mode 100644 .changeset/honest-shrimps-bake.md diff --git a/.changeset/honest-shrimps-bake.md b/.changeset/honest-shrimps-bake.md new file mode 100644 index 000000000..8d73f1419 --- /dev/null +++ b/.changeset/honest-shrimps-bake.md @@ -0,0 +1,6 @@ +--- +'@chugsplash/executor': patch +'@chugsplash/plugins': patch +--- + +Integrate etherscan verification into executor diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 48d7a8598..d8ac4c8f7 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -7,7 +7,6 @@ import { ChugSplashRegistryABI, ChugSplashManagerABI, ChugSplashManagerProxyArtifact, - // CHUGSPLASH_REGISTRY_ADDRESS, CHUGSPLASH_REGISTRY_PROXY_ADDRESS, } from '@chugsplash/contracts' @@ -69,14 +68,14 @@ export const writeDeploymentArtifact = ( export const getProxyAddress = ( projectName: string, - target: string + referenceName: string ): string => { // const chugSplashManagerAddress = getChugSplashManagerAddress(projectName) const chugSplashManagerAddress = getChugSplashManagerProxyAddress(projectName) return utils.getCreate2Address( chugSplashManagerAddress, - utils.keccak256(utils.toUtf8Bytes(target)), + utils.keccak256(utils.toUtf8Bytes(referenceName)), utils.solidityKeccak256( ['bytes', 'bytes'], [ diff --git a/packages/executor/src/index.ts b/packages/executor/src/index.ts index d9b875bef..96aeb42aa 100644 --- a/packages/executor/src/index.ts +++ b/packages/executor/src/index.ts @@ -7,8 +7,9 @@ import { CHUGSPLASH_REGISTRY_PROXY_ADDRESS, } from '@chugsplash/contracts' import { ChugSplashBundleState } from '@chugsplash/core' +import { getChainId } from '@eth-optimism/core-utils' -import { compileRemoteBundle } from './utils' +import { compileRemoteBundle, verifyChugSplashConfig } from './utils' type Options = { network: string @@ -116,6 +117,10 @@ export class ChugSplashExecutor extends BaseServiceV2 { deployer: signer, hide: false, }) + + if ((await getChainId(this.state.wallet.provider)) !== 31337) { + await verifyChugSplashConfig(hre, proposalEvent.args.configUri) + } } } } diff --git a/packages/executor/src/utils/compile.ts b/packages/executor/src/utils/compile.ts index 937900f5e..5493d9d8d 100644 --- a/packages/executor/src/utils/compile.ts +++ b/packages/executor/src/utils/compile.ts @@ -1,17 +1,12 @@ import * as dotenv from 'dotenv' -import { SolcBuild } from 'hardhat/types' -import { - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - TASK_COMPILE_SOLIDITY_RUN_SOLCJS, - TASK_COMPILE_SOLIDITY_RUN_SOLC, -} from 'hardhat/builtin-tasks/task-names' -import { add0x } from '@eth-optimism/core-utils' import { CanonicalChugSplashConfig, ChugSplashActionBundle, } from '@chugsplash/core' -import { create } from 'ipfs-http-client' -import { ContractArtifact } from '@chugsplash/plugins' +import { + TASK_CHUGSPLASH_FETCH, + TASK_CHUGSPLASH_BUNDLE_REMOTE, +} from '@chugsplash/plugins' // Load environment variables from .env dotenv.config() @@ -30,89 +25,11 @@ export const compileRemoteBundle = async ( bundle: ChugSplashActionBundle canonicalConfig: CanonicalChugSplashConfig }> => { - const canonicalConfig = await fetchChugSplashConfig(configUri) - const bundle = await hre.run('chugsplash-bundle-remote', { + const canonicalConfig = await hre.run(TASK_CHUGSPLASH_FETCH, { + configUri, + }) + const bundle = await hre.run(TASK_CHUGSPLASH_BUNDLE_REMOTE, { canonicalConfig, }) return { bundle, canonicalConfig } } - -export const getArtifactsFromCanonicalConfig = async ( - hre: any, - canonicalConfig: CanonicalChugSplashConfig -): Promise<{ - [contractName: string]: ContractArtifact -}> => { - const artifacts = {} - for (const source of canonicalConfig.inputs) { - const solcBuild: SolcBuild = await hre.run( - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - { - quiet: true, - solcVersion: source.solcVersion, - } - ) - - let output: any // TODO: Compiler output - if (solcBuild.isSolcJs) { - output = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { - input: source.input, - solcJsPath: solcBuild.compilerPath, - }) - } else { - output = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { - input: source.input, - solcPath: solcBuild.compilerPath, - }) - } - - for (const [sourceName, fileOutput] of Object.entries(output.contracts)) { - for (const [contractName, contractOutput] of Object.entries(fileOutput)) { - artifacts[contractName] = { - bytecode: add0x(contractOutput.evm.bytecode.object), - storageLayout: contractOutput.storageLayout, - contractName, - sourceName, - abi: contractOutput.abi, - sources: output.sources, - immutableReferences: - output.contracts[sourceName][contractName].evm.deployedBytecode - .immutableReferences, - } - } - } - } - return artifacts -} - -// TODO: change file name or add another file -export const fetchChugSplashConfig = async ( - configUri: string -): Promise => { - const projectCredentials = `${process.env.IPFS_PROJECT_ID}:${process.env.IPFS_API_KEY_SECRET}` - const ipfs = create({ - host: 'ipfs.infura.io', - port: 5001, - protocol: 'https', - headers: { - authorization: `Basic ${Buffer.from(projectCredentials).toString( - 'base64' - )}`, - }, - }) - - let config: CanonicalChugSplashConfig - if (configUri.startsWith('ipfs://')) { - const decoder = new TextDecoder() - let data = '' - const stream = await ipfs.cat(configUri.replace('ipfs://', '')) - for await (const chunk of stream) { - // Chunks of data are returned as a Uint8Array. Convert it back to a string - data += decoder.decode(chunk, { stream: true }) - } - config = JSON.parse(data) - } else { - throw new Error('unsupported URI type') - } - return config -} diff --git a/packages/executor/src/utils/etherscan.ts b/packages/executor/src/utils/etherscan.ts index ec3d7824a..65b127fd8 100644 --- a/packages/executor/src/utils/etherscan.ts +++ b/packages/executor/src/utils/etherscan.ts @@ -2,10 +2,17 @@ import assert from 'assert' import { Contract } from 'ethers' import { + CanonicalChugSplashConfig, CompilerInput, getChugSplashManagerProxyAddress, + getProxyAddress, + parseChugSplashConfig, } from '@chugsplash/core' -import { getConstructorArgs } from '@chugsplash/plugins' +import { + getConstructorArgs, + TASK_CHUGSPLASH_FETCH, + getArtifactsFromParsedCanonicalConfig, +} from '@chugsplash/plugins' import { TASK_VERIFY_GET_ETHERSCAN_ENDPOINT } from '@nomiclabs/hardhat-etherscan/dist/src/constants' import { EtherscanURLs } from '@nomiclabs/hardhat-etherscan/dist/src/types' import { @@ -28,11 +35,6 @@ import { ChugSplashManagerABI } from '@chugsplash/contracts' import { EthereumProvider } from 'hardhat/types' import { request } from 'undici' -import { - fetchChugSplashConfig, - getArtifactsFromCanonicalConfig, -} from './compile' - export interface EtherscanResponseBody { status: string message: string @@ -44,30 +46,32 @@ export const RESPONSE_OK = '1' export const verifyChugSplashConfig = async (hre: any, configUri: string) => { const { etherscanApiKey, etherscanApiEndpoints } = await getEtherscanInfo(hre) - const canonicalConfig = await fetchChugSplashConfig(configUri) - const artifacts = await getArtifactsFromCanonicalConfig(hre, canonicalConfig) - + const canonicalConfig = await hre.run(TASK_CHUGSPLASH_FETCH, { + configUri, + }) + const artifacts = await getArtifactsFromParsedCanonicalConfig( + hre, + parseChugSplashConfig(canonicalConfig) as CanonicalChugSplashConfig + ) const ChugSplashManager = new Contract( getChugSplashManagerProxyAddress(canonicalConfig.options.projectName), ChugSplashManagerABI, hre.ethers.provider ) - for (const [referenceName, contractConfig] of Object.entries( - canonicalConfig.contracts - )) { - const artifact = artifacts[contractConfig.contract] - const { abi, contractName, sourceName, fileOutput } = artifact + for (const referenceName of Object.keys(canonicalConfig.contracts)) { + const artifact = artifacts[referenceName] + const { abi, contractName, sourceName, compilerOutput } = artifact const { constructorArgValues } = getConstructorArgs( canonicalConfig, referenceName, abi, - fileOutput, + compilerOutput, sourceName, contractName ) - const contractAddress = await ChugSplashManager.implementations( + const implementationAddress = await ChugSplashManager.implementations( referenceName ) @@ -79,7 +83,7 @@ export const verifyChugSplashConfig = async (hre: any, configUri: string) => { hre.network.name, hre.network.provider, etherscanApiEndpoints, - contractAddress, + implementationAddress, sourceName, contractName, abi, @@ -88,6 +92,14 @@ export const verifyChugSplashConfig = async (hre: any, configUri: string) => { compilerInput.solcVersion, constructorArgValues ) + + await linkProxyWithImplementation( + etherscanApiEndpoints, + etherscanApiKey, + getProxyAddress(canonicalConfig.options.projectName, referenceName), + implementationAddress, + contractName + ) } } diff --git a/packages/executor/src/utils/initialize.ts b/packages/executor/src/utils/initialize.ts index 9d6578dbb..398e41cac 100644 --- a/packages/executor/src/utils/initialize.ts +++ b/packages/executor/src/utils/initialize.ts @@ -1,5 +1,5 @@ import { ethers } from 'ethers' -import { deployChugSplashContracts } from '@chugsplash/plugins' +import { deployChugSplashPredeploys } from '@chugsplash/plugins' import { CHUGSPLASH_CONSTRUCTOR_ARGS, ChugSplashBootLoaderArtifact, @@ -29,7 +29,7 @@ export const initializeChugSplashContracts = async ( hre: any, deployer: ethers.Signer ) => { - await deployChugSplashContracts(hre, deployer) + await deployChugSplashPredeploys(hre, deployer) const { etherscanApiKey, etherscanApiEndpoints } = await getEtherscanInfo(hre) diff --git a/packages/plugins/src/hardhat/artifacts.ts b/packages/plugins/src/hardhat/artifacts.ts index 8430a8696..dbc384bde 100644 --- a/packages/plugins/src/hardhat/artifacts.ts +++ b/packages/plugins/src/hardhat/artifacts.ts @@ -1,9 +1,19 @@ import path from 'path' import * as semver from 'semver' -import { SolidityStorageLayout, ChugSplashConfig } from '@chugsplash/core' -import { remove0x } from '@eth-optimism/core-utils' +import { + SolidityStorageLayout, + ChugSplashConfig, + CanonicalChugSplashConfig, +} from '@chugsplash/core' +import { add0x, remove0x } from '@eth-optimism/core-utils' import { ethers, utils } from 'ethers' +import { + TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, + TASK_COMPILE_SOLIDITY_RUN_SOLC, + TASK_COMPILE_SOLIDITY_RUN_SOLCJS, +} from 'hardhat/builtin-tasks/task-names' +import { SolcBuild } from 'hardhat/types' // TODO export type ContractArtifact = any @@ -291,3 +301,85 @@ export const getImmutableVariables = ( } return immutableVariables } + +export const getArtifactsFromParsedCanonicalConfig = async ( + hre: any, + parsedCanonicalConfig: CanonicalChugSplashConfig +): Promise<{ [referenceName: string]: any }> => { + const compilerOutputs: any[] = [] + // Get the compiler output for each compiler input. + for (const compilerInput of parsedCanonicalConfig.inputs) { + const solcBuild: SolcBuild = await hre.run( + TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, + { + quiet: true, + solcVersion: compilerInput.solcVersion, + } + ) + + let compilerOutput: any // TODO: Compiler output type + if (solcBuild.isSolcJs) { + compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { + input: compilerInput.input, + solcJsPath: solcBuild.compilerPath, + }) + } else { + compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { + input: compilerInput.input, + solcPath: solcBuild.compilerPath, + }) + } + compilerOutputs.push(compilerOutput) + } + + const artifacts = {} + // Generate an artifact for each contract in the ChugSplash config. + for (const [referenceName, contractConfig] of Object.entries( + parsedCanonicalConfig.contracts + )) { + let compilerOutputIndex = 0 + while (artifacts[referenceName] === undefined) { + // Iterate through the sources in the current compiler output to find the one that + // contains this contract. + const compilerOutput = compilerOutputs[compilerOutputIndex] + for (const [sourceName, sourceOutput] of Object.entries( + compilerOutput.contracts + )) { + // Check if the current source contains the contract. + if (sourceOutput.hasOwnProperty(contractConfig.contract)) { + const contractOutput = sourceOutput[contractConfig.contract] + + const creationCode = getCreationCode( + add0x(contractOutput.evm.bytecode.object), + parsedCanonicalConfig, + referenceName, + contractOutput.abi, + compilerOutput, + sourceName, + contractConfig.contract + ) + const immutableVariables = getImmutableVariables( + compilerOutput, + sourceName, + contractConfig.contract + ) + + artifacts[referenceName] = { + creationCode, + storageLayout: contractOutput.storageLayout, + immutableVariables, + abi: contractOutput.abi, + compilerOutput, + sourceName, + contractName: contractConfig.contract, + } + // We can exit the loop at this point since each contract only has a single artifact + // associated with it. + break + } + } + compilerOutputIndex += 1 + } + } + return artifacts +} diff --git a/packages/plugins/src/hardhat/deployments.ts b/packages/plugins/src/hardhat/deployments.ts index 2937662a1..8d7da9632 100644 --- a/packages/plugins/src/hardhat/deployments.ts +++ b/packages/plugins/src/hardhat/deployments.ts @@ -28,7 +28,7 @@ import { writeHardhatSnapshotId } from './utils' * @param hre Hardhat Runtime Environment. * @param contractName Name of the contract in the config file. */ -export const deployContracts = async ( +export const deployConfigs = async ( hre: any, verbose: boolean, hide: boolean, @@ -36,11 +36,11 @@ export const deployContracts = async ( ) => { const fileNames = fs.readdirSync(hre.config.paths.chugsplash) for (const fileName of fileNames) { - await deployChugSplashConfig(hre, fileName, verbose, hide, local) + await deployConfig(hre, fileName, verbose, hide, local) } } -export const deployChugSplashConfig = async ( +export const deployConfig = async ( hre: any, fileName: string, verbose: boolean, diff --git a/packages/plugins/src/hardhat/predeploys.ts b/packages/plugins/src/hardhat/predeploys.ts index 27d5806d3..93fa7ae79 100644 --- a/packages/plugins/src/hardhat/predeploys.ts +++ b/packages/plugins/src/hardhat/predeploys.ts @@ -21,7 +21,7 @@ import { CHUGSPLASH_CONSTRUCTOR_ARGS, } from '@chugsplash/contracts' -export const deployChugSplashContracts = async ( +export const deployChugSplashPredeploys = async ( hre: HardhatRuntimeEnvironment, deployer: ethers.Signer ): Promise => { diff --git a/packages/plugins/src/hardhat/tasks.ts b/packages/plugins/src/hardhat/tasks.ts index f25fde480..3d4b7e34b 100644 --- a/packages/plugins/src/hardhat/tasks.ts +++ b/packages/plugins/src/hardhat/tasks.ts @@ -1,20 +1,16 @@ import * as path from 'path' import * as fs from 'fs' -import { Contract, ethers, utils } from 'ethers' +import { Contract, ethers } from 'ethers' import { subtask, task, types } from 'hardhat/config' -import { SolcBuild } from 'hardhat/types' import { TASK_NODE, TASK_COMPILE, - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - TASK_COMPILE_SOLIDITY_RUN_SOLCJS, - TASK_COMPILE_SOLIDITY_RUN_SOLC, TASK_TEST, TASK_RUN, } from 'hardhat/builtin-tasks/task-names' import { create, IPFSHTTPClient } from 'ipfs-http-client' -import { add0x, getChainId, remove0x } from '@eth-optimism/core-utils' +import { getChainId } from '@eth-optimism/core-utils' import { computeBundleId, makeActionBundleFromConfig, @@ -30,8 +26,6 @@ import { isSetImplementationAction, fromRawChugSplashAction, getProxyAddress, - createDeploymentFolderForNetwork, - writeDeploymentArtifact, log as ChugSplashLog, } from '@chugsplash/core' import { @@ -45,37 +39,37 @@ import Hash from 'ipfs-only-hash' import * as dotenv from 'dotenv' import { + getArtifactsFromParsedCanonicalConfig, getBuildInfo, - getConstructorArgs, getContractArtifact, getCreationCode, getImmutableVariables, getStorageLayout, } from './artifacts' -import { deployContracts } from './deployments' -import { deployChugSplashContracts } from './predeploys' +import { deployConfigs } from './deployments' +import { deployChugSplashPredeploys } from './predeploys' import { writeHardhatSnapshotId } from './utils' // Load environment variables from .env dotenv.config() // internal tasks -const TASK_CHUGSPLASH_LOAD = 'chugsplash-load' -const TASK_CHUGSPLASH_FETCH = 'chugsplash-fetch' -const TASK_CHUGSPLASH_BUNDLE_LOCAL = 'chugsplash-bundle-local' -const TASK_CHUGSPLASH_BUNDLE_REMOTE = 'chugsplash-bundle-remote' +export const TASK_CHUGSPLASH_LOAD = 'chugsplash-load' +export const TASK_CHUGSPLASH_FETCH = 'chugsplash-fetch' +export const TASK_CHUGSPLASH_BUNDLE_LOCAL = 'chugsplash-bundle-local' +export const TASK_CHUGSPLASH_BUNDLE_REMOTE = 'chugsplash-bundle-remote' // public tasks -const TASK_CHUGSPLASH_DEPLOY = 'chugsplash-deploy' -const TASK_CHUGSPLASH_REGISTER = 'chugsplash-register' -const TASK_CHUGSPLASH_LIST_ALL_PROJECTS = 'chugsplash-list-projects' -const TASK_CHUGSPLASH_CHECK_BUNDLE = 'chugsplash-check-bundle' -const TASK_CHUGSPLASH_COMMIT = 'chugsplash-commit' -const TASK_CHUGSPLASH_PROPOSE = 'chugsplash-propose' -const TASK_CHUGSPLASH_APPROVE = 'chugsplash-approve' -const TASK_CHUGSPLASH_EXECUTE = 'chugsplash-execute' -const TASK_CHUGSPLASH_LIST_BUNDLES = 'chugsplash-list-bundles' -const TASK_CHUGSPLASH_STATUS = 'chugsplash-status' +export const TASK_CHUGSPLASH_DEPLOY = 'chugsplash-deploy' +export const TASK_CHUGSPLASH_REGISTER = 'chugsplash-register' +export const TASK_CHUGSPLASH_LIST_ALL_PROJECTS = 'chugsplash-list-projects' +export const TASK_CHUGSPLASH_CHECK_BUNDLE = 'chugsplash-check-bundle' +export const TASK_CHUGSPLASH_COMMIT = 'chugsplash-commit' +export const TASK_CHUGSPLASH_PROPOSE = 'chugsplash-propose' +export const TASK_CHUGSPLASH_APPROVE = 'chugsplash-approve' +export const TASK_CHUGSPLASH_EXECUTE = 'chugsplash-execute' +export const TASK_CHUGSPLASH_LIST_BUNDLES = 'chugsplash-list-bundles' +export const TASK_CHUGSPLASH_STATUS = 'chugsplash-status' subtask(TASK_CHUGSPLASH_LOAD) .addParam('deployConfig', undefined, undefined, types.string) @@ -151,77 +145,10 @@ subtask(TASK_CHUGSPLASH_BUNDLE_REMOTE) args.canonicalConfig ) as CanonicalChugSplashConfig - const compilerOutputs: any[] = [] - // Get the compiler output for each compiler input. - for (const compilerInput of parsedCanonicalConfig.inputs) { - const solcBuild: SolcBuild = await hre.run( - TASK_COMPILE_SOLIDITY_GET_SOLC_BUILD, - { - quiet: true, - solcVersion: compilerInput.solcVersion, - } - ) - - let compilerOutput: any // TODO: Compiler output type - if (solcBuild.isSolcJs) { - compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLCJS, { - input: compilerInput.input, - solcJsPath: solcBuild.compilerPath, - }) - } else { - compilerOutput = await hre.run(TASK_COMPILE_SOLIDITY_RUN_SOLC, { - input: compilerInput.input, - solcPath: solcBuild.compilerPath, - }) - } - compilerOutputs.push(compilerOutput) - } - - const artifacts = {} - // Generate an artifact for each contract in the ChugSplash config. - for (const [referenceName, contractConfig] of Object.entries( - parsedCanonicalConfig.contracts - )) { - let compilerOutputIndex = 0 - while (artifacts[referenceName] === undefined) { - // Iterate through the sources in the current compiler output to find the one that - // contains this contract. - const compilerOutput = compilerOutputs[compilerOutputIndex] - for (const [sourceName, sourceOutput] of Object.entries( - compilerOutput.contracts - )) { - // Check if the current source contains the contract. - if (sourceOutput.hasOwnProperty(contractConfig.contract)) { - const contractOutput = sourceOutput[contractConfig.contract] - - const creationCode = getCreationCode( - add0x(contractOutput.evm.bytecode.object), - parsedCanonicalConfig, - referenceName, - contractOutput.abi, - compilerOutput, - sourceName, - contractConfig.contract - ) - const immutableVariables = getImmutableVariables( - compilerOutput, - sourceName, - contractConfig.contract - ) - - artifacts[referenceName] = { - creationCode, - storageLayout: contractOutput.storageLayout, - immutableVariables, - } - // We can exit the loop at this point since each contract only has a single artifact - // associated with it. - break - } - } - compilerOutputIndex += 1 - } - } + const artifacts = await getArtifactsFromParsedCanonicalConfig( + hre, + parsedCanonicalConfig + ) return makeActionBundleFromConfig( parsedCanonicalConfig, @@ -302,8 +229,8 @@ task(TASK_CHUGSPLASH_DEPLOY) hre: any ) => { const signer = await hre.ethers.getSigner() - await deployChugSplashContracts(hre, signer) - await deployContracts(hre, args.log, args.hide, args.local) + await deployChugSplashPredeploys(hre, signer) + await deployConfigs(hre, args.log, args.hide, args.local) } ) @@ -1175,8 +1102,8 @@ task(TASK_NODE) if (!args.disable) { if ((await getChainId(hre.ethers.provider)) === 31337) { const deployer = await hre.ethers.getSigner() - await deployChugSplashContracts(hre, deployer) - await deployContracts(hre, args.log, args.hide, args.local) + await deployChugSplashPredeploys(hre, deployer) + await deployConfigs(hre, args.log, args.hide, args.local) await writeHardhatSnapshotId(hre) } } @@ -1210,8 +1137,8 @@ task(TASK_TEST) throw new Error('Snapshot failed to be reverted.') } } catch { - await deployChugSplashContracts(hre, await hre.ethers.getSigner()) - await deployContracts(hre, false, !args.show, args.local) + await deployChugSplashPredeploys(hre, await hre.ethers.getSigner()) + await deployConfigs(hre, false, !args.show, args.local) } finally { await writeHardhatSnapshotId(hre) } From 9d38797373a248e697ef16b121cd4687a8c5ce4c Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 11 Nov 2022 16:45:02 -0500 Subject: [PATCH 3/5] fix(pg): update chugsplash-register task to work locally --- .changeset/silent-deers-know.md | 6 + packages/core/src/utils.ts | 19 +- packages/plugins/src/hardhat/deployments.ts | 12 +- packages/plugins/src/hardhat/tasks.ts | 369 +++++++++++--------- 4 files changed, 219 insertions(+), 187 deletions(-) create mode 100644 .changeset/silent-deers-know.md diff --git a/.changeset/silent-deers-know.md b/.changeset/silent-deers-know.md new file mode 100644 index 000000000..6f2056aeb --- /dev/null +++ b/.changeset/silent-deers-know.md @@ -0,0 +1,6 @@ +--- +'@chugsplash/core': patch +'@chugsplash/plugins': patch +--- + +Update chugsplash-register task to work locally diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index d8ac4c8f7..d86e6f69e 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -142,30 +142,27 @@ export const getChugSplashManagerProxyAddress = (projectName: string) => { * @param projectName Name of the created project. * @param projectOwner Owner of the ChugSplashManager contract deployed by this call. * @param signer Signer to execute the transaction. - * @returns True if the project was successfully created and false if the project was already registered. + * @returns True if the project was registered for the first time in this call, and false if the + * project was already registered by the caller. */ export const registerChugSplashProject = async ( projectName: string, projectOwner: string, signer: Signer -) => { +): Promise => { const ChugSplashRegistry = getChugSplashRegistry(signer) if ( (await ChugSplashRegistry.projects(projectName)) === constants.AddressZero ) { - try { - const tx = await ChugSplashRegistry.register(projectName, projectOwner) - await tx.wait() - } catch (err) { - throw new Error( - 'Failed to register project. Try again with another project name.' - ) - } + await (await ChugSplashRegistry.register(projectName, projectOwner)).wait() + return true } else { const existingProjectOwner = await getProjectOwner(projectName, signer) if (existingProjectOwner !== (await signer.getAddress())) { throw new Error(`Project already registered by: ${existingProjectOwner}.`) + } else { + return false } } } @@ -203,7 +200,7 @@ export const getChugSplashManagerImplementationAddress = return managerImplementationAddress } -export const log = async (message: string, hide?: boolean) => { +export const ChugSplashLog = async (message: string, hide?: boolean) => { if (!hide) { console.log(message) } diff --git a/packages/plugins/src/hardhat/deployments.ts b/packages/plugins/src/hardhat/deployments.ts index 8d7da9632..f2e22eeab 100644 --- a/packages/plugins/src/hardhat/deployments.ts +++ b/packages/plugins/src/hardhat/deployments.ts @@ -14,7 +14,7 @@ import { isProxyDeployed, getChugSplashManagerProxyAddress, parseChugSplashConfig, - log, + ChugSplashLog, } from '@chugsplash/core' import { ChugSplashManagerABI, OWNER_BOND_AMOUNT } from '@chugsplash/contracts' import { getChainId } from '@eth-optimism/core-utils' @@ -31,12 +31,12 @@ import { writeHardhatSnapshotId } from './utils' export const deployConfigs = async ( hre: any, verbose: boolean, - hide: boolean, + silent: boolean, local: boolean ) => { const fileNames = fs.readdirSync(hre.config.paths.chugsplash) for (const fileName of fileNames) { - await deployConfig(hre, fileName, verbose, hide, local) + await deployConfig(hre, fileName, verbose, silent, local) } } @@ -44,7 +44,7 @@ export const deployConfig = async ( hre: any, fileName: string, verbose: boolean, - hide: boolean, + silent: boolean, local: boolean ) => { const configRelativePath = path.format({ @@ -65,7 +65,7 @@ export const deployConfig = async ( }) const parsedConfig = parseChugSplashConfig(config) - log(`Deploying: ${parsedConfig.options.projectName}`, hide) + ChugSplashLog(`Deploying: ${parsedConfig.options.projectName}`, silent) // Register the project with the signer as the owner. Once we've completed the deployment, we'll // transfer ownership to the project owner specified in the config. @@ -150,7 +150,7 @@ export const deployConfig = async ( bundle, parsedConfig, deployer, - hide, + silent, }) } } diff --git a/packages/plugins/src/hardhat/tasks.ts b/packages/plugins/src/hardhat/tasks.ts index 3d4b7e34b..775310cea 100644 --- a/packages/plugins/src/hardhat/tasks.ts +++ b/packages/plugins/src/hardhat/tasks.ts @@ -26,7 +26,7 @@ import { isSetImplementationAction, fromRawChugSplashAction, getProxyAddress, - log as ChugSplashLog, + ChugSplashLog, } from '@chugsplash/core' import { ChugSplashManagerABI, @@ -58,17 +58,17 @@ export const TASK_CHUGSPLASH_LOAD = 'chugsplash-load' export const TASK_CHUGSPLASH_FETCH = 'chugsplash-fetch' export const TASK_CHUGSPLASH_BUNDLE_LOCAL = 'chugsplash-bundle-local' export const TASK_CHUGSPLASH_BUNDLE_REMOTE = 'chugsplash-bundle-remote' +export const TASK_CHUGSPLASH_LIST_ALL_PROJECTS = 'chugsplash-list-projects' +export const TASK_CHUGSPLASH_LIST_BUNDLES = 'chugsplash-list-bundles' +export const TASK_CHUGSPLASH_CHECK_BUNDLE = 'chugsplash-check-bundle' +export const TASK_CHUGSPLASH_COMMIT = 'chugsplash-commit' +export const TASK_CHUGSPLASH_EXECUTE = 'chugsplash-execute' // public tasks export const TASK_CHUGSPLASH_DEPLOY = 'chugsplash-deploy' export const TASK_CHUGSPLASH_REGISTER = 'chugsplash-register' -export const TASK_CHUGSPLASH_LIST_ALL_PROJECTS = 'chugsplash-list-projects' -export const TASK_CHUGSPLASH_CHECK_BUNDLE = 'chugsplash-check-bundle' -export const TASK_CHUGSPLASH_COMMIT = 'chugsplash-commit' export const TASK_CHUGSPLASH_PROPOSE = 'chugsplash-propose' export const TASK_CHUGSPLASH_APPROVE = 'chugsplash-approve' -export const TASK_CHUGSPLASH_EXECUTE = 'chugsplash-execute' -export const TASK_CHUGSPLASH_LIST_BUNDLES = 'chugsplash-list-bundles' export const TASK_CHUGSPLASH_STATUS = 'chugsplash-status' subtask(TASK_CHUGSPLASH_LOAD) @@ -212,7 +212,7 @@ subtask(TASK_CHUGSPLASH_FETCH) task(TASK_CHUGSPLASH_DEPLOY) .addFlag('log', "Log all of ChugSplash's output") - .addFlag('hide', "Hide all of ChugSplash's output") + .addFlag('silent', "Hide all of ChugSplash's output") .addOptionalParam( 'local', 'Enable local execution within the CLI', @@ -223,79 +223,68 @@ task(TASK_CHUGSPLASH_DEPLOY) async ( args: { log: boolean - hide: boolean + silent: boolean local: boolean }, hre: any ) => { const signer = await hre.ethers.getSigner() await deployChugSplashPredeploys(hre, signer) - await deployConfigs(hre, args.log, args.hide, args.local) + await deployConfigs(hre, args.log, args.silent, args.local) } ) task(TASK_CHUGSPLASH_REGISTER) .setDescription('Registers a new ChugSplash project') - .addParam('deployConfig', 'path to chugsplash deploy config') - .addFlag('log', 'Log the output for this task') + .addVariadicPositionalParam( + 'configPaths', + 'Paths to ChugSplash config files', + [] + ) + .addFlag('silent', "Hide all of ChugSplash's output") .setAction( async ( args: { - deployConfig: string - log: boolean + configPaths: string[] + silent: boolean }, hre ) => { - const spinner = ora({ isSilent: !args.log }) - - const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { - deployConfig: args.deployConfig, - }) + if (args.configPaths.length === 0) { + throw new Error('You must specify a path to a ChugSplash config file.') + } const signer = hre.ethers.provider.getSigner() - await registerChugSplashProject( - config.options.projectName, - config.options.projectOwner, - signer - ) - - spinner.succeed('Project successfully created.') - } - ) + await deployChugSplashPredeploys(hre, signer) -task(TASK_CHUGSPLASH_LIST_ALL_PROJECTS) - .setDescription('Lists all existing ChugSplash projects') - .setAction(async (_, hre) => { - const spinner = ora() + const spinner = ora({ isSilent: args.silent }) - spinner.start('Getting list of all projects...') + for (const configPath of args.configPaths) { + const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { + deployConfig: configPath, + }) - const ChugSplashRegistry = getChugSplashRegistry( - hre.ethers.provider.getSigner() - ) + spinner.start(`Registering ${config.options.projectName}...`) - const events = await ChugSplashRegistry.queryFilter( - ChugSplashRegistry.filters.ChugSplashProjectRegistered() - ) - - spinner.stop() + const firstTimeRegistered = await registerChugSplashProject( + config.options.projectName, + config.options.projectOwner, + signer + ) - console.table( - events.map((event) => { - return { - name: event.args.projectName, - manager: event.args.manager, - } - }) - ) - }) + firstTimeRegistered + ? spinner.succeed('Project successfully registered.') + : spinner.succeed('Project was already registered by the caller.') + } + } + ) task(TASK_CHUGSPLASH_PROPOSE) .setDescription('Proposes a new ChugSplash bundle') .addParam('deployConfig', 'path to chugsplash deploy config') .addOptionalParam('ipfsUrl', 'IPFS gateway URL') - .addFlag('log', 'Log the output for this task') + .addFlag('log', "Log ChugSplash's output") .addFlag( 'local', 'Propose the bundle without committing it to IPFS. To be used for local deployments.' @@ -400,7 +389,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) types.any ) .addParam('deployer', 'Deploying signer', undefined, types.any) - .addFlag('hide', 'Whether to hide logging or not') + .addFlag('silent', "Hide ChugSplash's output") .setAction( async ( args: { @@ -409,7 +398,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) bundle: any // todo - figure out a type for this parsedConfig: ChugSplashConfig deployer: any // todo - figure out a type for this - hide: boolean + silent: boolean }, hre: any ) => { @@ -419,7 +408,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) bundle, parsedConfig, deployer, - hide, + silent, } = args if (bundleState.selectedExecutor === ethers.constants.AddressZero) { @@ -577,7 +566,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) // } // } - if (!hide) { + if (!silent) { const deployments = {} Object.entries(parsedConfig.contracts).forEach( ([referenceName, contractConfig], i) => @@ -590,7 +579,7 @@ subtask(TASK_CHUGSPLASH_EXECUTE) console.table(deployments) } - ChugSplashLog(`Deployed: ${parsedConfig.options.projectName}`, hide) + ChugSplashLog(`Deployed: ${parsedConfig.options.projectName}`, silent) } ) @@ -598,7 +587,7 @@ task(TASK_CHUGSPLASH_APPROVE) .setDescription('Allows a manager to approve a bundle to be executed.') .addParam('projectName', 'name of the chugsplash project') .addParam('bundleId', 'ID of the bundle') - .addFlag('log', 'Log the output for this task') + .addFlag('log', "Log ChugSplash's output") .setAction( async ( args: { @@ -645,111 +634,32 @@ task(TASK_CHUGSPLASH_APPROVE) } ) -task(TASK_CHUGSPLASH_LIST_BUNDLES) - .setDescription('Lists all bundles for a given project') - .addParam('projectName', 'name of the project') - .addFlag('includeExecuted', 'include bundles that have been executed') - .setAction( - async ( - args: { - projectName: string - includeExecuted: boolean - }, - hre - ) => { - const spinner = ora() - - spinner.start(`Getting list of all bundles...`) - - const ChugSplashRegistry = getChugSplashRegistry( - hre.ethers.provider.getSigner() - ) - - const ChugSplashManager = new ethers.Contract( - await ChugSplashRegistry.projects(args.projectName), - ChugSplashManagerABI, - hre.ethers.provider.getSigner() - ) - - // Get events for all bundles that have been proposed. This array includes - // events that have been approved and executed, which will be filtered out. - const proposedEvents = await ChugSplashManager.queryFilter( - ChugSplashManager.filters.ChugSplashBundleProposed() - ) - - // Exit early if there are no proposals for the project. - if (proposedEvents.length === 0) { - console.log('There are no bundles for this project.') - process.exit() - } +subtask(TASK_CHUGSPLASH_LIST_ALL_PROJECTS) + .setDescription('Lists all existing ChugSplash projects') + .setAction(async (_, hre) => { + const spinner = ora() - // Filter out the approved bundle event if there is a currently active bundle - const activeBundleId = await ChugSplashManager.activeBundleId() + spinner.start('Getting list of all projects...') - let approvedEvent: any - if (activeBundleId !== ethers.constants.HashZero) { - for (let i = 0; i < proposedEvents.length; i++) { - const bundleId = proposedEvents[i].args.bundleId - if (bundleId === activeBundleId) { - // Remove the active bundle event in-place and return it. - approvedEvent = proposedEvents.splice(i, 1) + const ChugSplashRegistry = getChugSplashRegistry( + hre.ethers.provider.getSigner() + ) - // It's fine to break out of the loop here since there is only one - // active bundle at a time. - break - } - } - } + const events = await ChugSplashRegistry.queryFilter( + ChugSplashRegistry.filters.ChugSplashProjectRegistered() + ) - const executedEvents = await ChugSplashManager.queryFilter( - ChugSplashManager.filters.ChugSplashBundleCompleted() - ) + spinner.stop() - for (const executed of executedEvents) { - for (let i = 0; i < proposedEvents.length; i++) { - const proposed = proposedEvents[i] - // Remove the event if the bundle hashes match - if (proposed.args.bundleId === executed.args.bundleId) { - proposedEvents.splice(i, 1) - } + console.table( + events.map((event) => { + return { + name: event.args.projectName, + manager: event.args.manager, } - } - - spinner.stop() - - if (proposedEvents.length === 0) { - // Accounts for the case where there is only one bundle, and it is approved. - console.log('There are currently no proposed bundles.') - } else { - // Display the proposed bundles - console.log(`Proposals for ${args.projectName}:`) - proposedEvents.forEach((event) => - console.log( - `Bundle ID: ${event.args.bundleId}\t\tConfig URI: ${event.args.configUri}` - ) - ) - } - - // Display the approved bundle if it exists - if (activeBundleId !== ethers.constants.HashZero) { - console.log('Approved:') - console.log( - `Bundle ID: ${activeBundleId}\t\tConfig URI: ${approvedEvent[0].args.configUri}` - ) - } - - // Display the executed bundles if the user has specified to do so - if (args.includeExecuted) { - console.log('\n') - console.log('Executed:') - executedEvents.forEach((event) => - console.log( - `Bundle ID: ${event.args.bundleId}\t\tConfig URI: ${event.args.configUri}` - ) - ) - } - } - ) + }) + ) + }) subtask(TASK_CHUGSPLASH_COMMIT) .setDescription('Commits a ChugSplash config file with artifacts to IPFS') @@ -759,7 +669,7 @@ subtask(TASK_CHUGSPLASH_COMMIT) 'local', 'Propose the bundle without committing it to IPFS. To be used for local deployments.' ) - .addFlag('log', 'Log the output for this task') + .addFlag('log', "Log ChugSplash's output") .setAction( async ( args: { @@ -896,12 +806,118 @@ subtask(TASK_CHUGSPLASH_COMMIT) } ) -task(TASK_CHUGSPLASH_CHECK_BUNDLE) +subtask(TASK_CHUGSPLASH_LIST_BUNDLES) + .setDescription('Lists all bundles for a given project') + .addParam('projectName', 'name of the project') + .addFlag('includeExecuted', 'include bundles that have been executed') + .setAction( + async ( + args: { + projectName: string + includeExecuted: boolean + }, + hre + ) => { + const spinner = ora() + + spinner.start(`Getting list of all bundles...`) + + const ChugSplashRegistry = getChugSplashRegistry( + hre.ethers.provider.getSigner() + ) + + const ChugSplashManager = new ethers.Contract( + await ChugSplashRegistry.projects(args.projectName), + ChugSplashManagerABI, + hre.ethers.provider.getSigner() + ) + + // Get events for all bundles that have been proposed. This array includes + // events that have been approved and executed, which will be filtered out. + const proposedEvents = await ChugSplashManager.queryFilter( + ChugSplashManager.filters.ChugSplashBundleProposed() + ) + + // Exit early if there are no proposals for the project. + if (proposedEvents.length === 0) { + console.log('There are no bundles for this project.') + process.exit() + } + + // Filter out the approved bundle event if there is a currently active bundle + const activeBundleId = await ChugSplashManager.activeBundleId() + + let approvedEvent: any + if (activeBundleId !== ethers.constants.HashZero) { + for (let i = 0; i < proposedEvents.length; i++) { + const bundleId = proposedEvents[i].args.bundleId + if (bundleId === activeBundleId) { + // Remove the active bundle event in-place and return it. + approvedEvent = proposedEvents.splice(i, 1) + + // It's fine to break out of the loop here since there is only one + // active bundle at a time. + break + } + } + } + + const executedEvents = await ChugSplashManager.queryFilter( + ChugSplashManager.filters.ChugSplashBundleCompleted() + ) + + for (const executed of executedEvents) { + for (let i = 0; i < proposedEvents.length; i++) { + const proposed = proposedEvents[i] + // Remove the event if the bundle hashes match + if (proposed.args.bundleId === executed.args.bundleId) { + proposedEvents.splice(i, 1) + } + } + } + + spinner.stop() + + if (proposedEvents.length === 0) { + // Accounts for the case where there is only one bundle, and it is approved. + console.log('There are currently no proposed bundles.') + } else { + // Display the proposed bundles + console.log(`Proposals for ${args.projectName}:`) + proposedEvents.forEach((event) => + console.log( + `Bundle ID: ${event.args.bundleId}\t\tConfig URI: ${event.args.configUri}` + ) + ) + } + + // Display the approved bundle if it exists + if (activeBundleId !== ethers.constants.HashZero) { + console.log('Approved:') + console.log( + `Bundle ID: ${activeBundleId}\t\tConfig URI: ${approvedEvent[0].args.configUri}` + ) + } + + // Display the executed bundles if the user has specified to do so + if (args.includeExecuted) { + console.log('\n') + console.log('Executed:') + executedEvents.forEach((event) => + console.log( + `Bundle ID: ${event.args.bundleId}\t\tConfig URI: ${event.args.configUri}` + ) + ) + } + } + ) + +subtask(TASK_CHUGSPLASH_CHECK_BUNDLE) .setDescription('Checks if a deployment config matches a bundle hash') .addParam('configUri', 'location of the config file') .addParam('bundleId', 'hash of the bundle') .addOptionalParam('ipfsUrl', 'IPFS gateway URL') - .addFlag('log', 'Log the output for this task') + .addFlag('log', "Log ChugSplash's output") .setAction( async ( args: { @@ -1079,9 +1095,16 @@ task(TASK_CHUGSPLASH_STATUS) // TODO: change 'any' type task(TASK_NODE) - .addFlag('disable', 'Disable ChugSplash from deploying on startup') + .addFlag( + 'setupInternals', + 'Setup the internal ChugSplash contracts. Skip executing all contracts defined in ChugSplash config files.' + ) + .addFlag( + 'disableChugsplash', + "Completely disable all of ChugSplash's activity." + ) .addFlag('log', "Log all of ChugSplash's output") - .addFlag('hide', "Hide all of ChugSplash's output") + .addFlag('silent', "Hide all of ChugSplash's output") .addOptionalParam( 'local', 'Enable local execution within the CLI', @@ -1091,21 +1114,22 @@ task(TASK_NODE) .setAction( async ( args: { - disable: boolean + setupInternals: boolean + disableChugsplash: boolean log: boolean - hide: boolean + silent: boolean local: boolean }, hre: any, runSuper ) => { - if (!args.disable) { - if ((await getChainId(hre.ethers.provider)) === 31337) { - const deployer = await hre.ethers.getSigner() - await deployChugSplashPredeploys(hre, deployer) - await deployConfigs(hre, args.log, args.hide, args.local) - await writeHardhatSnapshotId(hre) + if (!args.disableChugsplash) { + const deployer = await hre.ethers.getSigner() + await deployChugSplashPredeploys(hre, deployer) + if (!args.setupInternals) { + await deployConfigs(hre, args.log, args.silent, args.local) } + await writeHardhatSnapshotId(hre) } await runSuper(args) } @@ -1113,6 +1137,7 @@ task(TASK_NODE) task(TASK_TEST) .addFlag('show', 'Show ChugSplash deployment information') + .addFlag('logAll', "Log all of ChugSplash's internal output") .addOptionalParam( 'local', 'Enable local execution within the CLI', @@ -1120,7 +1145,11 @@ task(TASK_TEST) types.boolean ) .setAction( - async (args: { show: boolean; local: boolean }, hre: any, runSuper) => { + async ( + args: { show: boolean; logAll: boolean; local: boolean }, + hre: any, + runSuper + ) => { if ((await getChainId(hre.ethers.provider)) === 31337) { try { const snapshotIdPath = path.join( @@ -1138,7 +1167,7 @@ task(TASK_TEST) } } catch { await deployChugSplashPredeploys(hre, await hre.ethers.getSigner()) - await deployConfigs(hre, false, !args.show, args.local) + await deployConfigs(hre, args.logAll, !args.show, args.local) } finally { await writeHardhatSnapshotId(hre) } From 6941e8970f031ff21091288b1d8a3caa7e07194b Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 11 Nov 2022 21:02:53 -0500 Subject: [PATCH 4/5] fix(pg): remove spinner from subtasks --- .changeset/loud-foxes-wash.md | 5 + packages/core/src/utils.ts | 4 +- packages/demo/contracts/SimpleStorage.sol | 6 +- packages/plugins/src/hardhat/deployments.ts | 16 +- packages/plugins/src/hardhat/tasks.ts | 238 +++++++++----------- 5 files changed, 120 insertions(+), 149 deletions(-) create mode 100644 .changeset/loud-foxes-wash.md diff --git a/.changeset/loud-foxes-wash.md b/.changeset/loud-foxes-wash.md new file mode 100644 index 000000000..4cc4ca7ca --- /dev/null +++ b/.changeset/loud-foxes-wash.md @@ -0,0 +1,5 @@ +--- +'@chugsplash/plugins': patch +--- + +Remove spinner from subtasks. Also update chugsplash-propose task to be more descriptive and robust. diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index d86e6f69e..3ab2bb3b5 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -200,8 +200,8 @@ export const getChugSplashManagerImplementationAddress = return managerImplementationAddress } -export const ChugSplashLog = async (message: string, hide?: boolean) => { +export const ChugSplashLog = async (text: string, hide?: boolean) => { if (!hide) { - console.log(message) + console.log(text) } } diff --git a/packages/demo/contracts/SimpleStorage.sol b/packages/demo/contracts/SimpleStorage.sol index 37d9ee797..5aeaf0917 100644 --- a/packages/demo/contracts/SimpleStorage.sol +++ b/packages/demo/contracts/SimpleStorage.sol @@ -11,11 +11,7 @@ contract SimpleStorage { // We must instantiate the immutable variables in the constructor so that // Solidity doesn't throw an error. - constructor( - uint8 _number, - bool _stored, - address _otherStorage - ) { + constructor(uint8 _number, bool _stored, address _otherStorage) { number = _number; stored = _stored; otherStorage = _otherStorage; diff --git a/packages/plugins/src/hardhat/deployments.ts b/packages/plugins/src/hardhat/deployments.ts index f2e22eeab..8f62124b5 100644 --- a/packages/plugins/src/hardhat/deployments.ts +++ b/packages/plugins/src/hardhat/deployments.ts @@ -15,6 +15,7 @@ import { getChugSplashManagerProxyAddress, parseChugSplashConfig, ChugSplashLog, + ChugSplashActionBundle, } from '@chugsplash/core' import { ChugSplashManagerABI, OWNER_BOND_AMOUNT } from '@chugsplash/contracts' import { getChainId } from '@eth-optimism/core-utils' @@ -30,20 +31,18 @@ import { writeHardhatSnapshotId } from './utils' */ export const deployConfigs = async ( hre: any, - verbose: boolean, silent: boolean, local: boolean ) => { const fileNames = fs.readdirSync(hre.config.paths.chugsplash) for (const fileName of fileNames) { - await deployConfig(hre, fileName, verbose, silent, local) + await deployConfig(hre, fileName, silent, local) } } export const deployConfig = async ( hre: any, fileName: string, - verbose: boolean, silent: boolean, local: boolean ) => { @@ -61,7 +60,7 @@ export const deployConfig = async ( const deployerAddress = await deployer.getAddress() const config: ChugSplashConfig = await hre.run('chugsplash-load', { - deployConfig: configRelativePath, + configPath: configRelativePath, }) const parsedConfig = parseChugSplashConfig(config) @@ -76,9 +75,8 @@ export const deployConfig = async ( ) const { bundleId } = await hre.run('chugsplash-commit', { - deployConfig: configRelativePath, + configPath: configRelativePath, local, - log: verbose, }) const ChugSplashManager = new Contract( @@ -111,10 +109,9 @@ export const deployConfig = async ( } } - const { bundle } = await hre.run('chugsplash-propose', { - deployConfig: configRelativePath, + const bundle: ChugSplashActionBundle = await hre.run('chugsplash-propose', { + configPath: configRelativePath, local, - log: verbose, }) if ((await deployer.getBalance()).lt(OWNER_BOND_AMOUNT.mul(5))) { @@ -139,7 +136,6 @@ export const deployConfig = async ( await hre.run('chugsplash-approve', { projectName: parsedConfig.options.projectName, bundleId, - log: verbose, }) // todo call chugsplash-execute if deploying locally diff --git a/packages/plugins/src/hardhat/tasks.ts b/packages/plugins/src/hardhat/tasks.ts index 775310cea..1cbfcb840 100644 --- a/packages/plugins/src/hardhat/tasks.ts +++ b/packages/plugins/src/hardhat/tasks.ts @@ -72,27 +72,27 @@ export const TASK_CHUGSPLASH_APPROVE = 'chugsplash-approve' export const TASK_CHUGSPLASH_STATUS = 'chugsplash-status' subtask(TASK_CHUGSPLASH_LOAD) - .addParam('deployConfig', undefined, undefined, types.string) + .addParam('configPath', undefined, undefined, types.string) .setAction( - async (args: { deployConfig: string }, hre): Promise => { + async (args: { configPath: string }, hre): Promise => { // Make sure we have the latest compiled code. await hre.run(TASK_COMPILE, { quiet: true, }) - const config = loadChugSplashConfig(args.deployConfig) + const config = loadChugSplashConfig(args.configPath) return config } ) subtask(TASK_CHUGSPLASH_BUNDLE_LOCAL) - .addParam('deployConfig', undefined, undefined, types.string) + .addParam('configPath', undefined, undefined, types.string) .setAction( async ( - args: { deployConfig: string }, + args: { configPath: string }, hre ): Promise => { const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { - deployConfig: args.deployConfig, + configPath: args.configPath, }) const parsed = parseChugSplashConfig(config) @@ -211,7 +211,6 @@ subtask(TASK_CHUGSPLASH_FETCH) ) task(TASK_CHUGSPLASH_DEPLOY) - .addFlag('log', "Log all of ChugSplash's output") .addFlag('silent', "Hide all of ChugSplash's output") .addOptionalParam( 'local', @@ -222,7 +221,6 @@ task(TASK_CHUGSPLASH_DEPLOY) .setAction( async ( args: { - log: boolean silent: boolean local: boolean }, @@ -230,7 +228,7 @@ task(TASK_CHUGSPLASH_DEPLOY) ) => { const signer = await hre.ethers.getSigner() await deployChugSplashPredeploys(hre, signer) - await deployConfigs(hre, args.log, args.silent, args.local) + await deployConfigs(hre, args.silent, args.local) } ) @@ -262,7 +260,7 @@ task(TASK_CHUGSPLASH_REGISTER) for (const configPath of args.configPaths) { const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { - deployConfig: configPath, + configPath, }) spinner.start(`Registering ${config.options.projectName}...`) @@ -275,95 +273,118 @@ task(TASK_CHUGSPLASH_REGISTER) firstTimeRegistered ? spinner.succeed('Project successfully registered.') - : spinner.succeed('Project was already registered by the caller.') + : spinner.fail('Project has already been registered by the caller.') } } ) task(TASK_CHUGSPLASH_PROPOSE) - .setDescription('Proposes a new ChugSplash bundle') - .addParam('deployConfig', 'path to chugsplash deploy config') - .addOptionalParam('ipfsUrl', 'IPFS gateway URL') - .addFlag('log', "Log ChugSplash's output") - .addFlag( - 'local', - 'Propose the bundle without committing it to IPFS. To be used for local deployments.' + .setDescription('Proposes a new ChugSplash project') + .addPositionalParam( + 'configPath', + 'Path to the ChugSplash config file to propose' + ) + .addFlag('silent', "Hide all of ChugSplash's output") + .addOptionalParam( + 'ipfsUrl', + 'Optional IPFS gateway URL for publishing ChugSplash bundles remotely (live networks only).' ) .setAction( async ( args: { - deployConfig: string + configPath: string ipfsUrl: string - local: boolean - log: boolean + silent: boolean }, hre - ): Promise<{ - bundle: ChugSplashActionBundle - configUri: string - bundleId: string - }> => { - const spinner = ora({ isSilent: !args.log }) + ): Promise => { + const { configPath, ipfsUrl, silent } = args + + const signer = hre.ethers.provider.getSigner() + await deployChugSplashPredeploys(hre, signer) + + const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { + configPath, + }) + + const ChugSplashRegistry = getChugSplashRegistry(signer) + const chugsplashManagerAddress = await ChugSplashRegistry.projects( + config.options.projectName + ) + if (chugsplashManagerAddress === ethers.constants.AddressZero) { + if ((await getChainId(hre.ethers.provider)) === 31337) { + throw new Error( + `This project has not been registered on the local Hardhat network. You can register the project locally with the following commands: - // First, commit the bundle to IPFS and get the bundle hash that it returns. + npx hardhat node --setup-internals + npx hardhat chugsplash-register --network localhost ${configPath} + ` + ) + } else { + throw new Error( + `This project has not been registered on ${hre.network.name}. To register the project on this network, run the following command: + + npx hardhat chugsplash-register --network ${hre.network.name} ${configPath} + ` + ) + } + } + + const spinner = ora({ isSilent: silent }) + spinner.start(`Proposing the project on ${hre.network.name}...`) + + // Commit the bundle to IPFS (for live networks) or locally (for the Hardhat network). const { bundle, configUri, bundleId } = await hre.run( TASK_CHUGSPLASH_COMMIT, { - deployConfig: args.deployConfig, - ipfsUrl: args.ipfsUrl, - local: args.local, - log: args.log, + configPath, + ipfsUrl, + silent, + spinner, } ) - // Next, verify that the bundle has been committed to IPFS with the correct bundle hash. - // Skip this step if the deployment is local. - let config: ChugSplashConfig - if (args.local === false) { - ;({ config } = await hre.run(TASK_CHUGSPLASH_CHECK_BUNDLE, { + if ((await getChainId(hre.ethers.provider)) !== 31337) { + // For live networks, verify that the bundle has been committed to IPFS with the correct + // bundle hash. + await hre.run(TASK_CHUGSPLASH_CHECK_BUNDLE, { configUri, bundleId: computeBundleId( bundle.root, bundle.actions.length, configUri ), - ipfsUrl: args.ipfsUrl, - spinner, - })) - } else { - config = await hre.run(TASK_CHUGSPLASH_LOAD, { - deployConfig: args.deployConfig, + ipfsUrl, }) } - spinner.start('Proposing the bundle...') - - const ChugSplashRegistry = getChugSplashRegistry( - hre.ethers.provider.getSigner() - ) - const ChugSplashManager = new ethers.Contract( - await ChugSplashRegistry.projects(config.options.projectName), + chugsplashManagerAddress, ChugSplashManagerABI, - hre.ethers.provider.getSigner() + signer ) const bundleState: ChugSplashBundleState = await ChugSplashManager.bundles(bundleId) if (bundleState.status === ChugSplashBundleStatus.EMPTY) { - const tx = await ChugSplashManager.proposeChugSplashBundle( - bundle.root, - bundle.actions.length, - configUri - ) - await tx.wait() - spinner.succeed('Bundle successfully proposed.') + await ( + await ChugSplashManager.proposeChugSplashBundle( + bundle.root, + bundle.actions.length, + configUri + ) + ).wait() + spinner.succeed(`Project successfully proposed on ${hre.network.name}.`) } else if (bundleState.status === ChugSplashBundleStatus.PROPOSED) { - spinner.fail('Bundle already proposed.') + spinner.fail( + `Project has already been proposed on ${hre.network.name}.` + ) } else if (bundleState.status === ChugSplashBundleStatus.APPROVED) { - spinner.fail('Bundle is currently active.') + spinner.fail( + `Project was already proposed and is currently being executed on: ${hre.network.name}.` + ) } - return { bundle, configUri, bundleId } + return bundle } ) @@ -587,17 +608,17 @@ task(TASK_CHUGSPLASH_APPROVE) .setDescription('Allows a manager to approve a bundle to be executed.') .addParam('projectName', 'name of the chugsplash project') .addParam('bundleId', 'ID of the bundle') - .addFlag('log', "Log ChugSplash's output") + .addFlag('silent', "Hide all of ChugSplash's output") .setAction( async ( args: { projectName: string bundleId: string - log: boolean + silent: boolean }, hre ) => { - const spinner = ora({ isSilent: !args.log }) + const spinner = ora({ isSilent: args.silent }) const ChugSplashRegistry = getChugSplashRegistry( hre.ethers.provider.getSigner() @@ -637,10 +658,6 @@ task(TASK_CHUGSPLASH_APPROVE) subtask(TASK_CHUGSPLASH_LIST_ALL_PROJECTS) .setDescription('Lists all existing ChugSplash projects') .setAction(async (_, hre) => { - const spinner = ora() - - spinner.start('Getting list of all projects...') - const ChugSplashRegistry = getChugSplashRegistry( hre.ethers.provider.getSigner() ) @@ -649,8 +666,6 @@ subtask(TASK_CHUGSPLASH_LIST_ALL_PROJECTS) ChugSplashRegistry.filters.ChugSplashProjectRegistered() ) - spinner.stop() - console.table( events.map((event) => { return { @@ -663,20 +678,14 @@ subtask(TASK_CHUGSPLASH_LIST_ALL_PROJECTS) subtask(TASK_CHUGSPLASH_COMMIT) .setDescription('Commits a ChugSplash config file with artifacts to IPFS') - .addParam('deployConfig', 'path to chugsplash deploy config') + .addParam('configPath', 'path to chugsplash deploy config') + .addFlag('silent', "Hide all of ChugSplash's output") .addOptionalParam('ipfsUrl', 'IPFS gateway URL') - .addFlag( - 'local', - 'Propose the bundle without committing it to IPFS. To be used for local deployments.' - ) - .addFlag('log', "Log ChugSplash's output") .setAction( async ( args: { - deployConfig: string + configPath: string ipfsUrl: string - local: boolean - log: boolean }, hre ): Promise<{ @@ -684,14 +693,14 @@ subtask(TASK_CHUGSPLASH_COMMIT) configUri: string bundleId: string }> => { - const spinner = ora({ isSilent: !args.log }) + const { configPath, ipfsUrl } = args - spinner.start('Compiling deploy config...') + const isHardhatNetwork = + (await getChainId(hre.ethers.provider)) === 31337 ? true : false const config: ChugSplashConfig = await hre.run(TASK_CHUGSPLASH_LOAD, { - deployConfig: args.deployConfig, + configPath, }) - spinner.succeed('Compiled deploy config') let configSourceNames = Object.values(config.contracts) .map((contractConfig) => contractConfig.contract) @@ -744,18 +753,12 @@ subtask(TASK_CHUGSPLASH_COMMIT) 2 ) - if (args.local) { - spinner.start('Getting bundle hash from IPFS...') - } else { - spinner.start('Publishing config to IPFS...') - } - let ipfsHash - if (args.local) { + if (isHardhatNetwork) { ipfsHash = await Hash.of(ipfsData) - } else if (args.ipfsUrl) { + } else if (ipfsUrl) { const ipfs = create({ - url: args.ipfsUrl, + url: ipfsUrl, }) ipfsHash = (await ipfs.add(ipfsData)).path } else if ( @@ -776,21 +779,17 @@ subtask(TASK_CHUGSPLASH_COMMIT) ipfsHash = (await ipfs.add(ipfsData)).path } else { throw new Error( - 'You must either deploy locally, set your IPFS credentials in an environment file, or call this task with an IPFS url.' - ) - } + `To deploy on ${hre.network.name}, you must first setup an IPFS project with Infura: https://app.infura.io/. Once you've done this, copy and paste the following variables into your .env file: - if (args.local) { - spinner.succeed('Got IPFS bundle hash locally') - } else { - spinner.succeed('Published config to IPFS') + IPFS_PROJECT_ID: ... + IPFS_API_KEY_SECRET: ... + ` + ) } - spinner.start('Building artifact bundle...') const bundle = await hre.run(TASK_CHUGSPLASH_BUNDLE_LOCAL, { - deployConfig: args.deployConfig, + configPath, }) - spinner.succeed('Built artifact bundle') const configUri = `ipfs://${ipfsHash}` const bundleId = computeBundleId( @@ -799,9 +798,6 @@ subtask(TASK_CHUGSPLASH_COMMIT) configUri ) - spinner.succeed(`Config: ${configUri}`) - spinner.succeed(`Bundle: ${bundleId}`) - return { bundle, configUri, bundleId } } ) @@ -818,10 +814,6 @@ subtask(TASK_CHUGSPLASH_LIST_BUNDLES) }, hre ) => { - const spinner = ora() - - spinner.start(`Getting list of all bundles...`) - const ChugSplashRegistry = getChugSplashRegistry( hre.ethers.provider.getSigner() ) @@ -876,8 +868,6 @@ subtask(TASK_CHUGSPLASH_LIST_BUNDLES) } } - spinner.stop() - if (proposedEvents.length === 0) { // Accounts for the case where there is only one bundle, and it is approved. console.log('There are currently no proposed bundles.') @@ -917,23 +907,19 @@ subtask(TASK_CHUGSPLASH_CHECK_BUNDLE) .addParam('configUri', 'location of the config file') .addParam('bundleId', 'hash of the bundle') .addOptionalParam('ipfsUrl', 'IPFS gateway URL') - .addFlag('log', "Log ChugSplash's output") .setAction( async ( args: { configUri: string bundleId: string ipfsUrl: string - log: boolean + silent: boolean }, hre ): Promise<{ config: CanonicalChugSplashConfig bundle: ChugSplashActionBundle }> => { - const spinner = ora({ isSilent: !args.log }) - - spinner.start('Fetching config from IPFS...') const config: CanonicalChugSplashConfig = await hre.run( TASK_CHUGSPLASH_FETCH, { @@ -941,16 +927,13 @@ subtask(TASK_CHUGSPLASH_CHECK_BUNDLE) ipfsUrl: args.ipfsUrl, } ) - spinner.succeed('Fetched config') - spinner.start('Building artifact bundle...') const bundle: ChugSplashActionBundle = await hre.run( TASK_CHUGSPLASH_BUNDLE_REMOTE, { canonicalConfig: config, } ) - spinner.succeed('Built artifact bundle') const bundleId = computeBundleId( bundle.root, @@ -959,11 +942,9 @@ subtask(TASK_CHUGSPLASH_CHECK_BUNDLE) ) if (bundleId !== args.bundleId) { - spinner.fail( + throw new Error( 'Bundle ID generated from downloaded config does NOT match given hash' ) - } else { - spinner.succeed('Bundle verified') } return { @@ -1103,7 +1084,6 @@ task(TASK_NODE) 'disableChugsplash', "Completely disable all of ChugSplash's activity." ) - .addFlag('log', "Log all of ChugSplash's output") .addFlag('silent', "Hide all of ChugSplash's output") .addOptionalParam( 'local', @@ -1116,7 +1096,6 @@ task(TASK_NODE) args: { setupInternals: boolean disableChugsplash: boolean - log: boolean silent: boolean local: boolean }, @@ -1127,7 +1106,7 @@ task(TASK_NODE) const deployer = await hre.ethers.getSigner() await deployChugSplashPredeploys(hre, deployer) if (!args.setupInternals) { - await deployConfigs(hre, args.log, args.silent, args.local) + await deployConfigs(hre, args.silent, args.local) } await writeHardhatSnapshotId(hre) } @@ -1137,7 +1116,6 @@ task(TASK_NODE) task(TASK_TEST) .addFlag('show', 'Show ChugSplash deployment information') - .addFlag('logAll', "Log all of ChugSplash's internal output") .addOptionalParam( 'local', 'Enable local execution within the CLI', @@ -1145,11 +1123,7 @@ task(TASK_TEST) types.boolean ) .setAction( - async ( - args: { show: boolean; logAll: boolean; local: boolean }, - hre: any, - runSuper - ) => { + async (args: { show: boolean; local: boolean }, hre: any, runSuper) => { if ((await getChainId(hre.ethers.provider)) === 31337) { try { const snapshotIdPath = path.join( @@ -1167,7 +1141,7 @@ task(TASK_TEST) } } catch { await deployChugSplashPredeploys(hre, await hre.ethers.getSigner()) - await deployConfigs(hre, args.logAll, !args.show, args.local) + await deployConfigs(hre, !args.show, args.local) } finally { await writeHardhatSnapshotId(hre) } From 6a6f0c0d64a68053c8224326988dddda3f7b82b0 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Sat, 12 Nov 2022 13:02:40 -0500 Subject: [PATCH 5/5] fix(ct): hard-code build info file path temporarily --- .changeset/grumpy-apricots-sneeze.md | 5 +++++ packages/contracts/src/ifaces.ts | 11 +---------- 2 files changed, 6 insertions(+), 10 deletions(-) create mode 100644 .changeset/grumpy-apricots-sneeze.md diff --git a/.changeset/grumpy-apricots-sneeze.md b/.changeset/grumpy-apricots-sneeze.md new file mode 100644 index 000000000..29c899020 --- /dev/null +++ b/.changeset/grumpy-apricots-sneeze.md @@ -0,0 +1,5 @@ +--- +'@chugsplash/contracts': patch +--- + +Hard-code build info file as a temporary fix diff --git a/packages/contracts/src/ifaces.ts b/packages/contracts/src/ifaces.ts index 70f9df2d1..92d9994ab 100644 --- a/packages/contracts/src/ifaces.ts +++ b/packages/contracts/src/ifaces.ts @@ -10,16 +10,7 @@ export const ProxyUpdaterArtifact = require('../artifacts/contracts/ProxyUpdater export const DefaultAdapterArtifact = require('../artifacts/contracts/adapters/DefaultAdapter.sol/DefaultAdapter.json') export const ProxyArtifact = require('../artifacts/contracts/libraries/Proxy.sol/Proxy.json') -const buildInfoFolderPath = path.join( - '..', - 'contracts', - 'artifacts', - 'build-info' -) -const buildInfoFilePath = fs - .readdirSync(buildInfoFolderPath) - .map((file) => path.join(buildInfoFolderPath, file))[0] -export const buildInfo = JSON.parse(fs.readFileSync(buildInfoFilePath, 'utf8')) +export const buildInfo = require('../artifacts/build-info/b00e1a42c4580623826aa11edd72371a.json') export const ChugSplashRegistryABI = ChugSplashRegistryArtifact.abi export const ChugSplashBootLoaderABI = ChugSplashBootLoaderArtifact.abi