diff --git a/packages/contracts/deploy/10_create_repo/10_create_token_voting_repo.ts b/packages/contracts/deploy/10_create_repo/10_create_token_voting_repo.ts deleted file mode 100644 index 50045745..00000000 --- a/packages/contracts/deploy/10_create_repo/10_create_token_voting_repo.ts +++ /dev/null @@ -1,52 +0,0 @@ -import tokenVotingBuildMetadata from '../../src/build-metadata.json'; -import tokenVotingReleaseMetadata from '../../src/release-metadata.json'; -import { - createPluginRepo, - populatePluginRepo, - getContractAddress, - uploadToIPFS, -} from '../helpers'; -import {ethers} from 'ethers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log(`\nCreating token-voting repo.`); - - console.warn( - 'Please make sure pluginRepo is not created more than once with the same name.' - ); - - const {network} = hre; - - const tokenVotingReleaseCIDPath = await uploadToIPFS( - JSON.stringify(tokenVotingReleaseMetadata), - network.name - ); - const tokenVotingBuildCIDPath = await uploadToIPFS( - JSON.stringify(tokenVotingBuildMetadata), - network.name - ); - - const tokenVotingSetupContract = await getContractAddress( - 'TokenVotingSetup', - hre - ); - - await createPluginRepo(hre, 'TokenVotingRepoProxy', 'token-voting'); - await populatePluginRepo(hre, 'TokenVotingRepoProxy', [ - { - versionTag: [1, 2], - pluginSetupContract: tokenVotingSetupContract, - releaseMetadata: ethers.utils.hexlify( - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingReleaseCIDPath}`) - ), - buildMetadata: ethers.utils.hexlify( - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingBuildCIDPath}`) - ), - }, - ]); -}; - -export default func; -func.tags = ['New', 'CreateTokenVotingRepo']; diff --git a/packages/contracts/deploy/10_create_repo/11_create_token_voting_repo_conclude.ts b/packages/contracts/deploy/10_create_repo/11_create_token_voting_repo_conclude.ts deleted file mode 100644 index 7bd82236..00000000 --- a/packages/contracts/deploy/10_create_repo/11_create_token_voting_repo_conclude.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - PluginRepoFactory__factory, - PluginRepo__factory, -} from '../../../../typechain'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log(`Concluding TokenVotingSetup deployment.\n`); - const [deployer] = await hre.ethers.getSigners(); - - const {deployments} = hre; - - const PluginRepoFactoryDeployment = await deployments.get( - 'PluginRepoFactory' - ); - const pluginRepoFactory = PluginRepoFactory__factory.connect( - PluginRepoFactoryDeployment.address, - deployer - ); - - const initializeData = - PluginRepo__factory.createInterface().encodeFunctionData('initialize', [ - deployer.address, - ]); - - const pluginRepoBase = await pluginRepoFactory.pluginRepoBase(); - - hre.aragonToVerifyContracts.push({ - address: hre.aragonPluginRepos.TokenVotingRepoProxy, - args: [pluginRepoBase, initializeData], - }); -}; - -export default func; -func.tags = ['New', 'CreateTokenVotingRepo', 'Verify']; diff --git a/packages/contracts/deploy/20_new_version/10_token_voting_setup.ts b/packages/contracts/deploy/20_new_version/10_token_voting_setup.ts deleted file mode 100644 index d8983dbf..00000000 --- a/packages/contracts/deploy/20_new_version/10_token_voting_setup.ts +++ /dev/null @@ -1,52 +0,0 @@ -import tokenVotingSetupArtifact from '../../../../artifacts/src/plugins/governance/majority-voting/token/TokenVotingSetup.sol/TokenVotingSetup.json'; -import governanceERC20Artifact from '../../../../artifacts/src/token/ERC20/governance/GovernanceERC20.sol/GovernanceERC20.json'; -import governanceWrappedERC20Artifact from '../../../../artifacts/src/token/ERC20/governance/GovernanceWrappedERC20.sol/GovernanceWrappedERC20.json'; -import {MintSettings} from '../../../../test/token/erc20/governance-erc20'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const {deployments, ethers} = hre; - const {deploy} = deployments; - const [deployer] = await ethers.getSigners(); - - const zeroDaoAddress = ethers.constants.AddressZero; - const zeroTokenAddress = ethers.constants.AddressZero; - const emptyName = ''; - const emptySymbol = ''; - const emptyMintSettings: MintSettings = { - receivers: [], - amounts: [], - }; - - // Deploy the bases for the TokenVotingSetup - const governanceERC20DeployResult = await deploy('GovernanceERC20', { - contract: governanceERC20Artifact, - from: deployer.address, - args: [zeroDaoAddress, emptyName, emptySymbol, emptyMintSettings], - log: true, - }); - - const governanceWrappedERC20DeployResult = await deploy( - 'GovernanceWrappedERC20', - { - contract: governanceWrappedERC20Artifact, - from: deployer.address, - args: [zeroTokenAddress, emptyName, emptySymbol], - log: true, - } - ); - - // Deploy the TokenVotingSetup and provide the bases in the constructor - await deploy('TokenVotingSetup', { - contract: tokenVotingSetupArtifact, - from: deployer.address, - args: [ - governanceERC20DeployResult.address, - governanceWrappedERC20DeployResult.address, - ], - log: true, - }); -}; -export default func; -func.tags = ['New', 'TokenVotingSetup']; diff --git a/packages/contracts/deploy/20_new_version/11_token_voting_setup_conclude.ts b/packages/contracts/deploy/20_new_version/11_token_voting_setup_conclude.ts deleted file mode 100644 index 990d7c85..00000000 --- a/packages/contracts/deploy/20_new_version/11_token_voting_setup_conclude.ts +++ /dev/null @@ -1,46 +0,0 @@ -import {TokenVotingSetup__factory} from '../../../../typechain'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; -import {setTimeout} from 'timers/promises'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log(`Concluding token voting setup deployment.\n`); - const [deployer] = await hre.ethers.getSigners(); - - const {deployments, network} = hre; - - const TokenVotingSetupDeployment = await deployments.get('TokenVotingSetup'); - const tokenVotingSetup = TokenVotingSetup__factory.connect( - TokenVotingSetupDeployment.address, - deployer - ); - - // add a timeout for polygon because the call to `implementation()` can fail for newly deployed contracts in the first few seconds - if (network.name === 'polygon') { - console.log(`Waiting 30secs for ${network.name} to finish up...`); - await setTimeout(30000); - } - - hre.aragonToVerifyContracts.push( - await hre.deployments.get('GovernanceERC20') - ); - - hre.aragonToVerifyContracts.push( - await hre.deployments.get('GovernanceWrappedERC20') - ); - - hre.aragonToVerifyContracts.push({ - contract: - 'src/plugins/governance/majority-voting/token/TokenVotingSetup.sol:TokenVotingSetup', - ...TokenVotingSetupDeployment, - }); - hre.aragonToVerifyContracts.push({ - contract: - 'src/plugins/governance/majority-voting/token/TokenVoting.sol:TokenVoting', - address: await tokenVotingSetup.implementation(), - args: [], - }); -}; - -export default func; -func.tags = ['New', 'TokenVotingSetup', 'Verify']; diff --git a/packages/contracts/deploy/20_new_version/21_setup.ts b/packages/contracts/deploy/20_new_version/21_setup.ts index 041e76bc..a6bcc1fd 100644 --- a/packages/contracts/deploy/20_new_version/21_setup.ts +++ b/packages/contracts/deploy/20_new_version/21_setup.ts @@ -1,10 +1,22 @@ -import {PLUGIN_SETUP_CONTRACT_NAME} from '../../plugin-settings'; +import governanceERC20Artifact from '../../artifacts/src/ERC20/governance/GovernanceERC20.sol/GovernanceERC20.json'; +import governanceWrappedERC20Artifact from '../../artifacts/src/ERC20/governance/GovernanceWrappedERC20.sol/GovernanceWrappedERC20.json'; +import { + GOVERNANCE_ERC20_DEPLOY_ARGS, + GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS, + PLUGIN_SETUP_CONTRACT_NAME, +} from '../../plugin-settings'; +import { + GOVERNANCE_ERC20_CONTRACT_NAME, + GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME, +} from '../../plugin-settings'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; import path from 'path'; /** * Deploys the plugin setup contract with the plugin implementation inside. + * In the case of the token voting plugin, we also need to deploy the governance ERC20 + * and the wrapped variants. * @param {HardhatRuntimeEnvironment} hre */ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { @@ -14,11 +26,39 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const {deploy} = deployments; const {deployer} = await getNamedAccounts(); - await deploy(PLUGIN_SETUP_CONTRACT_NAME, { + // Deploy the bases for the TokenVotingSetup + const governanceERC20DeployResult = await deploy( + GOVERNANCE_ERC20_CONTRACT_NAME, + { + contract: governanceERC20Artifact, + from: deployer, + args: GOVERNANCE_ERC20_DEPLOY_ARGS, + log: true, + } + ); + + const governanceWrappedERC20DeployResult = await deploy( + GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME, + { + contract: governanceWrappedERC20Artifact, + from: deployer, + args: GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS, + log: true, + } + ); + + const res = await deploy(PLUGIN_SETUP_CONTRACT_NAME, { from: deployer, - args: [], + args: [ + governanceERC20DeployResult.address, + governanceWrappedERC20DeployResult.address, + ], log: true, }); + + console.log( + `Deployed contract '${PLUGIN_SETUP_CONTRACT_NAME}' at ${res.address}.` + ); }; export default func; diff --git a/packages/contracts/deploy/20_new_version/22_setup_conclude.ts b/packages/contracts/deploy/20_new_version/22_setup_conclude.ts index 1b514b68..fa64a5a6 100644 --- a/packages/contracts/deploy/20_new_version/22_setup_conclude.ts +++ b/packages/contracts/deploy/20_new_version/22_setup_conclude.ts @@ -1,5 +1,11 @@ -import {PLUGIN_SETUP_CONTRACT_NAME} from '../../plugin-settings'; -import {MyPluginSetup__factory, MyPlugin__factory} from '../../typechain'; +import { + GOVERNANCE_ERC20_CONTRACT_NAME, + GOVERNANCE_ERC20_DEPLOY_ARGS, + GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME, + GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS, + PLUGIN_SETUP_CONTRACT_NAME, +} from '../../plugin-settings'; +import {TokenVotingSetup__factory, TokenVoting__factory} from '../../typechain'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; import path from 'path'; @@ -17,24 +23,45 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Get the plugin setup address const setupDeployment = await deployments.get(PLUGIN_SETUP_CONTRACT_NAME); - const setup = MyPluginSetup__factory.connect( + const setup = TokenVotingSetup__factory.connect( setupDeployment.address, deployer ); // Get the plugin implementation address - const implementation = MyPlugin__factory.connect( + const implementation = TokenVoting__factory.connect( await setup.implementation(), deployer ); + const governanceERC20DeployResult = await deployments.get( + GOVERNANCE_ERC20_CONTRACT_NAME + ); + const governanceWrappedERC20DeployResult = await deployments.get( + GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME + ); + // Queue the plugin setup and implementation for verification on the block explorers hre.aragonToVerifyContracts.push({ address: setup.address, args: setupDeployment.args, }); + hre.aragonToVerifyContracts.push({ address: implementation.address, - args: [], + args: [ + governanceERC20DeployResult.address, + governanceWrappedERC20DeployResult.address, + ], + }); + + hre.aragonToVerifyContracts.push({ + address: governanceERC20DeployResult.address, + args: GOVERNANCE_ERC20_DEPLOY_ARGS, + }); + + hre.aragonToVerifyContracts.push({ + address: governanceWrappedERC20DeployResult.address, + args: GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS, }); }; diff --git a/packages/contracts/deploy/20_new_version/41_TokenVoting_Plugin.ts b/packages/contracts/deploy/20_new_version/41_TokenVoting_Plugin.ts deleted file mode 100644 index 1976d1a3..00000000 --- a/packages/contracts/deploy/20_new_version/41_TokenVoting_Plugin.ts +++ /dev/null @@ -1,123 +0,0 @@ -import tokenVotingSetupArtifact from '../../../artifacts/src/plugins/governance/majority-voting/token/TokenVotingSetup.sol/TokenVotingSetup.json'; -import governanceERC20Artifact from '../../../artifacts/src/token/ERC20/governance/GovernanceERC20.sol/GovernanceERC20.json'; -import governanceWrappedERC20Artifact from '../../../artifacts/src/token/ERC20/governance/GovernanceWrappedERC20.sol/GovernanceWrappedERC20.json'; -import tokenVotingBuildMetadata from '../../../src/plugins/governance/majority-voting/token/build-metadata.json'; -import tokenVotingReleaseMetadata from '../../../src/plugins/governance/majority-voting/token/release-metadata.json'; -import {MintSettings} from '../../../test/token/erc20/governance-erc20'; -import {PluginRepo__factory} from '../../../typechain'; -import {getContractAddress, uploadToIPFS} from '../../helpers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const TARGET_RELEASE = 1; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log('\nUpdate TokenVoting Plugin'); - const {deployments, ethers, network} = hre; - const {deploy} = deployments; - const [deployer] = await ethers.getSigners(); - - const zeroDaoAddress = ethers.constants.AddressZero; - const zeroTokenAddress = ethers.constants.AddressZero; - const emptyName = ''; - const emptySymbol = ''; - const emptyMintSettings: MintSettings = { - receivers: [], - amounts: [], - }; - - // Deploy the bases for the TokenVotingSetup - const governanceERC20DeployResult = await deploy('GovernanceERC20', { - contract: governanceERC20Artifact, - from: deployer.address, - args: [zeroDaoAddress, emptyName, emptySymbol, emptyMintSettings], - log: true, - }); - - const governanceWrappedERC20DeployResult = await deploy( - 'GovernanceWrappedERC20', - { - contract: governanceWrappedERC20Artifact, - from: deployer.address, - args: [zeroTokenAddress, emptyName, emptySymbol], - log: true, - } - ); - - // Deploy the TokenVotingSetup and provide the bases in the constructor - const deployResult = await deploy('TokenVotingSetup', { - contract: tokenVotingSetupArtifact, - from: deployer.address, - args: [ - governanceERC20DeployResult.address, - governanceWrappedERC20DeployResult.address, - ], - log: true, - }); - - const tokenVotingReleaseCIDPath = await uploadToIPFS( - JSON.stringify(tokenVotingReleaseMetadata), - network.name - ); - const tokenVotingBuildCIDPath = await uploadToIPFS( - JSON.stringify(tokenVotingBuildMetadata), - network.name - ); - - const tokenVotingRepoAddress = await getContractAddress( - 'TokenVotingRepoProxy', - hre - ); - const tokenVotingRepo = PluginRepo__factory.connect( - tokenVotingRepoAddress, - ethers.provider - ); - if ( - await tokenVotingRepo.callStatic.isGranted( - tokenVotingRepoAddress, - deployer.address, - await tokenVotingRepo.MAINTAINER_PERMISSION_ID(), - '0x00' - ) - ) { - console.log(`Deployer has permission to install new TokenVoting version`); - const tx = await tokenVotingRepo - .connect(deployer) - .createVersion( - TARGET_RELEASE, - deployResult.address, - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingBuildCIDPath}`), - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingReleaseCIDPath}`) - ); - console.log(`Creating new TokenVoting build version with ${tx.hash}`); - await tx.wait(); - return; - } - - const tx = await tokenVotingRepo - .connect(deployer) - .populateTransaction.createVersion( - TARGET_RELEASE, - deployResult.address, - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingBuildCIDPath}`), - ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingReleaseCIDPath}`) - ); - - if (!tx.to || !tx.data) { - throw new Error( - `Failed to populate TokenVoting Repo createVersion transaction` - ); - } - - console.log( - `Deployer has no permission to create a new version. Adding managementDAO action` - ); - hre.managementDAOActions.push({ - to: tx.to, - data: tx.data, - value: 0, - description: `Creates a new build for release 1 in the TokenVoting PluginRepo (${tokenVotingRepoAddress}) with TokenVotingSetup (${deployResult.address}).`, - }); -}; -export default func; -func.tags = ['Update', 'TokenVotingPlugin', 'v1.3.0']; diff --git a/packages/contracts/deploy/30_upgrade_repo/31_upgrade_repo.ts b/packages/contracts/deploy/30_upgrade_repo/31_upgrade_repo.ts index a6d47f77..7ca2b168 100644 --- a/packages/contracts/deploy/30_upgrade_repo/31_upgrade_repo.ts +++ b/packages/contracts/deploy/30_upgrade_repo/31_upgrade_repo.ts @@ -1,11 +1,20 @@ -import {findPluginRepo, getProductionNetworkName} from '../../utils/helpers'; +import { + findPluginRepo, + getProductionNetworkName, + impersonatedManagementDaoSigner, + isLocal, +} from '../../utils/helpers'; import { getLatestNetworkDeployment, getNetworkNameByAlias, } from '@aragon/osx-commons-configs'; -import {PLUGIN_REPO_PERMISSIONS} from '@aragon/osx-commons-sdk'; +import { + PLUGIN_REPO_PERMISSIONS, + UnsupportedNetworkError, +} from '@aragon/osx-commons-sdk'; import {PluginRepo__factory} from '@aragon/osx-ethers'; import {BytesLike} from 'ethers'; +import {writeFile} from 'fs/promises'; import {DeployFunction} from 'hardhat-deploy/types'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; import path from 'path'; @@ -15,6 +24,14 @@ type SemVer = [number, number, number]; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const [deployer] = await hre.ethers.getSigners(); const productionNetworkName: string = getProductionNetworkName(hre); + const network = getNetworkNameByAlias(productionNetworkName); + if (network === null) { + throw new UnsupportedNetworkError(productionNetworkName); + } + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } // Get PluginRepo const {pluginRepo, ensDomain} = await findPluginRepo(hre); @@ -28,8 +45,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Get the latest `PluginRepo` implementation as the upgrade target const latestPluginRepoImplementation = PluginRepo__factory.connect( - getLatestNetworkDeployment(getNetworkNameByAlias(productionNetworkName)!)! - .PluginRepoBase.address, + networkDeployments.PluginRepoBase.address, deployer ); @@ -53,22 +69,36 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Re-initialization will happen through a call to `function initializeFrom(uint8[3] calldata _previousProtocolVersion, bytes calldata _initData)` // that Aragon might add to the `PluginRepo` contract once it's required. /* - // Define the `initData` arguments that + // Define the `_initData` arguments const initData: BytesLike[] = []; // Encode the call to `function initializeFrom(uint8[3] calldata _previousProtocolVersion, bytes calldata _initData)` with `initData` const initializeFromCalldata = - newPluginRepoImplementation.interface.encodeFunctionData('initializeFrom', [ + latestPluginRepoImplementation.interface.encodeFunctionData('initializeFrom', [ current, initData, ]); */ const initializeFromCalldata: BytesLike = []; - // Check if deployer has the permission to upgrade the plugin repo + const isDeployerUpgrader = await pluginRepo.isGranted( + pluginRepo.address, + deployer.address, + PLUGIN_REPO_PERMISSIONS.UPGRADE_REPO_PERMISSION_ID, + [] + ); + + // If this is a local deployment and the deployer doesn't have `UPGRADE_REPO_PERMISSION_ID` permission + // we impersonate the management DAO for integration testing purposes. + const signer = + isDeployerUpgrader || !isLocal(hre) + ? deployer + : await impersonatedManagementDaoSigner(hre); + + // Check if the signer has the permission to upgrade the plugin repo if ( await pluginRepo.isGranted( pluginRepo.address, - deployer.address, + signer.address, PLUGIN_REPO_PERMISSIONS.UPGRADE_REPO_PERMISSION_ID, [] ) @@ -84,9 +114,35 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await pluginRepo.upgradeTo(latestPluginRepoImplementation.address); } } else { - throw Error( - `The new version cannot be published because the deployer ('${deployer.address}') - is lacking the ${PLUGIN_REPO_PERMISSIONS.UPGRADE_REPO_PERMISSION_ID} permission.` + // The deployer does not have `UPGRADE_REPO_PERMISSION_ID` permission and we are not deploying to a production network, + // so we write the data into a file for a management DAO member to create a proposal from it. + const upgradeAction = + initializeFromCalldata.length === 0 + ? { + to: pluginRepo.address, + upgradeTo: { + NewImplementation: latestPluginRepoImplementation.address, + }, + } + : { + to: pluginRepo.address, + upgradeToAndCall: { + NewImplementation: latestPluginRepoImplementation.address, + Data: initializeFromCalldata, + PayableAmount: 0, + }, + }; + const data = { + proposalTitle: `Upgrade the '${ensDomain}' plugin repo`, + proposalSummary: `Upgrades '${ensDomain}' plugin repo at '${pluginRepo.address}',' plugin in the '${ensDomain}' plugin repo.`, + proposalDescription: `TODO: Describe the changes to the 'PluginRepo' implementation.`, + actions: [upgradeAction], + }; + + const path = `./upgradeRepoProposalData-${hre.network.name}.json`; + await writeFile(path, JSON.stringify(data, null, 2)); + console.log( + `Saved data to '${path}'. Use this to create a proposal on the managing DAO calling the 'upgradeTo' or 'upgradeToAndCall' function on the ${ensDomain} plugin repo deployed at ${pluginRepo.address}.` ); } }; @@ -102,11 +158,18 @@ func.skip = async (hre: HardhatRuntimeEnvironment) => { const [deployer] = await hre.ethers.getSigners(); const productionNetworkName: string = getProductionNetworkName(hre); + const network = getNetworkNameByAlias(productionNetworkName); + if (network === null) { + throw new UnsupportedNetworkError(productionNetworkName); + } + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } // Get the latest `PluginRepo` implementation as the upgrade target const latestPluginRepoImplementation = PluginRepo__factory.connect( - getLatestNetworkDeployment(getNetworkNameByAlias(productionNetworkName)!)! - .PluginRepoBase.address, + networkDeployments.PluginRepoBase.address, deployer ); diff --git a/packages/contracts/deploy/30_upgrade_repo/40_TokenVoting_PluginRepo.ts b/packages/contracts/deploy/30_upgrade_repo/40_TokenVoting_PluginRepo.ts deleted file mode 100644 index 28f8e5fd..00000000 --- a/packages/contracts/deploy/30_upgrade_repo/40_TokenVoting_PluginRepo.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - PluginRepo__factory, - PluginRepoFactory__factory, -} from '../../../typechain'; -import {getContractAddress} from '../../helpers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log( - '\nUpgrade the `token-voting-repo` PluginRepo to the new implementation' - ); - - const pluginRepoFactoryAddress = await getContractAddress( - 'PluginRepoFactory', - hre - ); - const newPluginRepoImplementation = await PluginRepoFactory__factory.connect( - pluginRepoFactoryAddress, - hre.ethers.provider - ).pluginRepoBase(); - - const tokenVotingPluginRepoAddress = await getContractAddress( - 'TokenVotingRepoProxy', - hre - ); - const tokenVotingPluginRepo = PluginRepo__factory.connect( - tokenVotingPluginRepoAddress, - hre.ethers.provider - ); - const upgradeTX = await tokenVotingPluginRepo.populateTransaction.upgradeTo( - newPluginRepoImplementation - ); - - if (!upgradeTX.to || !upgradeTX.data) { - throw new Error(`Failed to populate upgradeTo transaction`); - } - - hre.managementDAOActions.push({ - to: upgradeTX.to, - data: upgradeTX.data, - value: 0, - description: `Upgrade the TokenVoting PluginRepo (${tokenVotingPluginRepoAddress}) to the new implementation (${newPluginRepoImplementation}).`, - }); -}; -export default func; -func.tags = ['TokenVotingPluginRepo', 'v1.3.0']; diff --git a/packages/contracts/deploy/30_upgrade_repo/41_TokenVoting_Plugin.ts b/packages/contracts/deploy/30_upgrade_repo/41_TokenVoting_Plugin.ts new file mode 100644 index 00000000..482a36f2 --- /dev/null +++ b/packages/contracts/deploy/30_upgrade_repo/41_TokenVoting_Plugin.ts @@ -0,0 +1,123 @@ +/** + * TODO: + * I'm not 100% sure about the manner in which we upgrade to new versions of upgradeable plugins, in particular how + * we handle: + * - Versioning the plugin + * - Upgrading the plugin + * - Updating the governance tokens + ** / + +// import tokenVotingSetupArtifact from '../../../artifacts/src/plugins/governance/majority-voting/token/TokenVotingSetup.sol/TokenVotingSetup.json'; +// import governanceERC20Artifact from '../../../artifacts/src/token/ERC20/governance/GovernanceERC20.sol/GovernanceERC20.json'; +// import governanceWrappedERC20Artifact from '../../../artifacts/src/token/ERC20/governance/GovernanceWrappedERC20.sol/GovernanceWrappedERC20.json'; +// import tokenVotingBuildMetadata from '../../../src/plugins/governance/majority-voting/token/build-metadata.json'; +// import tokenVotingReleaseMetadata from '../../../src/plugins/governance/majority-voting/token/release-metadata.json'; +// import {PluginRepo__factory} from '@aragon/osx-ethers'; +// import {getContractAddress, uploadToIPFS} from '../helpers'; +// import {DeployFunction} from 'hardhat-deploy/types'; +// import {HardhatRuntimeEnvironment} from 'hardhat/types'; +// import { GOVERNANCE_ERC20_DEPLOY_ARGS, GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS } from '../../plugin-settings'; + +// const TARGET_RELEASE = 1; + +// const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { +// console.log('\nUpdate TokenVoting Plugin'); +// const {deployments, ethers, network} = hre; +// const {deploy} = deployments; +// const [deployer] = await ethers.getSigners(); + +// // Deploy the bases for the TokenVotingSetup +// const governanceERC20DeployResult = await deploy('GovernanceERC20', { +// contract: governanceERC20Artifact, +// from: deployer.address, +// args: GOVERNANCE_ERC20_DEPLOY_ARGS, +// log: true, +// }); + +// const governanceWrappedERC20DeployResult = await deploy( +// 'GovernanceWrappedERC20', +// { +// contract: governanceWrappedERC20Artifact, +// from: deployer.address, +// args: GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS, +// log: true, +// } +// ); + +// // Deploy the TokenVotingSetup and provide the bases in the constructor +// const deployResult = await deploy('TokenVotingSetup', { +// contract: tokenVotingSetupArtifact, +// from: deployer.address, +// args: [ +// governanceERC20DeployResult.address, +// governanceWrappedERC20DeployResult.address, +// ], +// log: true, +// }); + +// const tokenVotingReleaseCIDPath = await uploadToIPFS( +// JSON.stringify(tokenVotingReleaseMetadata), +// network.name +// ); +// const tokenVotingBuildCIDPath = await uploadToIPFS( +// JSON.stringify(tokenVotingBuildMetadata), +// network.name +// ); + +// const tokenVotingRepoAddress = await getContractAddress( +// 'TokenVotingRepoProxy', +// hre +// ); +// const tokenVotingRepo = PluginRepo__factory.connect( +// tokenVotingRepoAddress, +// ethers.provider +// ); +// if ( +// await tokenVotingRepo.callStatic.isGranted( +// tokenVotingRepoAddress, +// deployer.address, +// await tokenVotingRepo.MAINTAINER_PERMISSION_ID(), +// '0x00' +// ) +// ) { +// console.log(`Deployer has permission to install new TokenVoting version`); +// const tx = await tokenVotingRepo +// .connect(deployer) +// .createVersion( +// TARGET_RELEASE, +// deployResult.address, +// ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingBuildCIDPath}`), +// ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingReleaseCIDPath}`) +// ); +// console.log(`Creating new TokenVoting build version with ${tx.hash}`); +// await tx.wait(); +// return; +// } + +// const tx = await tokenVotingRepo +// .connect(deployer) +// .populateTransaction.createVersion( +// TARGET_RELEASE, +// deployResult.address, +// ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingBuildCIDPath}`), +// ethers.utils.toUtf8Bytes(`ipfs://${tokenVotingReleaseCIDPath}`) +// ); + +// if (!tx.to || !tx.data) { +// throw new Error( +// `Failed to populate TokenVoting Repo createVersion transaction` +// ); +// } + +// console.log( +// `Deployer has no permission to create a new version. Adding managementDAO action` +// ); +// hre.managementDAOActions.push({ +// to: tx.to, +// data: tx.data, +// value: 0, +// description: `Creates a new build for release 1 in the TokenVoting PluginRepo (${tokenVotingRepoAddress}) with TokenVotingSetup (${deployResult.address}).`, +// }); +// }; +// export default func; +// func.tags = ['Update', 'TokenVotingPlugin', 'v1.3.0'];*/ diff --git a/packages/contracts/deploy/40_conclude/42_TokenVoting_Plugin_conclude.ts b/packages/contracts/deploy/40_conclude/42_TokenVoting_Plugin_conclude.ts deleted file mode 100644 index 2f1a8a70..00000000 --- a/packages/contracts/deploy/40_conclude/42_TokenVoting_Plugin_conclude.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {TokenVotingSetup__factory} from '../../../typechain'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - console.log('\nConcluding TokenVoting Plugin Update'); - const [deployer] = await hre.ethers.getSigners(); - - const TokenVotingSetupDeployment = await hre.deployments.get( - 'TokenVotingSetup' - ); - const tokenVotingSetup = TokenVotingSetup__factory.connect( - TokenVotingSetupDeployment.address, - deployer - ); - - hre.aragonToVerifyContracts.push( - await hre.deployments.get('GovernanceERC20') - ); - - hre.aragonToVerifyContracts.push( - await hre.deployments.get('GovernanceWrappedERC20') - ); - - hre.aragonToVerifyContracts.push(TokenVotingSetupDeployment); - hre.aragonToVerifyContracts.push({ - address: await tokenVotingSetup.implementation(), - args: [], - }); - - hre.aragonToVerifyContracts.push(); -}; -export default func; -func.tags = ['TokenVotingPlugin', 'Verify', 'v1.3.0']; diff --git a/packages/contracts/plugin-settings.ts b/packages/contracts/plugin-settings.ts index f19ee5bf..dce9a196 100644 --- a/packages/contracts/plugin-settings.ts +++ b/packages/contracts/plugin-settings.ts @@ -1,10 +1,14 @@ import buildMetadata from './src/build-metadata.json'; import releaseMetadata from './src/release-metadata.json'; import {VersionTag} from '@aragon/osx-commons-sdk'; +import {ethers} from 'hardhat'; -export const PLUGIN_CONTRACT_NAME = 'MyPlugin'; // This must match the filename `packages/contracts/src/MyPlugin.sol` and the contract name `MyPlugin` within. -export const PLUGIN_SETUP_CONTRACT_NAME = 'MyPluginSetup'; // This must match the filename `packages/contracts/src/MyPluginSetup.sol` and the contract name `MyPluginSetup` within. -export const PLUGIN_REPO_ENS_SUBDOMAIN_NAME = 'my'; // This will result in the ENS domain name 'my.plugin.dao.eth' +export const PLUGIN_CONTRACT_NAME = 'TokenVoting'; // This must match the filename `packages/contracts/src/MyPlugin.sol` and the contract name `MyPlugin` within. +export const PLUGIN_SETUP_CONTRACT_NAME = 'TokenVotingSetup'; // This must match the filename `packages/contracts/src/MyPluginSetup.sol` and the contract name `MyPluginSetup` within. +export const PLUGIN_REPO_ENS_SUBDOMAIN_NAME = 'token-voting'; // This will result in the ENS domain name 'my.plugin.dao.eth' + +export const GOVERNANCE_ERC20_CONTRACT_NAME = 'GovernanceERC20'; +export const GOVERNANCE_WRAPPED_ERC20_CONTRACT_NAME = 'GovernanceWrappedERC20'; export const VERSION: VersionTag = { release: 1, // Increment this number ONLY if breaking/incompatible changes were made. Updates between releases are NOT possible. @@ -16,3 +20,31 @@ export const METADATA = { build: buildMetadata, release: releaseMetadata, }; + +const zeroDaoAddress = ethers.constants.AddressZero; +const zeroTokenAddress = ethers.constants.AddressZero; +const emptyName = ''; +const emptySymbol = ''; + +export type MintSettings = { + receivers: string[]; + amounts: number[]; +}; + +export const emptyMintSettings: MintSettings = { + receivers: [], + amounts: [], +}; + +export const GOVERNANCE_ERC20_DEPLOY_ARGS = [ + zeroDaoAddress, + emptyName, + emptySymbol, + emptyMintSettings, +]; + +export const GOVERNANCE_WRAPPED_ERC20_DEPLOY_ARGS = [ + zeroTokenAddress, + emptyName, + emptySymbol, +]; diff --git a/packages/contracts/utils/helpers.ts b/packages/contracts/utils/helpers.ts index f40c8088..79e077a8 100644 --- a/packages/contracts/utils/helpers.ts +++ b/packages/contracts/utils/helpers.ts @@ -10,6 +10,8 @@ import { findEvent, } from '@aragon/osx-commons-sdk'; import { + DAO, + DAO__factory, ENSSubdomainRegistrar__factory, ENS__factory, IAddrResolver__factory, @@ -17,7 +19,9 @@ import { PluginRepoEvents, PluginRepo__factory, } from '@aragon/osx-ethers'; -import {ContractTransaction} from 'ethers'; +import {setBalance} from '@nomicfoundation/hardhat-network-helpers'; +import {SignerWithAddress} from '@nomiclabs/hardhat-ethers/signers'; +import {BigNumber, ContractTransaction} from 'ethers'; import {LogDescription, defaultAbiCoder, keccak256} from 'ethers/lib/utils'; import {ethers} from 'hardhat'; import {HardhatRuntimeEnvironment} from 'hardhat/types'; @@ -186,3 +190,38 @@ export async function createVersion( export const AragonOSxAsciiArt = " ____ _____ \n /\\ / __ \\ / ____| \n / \\ _ __ __ _ __ _ ___ _ __ | | | | (_____ __ \n / /\\ \\ | '__/ _` |/ _` |/ _ \\| '_ \\ | | | |\\___ \\ \\/ / \n / ____ \\| | | (_| | (_| | (_) | | | | | |__| |____) > < \n /_/ \\_\\_| \\__,_|\\__, |\\___/|_| |_| \\____/|_____/_/\\_\\ \n __/ | \n |___/ \n"; + +export async function getManagementDao( + hre: HardhatRuntimeEnvironment +): Promise { + const [deployer] = await hre.ethers.getSigners(); + const productionNetworkName = getProductionNetworkName(hre); + const network = getNetworkNameByAlias(productionNetworkName); + if (network === null) { + throw new UnsupportedNetworkError(productionNetworkName); + } + const networkDeployments = getLatestNetworkDeployment(network); + if (networkDeployments === null) { + throw `Deployments are not available on network ${network}.`; + } + + return DAO__factory.connect( + networkDeployments.ManagementDAOProxy.address, + deployer + ); +} + +export async function impersonatedManagementDaoSigner( + hre: HardhatRuntimeEnvironment +): Promise { + return await (async () => { + const managementDaoProxy = getManagementDao(hre); + const signer = await hre.ethers.getImpersonatedSigner( + ( + await managementDaoProxy + ).address + ); + await setBalance(signer.address, BigNumber.from(10).pow(18)); + return signer; + })(); +}