From ba5c553db3feb0bc1a621b571b80d8579c67d377 Mon Sep 17 00:00:00 2001 From: Orlando Date: Wed, 11 Oct 2023 11:52:50 +0100 Subject: [PATCH 01/15] chore: replaced old deployer scripts with new one --- script/Deployer.s.sol | 267 ++++++++++++++++++++++++++++++ script/ModulesDeployment.s.sol | 45 ----- script/PopulateVanillaSpace.s.sol | 72 -------- script/SpaceSetup.s.sol | 80 --------- 4 files changed, 267 insertions(+), 197 deletions(-) create mode 100644 script/Deployer.s.sol delete mode 100644 script/ModulesDeployment.s.sol delete mode 100644 script/PopulateVanillaSpace.s.sol delete mode 100644 script/SpaceSetup.s.sol diff --git a/script/Deployer.s.sol b/script/Deployer.s.sol new file mode 100644 index 00000000..83587cff --- /dev/null +++ b/script/Deployer.s.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import { Script } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; + +import { ProxyFactory } from "../src/ProxyFactory.sol"; +import { Space } from "../src/Space.sol"; +import { Strategy, InitializeCalldata } from "../src/types.sol"; + +import { VanillaExecutionStrategy } from "../src/execution-strategies/VanillaExecutionStrategy.sol"; +import { AvatarExecutionStrategy } from "../src/execution-strategies/AvatarExecutionStrategy.sol"; +import { TimelockExecutionStrategy } from "../src/execution-strategies/timelocks/TimelockExecutionStrategy.sol"; +import { + OptimisticTimelockExecutionStrategy +} from "../src/execution-strategies/timelocks/OptimisticTimelockExecutionStrategy.sol"; +import { + CompTimelockCompatibleExecutionStrategy +} from "../src/execution-strategies/timelocks/CompTimelockCompatibleExecutionStrategy.sol"; +import { + OptimisticCompTimelockCompatibleExecutionStrategy +} from "../src/execution-strategies/timelocks/OptimisticCompTimelockCompatibleExecutionStrategy.sol"; + +import { VanillaAuthenticator } from "../src/authenticators/VanillaAuthenticator.sol"; +import { EthSigAuthenticator } from "../src/authenticators/EthSigAuthenticator.sol"; +import { EthTxAuthenticator } from "../src/authenticators/EthTxAuthenticator.sol"; + +import { VanillaVotingStrategy } from "../src/voting-strategies/VanillaVotingStrategy.sol"; +import { CompVotingStrategy } from "../src/voting-strategies/CompVotingStrategy.sol"; +import { OZVotesVotingStrategy } from "../src/voting-strategies/OZVotesVotingStrategy.sol"; +import { WhitelistVotingStrategy } from "../src/voting-strategies/WhitelistVotingStrategy.sol"; + +import { + VanillaProposalValidationStrategy +} from "../src/proposal-validation-strategies/VanillaProposalValidationStrategy.sol"; +import { + PropositionPowerProposalValidationStrategy +} from "../src/proposal-validation-strategies/PropositionPowerProposalValidationStrategy.sol"; +import { + ActiveProposalsLimiterProposalValidationStrategy +} from "../src/proposal-validation-strategies/ActiveProposalsLimiterProposalValidationStrategy.sol"; +import { + PropositionPowerAndActiveProposalsLimiterValidationStrategy +} from "../src/proposal-validation-strategies/PropositionPowerAndActiveProposalsLimiterValidationStrategy.sol"; + +interface SingletonFactory { + function deploy(bytes memory _initCode, bytes32 salt) external returns (address payable); +} + +/// @notice Script to deploy the Snapshot-X contracts +contract Deployer is Script { + error SpaceInitializationFailed(); + + SingletonFactory internal singletonFactory = SingletonFactory(0xce0042B868300000d44A59004Da54A005ffdcf9f); + address internal deployer; + string internal deployments; + string internal deploymentsPath; + + string internal name = "snapshot-x"; + string internal version = "1.0.0"; + + using stdJson for string; + + // Nonce used in the CREATE2 salts computation + uint256 internal saltNonce = 0; + + function run() public { + deployer = vm.envAddress("DEPLOYER_ADDRESS"); + + string memory network = vm.envString("NETWORK"); + + deploymentsPath = string.concat(string.concat("./deployments/", network), ".json"); + + vm.startBroadcast(deployer); + + // ------- EXECUTION STRATEGIES ------- + + (address avatarExecutionStrategy, ) = noRedeploy( + deployer, + abi.encodePacked( + type(AvatarExecutionStrategy).creationCode, + abi.encode(address(0x1), address(0x1), new address[](0), 0) + ), + saltNonce + ); + + deployments.serialize("AvatarExecutionStrategyImplementation", avatarExecutionStrategy); + + (address timelockExecutionStrategy, ) = noRedeploy( + deployer, + abi.encodePacked(type(TimelockExecutionStrategy).creationCode), + saltNonce + ); + + deployments.serialize("TimelockExecutionStrategyImplementation", timelockExecutionStrategy); + + (address optimisticTimelockExecutionStrategy, ) = noRedeploy( + deployer, + abi.encodePacked(type(OptimisticTimelockExecutionStrategy).creationCode), + saltNonce + ); + + deployments.serialize("OptimisticTimelockExecutionStrategyImplementation", optimisticTimelockExecutionStrategy); + + (address compTimelockCompatibleExecutionStrategy, ) = noRedeploy( + deployer, + abi.encodePacked( + type(CompTimelockCompatibleExecutionStrategy).creationCode, + abi.encode(address(0x1), address(0x1), new address[](0), 0, 0) + ), + saltNonce + ); + + deployments.serialize( + "CompTimelockCompatibleExecutionStrategyImplementation", + compTimelockCompatibleExecutionStrategy + ); + + (address optimisticCompTimelockCompatibleExecutionStrategy, ) = noRedeploy( + deployer, + abi.encodePacked( + type(OptimisticCompTimelockCompatibleExecutionStrategy).creationCode, + abi.encode(address(0x1), address(0x1), new address[](0), 0, 0) + ), + saltNonce + ); + + deployments.serialize( + "OptimisticCompTimelockCompatibleExecutionStrategyImplementation", + optimisticCompTimelockCompatibleExecutionStrategy + ); + + // ------- AUTHENTICATORS ------- + + (address vanillaAuthenticator, ) = noRedeploy(deployer, type(VanillaAuthenticator).creationCode, saltNonce); + deployments.serialize("VanillaAuthenticator", vanillaAuthenticator); + + (address ethTxAuthenticator, ) = noRedeploy(deployer, type(EthTxAuthenticator).creationCode, saltNonce); + deployments.serialize("EthTxAuthenticator", ethTxAuthenticator); + + (address ethSigAuthenticator, ) = noRedeploy( + deployer, + abi.encodePacked(type(EthSigAuthenticator).creationCode, abi.encode(name, version)), + saltNonce + ); + deployments.serialize("EthSigAuthenticator", ethSigAuthenticator); + + // ------- VOTING STRATEGIES ------- + + (address vanillaVotingStrategy, ) = noRedeploy(deployer, type(VanillaVotingStrategy).creationCode, saltNonce); + deployments.serialize("VanillaVotingStrategy", vanillaVotingStrategy); + + (address whitelistVotingStrategy, ) = noRedeploy( + deployer, + type(WhitelistVotingStrategy).creationCode, + saltNonce + ); + deployments.serialize("WhitelistVotingStrategy", whitelistVotingStrategy); + + (address compVotingStrategy, ) = noRedeploy(deployer, type(CompVotingStrategy).creationCode, saltNonce); + deployments.serialize("CompVotingStrategy", compVotingStrategy); + + (address ozVotesVotingStrategy, ) = noRedeploy(deployer, type(OZVotesVotingStrategy).creationCode, saltNonce); + deployments.serialize("OZVotesVotingStrategy", ozVotesVotingStrategy); + + // ------- PROPOSAL VALIDATION STRATEGIES ------- + + (address vanillaProposalValidationStrategy, ) = noRedeploy( + deployer, + type(VanillaProposalValidationStrategy).creationCode, + saltNonce + ); + deployments.serialize("VanillaProposalValidationStrategy", vanillaProposalValidationStrategy); + + (address propositionPowerProposalValidationStrategy, ) = noRedeploy( + deployer, + type(PropositionPowerProposalValidationStrategy).creationCode, + saltNonce + ); + deployments.serialize("PropositionPowerProposalValidationStrategy", propositionPowerProposalValidationStrategy); + + (address activeProposalsLimiterProposalValidationStrategy, ) = noRedeploy( + deployer, + type(ActiveProposalsLimiterProposalValidationStrategy).creationCode, + saltNonce + ); + + deployments.serialize( + "ActiveProposalsLimiterProposalValidationStrategy", + activeProposalsLimiterProposalValidationStrategy + ); + + (address propositionPowerAndActiveProposalsLimiterValidationStrategy, ) = noRedeploy( + deployer, + type(PropositionPowerAndActiveProposalsLimiterValidationStrategy).creationCode, + saltNonce + ); + + deployments.serialize( + "PropositionPowerAndActiveProposalsLimiterValidationStrategy", + propositionPowerAndActiveProposalsLimiterValidationStrategy + ); + + // ------- PROXY FACTORY ------- + + (address proxyFactory, ) = noRedeploy(deployer, type(ProxyFactory).creationCode, saltNonce); + deployments.serialize("ProxyFactory", proxyFactory); + + // ------- SPACE ------- + + (address space, ) = noRedeploy(deployer, type(Space).creationCode, saltNonce); + + // If the master space is not initialized, initialize it + if (Space(space).owner() == address(0x0)) { + // Initializer for the master space, to render it unusable + Strategy[] memory emptyStrategyArray = new Strategy[](1); + emptyStrategyArray[0] = Strategy(address(0x1), new bytes(0)); + string[] memory emptyStringArray = new string[](1); + emptyStringArray[0] = ""; + address[] memory emptyAddressArray = new address[](1); + emptyAddressArray[0] = address(0x1); + Space(space).initialize( + InitializeCalldata( + address(0x1), + 1, + 1, + 1, + Strategy(address(0x1), new bytes(0)), + "", + "", + "", + emptyStrategyArray, + emptyStringArray, + emptyAddressArray + ) + ); + } + + if (Space(space).owner() != address(0x1)) { + // Initialization of the master space was frontrun + revert SpaceInitializationFailed(); + } + + deployments = deployments.serialize("SpaceImplementation", space); + + deployments.write(deploymentsPath); + + vm.stopBroadcast(); + } + + // Deploys contract if it doesn't exist, otherwise returns the create2 address + function noRedeploy( + address _deployer, + bytes memory _initCode, + uint256 _saltNonce + ) internal returns (address, bool) { + bytes32 salt = keccak256(abi.encodePacked(_deployer, _saltNonce)); + address addy = computeCreate2Address(salt, keccak256(_initCode), address(singletonFactory)); + if (addy.code.length == 0) { + address _addy = singletonFactory.deploy(_initCode, salt); + assert(_addy == addy); + return (addy, true); + } else { + return (addy, false); + } + } +} diff --git a/script/ModulesDeployment.s.sol b/script/ModulesDeployment.s.sol deleted file mode 100644 index bb87e029..00000000 --- a/script/ModulesDeployment.s.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import { Script } from "forge-std/Script.sol"; -import { Space } from "../src/Space.sol"; -import { VanillaAuthenticator } from "../src/authenticators/VanillaAuthenticator.sol"; -import { VanillaVotingStrategy } from "../src/voting-strategies/VanillaVotingStrategy.sol"; -import { VanillaExecutionStrategy } from "../src/execution-strategies/VanillaExecutionStrategy.sol"; -import { - PropositionPowerProposalValidationStrategy -} from "../src/proposal-validation-strategies/PropositionPowerProposalValidationStrategy.sol"; -import { EthSigAuthenticator } from "../src/authenticators/EthSigAuthenticator.sol"; -import { EthTxAuthenticator } from "../src/authenticators/EthTxAuthenticator.sol"; -import { CompVotingStrategy } from "../src/voting-strategies/CompVotingStrategy.sol"; -import { WhitelistVotingStrategy } from "../src/voting-strategies/WhitelistVotingStrategy.sol"; -import { ProxyFactory } from "../src/ProxyFactory.sol"; - -contract ModulesDeployment is Script { - VanillaVotingStrategy public vanillaVotingStrategy; - CompVotingStrategy public compVotingStrategy; - WhitelistVotingStrategy public whitelistVotingStrategy; - VanillaAuthenticator public vanillaAuthenticator; - PropositionPowerProposalValidationStrategy public propositionPowerProposalValidationStrategy; - EthSigAuthenticator public ethSigAuthenticator; - EthTxAuthenticator public ethTxAuthenticator; - VanillaExecutionStrategy public vanillaExecutionStrategy; - ProxyFactory public spaceFactory; - - function run() public { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - vanillaVotingStrategy = new VanillaVotingStrategy(); - compVotingStrategy = new CompVotingStrategy(); - whitelistVotingStrategy = new WhitelistVotingStrategy(); - vanillaAuthenticator = new VanillaAuthenticator(); - ethSigAuthenticator = new EthSigAuthenticator("snapshot-x", "0.1.0"); - ethTxAuthenticator = new EthTxAuthenticator(); - // TODO: set quorum prior to this deploy (or remove) - address deployerAddr = vm.addr(deployerPrivateKey); - vanillaExecutionStrategy = new VanillaExecutionStrategy(deployerAddr, 1); - propositionPowerProposalValidationStrategy = new PropositionPowerProposalValidationStrategy(); - spaceFactory = new ProxyFactory(); - vm.stopBroadcast(); - } -} diff --git a/script/PopulateVanillaSpace.s.sol b/script/PopulateVanillaSpace.s.sol deleted file mode 100644 index ce8e0185..00000000 --- a/script/PopulateVanillaSpace.s.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import { Script } from "forge-std/Script.sol"; -import { Space } from "../src/Space.sol"; -import { VanillaAuthenticator } from "../src/authenticators/VanillaAuthenticator.sol"; -import { VanillaVotingStrategy } from "../src/voting-strategies/VanillaVotingStrategy.sol"; -import { VanillaExecutionStrategy } from "../src/execution-strategies/VanillaExecutionStrategy.sol"; -import { Choice, IndexedStrategy, Strategy } from "../src/types.sol"; - -contract PopulateVanillaSpace is Script { - bytes4 internal constant PROPOSE_SELECTOR = bytes4(keccak256("propose(address,string,(address,bytes),bytes)")); - bytes4 internal constant VOTE_SELECTOR = bytes4(keccak256("vote(address,uint256,uint8,(uint8,bytes)[],string)")); - - Space internal space; - VanillaVotingStrategy internal vanillaVotingStrategy; - VanillaAuthenticator internal vanillaAuthenticator; - VanillaExecutionStrategy internal vanillaExecutionStrategy; - - string public proposalMetadataURI = "SOC Test Proposal"; - Strategy public executionStrategy; - IndexedStrategy[] public userVotingStrategies; - - string internal voteMetadataURI = ""; - - function run() public { - space = Space(0x95DC6f73301356c9909921e21b735601C42fc1a8); - vanillaAuthenticator = VanillaAuthenticator(0xc4fb316710643f7FfBB566e5586862076198DAdB); - vanillaExecutionStrategy = VanillaExecutionStrategy(0x81519C29621Ba131ea398c15B17391F53e8B9A94); - - executionStrategy = Strategy(address(vanillaExecutionStrategy), new bytes(0)); - userVotingStrategies.push(IndexedStrategy(0, new bytes(0))); - - uint256 proposalId = _createProposal( - address(this), - proposalMetadataURI, - executionStrategy, - userVotingStrategies - ); - - _vote(address(this), proposalId, Choice.For, userVotingStrategies, voteMetadataURI); - } - - function _createProposal( - address _author, - string memory _metadataURI, - Strategy memory _executionStrategy, - IndexedStrategy[] memory _userVotingStrategies - ) internal returns (uint256) { - vanillaAuthenticator.authenticate( - address(space), - PROPOSE_SELECTOR, - abi.encode(_author, _metadataURI, _executionStrategy, _userVotingStrategies) - ); - - return space.nextProposalId() - 1; - } - - function _vote( - address _author, - uint256 _proposalId, - Choice _choice, - IndexedStrategy[] memory _userVotingStrategies, - string memory _voteMetadataURI - ) internal { - vanillaAuthenticator.authenticate( - address(space), - VOTE_SELECTOR, - abi.encode(_author, _proposalId, _choice, _userVotingStrategies, _voteMetadataURI) - ); - } -} diff --git a/script/SpaceSetup.s.sol b/script/SpaceSetup.s.sol deleted file mode 100644 index 75b53519..00000000 --- a/script/SpaceSetup.s.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -import { Script } from "forge-std/Script.sol"; -import { ProxyFactory } from "../src/ProxyFactory.sol"; -import { Space } from "../src/Space.sol"; -import { VanillaAuthenticator } from "../src/authenticators/VanillaAuthenticator.sol"; -import { VanillaVotingStrategy } from "../src/voting-strategies/VanillaVotingStrategy.sol"; -import { VanillaExecutionStrategy } from "../src/execution-strategies/VanillaExecutionStrategy.sol"; -import { Strategy } from "../src/types.sol"; - -// solhint-disable-next-line max-states-count -contract SpaceSetup is Script { - Space public space; - address internal masterSpace = address(0x1234); - ProxyFactory public spaceFactory = ProxyFactory(0xcae03d02f6840D865ccDD6668f1C2FDCA47F2240); - address public vanillaVotingStrategy = address(0x395eD61716b48DC904140b515e9F682E33330154); - address public compVotingStrategy = address(0xbBD17346378F76c1c94032594b57C93c24857B19); - address public whitelistVotingStrategy = address(0xC89a0C93Af823F794F96F7b2B63Fc2a1f1AE9427); - address public vanillaAuthenticator = address(0x86bfa0726CBA0FeBEeE457F04b705AB74B54D01c); - address public ethSigAuthenticator = address(0x328c6F186639f1981Dc25eD4517E8Ed2aDd85569); - address public ethTxAuthenticator = address(0x37315Ce75920B653f0f13734c709e199876455C9); - address public vanillaExecutionStrategy = address(0xb1001Fdf62C020761039A750b27e73C512fDaa5E); - address public votingPowerProposalValidationContract = address(42); // TODO: update - Strategy public votingPowerProposalValidationStrategy; - address public owner = address(0x2842c82E20ab600F443646e1BC8550B44a513D82); - uint32 public votingDelay; - uint32 public minVotingDuration; - uint32 public maxVotingDuration; - uint256 public proposalThreshold; - uint32 public quorum; - string internal metadataURI = "SX Test Space"; - - function run() public { - Strategy[] memory votingStrategies = new Strategy[](2); - bytes[] memory votingStrategyMetadata = new bytes[](2); - votingStrategies[0] = Strategy(vanillaVotingStrategy, new bytes(0)); - votingStrategyMetadata[0] = new bytes(0); - address uni = address(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); // Goerli UNI token - votingStrategies[1] = Strategy(compVotingStrategy, abi.encode(uni)); - votingStrategyMetadata[1] = new bytes(18); // UNI token decimals - - address[] memory authenticators = new address[](3); - authenticators[0] = vanillaAuthenticator; - authenticators[1] = ethSigAuthenticator; - authenticators[2] = ethTxAuthenticator; - Strategy[] memory executionStrategies = new Strategy[](1); - quorum = 1; - executionStrategies[0] = Strategy(vanillaExecutionStrategy, new bytes(quorum)); - votingDelay = 0; - minVotingDuration = 0; - maxVotingDuration = 1000; - proposalThreshold = 1; - votingPowerProposalValidationStrategy = Strategy( - votingPowerProposalValidationContract, - abi.encode(proposalThreshold, votingStrategies) - ); - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - vm.startBroadcast(deployerPrivateKey); - spaceFactory.deployProxy( - masterSpace, - abi.encodeWithSelector( - Space.initialize.selector, - owner, - votingDelay, - minVotingDuration, - maxVotingDuration, - votingPowerProposalValidationStrategy, - metadataURI, - votingStrategies, - votingStrategyMetadata, - authenticators, - executionStrategies - ), - 0 - ); - - vm.stopBroadcast(); - } -} From 1f7e0624de56633ba242598953f4e4d63627a120 Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 10:46:36 +0100 Subject: [PATCH 02/15] chore: deployer test base --- foundry.toml | 2 +- test/Deployer.t.sol | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/Deployer.t.sol diff --git a/foundry.toml b/foundry.toml index 4be464f8..802c89b3 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,7 +14,7 @@ src = "src" test = "test" via_ir = true ffi = true -fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}] +fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}, { access = "read-write", path = "./deployments/"}] [profile.ci] fuzz = { runs = 1_000 } diff --git a/test/Deployer.t.sol b/test/Deployer.t.sol new file mode 100644 index 00000000..28ffb7b7 --- /dev/null +++ b/test/Deployer.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import { Test } from "forge-std/Test.sol"; +import { Deployer } from "../script/Deployer.s.sol"; + +contract DeployerTest is Test { + Deployer deployer; + + bytes internal constant singletonFactoryBytecode = + hex"6080604052348015600f57600080fd5b506004361060285760" + hex"003560e01c80634af63f0214602d575b600080fd5b60cf6004" + hex"8036036040811015604157600080fd5b810190602081018135" + hex"640100000000811115605b57600080fd5b8201836020820111" + hex"15606c57600080fd5b80359060200191846001830284011164" + hex"010000000083111715608d57600080fd5b91908080601f0160" + hex"20809104026020016040519081016040528093929190818152" + hex"60200183838082843760009201919091525092955050913592" + hex"5060eb915050565b604080516001600160a01b039092168252" + hex"519081900360200190f35b6000818351602085016000f59392" + hex"50505056fea26469706673582212206b44f8a82cb6b156bfcc" + hex"3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f" + hex"6c63430006020033"; + + function setUp() public { + // Setting the bytecode at the singleton address of the singleton factory. + vm.etch(address(0xce0042B868300000d44A59004Da54A005ffdcf9f), singletonFactoryBytecode); + + deployer = new Deployer(); + } + + function testDeployer() public { + deployer.run(); + } +} From e5043f1b6d429b1b97f9e541db252c5a43f93764 Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 11:03:40 +0100 Subject: [PATCH 03/15] chore: added merkle whitelist to deployer script --- script/Deployer.s.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/script/Deployer.s.sol b/script/Deployer.s.sol index 83587cff..7ab068ea 100644 --- a/script/Deployer.s.sol +++ b/script/Deployer.s.sol @@ -29,6 +29,7 @@ import { VanillaVotingStrategy } from "../src/voting-strategies/VanillaVotingStr import { CompVotingStrategy } from "../src/voting-strategies/CompVotingStrategy.sol"; import { OZVotesVotingStrategy } from "../src/voting-strategies/OZVotesVotingStrategy.sol"; import { WhitelistVotingStrategy } from "../src/voting-strategies/WhitelistVotingStrategy.sol"; +import { MerkleWhitelistVotingStrategy } from "../src/voting-strategies/MerkleWhitelistVotingStrategy.sol"; import { VanillaProposalValidationStrategy @@ -163,6 +164,13 @@ contract Deployer is Script { (address ozVotesVotingStrategy, ) = noRedeploy(deployer, type(OZVotesVotingStrategy).creationCode, saltNonce); deployments.serialize("OZVotesVotingStrategy", ozVotesVotingStrategy); + (address merkleWhitelistVotingStrategy, ) = noRedeploy( + deployer, + type(MerkleWhitelistVotingStrategy).creationCode, + saltNonce + ); + deployments.serialize("MerkleWhitelistVotingStrategy", merkleWhitelistVotingStrategy); + // ------- PROPOSAL VALIDATION STRATEGIES ------- (address vanillaProposalValidationStrategy, ) = noRedeploy( From f1d30e049fec0e9d2589bd4eec509978752d7513 Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 12:57:47 +0100 Subject: [PATCH 04/15] chore: use test.json for test deployer script runs --- test/Deployer.t.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Deployer.t.sol b/test/Deployer.t.sol index 28ffb7b7..07247b06 100644 --- a/test/Deployer.t.sol +++ b/test/Deployer.t.sol @@ -23,6 +23,8 @@ contract DeployerTest is Test { hex"6c63430006020033"; function setUp() public { + vm.setEnv("NETWORK", "test"); + // Setting the bytecode at the singleton address of the singleton factory. vm.etch(address(0xce0042B868300000d44A59004Da54A005ffdcf9f), singletonFactoryBytecode); From d914b6042093db5fd5e959768f422f0a9e68c2fd Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 13:29:45 +0100 Subject: [PATCH 05/15] chore: added merkle whitelist to readme blueprint --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ccd0b9a..60dbe639 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ src ├─ voting-strategies │ ├─ CompVotingStrategy.sol - "Strategy that uses delegated balances of Comp tokens as voting power" │ ├─ OZVotesVotingStrategy.sol - "Strategy that uses delegated balances of OZ Votes tokens as voting power" -│ ├─ WhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist, otherwise zero" +│ ├─ WhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist, otherwise zero. Whitelist is stored in a bytes array On-Chain." +│ ├─ MerkleWhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist, otherwise zero. Whitelist is stored in a Merkle tree Off-Chain, with only the root being stored On-Chain." │ └─ VanillaVotingStrategy.sol — "Vanilla Strategy" ├─ execution-strategies │ ├─ timelocks From 6d8de7fb50129e9c0f7c21613306876c48cfeff6 Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 13:00:11 +0100 Subject: [PATCH 06/15] chore: updated gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 53635689..04f4a258 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ node_modules # broadcasts /broadcast/ lcov.info + +/deployments/test.json From a06eda4050e715e8943a1c40e3db972f0e36e27d Mon Sep 17 00:00:00 2001 From: Orlando Date: Thu, 12 Oct 2023 13:38:16 +0100 Subject: [PATCH 07/15] chore: set deployer address in deployer test --- test/Deployer.t.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Deployer.t.sol b/test/Deployer.t.sol index 07247b06..b3bf6ab4 100644 --- a/test/Deployer.t.sol +++ b/test/Deployer.t.sol @@ -24,6 +24,7 @@ contract DeployerTest is Test { function setUp() public { vm.setEnv("NETWORK", "test"); + vm.setEnv("DEPLOYER_ADDRESS", vm.toString(address(this))); // Setting the bytecode at the singleton address of the singleton factory. vm.etch(address(0xce0042B868300000d44A59004Da54A005ffdcf9f), singletonFactoryBytecode); From 6a60908c633efd471b3e1b40cd4877331324e305 Mon Sep 17 00:00:00 2001 From: Orlando Date: Fri, 13 Oct 2023 12:03:24 +0100 Subject: [PATCH 08/15] chore: empty deployments dir --- deployments/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 deployments/.gitkeep diff --git a/deployments/.gitkeep b/deployments/.gitkeep new file mode 100644 index 00000000..e69de29b From 2d44d28f2a41720d715b55aab1999622c893041b Mon Sep 17 00:00:00 2001 From: Orlando Date: Fri, 13 Oct 2023 12:05:16 +0100 Subject: [PATCH 09/15] chore: renamed deployments/.gitkeep -> deployments/.keep --- deployments/{.gitkeep => .keep} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename deployments/{.gitkeep => .keep} (100%) diff --git a/deployments/.gitkeep b/deployments/.keep similarity index 100% rename from deployments/.gitkeep rename to deployments/.keep From 2012fcfe3741554737317c42e85bf576a121db3a Mon Sep 17 00:00:00 2001 From: Orlando Date: Fri, 13 Oct 2023 12:14:10 +0100 Subject: [PATCH 10/15] chore: updated .env.example --- .env.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 11e0fefd..4077496e 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -PRIVATE_KEY= -GOERLI_RPC_URL= +DEPLOYER_ADDRESS= +RPC_URL= ETHERSCAN_API_KEY= -OWNER= \ No newline at end of file +NETWORK= \ No newline at end of file From 6645e3966f7cfa16a43ab33fe97ea79a218b2142 Mon Sep 17 00:00:00 2001 From: Orland0x <37511817+Orland0x@users.noreply.github.com> Date: Fri, 13 Oct 2023 07:47:27 -0400 Subject: [PATCH 11/15] chore: add deployment instructions to readme --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 60dbe639..05ae0682 100644 --- a/README.md +++ b/README.md @@ -78,3 +78,26 @@ Get a test coverage report: ```sh $ forge coverage ``` + +### Deployment + +To deploy the protocol to an EVM chain, first set the following environment variables: + +```sh +# The address of the account that the will be used to deploy from. +DEPLOYER_ADDRESS= +# The name of the chain you want to deploy on. The contract addresses of the deployed contracts will be stored at /deployments/network.json +NETWORK= +# An RPC URL for the chain. +RPC_URL= +# An API key for a block explorer on the chain (Optional). +ETHERSCAN_API_KEY= +``` + +Following this, a [Foundry Script](https://book.getfoundry.sh/tutorials/solidity-scripting) can be run to deploy the entire protocol. Example usage to deploy from a Ledger Hardware Wallet and verify on a block explorer: + +```sh +forge script script/Deployer.s.sol:Deployer --rpc-url $RPC_URL --optimize --broadcast --verify -vvvv --ledger --sender $DEPLOYER_ADDRESS --hd-paths "m/44'/60'/4'/0/0" +``` +The script uses the [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470) for the deployments which ensures that the addresses of the contracts are the same on all chains (so long as the chain is fully EVM equivalent). + From 7e154b1bfe6937f5b97f673adf655ca7ad76e7f0 Mon Sep 17 00:00:00 2001 From: Orland0x <37511817+Orland0x@users.noreply.github.com> Date: Fri, 13 Oct 2023 07:50:19 -0400 Subject: [PATCH 12/15] chore: typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 05ae0682..57cc928d 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,9 @@ $ forge coverage To deploy the protocol to an EVM chain, first set the following environment variables: ```sh -# The address of the account that the will be used to deploy from. +# The address of the account that the protocol will be deployed from. DEPLOYER_ADDRESS= -# The name of the chain you want to deploy on. The contract addresses of the deployed contracts will be stored at /deployments/network.json +# The name of the chain you want to deploy on. The addresses of the deployed contracts will be stored at /deployments/network.json NETWORK= # An RPC URL for the chain. RPC_URL= From 6aad776fe4e39a9d215cacacc9c8e385f5f4b814 Mon Sep 17 00:00:00 2001 From: Orlando Date: Fri, 13 Oct 2023 13:12:10 +0100 Subject: [PATCH 13/15] chore: formatting --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 57cc928d..98576317 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ $ forge coverage ### Deployment -To deploy the protocol to an EVM chain, first set the following environment variables: +To deploy the protocol to an EVM chain, first set the following environment variables: ```sh # The address of the account that the protocol will be deployed from. @@ -94,10 +94,12 @@ RPC_URL= ETHERSCAN_API_KEY= ``` -Following this, a [Foundry Script](https://book.getfoundry.sh/tutorials/solidity-scripting) can be run to deploy the entire protocol. Example usage to deploy from a Ledger Hardware Wallet and verify on a block explorer: +Following this, a [Foundry Script](https://book.getfoundry.sh/tutorials/solidity-scripting) can be run to deploy the +entire protocol. Example usage to deploy from a Ledger Hardware Wallet and verify on a block explorer: ```sh forge script script/Deployer.s.sol:Deployer --rpc-url $RPC_URL --optimize --broadcast --verify -vvvv --ledger --sender $DEPLOYER_ADDRESS --hd-paths "m/44'/60'/4'/0/0" ``` -The script uses the [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470) for the deployments which ensures that the addresses of the contracts are the same on all chains (so long as the chain is fully EVM equivalent). +The script uses the [Singleton Factory](https://eips.ethereum.org/EIPS/eip-2470) for the deployments which ensures that +the addresses of the contracts are the same on all chains (so long as the chain is fully EVM equivalent). From c66ce9f5852c539b242b954a7a05d9974f45294f Mon Sep 17 00:00:00 2001 From: Orlando Date: Mon, 16 Oct 2023 14:55:19 +0100 Subject: [PATCH 14/15] chore: deployer script fixes --- script/Deployer.s.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/script/Deployer.s.sol b/script/Deployer.s.sol index 7ab068ea..5ec5f04b 100644 --- a/script/Deployer.s.sol +++ b/script/Deployer.s.sol @@ -221,12 +221,12 @@ contract Deployer is Script { // If the master space is not initialized, initialize it if (Space(space).owner() == address(0x0)) { // Initializer for the master space, to render it unusable - Strategy[] memory emptyStrategyArray = new Strategy[](1); - emptyStrategyArray[0] = Strategy(address(0x1), new bytes(0)); - string[] memory emptyStringArray = new string[](1); - emptyStringArray[0] = ""; - address[] memory emptyAddressArray = new address[](1); - emptyAddressArray[0] = address(0x1); + Strategy[] memory dummyStrategyArray = new Strategy[](1); + dummyStrategyArray[0] = Strategy(address(0x1), new bytes(0)); + string[] memory dummyStringArray = new string[](1); + dummyStringArray[0] = ""; + address[] memory dummyAddressArray = new address[](1); + dummyAddressArray[0] = address(0x1); Space(space).initialize( InitializeCalldata( address(0x1), @@ -237,14 +237,14 @@ contract Deployer is Script { "", "", "", - emptyStrategyArray, - emptyStringArray, - emptyAddressArray + dummyStrategyArray, + dummyStringArray, + dummyAddressArray ) ); } - - if (Space(space).owner() != address(0x1)) { + (address addr, ) = Space(space).proposalValidationStrategy(); + if (Space(space).owner() != address(0x1) || addr != address(0x1)) { // Initialization of the master space was frontrun revert SpaceInitializationFailed(); } From 93e55c36724e8c37cac517ba56c6377e72a57d70 Mon Sep 17 00:00:00 2001 From: Orlando Date: Mon, 16 Oct 2023 15:03:26 +0100 Subject: [PATCH 15/15] chore: updated readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98576317..f5d54fe7 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ src ├─ voting-strategies │ ├─ CompVotingStrategy.sol - "Strategy that uses delegated balances of Comp tokens as voting power" │ ├─ OZVotesVotingStrategy.sol - "Strategy that uses delegated balances of OZ Votes tokens as voting power" -│ ├─ WhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist, otherwise zero. Whitelist is stored in a bytes array On-Chain." -│ ├─ MerkleWhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist, otherwise zero. Whitelist is stored in a Merkle tree Off-Chain, with only the root being stored On-Chain." +│ ├─ WhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist. Whitelist is stored in a bytes array On-Chain." +│ ├─ MerkleWhitelistVotingStrategy.sol — "Strategy that gives predetermined voting power for members in a whitelist. Whitelist is stored in a Merkle tree Off-Chain, with only the root being stored On-Chain." │ └─ VanillaVotingStrategy.sol — "Vanilla Strategy" ├─ execution-strategies │ ├─ timelocks