Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CompoundGovernorVotesUpgradeable #27

Merged
merged 7 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions contracts/CompoundGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ pragma solidity 0.8.26;

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {GovernorUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
import {GovernorVotesUpgradeable} from
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol";
import {GovernorVotesCompUpgradeable} from "contracts/extensions/GovernorVotesCompUpgradeable.sol";
import {GovernorSettableFixedQuorumUpgradeable} from "contracts/extensions/GovernorSettableFixedQuorumUpgradeable.sol";
import {GovernorCountingFractionalUpgradeable} from
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorCountingFractionalUpgradeable.sol";
Expand All @@ -16,7 +15,7 @@ import {GovernorSettingsUpgradeable} from
import {GovernorPreventLateQuorumUpgradeable} from
"@openzeppelin/contracts-upgradeable/governance/extensions/GovernorPreventLateQuorumUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IComp} from "contracts/interfaces/IComp.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

/// @title CompoundGovernor
Expand All @@ -25,7 +24,7 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
/// @custom:security-contact TODO: Add security contact
contract CompoundGovernor is
Initializable,
GovernorVotesUpgradeable,
GovernorVotesCompUpgradeable,
GovernorTimelockCompoundUpgradeable,
GovernorSettingsUpgradeable,
GovernorCountingFractionalUpgradeable,
Expand All @@ -51,15 +50,15 @@ contract CompoundGovernor is
uint48 _initialVotingDelay,
uint32 _initialVotingPeriod,
uint256 _initialProposalThreshold,
IVotes _compAddress,
IComp _compAddress,
uint256 _quorumVotes,
ICompoundTimelock _timelockAddress,
uint48 _initialVoteExtension,
address _initialOwner
) public initializer {
__Governor_init("Compound Governor");
__GovernorSettings_init(_initialVotingDelay, _initialVotingPeriod, _initialProposalThreshold);
__GovernorVotes_init(_compAddress);
__GovernorVotesComp_init(_compAddress);
__GovernorTimelockCompound_init(_timelockAddress);
__GovernorPreventLateQuorum_init(_initialVoteExtension);
__GovernorSettableFixedQuorum_init(_quorumVotes);
Expand Down
77 changes: 77 additions & 0 deletions contracts/extensions/GovernorVotesCompUpgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {GovernorUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {IComp} from "contracts/interfaces/IComp.sol";

/// @title GovernorVotesCompUpgradeable
/// @author [ScopeLift](https://scopelift.co)
/// @notice Modified GovernorVotes contract that supports Compound's COMP token.
/// @custom:security-contact TODO: Add security contact
abstract contract GovernorVotesCompUpgradeable is Initializable, GovernorUpgradeable {
/// @custom:storage-location IComp:storage.GovernorVotesComp
struct GovernorVotesCompStorage {
IComp _token;
}

// keccak256(abi.encode(uint256(keccak256("storage.GovernorVotesComp")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant GovernorVotesCompStorageLocation =
0x2130f92f3b57a0ca0ff53b681825494ca70980d3b0ddffac97113db00028b600;

function _getGovernorVotesCompStorage() private pure returns (GovernorVotesCompStorage storage $) {
assembly {
$.slot := GovernorVotesCompStorageLocation
}
}

function __GovernorVotesComp_init(IComp _tokenAddress) internal onlyInitializing {
__GovernorVotesComp_init_unchained(_tokenAddress);
}

function __GovernorVotesComp_init_unchained(IComp _tokenAddress) internal onlyInitializing {
GovernorVotesCompStorage storage $ = _getGovernorVotesCompStorage();
$._token = IComp(address(_tokenAddress));
}

/// @notice Returns the IComp token used for governance.
/// @dev This function retrieves the token address from the contract's storage.
/// @return IComp The COMP token interface used for governance voting.
function token() public view virtual returns (IComp) {
GovernorVotesCompStorage storage $ = _getGovernorVotesCompStorage();
return $._token;
}

/// @notice This function implements the clock interface as specified in ERC-6372.
/// @dev Returns the current clock value used for governance voting.
/// @return uint48 The current block number cast to uint48.
function clock() public view virtual override returns (uint48) {
return Time.blockNumber();
}

/// @notice Returns a machine-readable description of the clock as specified in ERC-6372.
/// @dev This function provides information about the clock mode used for governance timing.
/// @return string A string describing the clock mode, indicating that block numbers are used
/// as the time measure, with the default starting point.
function CLOCK_MODE() public view virtual override returns (string memory) {
return "mode=blocknumber&from=default";
}

/// @notice Retrieves the voting weight for a specific account at a given timepoint.
/// @dev This function overrides the base _getVotes function to use Compound's getPriorVotes mechanism.
/// @param _account The address of the account to check the voting weight for.
/// @param _timepoint The timepoint at which to check the voting weight.
/// @param /*params*/ Unused parameter, kept for compatibility with the base function signature.
/// @return uint256 The voting weight of the account at the specified timepoint.
function _getVotes(address _account, uint256 _timepoint, bytes memory /*params*/ )
internal
view
virtual
override
returns (uint256)
{
return token().getPriorVotes(_account, _timepoint);
}
}
34 changes: 34 additions & 0 deletions contracts/interfaces/IComp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

interface IComp {
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
}

event Approval(address indexed owner, address indexed spender, uint256 amount);
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
event Transfer(address indexed from, address indexed to, uint256 amount);

function DELEGATION_TYPEHASH() external view returns (bytes32);
function DOMAIN_TYPEHASH() external view returns (bytes32);
function allowance(address account, address spender) external view returns (uint256);
function approve(address spender, uint256 rawAmount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function checkpoints(address, uint32) external view returns (uint32 fromBlock, uint96 votes);
function decimals() external view returns (uint8);
function delegate(address delegatee) external;
function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external;
function delegates(address) external view returns (address);
function getCurrentVotes(address account) external view returns (uint96);
function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);
function name() external view returns (string memory);
function nonces(address) external view returns (uint256);
function numCheckpoints(address) external view returns (uint32);
function symbol() external view returns (string memory);
function totalSupply() external view returns (uint256);
function transfer(address dst, uint256 rawAmount) external returns (bool);
function transferFrom(address src, address dst, uint256 rawAmount) external returns (bool);
}
58 changes: 58 additions & 0 deletions contracts/test/GovernorVotesCompUpgradeable.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {Test} from "forge-std/Test.sol";
import {GovernorVotesCompUpgradeable} from "contracts/extensions/GovernorVotesCompUpgradeable.sol";
import {CompoundGovernorConstants} from "script/CompoundGovernorConstants.sol";
import {IComp} from "contracts/interfaces/IComp.sol";

contract GovernorVotesCompUpgradeableTestHarness is GovernorVotesCompUpgradeable {
function initialize(IComp _compToken) public initializer {
__GovernorVotesComp_init(_compToken);
}

function COUNTING_MODE() external view override returns (string memory) {}
function _countVote(uint256 _proposalId, address _account, uint8 _support, uint256 _weight, bytes memory _params)
internal
override
returns (uint256)
{}
function _quorumReached(uint256 _proposalId) internal view override returns (bool) {}
function _voteSucceeded(uint256 _proposalId) internal view override returns (bool) {}
function hasVoted(uint256 _proposalId, address account) external view override returns (bool) {}
function quorum(uint256 _timepoint) public view override returns (uint256) {}
function votingDelay() public view override returns (uint256) {}
function votingPeriod() public view override returns (uint256) {}
}

contract GovernorVotesCompUpgradeableTest is Test, CompoundGovernorConstants {
GovernorVotesCompUpgradeableTestHarness governorVotes;

function setUp() public {
vm.createSelectFork(vm.envOr("RPC_URL", string("Please set RPC_URL in your .env file")), FORK_BLOCK);
governorVotes = new GovernorVotesCompUpgradeableTestHarness();
governorVotes.initialize(IComp(COMP_TOKEN_ADDRESS));
}

function test_Initialize() public view {
assertEq(address(governorVotes.token()), address(COMP_TOKEN_ADDRESS));
}

function test_ReturnsBlockNumberAsClock() public view {
assertEq(governorVotes.clock(), block.number);
}

function test_ReturnsBlockNumberAsDefaultClockMode() public view {
assertEq(governorVotes.CLOCK_MODE(), "mode=blocknumber&from=default");
}

function testFuzz_ReturnsCorrectVotes(uint256 _blockNumber) public view {
_blockNumber = bound(_blockNumber, 0, FORK_BLOCK - 1);
for (uint256 i; i < _majorDelegates.length; i++) {
assertEq(
governorVotes.getVotes(_majorDelegates[i], _blockNumber),
IComp(COMP_TOKEN_ADDRESS).getPriorVotes(_majorDelegates[i], _blockNumber)
);
}
}
}
24 changes: 24 additions & 0 deletions script/CompoundGovernorConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,28 @@ contract CompoundGovernorConstants {

// The fork block for testing
uint256 FORK_BLOCK = 20_885_000;

address[] public _majorDelegates;

constructor() {
_majorDelegates = new address[](18);
_majorDelegates[0] = 0x1B686eE8E31c5959D9F5BBd8122a58682788eeaD; // L2BEAT
_majorDelegates[1] = 0xF4B0556B9B6F53E00A1FDD2b0478Ce841991D8fA; // olimpio
_majorDelegates[2] = 0x11cd09a0c5B1dc674615783b0772a9bFD53e3A8F; // Gauntlet
_majorDelegates[3] = 0xB933AEe47C438f22DE0747D57fc239FE37878Dd1; // Wintermute
_majorDelegates[4] = 0x0eB5B03c0303f2F47cD81d7BE4275AF8Ed347576; // Treasure
_majorDelegates[5] = 0xF92F185AbD9E00F56cb11B0b709029633d1E37B4; //
_majorDelegates[6] = 0x186e505097BFA1f3cF45c2C9D7a79dE6632C3cdc;
_majorDelegates[7] = 0x5663D01D8109DDFC8aACf09fBE51F2d341bb3643;
_majorDelegates[8] = 0x2ef27b114917dD53f8633440A7C0328fef132e2F; // MUX Protocol
_majorDelegates[9] = 0xE48C655276C23F1534AE2a87A2bf8A8A6585Df70; // ercwl
_majorDelegates[10] = 0x8A3e9846df0CDc723C06e4f0C642ffFF82b54610;
_majorDelegates[11] = 0xAD16ebE6FfC7d96624A380F394cD64395B0C6144; // DK (Premia)
_majorDelegates[12] = 0xA5dF0cf3F95C6cd97d998b9D990a86864095d9b0; // Blockworks Research
_majorDelegates[13] = 0x839395e20bbB182fa440d08F850E6c7A8f6F0780; // Griff Green
_majorDelegates[14] = 0x2e3BEf6830Ae84bb4225D318F9f61B6b88C147bF; // Camelot
_majorDelegates[15] = 0x8F73bE66CA8c79382f72139be03746343Bf5Faa0; // mihal.eth
_majorDelegates[16] = 0xb5B069370Ef24BC67F114e185D185063CE3479f8; // Frisson
_majorDelegates[17] = 0xdb5781a835b60110298fF7205D8ef9678Ff1f800; // yoav.eth
}
}
4 changes: 2 additions & 2 deletions script/DeployCompoundGovernor.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pragma solidity 0.8.26;

import {Script} from "forge-std/Script.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import {IComp} from "contracts/interfaces/IComp.sol";
import {ICompoundTimelock} from "@openzeppelin/contracts/vendor/compound/ICompoundTimelock.sol";
import {CompoundGovernor} from "contracts/CompoundGovernor.sol";
import {CompoundGovernorConstants} from "script/CompoundGovernorConstants.sol";
Expand Down Expand Up @@ -33,7 +33,7 @@ contract DeployCompoundGovernor is Script, CompoundGovernorConstants {
INITIAL_VOTING_DELAY,
INITIAL_VOTING_PERIOD,
INITIAL_PROPOSAL_THRESHOLD,
IVotes(COMP_TOKEN_ADDRESS),
IComp(COMP_TOKEN_ADDRESS),
INITIAL_QUORUM,
ICompoundTimelock(TIMELOCK_ADDRESS),
INITIAL_VOTE_EXTENSION,
Expand Down
Loading