Skip to content

Commit

Permalink
feat: add wonderGovernor and wonderVotes contract examples
Browse files Browse the repository at this point in the history
Signed-off-by: 0xRaccoon <[email protected]>
  • Loading branch information
0xRaccoon authored Dec 21, 2023
1 parent 6ef07b1 commit 5bfd84b
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ out-via-ir

# Config files
.env
.vscode

# Avoid ignoring gitkeep
!/**/.gitkeep
Expand Down
4 changes: 4 additions & 0 deletions solidity/contracts/governance/WonderGovernor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -889,4 +889,8 @@ abstract contract WonderGovernor is
function proposalTypes() public view virtual override returns (uint8[] memory) {
return _proposalTypes();
}

function _getProposal(uint256 _proposalId) internal view virtual returns (ProposalCore memory) {
return _proposals[_proposalId];
}
}
6 changes: 5 additions & 1 deletion solidity/contracts/governance/utils/WonderVotes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,11 @@ abstract contract WonderVotes is Context, EIP712, Nonces, IERC6372, IWonderVotes
if (_weightSum != _weightNormalizer()) revert InvalidWeightSum(_weightSum);

Delegate[] memory _oldDelegates = delegates(account, proposalType);
_delegatees[account][proposalType] = delegatees;

delete _delegatees[account][proposalType];
for (uint256 i = 0; i < delegatees.length; i++) {
_delegatees[account][proposalType].push(delegatees[i]);
}

emit DelegateChanged(account, proposalType, _oldDelegates, delegatees);
_moveDelegateVotes(proposalType, _oldDelegates, delegatees, _getVotingUnits(account));
Expand Down
134 changes: 134 additions & 0 deletions solidity/examples/AliceGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import 'contracts/governance/WonderGovernor.sol';
import 'contracts/governance/utils/WonderVotes.sol';

contract AliceGovernor is WonderGovernor {
WonderVotes public votes;
string internal _countingMode = 'support=bravo&quorum=bravo';
uint8[] internal __proposalTypes = [1, 2, 3];

mapping(uint256 proposalId => mapping(address => Receipt)) public receipts;
mapping(uint256 proposalId => ProposalTrack) public proposalTracks;

/// @notice Ballot receipt record for a voter
struct Receipt {
/// @notice Whether or not a vote has been cast
bool hasVoted;
/// @notice 0 = Against, 1 = For, 2 = Abstain
uint8 support;
/// @notice The number of votes the voter had, which were cast
uint256 votes;
}

struct ProposalTrack {
uint256 proposalId;
uint256 votes;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
}

constructor(address _wonderToken) WonderGovernor('AliceGovernor') {
votes = WonderVotes(_wonderToken);
}

function CLOCK_MODE() public view override returns (string memory _clockMode) {
return votes.CLOCK_MODE();
}

function COUNTING_MODE() external view override returns (string memory) {
return _countingMode;
}

function _getVotes(
address _account,
uint8 _proposalType,
uint256 _timepoint,
bytes memory _params
) internal view virtual override returns (uint256) {
return votes.getPastVotes(_account, _proposalType, _timepoint);
}

function clock() public view override returns (uint48) {
return votes.clock();
}

function votingPeriod() public view override returns (uint256) {
// ~3 days in blocks (assuming 15s blocks)
return 17_280;
}

function votingDelay() public view override returns (uint256) {
// 1 block
return 1;
}

function quorum(uint256 _timepoint, uint8 _proposalType) public view override returns (uint256) {
// same quorum for all proposals types and timepoints
return 400_000e18;
}

function _proposalTypes() internal view override returns (uint8[] memory) {
return __proposalTypes;
}

function _isValidProposalType(uint8 _proposalType) internal view virtual override returns (bool) {
return _proposalType < __proposalTypes.length;
}

function _countVote(
uint256 _proposalId,
address _account,
uint8 _support,
uint256 _weight,
bytes memory _params
) internal virtual override {
if (_support == 0) {
proposalTracks[_proposalId].againstVotes += _weight;
} else if (_support == 1) {
proposalTracks[_proposalId].forVotes += _weight;
} else if (_support == 2) {
proposalTracks[_proposalId].abstainVotes += _weight;
} else {
revert InvalidVoteType(_support);
}

Receipt storage receipt = receipts[_proposalId][_account];

receipt.hasVoted = true;
receipt.support = _support;
receipt.votes = _weight;
}

function hasVoted(uint256 _proposalId, address _account) external view override returns (bool) {
return receipts[_proposalId][_account].hasVoted;
}

function _quorumReached(uint256 _proposalId) internal view virtual override returns (bool) {
ProposalTrack memory _proposalTrack = proposalTracks[_proposalId];
ProposalCore memory _proposal = _getProposal(_proposalId);

uint256 _totalVotes = _proposalTrack.forVotes + _proposalTrack.againstVotes + _proposalTrack.abstainVotes;
return _totalVotes >= quorum(_proposal.voteStart, _proposal.proposalType);
}

function _voteSucceeded(uint256 _proposalId) internal view virtual override returns (bool) {
ProposalTrack memory _proposalTrack = proposalTracks[_proposalId];

bool _succeded = _quorumReached(_proposalId) && _proposalTrack.forVotes > _proposalTrack.againstVotes;
return _succeded;
}

function proposalThreshold(uint8 _proposalType) public view override returns (uint256) {
// same threshold for all proposals types
return 100_000e18;
}

function isValidProposalType(uint8 _proposalType) external view returns (bool) {
return _isValidProposalType(_proposalType);
}

error InvalidVoteType(uint8 voteType);
}
35 changes: 35 additions & 0 deletions solidity/examples/RabbitToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {WonderERC20Votes} from 'contracts/token/ERC20/extensions/WonderERC20Votes.sol';
import {AliceGovernor} from './AliceGovernor.sol';
import {EIP712} from '@openzeppelin/contracts/utils/cryptography/EIP712.sol';
import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';

contract RabbitToken is WonderERC20Votes {
AliceGovernor public governor;

constructor(AliceGovernor _governor) EIP712('RabbitToken', '1') ERC20('RabbitToken', 'RBT') {
governor = _governor;
}

function _getProposalTypes() internal view virtual override returns (uint8[] memory) {
return governor.proposalTypes();
}

function _maxDelegates() internal view virtual override returns (uint8) {
return 4;
}

function _validProposalType(uint8 _proposalType) internal view virtual override returns (bool) {
return governor.isValidProposalType(_proposalType);
}

function _weightNormalizer() internal view virtual override returns (uint256) {
return 100;
}

function proposalTypes() external view returns (uint8[] memory) {
return governor.proposalTypes();
}
}
1 change: 1 addition & 0 deletions solidity/interfaces/governance/utils/IWonderVotes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ interface IWonderVotes {
*/
function delegateBySig(
Delegate[] memory delegates,
uint8 proposalType,
uint256 nonce,
uint256 expiry,
uint8 v,
Expand Down

0 comments on commit 5bfd84b

Please sign in to comment.