Governor Bravo Proposal
Overview
Proposal Contract
function name() public pure override returns (string memory) { return "BRAVO_MOCK"; }function description() public pure override returns (string memory) { return "Bravo proposal mock"; }function deploy() public override { // Set Governor Bravo's timelock as the owner for the vault and token address owner = addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO"); // Deploy the vault address if not already deployed and transfer ownership to the timelock if (!addresses.isAddressSet("BRAVO_VAULT")) { Vault bravoVault = new Vault(); addresses.addAddress("BRAVO_VAULT", address(bravoVault), true); bravoVault.transferOwnership(owner); } // Deploy the token address if not already deployed, transfer ownership to the timelock // and transfer all initial minted tokens from the deployer to the timelock if (!addresses.isAddressSet("BRAVO_VAULT_TOKEN")) { Token token = new Token(); addresses.addAddress("BRAVO_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("PROTOCOL_TIMELOCK_BRAVO")) { /// STATICCALL -- non-mutative and hence not recorded for the run stage // Get the vault address address bravoVault = addresses.getAddress("BRAVO_VAULT"); // Get the token address address token = addresses.getAddress("BRAVO_VAULT_TOKEN"); // Get the timelock bravo's token balance uint256 balance = Token(token).balanceOf( addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO") ); /// CALLS -- mutative and recorded // Whitelist the deployed token on the deployed vault Vault(bravoVault).whitelistToken(token, true); // Approve the token for the vault Token(token).approve(bravoVault, balance); // Deposit all tokens into the vault Vault(bravoVault).deposit(token, balance); }function run() public override { // Create and select 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 addresses object reading addresses from json file. setAddresses( new Addresses(addressesFolderPath, chainIds) ); // Set Governor Bravo. This address is used for proposal simulation and check on // chain proposal state. setGovernor(addresses.getAddress("PROTOCOL_GOVERNOR")); // Call the run function of parent contract 'Proposal.sol'. super.run(); }/// @notice Simulate governance proposal function simulate() public override { address proposerAddress = address(1); IERC20VotesComp governanceToken = governor.comp(); { // Ensure proposer has meets minimum proposal threshold and quorum votes to pass the proposal uint256 quorumVotes = governor.quorumVotes(); uint256 proposalThreshold = governor.proposalThreshold(); uint256 votingPower = quorumVotes > proposalThreshold ? quorumVotes : proposalThreshold; deal(address(governanceToken), proposerAddress, votingPower); // Delegate proposer's votes to itself vm.prank(proposerAddress); IERC20VotesComp(governanceToken).delegate(proposerAddress); vm.roll(block.number + 1); } bytes memory proposeCalldata = getCalldata(); // Register the proposal vm.prank(proposerAddress); bytes memory data = address(governor).functionCall(proposeCalldata); uint256 proposalId = abi.decode(data, (uint256)); // Check proposal is in Pending state require( governor.state(proposalId) == IGovernorBravo.ProposalState.Pending ); // Roll to Active state (voting period) vm.roll(block.number + governor.votingDelay() + 1); require( governor.state(proposalId) == IGovernorBravo.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) == IGovernorBravo.ProposalState.Succeeded ); // Queue the proposal governor.queue(proposalId); require( governor.state(proposalId) == IGovernorBravo.ProposalState.Queued ); // Warp to allow proposal execution on timelock ITimelockBravo timelock = ITimelockBravo(governor.timelock()); vm.warp(block.timestamp + timelock.delay()); // Execute the proposal governor.execute(proposalId); require( governor.state(proposalId) == IGovernorBravo.ProposalState.Executed ); }function validate() public override { // Get the vault address Vault bravoVault = Vault(addresses.getAddress("BRAVO_VAULT")); // Get the token address Token token = Token(addresses.getAddress("BRAVO_VAULT_TOKEN")); // Get Governor Bravo's timelock address address timelock = addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO"); // 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(bravoVault.owner(), address(timelock)); // Ensure the vault is not paused assertFalse(bravoVault.paused()); // Ensure the token is whitelisted on the vault assertTrue(bravoVault.tokenWhitelist(address(token))); // Get the vault's token balance uint256 balance = token.balanceOf(address(bravoVault)); // Get the timelock deposits in the vault (uint256 amount, ) = bravoVault.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(bravoVault)), token.totalSupply()); }
Proposal Simulation
Deploying a Governor Bravo on Testnet
Setting Up the Addresses JSON
Running the Proposal
Last updated