Skip to content

ScopeLift/pooltogether-governor-upgrade

Repository files navigation

PoolTogether Governor Bravo Upgrade

An upgrade to a "Bravo" compatible Governor for the PoolTogether DAO, built using the OpenZeppelin implemented and Flexible Voting.

Getting started

Clone the repo

git clone git@github.com:ScopeLift/pooltogether-governor-upgrade.git
cd pooltogether-governor-upgrade

Copy the .env.template file and populate it with values

cp .env.template .env
# Open the .env file and add your values
forge install
forge build
forge test

Formatting

Formatting is done via scopelint. To install scopelint, run:

cargo install scopelint

Apply formatting

scopelint fmt

Check formatting

scopelint check

Scopelint spec compatibility

Some tests will not show up when running scopelint spec because the methods they are testing are inherited in the PoolTogetherGovernor. In order to get an accurate picture of the tests with scopelint spec add an explicit propose method to the PoolTogetherGovernor. It should look like this:

 function propose(
    address[] memory targets,
    uint256[] memory values,
    bytes[] memory calldatas,
    string memory description
  ) public override(Governor, IGovernor) returns (uint256) {
    return Governor.propose(targets, values, calldatas, description);
  }

Scripts

  • script/Deploy.s.sol - Deploys the PoolTogetherGovernor contract

To test these scripts locally, start a local fork with anvil:

anvil --fork-url YOUR_RPC_URL --fork-block-number 17665572

Then execute the deploy script.

NOTE: You must populate the DEPLOYER_PRIVATE_KEY in your .env file for this to work.

forge script script/Deploy.s.sol --tc Deploy --rpc-url http://localhost:8545 --broadcast

Pull the contract address for the new Governor from the deploy script address, then execute the Proposal script.

NOTE: You must populate the PROPOSER_PRIVATE_KEY in your .env file for this to work. Additionally, the private key must correspond to the proposer address defined in the Proposal.s.sol script. You can update this variable to an address you control, however the proposal itself will still revert in this case, unless you provide the private key of an address that has sufficient POOL Token delegation to have the right to submit a proposal.

forge script script/Propose.s.sol --sig "run(address)" NEW_GOVERNOR_ADDRESS --rpc-url http://localhost:8545 --broadcast

Non-standard Governor changes

When upgrading PoolTogether's Alpha Governor implementation we needed to fallback on non-standard interfaces.

Timelock

PoolTogether's timelock is not compatible with Openzeppelin's ICompoundTimelock which is used by GovernorTimelockCompound, and is in charge of queuing and executing proposals. Due to this incompatibility we had to fork the GovernorTimelockCompound.sol and change the interface to conform to the PoolTogether interface.

The main issue with the interface is that PoolTogether's contract has a method gracePeriod when ICompoundTimelock is expecting the method to be called GRACE_PERIOD. This function is used in GovernorTimelockCompound's state method and caused us to have to fork the GovernorTimelockCompound. Below is a table of all the changes we had to make between the Openzeppelin GovernorTimelockCompound and our forked version.

Change name original Changed version
_timelock type here here
Constructor argument type here here
state grace period call here here
Cast timelock to address here here
Update updateTimelock function args here here
Update _updateTimelock function args here here

A full diff of the two contracts can be found here

Token

In the new Governor we inherit from GovernorVotesComp which expects an ERC20VotesComp token. The PoolTogether token is missing a few functions that exist in ERC20VotesComp and we have listed those missing functions below. The reason we did not fork the Openzeppelin contract and use a custom interface was because GovernorVotesComp calls a single function getPriorVotes which exists on the PoolTogether token.

A full diff of the two contracts can be found here

Missing functions in POOL
  1. function DOMAIN_SEPARATOR() external view returns (bytes32);
  2. function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);
  3. function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
  4. function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
  5. function getVotes(address account) external view returns (uint256);
  6. function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
  7. function numCheckpoints(address account) external view returns (uint32);
  8. function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
Functions in POOL with a different signature compared with ERC20Votes
  • checkpoints
    • ERC20Votes: function checkpoints(address account, uint32 pos) external view returns (Checkpoint memory);
    • POOL: function checkpoints(address, uint32) external view returns (uint32 fromBlock, uint96 votes);

Testing issues

This repo heavily leverages fuzz fork tests causing a significant number of RPC requests to be made. We leverage caching to minimize the number of RPC calls after the tests are run for the first time, but running these tests for the first may cause timeouts and consume a significant number of RPC calls.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •