From 13a987167450a65fe27d9df940628c26b6780b33 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 2 Feb 2023 10:57:25 +0100 Subject: [PATCH] !feat: proposal creation helpers (#42) BREAKING CHANGE: - gov helpers no longer re-exports addresses, fetch them from address-book instead (https://github.com/bgd-labs/aave-address-book/blob/main/src/AaveGovernanceV2.sol) - `createProposal` was renamed to `createTestProposal` - the new `createProposal` is intended to be used for actual proposal creation and enforces delegatecall style proposals --- src/GovHelpers.sol | 107 +++++++++++++++++++++++++++----- src/test/GovTest.t.sol | 22 +++++++ src/test/ProxyHelpersTest.t.sol | 4 +- 3 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 src/test/GovTest.t.sol diff --git a/src/GovHelpers.sol b/src/GovHelpers.sol index 76008e7db..7aadd66f7 100644 --- a/src/GovHelpers.sol +++ b/src/GovHelpers.sol @@ -6,6 +6,7 @@ import {Vm} from 'forge-std/Vm.sol'; import {Test} from 'forge-std/Test.sol'; import {AaveGovernanceV2, IAaveGovernanceV2, IExecutorWithTimelock} from 'aave-address-book/AaveGovernanceV2.sol'; import {IPoolAddressesProvider} from 'aave-address-book/AaveV3.sol'; +import {AaveMisc} from 'aave-address-book/AaveMisc.sol'; import {ProxyHelpers} from './ProxyHelpers.sol'; library GovHelpers { @@ -19,24 +20,96 @@ library GovHelpers { bytes32 ipfsHash; } - IAaveGovernanceV2 internal constant GOV = - IAaveGovernanceV2(0xEC568fffba86c094cf06b22134B23074DFE2252c); + struct Payload { + address target; + string signature; + bytes callData; + } - address public constant SHORT_EXECUTOR = 0xEE56e2B3D491590B5b31738cC34d5232F378a8D5; + function buildMainnet(address payloadAddress) internal returns (Payload memory) { + require( + payloadAddress != AaveGovernanceV2.CROSSCHAIN_FORWARDER_OPTIMISM && + payloadAddress != AaveGovernanceV2.CROSSCHAIN_FORWARDER_ARBITRUM && + payloadAddress != AaveGovernanceV2.CROSSCHAIN_FORWARDER_POLYGON, + 'PAYLOAD_CANT_BE_FORWARDER' + ); - address public constant LONG_EXECUTOR = 0x79426A1c24B2978D90d7A5070a46C65B07bC4299; + return Payload({target: payloadAddress, signature: 'execute()', callData: ''}); + } - address public constant AAVE = 0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9; + function buildOptimism(address payloadAddress) internal returns (Payload memory) { + return + _buildL2({ + forwarder: AaveGovernanceV2.CROSSCHAIN_FORWARDER_OPTIMISM, + payloadAddress: payloadAddress + }); + } - address internal constant AAVE_WHALE = address(0x25F2226B597E8F9514B3F68F00f494cF4f286491); + function buildArbitrum(address payloadAddress) internal returns (Payload memory) { + return + _buildL2({ + forwarder: AaveGovernanceV2.CROSSCHAIN_FORWARDER_ARBITRUM, + payloadAddress: payloadAddress + }); + } + + function buildPolygon(address payloadAddress) internal returns (Payload memory) { + return + _buildL2({ + forwarder: AaveGovernanceV2.CROSSCHAIN_FORWARDER_POLYGON, + payloadAddress: payloadAddress + }); + } + + function _buildL2(address forwarder, address payloadAddress) private returns (Payload memory) { + return + Payload({ + target: forwarder, + signature: 'execute(address)', + callData: abi.encode(payloadAddress) + }); + } + + function createProposal(Payload[] memory delegateCalls, bytes32 ipfsHash) + internal + returns (uint256) + { + require(block.chainid == 1, 'MAINNET_ONLY'); + require(delegateCalls.length != 0, 'MINIMUM_ONE_PAYLOAD'); + require(ipfsHash != bytes32(0), 'NON_ZERO_IPFS_HASH'); + + address[] memory targets = new address[](delegateCalls.length); + uint256[] memory values = new uint256[](delegateCalls.length); + string[] memory signatures = new string[](delegateCalls.length); + bytes[] memory calldatas = new bytes[](delegateCalls.length); + bool[] memory withDelegatecalls = new bool[](delegateCalls.length); + for (uint256 i = 0; i < delegateCalls.length; i++) { + targets[i] = delegateCalls[i].target; + signatures[i] = delegateCalls[i].signature; + calldatas[i] = delegateCalls[i].callData; + values[i] = 0; + withDelegatecalls[i] = true; + } + + return + AaveGovernanceV2.GOV.create( + IExecutorWithTimelock(AaveGovernanceV2.SHORT_EXECUTOR), + targets, + values, + signatures, + calldatas, + withDelegatecalls, + ipfsHash + ); + } /** - * Impersonate the ecosystem reserve and created the proposal. + * @dev Impersonate the ecosystem reserve and creates the proposal. */ - function createProposal(Vm vm, SPropCreateParams memory params) internal returns (uint256) { - vm.deal(AAVE_WHALE, 1 ether); - vm.startPrank(AAVE_WHALE); - uint256 proposalId = GOV.create( + function createTestProposal(Vm vm, SPropCreateParams memory params) internal returns (uint256) { + vm.deal(AaveMisc.ECOSYSTEM_RESERVE, 1 ether); + vm.startPrank(AaveMisc.ECOSYSTEM_RESERVE); + uint256 proposalId = AaveGovernanceV2.GOV.create( IExecutorWithTimelock(params.executor), params.targets, params.values, @@ -60,13 +133,13 @@ library GovHelpers { function passVoteAndExecute(Vm vm, uint256 proposalId) internal { uint256 power = 5000000 ether; vm.roll(block.number + 1); - vm.store(address(GOV), _getProposalSlot(proposalId), bytes32(power)); - uint256 endBlock = GOV.getProposalById(proposalId).endBlock; + vm.store(address(AaveGovernanceV2.GOV), _getProposalSlot(proposalId), bytes32(power)); + uint256 endBlock = AaveGovernanceV2.GOV.getProposalById(proposalId).endBlock; vm.roll(endBlock + 1); - GOV.queue(proposalId); - uint256 executionTime = GOV.getProposalById(proposalId).executionTime; + AaveGovernanceV2.GOV.queue(proposalId); + uint256 executionTime = AaveGovernanceV2.GOV.getProposalById(proposalId).executionTime; vm.warp(executionTime + 1); - GOV.execute(proposalId); + AaveGovernanceV2.GOV.execute(proposalId); } function getProposalById(uint256 proposalId) @@ -74,7 +147,7 @@ library GovHelpers { view returns (IAaveGovernanceV2.ProposalWithoutVotes memory) { - return GOV.getProposalById(proposalId); + return AaveGovernanceV2.GOV.getProposalById(proposalId); } } diff --git a/src/test/GovTest.t.sol b/src/test/GovTest.t.sol new file mode 100644 index 000000000..783208542 --- /dev/null +++ b/src/test/GovTest.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; +import {GovHelpers} from '../GovHelpers.sol'; +import {AaveMisc} from 'aave-address-book/AaveMisc.sol'; + +contract GovernanceTest is Test { + function setUp() public { + vm.createSelectFork('mainnet', 16526807); + } + + function testCreateProposal() public { + GovHelpers.Payload[] memory payloads = new GovHelpers.Payload[](2); + payloads[0] = GovHelpers.buildMainnet(address(1)); + payloads[1] = GovHelpers.buildPolygon(address(2)); + + vm.startPrank(AaveMisc.ECOSYSTEM_RESERVE); + GovHelpers.createProposal(payloads, bytes32('ipfs')); + vm.stopPrank(); + } +} diff --git a/src/test/ProxyHelpersTest.t.sol b/src/test/ProxyHelpersTest.t.sol index e87dd5464..8f1c2073f 100644 --- a/src/test/ProxyHelpersTest.t.sol +++ b/src/test/ProxyHelpersTest.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; import 'forge-std/Test.sol'; +import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; import {ProxyHelpers} from '../ProxyHelpers.sol'; -import {GovHelpers} from '../GovHelpers.sol'; contract ProxyHelpersTest is Test { function setUp() public { @@ -15,7 +15,7 @@ contract ProxyHelpersTest is Test { vm, 0x41A08648C3766F9F9d85598fF102a08f4ef84F84 ); - assertEq(admin, GovHelpers.SHORT_EXECUTOR); + assertEq(admin, AaveGovernanceV2.SHORT_EXECUTOR); } function testImplementation() public {