OZ Governor Proposal
Overview
Proposal Contract
function name() public pure override returns (string memory) { return "OZ_GOVERNOR_PROPOSAL"; }function description() public pure override returns (string memory) { return "OZ Governor proposal mock 1"; }function deploy() public override { // Set OZ Governor's timelock as the owner for vault and token. address owner = addresses.getAddress("OZ_GOVERNOR_TIMELOCK"); // Deploy vault address if not already deployed and transfer ownership to timelock. if (!addresses.isAddressSet("OZ_GOVERNOR_VAULT")) { Vault OZGovernorVault = new Vault(); addresses.addAddress( "OZ_GOVERNOR_VAULT", address(OZGovernorVault), true ); OZGovernorVault.transferOwnership(owner); } // Deploy token address if not already deployed, transfer ownership to timelock // and transfer all initial minted tokens from the deployer to the timelock. if (!addresses.isAddressSet("OZ_GOVERNOR_VAULT_TOKEN")) { Token token = new Token(); addresses.addAddress( "OZ_GOVERNOR_VAULT_TOKEN", address(token), true ); token.transferOwnership(owner); // During forge script execution, the deployer of the contracts is // the DEPLOYER_EOA. However, when running through forge test, the deployer of the contracts is this contract. uint256 balance = token.balanceOf(address(this)) > 0 ? token.balanceOf(address(this)) : token.balanceOf(addresses.getAddress("DEPLOYER_EOA")); token.transfer(address(owner), balance); } }function build() public override buildModifier(addresses.getAddress("OZ_GOVERNOR_TIMELOCK")) { /// STATICCALL -- non-mutative and hence not recorded for the run stage // Get vault address address OZGovernorVault = addresses.getAddress("OZ_GOVERNOR_VAULT"); // Get token address address token = addresses.getAddress("OZ_GOVERNOR_VAULT_TOKEN"); // Get OZ Governor timelock's token balance. uint256 balance = Token(token).balanceOf( addresses.getAddress("OZ_GOVERNOR_TIMELOCK") ); /// CALLS -- mutative and recorded // Whitelists the deployed token on the deployed vault. Vault(OZGovernorVault).whitelistToken(token, true); // Approve the token for the vault. Token(token).approve(OZGovernorVault, balance); // Deposit all tokens into the vault. Vault(OZGovernorVault).deposit(token, balance); }function run() public override { // Create and select the sepolia fork for proposal execution. primaryForkId = vm.createFork("sepolia"); vm.selectFork(primaryForkId); string memory addressesFolderPath = "./addresses"; uint256[] memory chainIds = new uint256[](1); chainIds[0] = 11155111; // Set the addresses object by reading addresses from the json file. setAddresses( new Addresses(addressesFolderPath, chainIds) ); // Set OZ Governor. This address is used for proposal simulation and checking on-chain proposal state. setGovernor(addresses.getAddress("PROTOCOL_GOVERNOR")); // Call the run function of the parent contract 'Proposal.sol'. super.run(); }/// @notice Simulate governance proposal function simulate() public virtual override { address proposerAddress = address(1); IVotes governanceToken = IVotes( IGovernorVotes(address(governor)).token() ); { // Ensure proposer has meets minimum proposal threshold and quorum votes to pass the proposal uint256 quorumVotes = governor.quorum(block.number - 1); uint256 proposalThreshold = governor.proposalThreshold(); uint256 votingPower = quorumVotes > proposalThreshold ? quorumVotes : proposalThreshold; deal(address(governanceToken), proposerAddress, votingPower); vm.roll(block.number - 1); // Delegate proposer's votes to itself vm.prank(proposerAddress); IVotes(governanceToken).delegate(proposerAddress); vm.roll(block.number + 2); } bytes memory proposeCalldata = getCalldata(); // Register the proposal vm.prank(proposerAddress); bytes memory data = address(governor).functionCall(proposeCalldata); uint256 returnedProposalId = abi.decode(data, (uint256)); ( address[] memory targets, uint256[] memory values, bytes[] memory calldatas ) = getProposalActions(); // Check that the proposal was registered correctly uint256 proposalId = governor.hashProposal( targets, values, calldatas, keccak256(abi.encodePacked(description())) ); require(returnedProposalId == proposalId, "Proposal id mismatch"); // Check proposal is in Pending state require(governor.state(proposalId) == IGovernor.ProposalState.Pending); // Roll to Active state (voting period) vm.roll(block.number + governor.votingDelay() + 1); require(governor.state(proposalId) == IGovernor.ProposalState.Active); // Vote YES vm.prank(proposerAddress); governor.castVote(proposalId, 1); // Roll to allow proposal state transitions vm.roll(block.number + governor.votingPeriod()); require( governor.state(proposalId) == IGovernor.ProposalState.Succeeded ); vm.warp(block.timestamp + governor.proposalEta(proposalId) + 1); // Queue the proposal governor.queue( targets, values, calldatas, keccak256(abi.encodePacked(description())) ); require(governor.state(proposalId) == IGovernor.ProposalState.Queued); // Warp to allow proposal execution on timelock ITimelockController timelock = ITimelockController( IGovernorTimelockControl(address(governor)).timelock() ); vm.warp(block.timestamp + timelock.getMinDelay()); // Execute the proposal governor.execute( targets, values, calldatas, keccak256(abi.encodePacked(description())) ); require(governor.state(proposalId) == IGovernor.ProposalState.Executed); }function validate() public override { // Get the vault address Vault OZGovernorVault = Vault( addresses.getAddress("OZ_GOVERNOR_VAULT") ); // Get the token address Token token = Token(addresses.getAddress("OZ_GOVERNOR_VAULT_TOKEN")); // Get OZ Governor's timelock address address timelock = addresses.getAddress("OZ_GOVERNOR_TIMELOCK"); // Ensure the token total supply is 10 million assertEq(token.totalSupply(), 10_000_000e18); // Ensure the timelock is the owner of the deployed token. assertEq(token.owner(), address(timelock)); // Ensure the timelock is the owner of the deployed vault assertEq(OZGovernorVault.owner(), address(timelock)); // Ensure the vault is not paused assertFalse(OZGovernorVault.paused()); // Ensure the token is whitelisted on the vault assertTrue(OZGovernorVault.tokenWhitelist(address(token))); // Get the vault's token balance uint256 balance = token.balanceOf(address(OZGovernorVault)); // Get the timelock deposits in the vault (uint256 amount, ) = OZGovernorVault.deposits( address(token), address(timelock) ); // Ensure the timelock deposit is the same as the vault's token balance assertEq(amount, balance); // Ensure all minted tokens are deposited into the vault assertEq( token.balanceOf(address(OZGovernorVault)), token.totalSupply() ); }
Proposal Simulation
Deploying a OZ Governor on Testnet
Setting Up the Addresses JSON
Running the Proposal
Last updated