Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: replaced old deployer scripts with new one #243

Merged
merged 15 commits into from
Oct 16, 2023
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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the owner variable the only variable worth checking ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah added a check for proposal validation strategy which prevents proposals from being created. That should prevent all actions in the space then i think

}

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
Loading