From 4083b5c4255162d18c51bd078f65a4b8b54479bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= Date: Wed, 25 Sep 2024 19:02:48 +0100 Subject: [PATCH] Almost ready --- .env.example | 3 -- script/Deploy.s.sol | 84 ++++++++++++++++++++++---------- src/factory/GaugesDaoFactory.sol | 66 +++++++++++++++++-------- 3 files changed, 103 insertions(+), 50 deletions(-) diff --git a/.env.example b/.env.example index 04b761d..38d0cc9 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,6 @@ DEPLOY_AS_PRODUCTION=true MULTISIG_MEMBERS_JSON_FILE_NAME="/script/multisig-members.json" # MULTISIG PARAMETERS -MIN_PROPOSAL_DURATION="864000" # in seconds (10 days) MIN_APPROVALS="5" # How many multisig approvals are required MULTISIG_PROPOSAL_EXPIRATION_PERIOD="864000" # How long until a pending proposal expires (10 days) @@ -51,5 +50,3 @@ SIMPLE_GAUGE_VOTER_REPO_ENS_SUBDOMAIN="my-simple-gauge-voter-0" DAO_FACTORY="0xE640Da5AD169630555A86D9b6b9C145B4961b1EB" PLUGIN_SETUP_PROCESSOR="0xCe0B4124dea6105bfB85fB4461c4D39f360E9ef3" PLUGIN_REPO_FACTORY="0x95D563382BeD5AcB458759EE05b27DF2CB019Cc7" - -# GOVERNANCE_ERC20_BASE="0xC24188a73dc09aA7C721f96Ad8857B469C01dC9f" diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index a99fee1..2d837b9 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -9,11 +9,12 @@ import {VotingEscrow, Clock, Lock, QuadraticIncreasingEscrow, ExitQueue, SimpleG import {PluginRepo} from "@aragon/osx/framework/plugin/repo/PluginRepo.sol"; import {PluginRepoFactory} from "@aragon/osx/framework/plugin/repo/PluginRepoFactory.sol"; import {PluginSetupProcessor} from "@aragon/osx/framework/plugin/setup/PluginSetupProcessor.sol"; +import {MockERC20} from "@mocks/MockERC20.sol"; contract Deploy is Script { SimpleGaugeVoterSetup simpleGaugeVoterSetup; - /// @dev Thrown when attempting to create a DAO with an empty multisig + /// @dev Thrown when attempting to deploy a multisig with no members error EmptyMultisig(); modifier broadcast() { @@ -26,18 +27,11 @@ contract Deploy is Script { vm.stopBroadcast(); } + /// @notice Runs the deployment flow, records the given parameters and artifacts, and it becomes read only function run() public broadcast { - // NOTE: - // Deploying the plugin setup's separately because of the code size limit - - // Note: Multisig is already deployed, not redeploying - - // Deploy the voter plugin setup - // TODO: - - DeploymentParameters memory parameters = getDeploymentParameters( - vm.envBool("DEPLOY_AS_PRODUCTION") - ); + // Prepare all parameters + bool isProduction = vm.envBool("DEPLOY_AS_PRODUCTION"); + DeploymentParameters memory parameters = getDeploymentParameters(isProduction); // Create the DAO GaugesDaoFactory factory = new GaugesDaoFactory(parameters); @@ -50,13 +44,20 @@ contract Deploy is Script { function getDeploymentParameters( bool isProduction ) internal returns (DeploymentParameters memory parameters) { + address[] memory multisigMembers = readMultisigMembers(); + TokenParameters[] memory tokenParameters = getTokenParameters(isProduction); + + // NOTE: Multisig is already deployed, using the existing Aragon's repo + // NOTE: Deploying the plugin setup from the current script to avoid code size constraints + + SimpleGaugeVoterSetup gaugeVoterPluginSetup = deploySimpleGaugeVoterPluginSetup(); + parameters = DeploymentParameters({ // Multisig settings - minProposalDuration: uint64(vm.envUint("MIN_PROPOSAL_DURATION")), minApprovals: uint8(vm.envUint("MIN_APPROVALS")), - multisigMembers: readMultisigMembers(), + multisigMembers: multisigMembers, // Gauge Voter - tokenParameters: getTokenParameters(isProduction), + tokenParameters: tokenParameters, feePercent: vm.envUint("FEE_PERCENT_WEI"), warmupPeriod: uint64(vm.envUint("WARMUP_PERIOD")), cooldownPeriod: uint64(vm.envUint("COOLDOWN_PERIOD")), @@ -67,7 +68,7 @@ contract Deploy is Script { multisigPluginRelease: uint8(vm.envUint("MULTISIG_PLUGIN_RELEASE")), multisigPluginBuild: uint16(vm.envUint("MULTISIG_PLUGIN_BUILD")), // Voter plugin setup and ENS - voterPluginSetup: deploySimpleGaugeVoterPluginSetup(), + voterPluginSetup: gaugeVoterPluginSetup, voterEnsSubdomain: vm.envString("SIMPLE_GAUGE_VOTER_REPO_ENS_SUBDOMAIN"), // OSx addresses osxDaoFactory: vm.envAddress("DAO_FACTORY"), @@ -99,7 +100,7 @@ contract Deploy is Script { function getTokenParameters( bool isProduction - ) internal view returns (TokenParameters[] memory tokenParameters) { + ) internal returns (TokenParameters[] memory tokenParameters) { if (isProduction) { // USE TOKEN(s) console.log("Using production parameters"); @@ -122,15 +123,39 @@ contract Deploy is Script { } } else { // MINT TEST TOKEN - console.log("Using testing parameters (minting 2 token)"); + console.log("Using testing parameters (minting 2 test tokens)"); + + address[] memory multisigMembers = readMultisigMembers(); + address[] memory tokens = new address[](2); + tokens[0] = createTestToken(multisigMembers); + tokens[1] = createTestToken(multisigMembers); - // TODO: + tokenParameters = new TokenParameters[](2); + tokenParameters[0] = TokenParameters({ + token: tokens[0], + veTokenName: "VE Token 1", + veTokenSymbol: "veTK1" + }); + tokenParameters[1] = TokenParameters({ + token: tokens[1], + veTokenName: "VE Token 2", + veTokenSymbol: "veTK2" + }); } } - function createTestToken(address[] memory holders) internal { - // TODO: - address newToken = vm.envAddress("GOVERNANCE_ERC20_BASE"); + function createTestToken(address[] memory holders) internal returns (address) { + MockERC20 newToken = new MockERC20(); + + for (uint i = 0; i < holders.length; ) { + newToken.mint(holders[i], 50 ether); + + unchecked { + i++; + } + } + + return address(newToken); } function printDeploymentSummary(GaugesDaoFactory factory) internal view { @@ -147,9 +172,16 @@ contract Deploy is Script { console.log("Plugins"); console.log("- Multisig plugin:", address(deployment.multisigPlugin)); console.log(""); - for (uint i = 0; i < deployment.voterPlugins.length; ) { - console.log("- Token:", address(deploymentParameters.tokenParameters[i].token)); - console.log(" Gauge voter plugin:", address(deployment.voterPlugins[i])); + + for (uint i = 0; i < deployment.gaugePluginSets.length; ) { + console.log("- Using token:", address(deploymentParameters.tokenParameters[i].token)); + console.log(" Gauge voter plugin:", address(deployment.gaugePluginSets[i].plugin)); + console.log(" Curve:", address(deployment.gaugePluginSets[i].curve)); + console.log(" Exit Queue:", address(deployment.gaugePluginSets[i].exitQueue)); + console.log(" Voting Escrow:", address(deployment.gaugePluginSets[i].votingEscrow)); + console.log(" Clock:", address(deployment.gaugePluginSets[i].clock)); + console.log(" NFT Lock:", address(deployment.gaugePluginSets[i].nftLock)); + unchecked { i++; } @@ -158,7 +190,7 @@ contract Deploy is Script { console.log("Plugin repositories"); console.log( - "- Eultisig plugin repository (existing):", + "- Multisig plugin repository (existing):", address(deploymentParameters.multisigPluginRepo) ); console.log("- Gauge voter plugin repository:", address(deployment.voterPluginRepo)); diff --git a/src/factory/GaugesDaoFactory.sol b/src/factory/GaugesDaoFactory.sol index df5ea50..044c123 100644 --- a/src/factory/GaugesDaoFactory.sol +++ b/src/factory/GaugesDaoFactory.sol @@ -7,7 +7,7 @@ import {Clock} from "@clock/Clock.sol"; import {IEscrowCurveUserStorage} from "@escrow-interfaces/IEscrowCurveIncreasing.sol"; import {IWithdrawalQueueErrors} from "src/escrow/increasing/interfaces/IVotingEscrowIncreasing.sol"; import {IGaugeVote} from "src/voting/ISimpleGaugeVoter.sol"; -import {SimpleGaugeVoter, SimpleGaugeVoterSetup, ISimpleGaugeVoterSetupParams} from "src/voting/SimpleGaugeVoterSetup.sol"; +import {VotingEscrow, Clock, Lock, QuadraticIncreasingEscrow, ExitQueue, SimpleGaugeVoter, SimpleGaugeVoterSetup, ISimpleGaugeVoterSetupParams} from "src/voting/SimpleGaugeVoterSetup.sol"; import {Addresslist} from "@aragon/osx/plugins/utils/Addresslist.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {PluginSetupProcessor} from "@aragon/osx/framework/plugin/setup/PluginSetupProcessor.sol"; @@ -23,22 +23,24 @@ import {createERC1967Proxy} from "@aragon/osx/utils/Proxy.sol"; import {PermissionLib} from "@aragon/osx/core/permission/PermissionLib.sol"; /// @notice The struct containing all the parameters to deploy the DAO -/// @param token1Address The address of the IVotes compatible ERC20 token contract to use for the voting power -/// @param token2Address The address of the IVotes compatible ERC20 token contract to use for the voting power /// @param minApprovals The amount of approvals required for the multisig to be able to execute a proposal on the DAO -// OSx +/// @param multisigMembers The list of addresses to be defined as the initial multisig signers +/// @param tokenParameters A list with the tokens and metadata for which a plugin and a VE should be deployed +/// @param feePercent The fee taken on withdrawals (1 ether = 100%) +/// @param warmupPeriod Delay in seconds after depositing before voting becomes possible +/// @param cooldownPeriod Delay seconds after queuing an exit before withdrawing becomes possible +/// @param minLockDuration Min seconds a user must have locked in escrow before they can queue an exit +/// @param votingPaused Prevent voting until manually activated by the multisig +/// @param multisigPluginRepo Address of Aragon's multisig plugin repository on the given network +/// @param multisigPluginRelease The release of the multisig plugin to target +/// @param multisigPluginBuild The build of the multisig plugin to target +/// @param voterPluginSetup The address of the Gauges Voter plugin setup contract to create a repository with +/// @param voterEnsSubdomain The ENS subdomain under which the plugin reposiroty will be created /// @param osxDaoFactory The address of the OSx DAO factory contract, used to retrieve the DAO implementation address /// @param pluginSetupProcessor The address of the OSx PluginSetupProcessor contract on the target chain /// @param pluginRepoFactory The address of the OSx PluginRepoFactory contract on the target chain -// Plugins -/// @param multisigPluginSetup The address of the already deployed plugin setup for the standard multisig -/// @param votingPluginSetup The address of the already deployed plugin setup for the optimistic voting plugin -/// @param multisigMembers The list of addresses to be defined as the initial multisig signers -/// @param multisigEnsDomain The subdomain to use as the ENS for the standard mulsitig plugin setup. Note: it must be unique and available. -/// @param votingEnsDomain The subdomain to use as the ENS for the optimistic voting plugin setup. Note: it must be unique and available. struct DeploymentParameters { // Multisig settings - uint64 minProposalDuration; uint16 minApprovals; address[] multisigMembers; // Gauge Voter @@ -66,15 +68,27 @@ struct TokenParameters { string veTokenSymbol; } +/// @notice Struct containing the plugin and all of its helpers +struct GaugePluginSet { + SimpleGaugeVoter plugin; + QuadraticIncreasingEscrow curve; + ExitQueue exitQueue; + VotingEscrow votingEscrow; + Clock clock; + Lock nftLock; +} + +/// @notice Contains the artifacts that resulted from running a deployment struct Deployment { DAO dao; // Plugins Multisig multisigPlugin; - SimpleGaugeVoter[] voterPlugins; + GaugePluginSet[] gaugePluginSets; // Plugin repo's PluginRepo voterPluginRepo; } +/// @notice A singleton contract designed to run the deployment once and become a read-only store of the contracts deployed contract GaugesDaoFactory { /// @notice Thrown when attempting to call deployOnce() when the DAO is already deployed. error AlreadyDeployed(); @@ -88,6 +102,7 @@ contract GaugesDaoFactory { parameters = _parameters; } + /// @notice Run the deployment and store the artifacts in a read-only store that can be retrieved via `getDeployment()` and `getDeploymentParameters()` function deployOnce() public { if (address(deployment.dao) != address(0)) revert AlreadyDeployed(); @@ -122,29 +137,27 @@ contract GaugesDaoFactory { // GAUGE VOTER(s) { IPluginSetup.PreparedSetupData memory preparedVoterSetupData; - SimpleGaugeVoter voterPlugin; PluginRepo.Tag memory repoTag = PluginRepo.Tag(1, 1); - - deployment.voterPlugins = new SimpleGaugeVoter[](parameters.tokenParameters.length); + GaugePluginSet memory pluginSet; for (uint i = 0; i < parameters.tokenParameters.length; ) { ( - voterPlugin, + pluginSet, deployment.voterPluginRepo, preparedVoterSetupData ) = prepareSimpleGaugeVoterPlugin(dao, parameters.tokenParameters[i], repoTag); + deployment.gaugePluginSets.push(pluginSet); + applyPluginInstallation( dao, - address(voterPlugin), + address(pluginSet.plugin), deployment.voterPluginRepo, repoTag, preparedVoterSetupData ); - deployment.voterPlugins[i] = voterPlugin; - unchecked { i++; } @@ -229,7 +242,7 @@ contract GaugesDaoFactory { DAO dao, TokenParameters memory tokenParameters, PluginRepo.Tag memory repoTag - ) internal returns (SimpleGaugeVoter, PluginRepo, IPluginSetup.PreparedSetupData memory) { + ) internal returns (GaugePluginSet memory, PluginRepo, IPluginSetup.PreparedSetupData memory) { // Publish repo PluginRepo pluginRepo = PluginRepoFactory(parameters.pluginRepoFactory) .createPluginRepoWithFirstVersion( @@ -263,7 +276,18 @@ contract GaugesDaoFactory { settingsData ) ); - return (SimpleGaugeVoter(plugin), pluginRepo, preparedSetupData); + + address[] memory helpers = preparedSetupData.helpers; + GaugePluginSet memory pluginSet = GaugePluginSet({ + plugin: SimpleGaugeVoter(plugin), + curve: QuadraticIncreasingEscrow(helpers[0]), + exitQueue: ExitQueue(helpers[1]), + votingEscrow: VotingEscrow(helpers[2]), + clock: Clock(helpers[3]), + nftLock: Lock(helpers[4]) + }); + + return (pluginSet, pluginRepo, preparedSetupData); } function applyPluginInstallation(