Skip to content

Commit

Permalink
test/bonding: Test GovernorCountingOverridable
Browse files Browse the repository at this point in the history
  • Loading branch information
victorges committed Jul 18, 2023
1 parent ea54d0d commit 80cf668
Show file tree
Hide file tree
Showing 5 changed files with 625 additions and 5 deletions.
78 changes: 78 additions & 0 deletions contracts/test/mocks/GovernorCountingOverridableTestable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol";

import "../../treasury/GovernorCountingOverridable.sol";

/**
* @dev This is a concrete contract to test the GovernorCountingOverridable extension. It implements the minimum
* necessary to get a working Governor to test the extension.
*/
contract GovernorCountingOverridableTestable is
Initializable,
GovernorUpgradeable,
GovernorSettingsUpgradeable,
GovernorVotesUpgradeable,
GovernorCountingOverridable
{
// use non-standard values for these to test if it's really used
uint256 constant QUOTA = 420000; // 42%
uint256 constant QUORUM = 370000; // 37%

IVotes internal iVotes; // 🍎

function initialize(IVotes _votes) public initializer {
iVotes = _votes;

__Governor_init("GovernorCountingOverridableConcrete");
__GovernorSettings_init(
0, /* no voting delay */
100, /* 100 blocks voting period */
0 /* no minimum proposal threshold */
);

__GovernorVotes_init(iVotes);
__GovernorCountingOverridable_init();
}

function votes() public view override returns (IVotes) {
return iVotes;
}

function quota() public pure override returns (uint256) {
return QUOTA;
}

function quorum(uint256 timepoint) public view virtual override returns (uint256) {
uint256 totalSupply = iVotes.getPastTotalSupply(timepoint);
return MathUtils.percOf(totalSupply, QUORUM);
}

/**
* @dev Expose internal _quorumReached function for testing.
*/
function quorumReached(uint256 proposalId) public view returns (bool) {
return super._quorumReached(proposalId);
}

/**
* @dev Expose internal _voteSucceeded function for testing.
*/
function voteSucceeded(uint256 proposalId) public view returns (bool) {
return super._voteSucceeded(proposalId);
}

function proposalThreshold()
public
view
override(GovernorUpgradeable, GovernorSettingsUpgradeable)
returns (uint256)
{
return super.proposalThreshold();
}
}
110 changes: 110 additions & 0 deletions contracts/test/mocks/VotesMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20SnapshotUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

import { IVotes } from "../../treasury/GovernorCountingOverridable.sol";
import "../../bonding/libraries/SortedArrays.sol";

/**
* @dev Minimum implementation of an IVotes interface to test the GovernorCountingOverridable extension.
*/
contract VotesMock is
Initializable,
ERC20Upgradeable,
ERC20BurnableUpgradeable,
OwnableUpgradeable,
ERC20VotesUpgradeable,
IVotes
{
mapping(address => uint256[]) private _delegateChangingTimes;
mapping(address => mapping(uint256 => address)) private _delegatedAtTime;

function initialize() public initializer {
__ERC20_init("VotesMock", "VTCK");
__ERC20Burnable_init();
__Ownable_init();
__ERC20Votes_init();
}

function delegatedAt(address _account, uint256 _timepoint) external view returns (address) {
uint256[] storage rounds = _delegateChangingTimes[_account];
if (rounds.length == 0 || _timepoint < rounds[0]) {
return address(0);
}

uint256 prevRound = SortedArrays.findLowerBound(rounds, _timepoint);
return _delegatedAtTime[_account][prevRound];
}

function _delegate(address _delegator, address _to) internal override {
super._delegate(_delegator, _to);

uint256 currTime = clock();

uint256[] storage rounds = _delegateChangingTimes[_delegator];
SortedArrays.pushSorted(rounds, currTime);
_delegatedAtTime[_delegator][currTime] = _to;
}

/**
* @dev Simulates the behavior of our actual voting power, where the delegator also has voting power which can
* override their transcoder's vote. This is not the case in the OpenZeppelin implementation.
*/
function getPastVotes(address account, uint256 blockNumber)
public
view
override(IVotesUpgradeable, ERC20VotesUpgradeable)
returns (uint256)
{
// Blatant simplification that only works in our tests where we never change participants balance during
// proposal voting period. We check and return delegators current state instead of tracking historical values.
if (delegates(account) != account) {
return balanceOf(account);
}
return super.getPastVotes(account, blockNumber);
}

/**
* @dev Same as above. Still don't understand why the OZ implementation for these 2 is incompatible, with getPast*
* reverting if you query it with the current round.
*/
function getVotes(address account)
public
view
override(IVotesUpgradeable, ERC20VotesUpgradeable)
returns (uint256)
{
if (delegates(account) != account) {
return balanceOf(account);
}
return super.getVotes(account);
}

function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}

// The following functions are overrides required by Solidity.

function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
super._afterTokenTransfer(from, to, amount);
}

function _mint(address to, uint256 amount) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
super._mint(to, amount);
}

function _burn(address account, uint256 amount) internal override(ERC20Upgradeable, ERC20VotesUpgradeable) {
super._burn(account, amount);
}
}
6 changes: 3 additions & 3 deletions test/helpers/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ const percPoints = (a, b) => {

// Returns a * (b / c) scaled by PERC_DIVISOR
// See percOf() in contracts/libraries/MathUtils.sol
const percOf = (a, b, c) => {
const percOf = (a, b, c = BigNumber.from(constants.PERC_DIVISOR)) => {
return _percOf(a, b, c, BigNumber.from(constants.PERC_DIVISOR))
}

const precise = {
percPoints: (a, b) => {
return _percPoints(a, b, constants.PERC_DIVISOR_PRECISE)
},
percOf: (a, b, c) => {
percOf: (a, b, c = constants.PERC_DIVISOR_PRECISE) => {
return _percOf(a, b, c, constants.PERC_DIVISOR_PRECISE)
}
}
Expand All @@ -25,7 +25,7 @@ const v2 = {
percPoints: (a, b) => {
return _percPoints(a, b, constants.PERC_DIVISOR_V2)
},
percOf: (a, b, c) => {
percOf: (a, b, c = constants.PERC_DIVISOR_V2) => {
return _percOf(a, b, c, constants.PERC_DIVISOR_V2)
}
}
Expand Down
Loading

0 comments on commit 80cf668

Please sign in to comment.