Skip to content

Commit

Permalink
chore: replaced old deployer scripts with new one (#243)
Browse files Browse the repository at this point in the history
* chore: replaced old  deployer scripts with new one

* chore: deployer test base

* chore: added merkle whitelist to deployer script

* chore: use test.json for test deployer script runs

* chore: added merkle whitelist to readme blueprint

* chore: updated gitignore

* chore: set deployer address in deployer test

* chore: empty deployments dir

* chore: renamed deployments/.gitkeep -> deployments/.keep

* chore: updated .env.example

* chore: add deployment instructions to readme

* chore: typos

* chore: formatting

* chore: deployer script fixes

* chore: updated readme

---------

Co-authored-by: Orlando <[email protected]>
  • Loading branch information
Orland0x and Orlando authored Oct 16, 2023
1 parent 4a693e4 commit 6b760df
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 202 deletions.
6 changes: 3 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PRIVATE_KEY=
GOERLI_RPC_URL=
DEPLOYER_ADDRESS=
RPC_URL=
ETHERSCAN_API_KEY=
OWNER=
NETWORK=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ node_modules
# broadcasts
/broadcast/
lcov.info

/deployments/test.json
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. 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
Expand Down Expand Up @@ -77,3 +78,28 @@ 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 protocol will be deployed from.
DEPLOYER_ADDRESS=
# 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=
# 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).
Empty file added deployments/.keep
Empty file.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
275 changes: 275 additions & 0 deletions script/Deployer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// 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 { MerkleWhitelistVotingStrategy } from "../src/voting-strategies/MerkleWhitelistVotingStrategy.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);

(address merkleWhitelistVotingStrategy, ) = noRedeploy(
deployer,
type(MerkleWhitelistVotingStrategy).creationCode,
saltNonce
);
deployments.serialize("MerkleWhitelistVotingStrategy", merkleWhitelistVotingStrategy);

// ------- 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 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),
1,
1,
1,
Strategy(address(0x1), new bytes(0)),
"",
"",
"",
dummyStrategyArray,
dummyStringArray,
dummyAddressArray
)
);
}
(address addr, ) = Space(space).proposalValidationStrategy();
if (Space(space).owner() != address(0x1) || addr != 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);
}
}
}
45 changes: 0 additions & 45 deletions script/ModulesDeployment.s.sol

This file was deleted.

Loading

0 comments on commit 6b760df

Please sign in to comment.