Proposal Functions
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 { address owner = addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO"); if (!addresses.isAddressSet("BRAVO_VAULT")) { // Here the vault is deployed Vault bravoVault = new Vault(); addresses.addAddress("BRAVO_VAULT", address(bravoVault), true); bravoVault.transferOwnership(owner); } if (!addresses.isAddressSet("BRAVO_VAULT_TOKEN")) { // Here the token is deployed Token token = new Token(); addresses.addAddress("BRAVO_VAULT_TOKEN", address(token), true); token.transferOwnership(owner); uint256 balance = token.balanceOf(address(this)) > 0 ? token.balanceOf(address(this)) : token.balanceOf(addresses.getAddress("DEPLOYER_EOA")); token.transfer(address(owner), balance); } }modifier buildModifier(address toPrank) { _startBuild(toPrank); _; _endBuild(toPrank); } /// @notice to be used by the build function to create a governance proposal /// kick off the process of creating a governance proposal by: /// 1). taking a snapshot of the current state of the contract /// 2). starting prank as the caller /// 3). starting a recording of all calls created during the proposal /// @param toPrank the address that will be used as the caller for the /// actions, e.g., multisig address, timelock address, etc. function _startBuild(address toPrank) private { vm.startPrank(toPrank); _startSnapshot = vm.snapshot(); vm.startStateDiffRecording(); } /// @notice to be used at the end of the build function to snapshot /// the actions performed by the proposal and revert these changes /// then, stop the prank and record the actions that were taken by the proposal. /// @param caller the address that will be used as the caller for the /// actions, e.g., multisig address, timelock address, etc. function _endBuild(address caller) private { VmSafe.AccountAccess[] memory accountAccesses = vm .stopAndReturnStateDiff(); vm.stopPrank(); /// roll back all state changes made during the governance proposal require( vm.revertTo(_startSnapshot), "failed to revert back to snapshot, unsafe state to run proposal" ); for (uint256 i = 0; i < accountAccesses.length; i++) { /// only care about calls from the original caller, /// static calls are ignored, /// calls to and from Addresses and the vm contract are ignored if ( accountAccesses[i].account != address(addresses) && accountAccesses[i].account != address(vm) && /// ignore calls to vm in the build function accountAccesses[i].accessor != address(addresses) && accountAccesses[i].kind == VmSafe.AccountAccessKind.Call && accountAccesses[i].accessor == caller /// caller is correct, not a subcall ) { _validateAction( accountAccesses[i].account, accountAccesses[i].value, accountAccesses[i].data ); actions.push( Action({ value: accountAccesses[i].value, target: accountAccesses[i].account, arguments: accountAccesses[i].data, description: string( abi.encodePacked( "calling ", vm.toString(accountAccesses[i].account), " with ", vm.toString(accountAccesses[i].value), " eth and ", vm.toString(accountAccesses[i].data), " data." ) ) }) ); } _validateActions(); } }function build() public override buildModifier(addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO")) { /// STATICCALL -- non-mutative and hence not recorded for the run stage // Get vault address. address bravoVault = addresses.getAddress("BRAVO_VAULT"); // Get token address. address token = addresses.getAddress("BRAVO_VAULT_TOKEN"); // Get Bravo timelock's token balance. uint256 balance = Token(token).balanceOf( addresses.getAddress("PROTOCOL_TIMELOCK_BRAVO") ); /// CALLS -- mutative and recorded // Whitelists 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 _validateAction( address target, uint256 value, bytes memory data ) internal virtual { uint256 actionsLength = actions.length; for (uint256 i = 0; i < actionsLength; i++) { // Check if the target, arguments and value matches with other exciting actions. bool isDuplicateTarget = actions[i].target == target; bool isDuplicateArguments = keccak256(actions[i].arguments) == keccak256(data); bool isDuplicateValue = actions[i].value == value; require( !(isDuplicateTarget && isDuplicateArguments && isDuplicateValue), "Duplicated action found" ); } }function _validateActions() internal view override { uint256 actionsLength = actions.length; require( actionsLength == 1, "Arbitrum proposals must have a single action" ); require(actions[0].target != address(0), "Invalid target for proposal"); /// if there are no args and no eth, the action is not valid require( (actions[0].arguments.length == 0 && actions[0].value > 0) || actions[0].arguments.length > 0, "Invalid arguments for proposal" ); // Value is ignored on L2 proposals if (executionChain == ProposalExecutionChain.ARB_ONE) { require(actions[0].value == 0, "Value must be 0 for L2 execution"); } }function getProposalActions() public view virtual override returns ( address[] memory targets, uint256[] memory values, bytes[] memory arguments ) { uint256 actionsLength = actions.length; require(actionsLength > 0, "No actions found"); targets = new address[](actionsLength); values = new uint256[](actionsLength); arguments = new bytes[](actionsLength); for (uint256 i; i < actionsLength; i++) { require( actions[i].target != address(0), "Invalid target for proposal" ); /// if there are no args and no eth, the action is not valid require( (actions[i].arguments.length == 0 && actions[i].value > 0) || actions[i].arguments.length > 0, "Invalid arguments for proposal" ); targets[i] = actions[i].target; arguments[i] = actions[i].arguments; values[i] = actions[i].value; } }function getCalldata() public virtual override returns (bytes memory data) { ( address[] memory targets, uint256[] memory values, bytes[] memory calldatas ) = getProposalActions(); data = abi.encodeWithSignature( "propose(address[],uint256[],bytes[],string)", targets, values, calldatas, description() ); }
Last updated