From 3a62da623b9724ede45bcdb1b8464838f9c84917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Wed, 10 Apr 2024 19:14:00 +0200 Subject: [PATCH] Independent Managing DAO config script --- .env.example | 1 + .../10_install_mgmt_dao_plugins.ts | 145 ------- .../11_drop_deployer_permission.ts | 41 -- .../99_verification/20_mgmt_dao_plugin.ts | 36 -- .../deploy/99_verification/20_summary.ts | 42 ++ .../99_verification/21_mgmt_dao_root.ts | 34 -- packages/contracts/hardhat.config.ts | 12 +- .../contracts/scripts/managing-dao-setup.ts | 379 ++++++++++++++++++ 8 files changed, 432 insertions(+), 258 deletions(-) delete mode 100644 packages/contracts/deploy/03_managing_dao/10_install_mgmt_dao_plugins.ts delete mode 100644 packages/contracts/deploy/03_managing_dao/11_drop_deployer_permission.ts delete mode 100644 packages/contracts/deploy/99_verification/20_mgmt_dao_plugin.ts create mode 100644 packages/contracts/deploy/99_verification/20_summary.ts delete mode 100644 packages/contracts/deploy/99_verification/21_mgmt_dao_root.ts create mode 100644 packages/contracts/scripts/managing-dao-setup.ts diff --git a/.env.example b/.env.example index 8ffebed..18218bb 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,7 @@ PLUGIN_SETUP_PROCESSOR_ADDRESS="0xAc7e4fB4a2f58b7EA5516f47b6f804956Faf0134" MANAGING_DAO_ADDRESS="0x617e4fB3a2358b7EA5a16f47b6f1f4956Faf0162" # Management DAO governance plugin settings +GOVERNANCE_PLUGIN_REPO_ADDRESS="0xf956Fa58b7EB3a2347b6f1f4f0A5a16f162617e4" MGMT_DAO_PROPOSAL_DURATION="604800" # 60 * 60 * 24 * 7 seconds MGMT_DAO_MIN_PROPOSAL_PARTICIPATION="500000" # 50% MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD="500000" # 50% diff --git a/packages/contracts/deploy/03_managing_dao/10_install_mgmt_dao_plugins.ts b/packages/contracts/deploy/03_managing_dao/10_install_mgmt_dao_plugins.ts deleted file mode 100644 index 58acbe0..0000000 --- a/packages/contracts/deploy/03_managing_dao/10_install_mgmt_dao_plugins.ts +++ /dev/null @@ -1,145 +0,0 @@ -import {GovernancePluginsSetupParams} from '../../plugin-setup-params'; -import { - GovernancePluginsSetup__factory, - MajorityVotingBase, - PluginSetupProcessor__factory, -} from '../../typechain'; -import { - InstallationPreparedEvent, - PluginSetupRefStruct, -} from '../../typechain/@aragon/osx/framework/plugin/setup/PluginSetupProcessor'; -import {isLocalChain} from '../../utils/hardhat'; -import {findEvent, hashHelpers} from '../../utils/helpers'; -import {getPluginRepoInfo} from '../../utils/plugin-repo-info'; -import {IDAO, IDAO__factory, PluginSetupProcessor} from '@aragon/osx-ethers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const MANAGING_DAO_ADDRESS = process.env.MANAGING_DAO_ADDRESS ?? ''; -const PLUGIN_SETUP_PROCESSOR_ADDRESS = - process.env.PLUGIN_SETUP_PROCESSOR_ADDRESS ?? ''; -const MGMT_DAO_PROPOSAL_DURATION = - parseInt(process.env.MGMT_DAO_PROPOSAL_DURATION ?? '604800') || - 60 * 60 * 24 * 7; -const MGMT_DAO_MIN_PROPOSAL_PARTICIPATION = - parseInt(process.env.MGMT_DAO_MIN_PROPOSAL_PARTICIPATION ?? '500000') || - 500_000; // 50% -const MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD = - parseInt(process.env.MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD ?? '500000') || - 500_000; // 50% -const MGMT_DAO_INITIAL_EDITORS = process.env.MGMT_DAO_INITIAL_EDITORS - ? process.env.MGMT_DAO_INITIAL_EDITORS.split(',') - : ([] as string[]); - -// Main function -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const {deployments, network} = hre; - const [deployer] = await hre.ethers.getSigners(); - - // Locate the plugin setup address - const setupDeployment = await deployments.get( - GovernancePluginsSetupParams.PLUGIN_SETUP_CONTRACT_NAME - ); - const pluginSetup = GovernancePluginsSetup__factory.connect( - setupDeployment.address, - deployer - ); - - // Call prepare installation - const settings: MajorityVotingBase.VotingSettingsStruct = { - duration: MGMT_DAO_PROPOSAL_DURATION, - minParticipation: MGMT_DAO_MIN_PROPOSAL_PARTICIPATION, - supportThreshold: MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD, - votingMode: 1, // Early execution - }; - const memberAccessProposalDuration = MGMT_DAO_PROPOSAL_DURATION * 3; // Time before expired - const pluginUpgrader = '0x0000000000000000000000000000000000000000'; // Only the DAO - const installData = await pluginSetup.encodeInstallationParams( - settings, - MGMT_DAO_INITIAL_EDITORS, - memberAccessProposalDuration, - pluginUpgrader - ); - - if (!PLUGIN_SETUP_PROCESSOR_ADDRESS) - throw new Error('PLUGIN_SETUP_PROCESSOR_ADDRESS cannot be empty'); - - console.log('Preparing an installation of the standard governance plugin'); - const psp = PluginSetupProcessor__factory.connect( - PLUGIN_SETUP_PROCESSOR_ADDRESS, - deployer - ); - - const pluginRepoInfo = getPluginRepoInfo( - GovernancePluginsSetupParams.PLUGIN_REPO_ENS_NAME, - network.name - ); - if (!pluginRepoInfo) throw new Error('The plugin repo cannot be found'); - - const pluginSetupRef: PluginSetupRefStruct = { - pluginSetupRepo: pluginRepoInfo.address, - versionTag: GovernancePluginsSetupParams.VERSION, - }; - - const tx = await psp.prepareInstallation(MANAGING_DAO_ADDRESS, { - pluginSetupRef, - data: installData, - }); - await tx.wait(); - - const preparedEvent = await findEvent( - tx, - 'InstallationPrepared' - ); - if (!preparedEvent) { - throw new Error('Failed to get InstallationPrepared event'); - } - - const pluginAddress = preparedEvent.args.plugin; - const helpers = preparedEvent.args.preparedSetupData.helpers; - const permissions = preparedEvent.args.preparedSetupData.permissions; - - // Encode a call to execute() > applyInstallation - console.log('Installing the plugin to the managing DAO'); - const applyParams: PluginSetupProcessor.ApplyInstallationParamsStruct = { - plugin: pluginAddress, - helpersHash: hashHelpers(helpers), - permissions, - pluginSetupRef: { - pluginSetupRepo: pluginRepoInfo.address, - versionTag: GovernancePluginsSetupParams.VERSION, - }, - }; - - const actions: IDAO.ActionStruct[] = [ - { - to: PLUGIN_SETUP_PROCESSOR_ADDRESS, - value: 0, - data: PluginSetupProcessor__factory.createInterface().encodeFunctionData( - 'applyInstallation', - [MANAGING_DAO_ADDRESS, applyParams] - ), - }, - ]; - - const tx2 = await IDAO__factory.connect( - MANAGING_DAO_ADDRESS, - deployer - ).execute( - '0x0000000000000000000000000000000000000000000000000000000000000000', - actions, - 0 - ); - await tx2.wait(); - - hre.managingDao.address = MANAGING_DAO_ADDRESS; - hre.managingDao.governancePlugin = pluginAddress; -}; - -export default func; -func.tags = [ - GovernancePluginsSetupParams.PLUGIN_SETUP_CONTRACT_NAME, - 'ManagingDAO', -]; -func.skip = (hre: HardhatRuntimeEnvironment) => - Promise.resolve(isLocalChain(hre.network.name)); diff --git a/packages/contracts/deploy/03_managing_dao/11_drop_deployer_permission.ts b/packages/contracts/deploy/03_managing_dao/11_drop_deployer_permission.ts deleted file mode 100644 index 416d91c..0000000 --- a/packages/contracts/deploy/03_managing_dao/11_drop_deployer_permission.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {isLocalChain} from '../../utils/hardhat'; -import {DAO__factory, IDAO} from '@aragon/osx-ethers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const MANAGING_DAO_ADDRESS = process.env.MANAGING_DAO_ADDRESS ?? ''; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const [deployer] = await hre.ethers.getSigners(); - - console.log('Revoking the execute permission to the deployment wallet'); - - // Revoke the permission to the deployer wallet - const mgmtDAO = DAO__factory.connect(MANAGING_DAO_ADDRESS, deployer); - - const actions: IDAO.ActionStruct[] = [ - { - to: MANAGING_DAO_ADDRESS, - value: 0, - data: DAO__factory.createInterface().encodeFunctionData('revoke', [ - MANAGING_DAO_ADDRESS, - deployer.address, - await mgmtDAO.EXECUTE_PERMISSION_ID(), - ]), - }, - ]; - - const tx = await DAO__factory.connect(MANAGING_DAO_ADDRESS, deployer).execute( - '0x0000000000000000000000000000000000000000000000000000000000000000', - actions, - 0 - ); - await tx.wait(); - - console.log('Execute permission revoked'); -}; - -export default func; -func.tags = ['ManagingDAO']; -func.skip = (hre: HardhatRuntimeEnvironment) => - Promise.resolve(isLocalChain(hre.network.name)); diff --git a/packages/contracts/deploy/99_verification/20_mgmt_dao_plugin.ts b/packages/contracts/deploy/99_verification/20_mgmt_dao_plugin.ts deleted file mode 100644 index b207b59..0000000 --- a/packages/contracts/deploy/99_verification/20_mgmt_dao_plugin.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {isLocalChain} from '../../utils/hardhat'; -import {DAO__factory} from '@aragon/osx-ethers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const [deployer] = await hre.ethers.getSigners(); - - console.log('\nChecking the Managing DAO governance plugin'); - - if (!hre.managingDao.address) { - throw new Error('The Managing DAO is not ready'); - } else if (!hre.managingDao.governancePlugin) { - throw new Error('The governance plugin of the Managing DAO is not ready'); - } - const mgmtDAO = DAO__factory.connect(hre.managingDao.address, deployer); - - const perm = await mgmtDAO.hasPermission( - mgmtDAO.address, - hre.managingDao.governancePlugin, - await mgmtDAO.EXECUTE_PERMISSION_ID(), - '0x' - ); - if (!perm) { - throw new Error( - 'The governance plugin of the Managing DAO cannot execute proposals on it' - ); - } -}; - -export default func; - -func.tags = ['Verification']; -func.runAtTheEnd = true; -func.skip = (hre: HardhatRuntimeEnvironment) => - Promise.resolve(isLocalChain(hre.network.name)); diff --git a/packages/contracts/deploy/99_verification/20_summary.ts b/packages/contracts/deploy/99_verification/20_summary.ts new file mode 100644 index 0000000..b135f38 --- /dev/null +++ b/packages/contracts/deploy/99_verification/20_summary.ts @@ -0,0 +1,42 @@ +import {GovernancePluginsSetupParams} from '../../plugin-setup-params'; +import {isLocalChain} from '../../utils/hardhat'; +import {getPluginRepoInfo} from '../../utils/plugin-repo-info'; +import {DeployFunction} from 'hardhat-deploy/types'; +import {HardhatRuntimeEnvironment} from 'hardhat/types'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const pluginRepoInfo = getPluginRepoInfo( + GovernancePluginsSetupParams.PLUGIN_REPO_ENS_NAME, + hre.network.name + ); + if (!pluginRepoInfo) + throw new Error('Could not read the address of the deployed contract'); + + console.log('\nSummary'); + console.log( + 'If you wish to configure the Managing DAO, update the .env file:' + ); + + console.log(`GOVERNANCE_PLUGIN_REPO_ADDRESS="${pluginRepoInfo.address}"`); + + console.log(''); + console.log('Also, make sure to define the following values:'); + console.log( + `MGMT_DAO_PROPOSAL_DURATION="604800" # 60 * 60 * 24 * 7 (seconds)` + ); + console.log(`MGMT_DAO_MIN_PROPOSAL_PARTICIPATION="500000" # 50%`); + console.log(`MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD="500000" # 50%`); + console.log( + `MGMT_DAO_INITIAL_EDITORS="0x1234,0x2345,0x3456,0x4567..." # Comma separated addresses` + ); + + console.log(''); + console.log('Done'); +}; + +export default func; + +func.tags = ['Verification']; +func.runAtTheEnd = true; +func.skip = (hre: HardhatRuntimeEnvironment) => + Promise.resolve(isLocalChain(hre.network.name)); diff --git a/packages/contracts/deploy/99_verification/21_mgmt_dao_root.ts b/packages/contracts/deploy/99_verification/21_mgmt_dao_root.ts deleted file mode 100644 index 02249e6..0000000 --- a/packages/contracts/deploy/99_verification/21_mgmt_dao_root.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {isLocalChain} from '../../utils/hardhat'; -import {DAO__factory} from '@aragon/osx-ethers'; -import {DeployFunction} from 'hardhat-deploy/types'; -import {HardhatRuntimeEnvironment} from 'hardhat/types'; - -const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const [deployer] = await hre.ethers.getSigners(); - - console.log('\nChecking the permissions of the deployment wallet'); - - if (!hre.managingDao.address) { - throw new Error('The Managing DAO is not ready'); - } - const mgmtDAO = DAO__factory.connect(hre.managingDao.address, deployer); - - const perm = await mgmtDAO.hasPermission( - mgmtDAO.address, - deployer.address, - await mgmtDAO.EXECUTE_PERMISSION_ID(), - '0x' - ); - if (perm) { - throw new Error( - 'The governance plugin of the Managing DAO cannot execute proposals on it' - ); - } -}; - -export default func; - -func.tags = ['Verification']; -func.runAtTheEnd = true; -func.skip = (hre: HardhatRuntimeEnvironment) => - Promise.resolve(isLocalChain(hre.network.name)); diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index e04175d..75d1060 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -84,8 +84,8 @@ for (const network in networks) { // Extend HardhatRuntimeEnvironment extendEnvironment((hre: HardhatRuntimeEnvironment) => { - hre.aragonToVerifyContracts = []; - hre.managingDao = { + (hre as any).aragonToVerifyContracts = []; + (hre as any).managingDao = { address: '', governancePlugin: '', }; @@ -110,6 +110,14 @@ const config: HardhatUserConfig = { browserURL: 'https://goerli.basescan.org', }, }, + { + network: 'custom', + chainId: networks.custom.chainId!, + urls: { + apiURL: apiUrls.custom, + browserURL: '', + }, + }, ], }, mocha: { diff --git a/packages/contracts/scripts/managing-dao-setup.ts b/packages/contracts/scripts/managing-dao-setup.ts new file mode 100644 index 0000000..389b94c --- /dev/null +++ b/packages/contracts/scripts/managing-dao-setup.ts @@ -0,0 +1,379 @@ +import {GovernancePluginsSetupParams} from '../plugin-setup-params'; +import { + GovernancePluginsSetup__factory, + MajorityVotingBase, + PluginSetupProcessor__factory, +} from '../typechain'; +import {ExecutedEvent} from '../typechain/@aragon/osx/core/dao/DAO'; +import { + InstallationPreparedEvent, + PluginSetupRefStruct, +} from '../typechain/@aragon/osx/framework/plugin/setup/PluginSetupProcessor'; +import {findEvent, hashHelpers} from '../utils/helpers'; +import { + DAO__factory, + IDAO, + IDAO__factory, + PluginRepo__factory, + PluginSetupProcessor, +} from '@aragon/osx-ethers'; +import {config as dotenvConfig} from 'dotenv'; +import {Wallet, providers} from 'ethers'; +import {resolve} from 'path'; + +const dotenvConfigPath: string = + process.env.DOTENV_CONFIG_PATH || '../../../.env'; +dotenvConfig({path: resolve(__dirname, dotenvConfigPath)}); + +if (!process.env.PRIVATE_KEY) { + throw new Error('PRIVATE_KEY in .env not set'); +} else if (!process.env.INFURA_API_KEY) { + throw new Error('INFURA_API_KEY in .env not set'); +} + +const { + NETWORK_NAME, + INFURA_API_KEY, + MANAGING_DAO_ADDRESS, + PLUGIN_SETUP_PROCESSOR_ADDRESS, + GOVERNANCE_PLUGIN_REPO_ADDRESS, +} = process.env; + +const MGMT_DAO_PROPOSAL_DURATION = + parseInt(process.env.MGMT_DAO_PROPOSAL_DURATION ?? '604800') || + 60 * 60 * 24 * 7; +const MGMT_DAO_MIN_PROPOSAL_PARTICIPATION = + parseInt(process.env.MGMT_DAO_MIN_PROPOSAL_PARTICIPATION ?? '500000') || + 500_000; // 50% +const MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD = + parseInt(process.env.MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD ?? '500000') || + 500_000; // 50% +const MGMT_DAO_INITIAL_EDITORS = process.env.MGMT_DAO_INITIAL_EDITORS + ? process.env.MGMT_DAO_INITIAL_EDITORS.split(',') + : ([] as string[]); + +const infuraProvider = new providers.InfuraProvider( + NETWORK_NAME, + INFURA_API_KEY +); +const deployer = new Wallet(process.env.PRIVATE_KEY!).connect(infuraProvider); + +async function main() { + if ( + !NETWORK_NAME || + !INFURA_API_KEY || + !MANAGING_DAO_ADDRESS || + !PLUGIN_SETUP_PROCESSOR_ADDRESS || + !GOVERNANCE_PLUGIN_REPO_ADDRESS + ) { + console.error('Some of .env file values are missing or empty'); + process.exit(1); + } + + await checkManagingDaoPre(); + + // Prepare the plugin details + const preparedInstallation = await prepareInstallation(); + + // Apply the installation + await applyInstallation(preparedInstallation); + + // Drop the execute permission + await dropExecutePermission(); + + // Check the final permissions + await checkManagingDaoPost(preparedInstallation); +} + +// Helpers + +async function checkManagingDaoPre() { + console.log('Configuring the Managing DAO deployed at', MANAGING_DAO_ADDRESS); + const mgmtDAO = DAO__factory.connect(MANAGING_DAO_ADDRESS!, deployer); + + // Deployer should have execute permission on the DAO + const canExecute = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + deployer.address, + await mgmtDAO.EXECUTE_PERMISSION_ID(), + '0x' + ); + if (!canExecute) { + throw new Error( + 'The given deployment wallet cannot execute actions on the DAO' + ); + } + + // The DAO should have root permission on itself + let isRoot = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + MANAGING_DAO_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + '0x' + ); + if (!isRoot) { + throw new Error('The given Managing DAO is not root on itself'); + } + + // The PSP should not have root permission on the DAO + isRoot = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + PLUGIN_SETUP_PROCESSOR_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + '0x' + ); + if (isRoot) { + throw new Error( + 'The Plugin Setup Processor should not have root permission on the managing DAO (yet)' + ); + } +} + +async function prepareInstallation() { + const pluginRepo = PluginRepo__factory.connect( + GOVERNANCE_PLUGIN_REPO_ADDRESS!, + deployer + ); + const pluginSetupInfo = await pluginRepo['getLatestVersion(uint8)']( + GovernancePluginsSetupParams.VERSION.release + ); + if (!pluginSetupInfo.pluginSetup) { + throw new Error('The Governance plugin is not available'); + } + const pluginSetup = GovernancePluginsSetup__factory.connect( + pluginSetupInfo.pluginSetup, + deployer + ); + + const settings: MajorityVotingBase.VotingSettingsStruct = { + duration: MGMT_DAO_PROPOSAL_DURATION, + minParticipation: MGMT_DAO_MIN_PROPOSAL_PARTICIPATION, + supportThreshold: MGMT_DAO_PROPOSAL_SUPPORT_THRESHOLD, + votingMode: 1, // Early execution + }; + const memberAccessProposalDuration = MGMT_DAO_PROPOSAL_DURATION * 3; // Time before expired + const pluginUpgrader = '0x0000000000000000000000000000000000000000'; // Just the DAO + const installData = await pluginSetup.encodeInstallationParams( + settings, + MGMT_DAO_INITIAL_EDITORS, + memberAccessProposalDuration, + pluginUpgrader + ); + + if (!PLUGIN_SETUP_PROCESSOR_ADDRESS) { + throw new Error('PLUGIN_SETUP_PROCESSOR_ADDRESS cannot be empty'); + } + + console.log('Preparing an installation of the standard governance plugin'); + const psp = PluginSetupProcessor__factory.connect( + PLUGIN_SETUP_PROCESSOR_ADDRESS, + deployer + ); + + const pluginSetupRef: PluginSetupRefStruct = { + pluginSetupRepo: GOVERNANCE_PLUGIN_REPO_ADDRESS!, + versionTag: GovernancePluginsSetupParams.VERSION, + }; + + const tx = await psp.prepareInstallation(MANAGING_DAO_ADDRESS!, { + pluginSetupRef, + data: installData, + }); + await tx.wait(); + + const preparedEvent = await findEvent( + tx, + 'InstallationPrepared' + ); + if (!preparedEvent) { + throw new Error('Failed to get InstallationPrepared event'); + } + console.log( + '- Deployed a MainVotingPlugin plugin at', + preparedEvent.args.plugin + ); + console.log( + '- Deployed a MemberAccessPlugin plugin at', + preparedEvent.args.preparedSetupData.helpers[0] + ); + + return { + pluginAddress: preparedEvent.args.plugin, + helpers: preparedEvent.args.preparedSetupData.helpers, + permissions: preparedEvent.args.preparedSetupData.permissions, + }; +} + +async function applyInstallation( + preparedInstallation: Awaited> +) { + const {helpers, permissions, pluginAddress} = preparedInstallation; + const mgmtDAO = DAO__factory.connect(MANAGING_DAO_ADDRESS!, deployer); + + // Encode a call to execute() > applyInstallation + console.log('Installing the plugin to the managing DAO'); + const applyInstallationParams: PluginSetupProcessor.ApplyInstallationParamsStruct = + { + plugin: pluginAddress, + helpersHash: hashHelpers(helpers), + permissions, + pluginSetupRef: { + pluginSetupRepo: GOVERNANCE_PLUGIN_REPO_ADDRESS!, + versionTag: GovernancePluginsSetupParams.VERSION, + }, + }; + + console.log('- Allowing the PSP to manage permissions'); + console.log('- Telling the PSP to apply the installation'); + console.log("- Revoking the PSP's permission to manage permissions"); + + const actions: IDAO.ActionStruct[] = [ + // Allow the PSP to install a plugin + { + to: MANAGING_DAO_ADDRESS!, + value: 0, + data: DAO__factory.createInterface().encodeFunctionData('grant', [ + MANAGING_DAO_ADDRESS!, + PLUGIN_SETUP_PROCESSOR_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + ]), + }, + // Install the plugin + { + to: PLUGIN_SETUP_PROCESSOR_ADDRESS!, + value: 0, + data: PluginSetupProcessor__factory.createInterface().encodeFunctionData( + 'applyInstallation', + [MANAGING_DAO_ADDRESS!, applyInstallationParams] + ), + }, + // Revoke the PSP permission + { + to: MANAGING_DAO_ADDRESS!, + value: 0, + data: DAO__factory.createInterface().encodeFunctionData('revoke', [ + MANAGING_DAO_ADDRESS!, + PLUGIN_SETUP_PROCESSOR_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + ]), + }, + ]; + + const tx = await IDAO__factory.connect( + MANAGING_DAO_ADDRESS!, + deployer + ).execute( + '0x0000000000000000000000000000000000000000000000000000000000000000', + actions, + 0 + ); + await tx.wait(); + + // Executed(address,bytes32,tuple[],uint256,uint256,bytes[]) + const executedEvent = await findEvent(tx, 'Executed'); + if (!executedEvent) { + throw new Error('Could not execute the applyInstallation action'); + } + + console.log('Installation confirmed'); +} + +async function dropExecutePermission() { + const mgmtDAO = DAO__factory.connect(MANAGING_DAO_ADDRESS!, deployer); + + // Encode a call to execute() > revoke + console.log('Revoking EXECUTE permission from the deployment wallet'); + + const actions: IDAO.ActionStruct[] = [ + // Revoke the PSP permission + { + to: MANAGING_DAO_ADDRESS!, + value: 0, + data: DAO__factory.createInterface().encodeFunctionData('revoke', [ + MANAGING_DAO_ADDRESS!, + deployer.address, + await mgmtDAO.ROOT_PERMISSION_ID(), + ]), + }, + ]; + + const tx = await IDAO__factory.connect( + MANAGING_DAO_ADDRESS!, + deployer + ).execute( + '0x0000000000000000000000000000000000000000000000000000000000000001', + actions, + 0 + ); + await tx.wait(); + + // Executed(address,bytes32,tuple[],uint256,uint256,bytes[]) + const executedEvent = await findEvent(tx, 'Executed'); + if (!executedEvent) { + throw new Error('Could not execute the revoke action'); + } + + console.log('Revoked EXECUTE permission from the deployment wallet'); +} + +async function checkManagingDaoPost( + preparedInstallation: Awaited> +) { + console.log("Checking the Managing DAO's final permissions"); + const mgmtDAO = DAO__factory.connect(MANAGING_DAO_ADDRESS!, deployer); + + // Deployer should not have execute permission on the DAO + let canExecute = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + deployer.address, + await mgmtDAO.EXECUTE_PERMISSION_ID(), + '0x' + ); + if (canExecute) { + throw new Error( + 'The given deployment wallet should no longer have EXECUTE permission on the DAO' + ); + } + + // The plugin should have execute permission on the DAO + canExecute = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + preparedInstallation.pluginAddress, + await mgmtDAO.EXECUTE_PERMISSION_ID(), + '0x' + ); + if (!canExecute) { + throw new Error( + 'The MainVotingPlugin should have execute permission on the Managing DAO' + ); + } + + // The DAO should have root permission on itself + let isRoot = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + MANAGING_DAO_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + '0x' + ); + if (!isRoot) { + throw new Error('The given Managing DAO is not root on itself'); + } + + // The PSP should not have root permission on the DAO + isRoot = await mgmtDAO.hasPermission( + MANAGING_DAO_ADDRESS!, + PLUGIN_SETUP_PROCESSOR_ADDRESS!, + await mgmtDAO.ROOT_PERMISSION_ID(), + '0x' + ); + if (isRoot) { + throw new Error( + 'The Plugin Setup Processor should not have root permission on the managing DAO (yet)' + ); + } +} + +main().catch(err => { + console.error(err); + process.exit(1); +});