-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: add wonderGovernor and wonderVotes contract examples #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ out-via-ir | |
|
||
# Config files | ||
.env | ||
.vscode | ||
|
||
# Avoid ignoring gitkeep | ||
!/**/.gitkeep | ||
|
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); | ||
} |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hehe <3 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't we just use a state variable instead of a function for this? use the getter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we could. But that will require us to change the |
||
} | ||
|
||
function proposalTypes() external view returns (uint8[] memory) { | ||
return governor.proposalTypes(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be cool to see some example where this has logic, like a percentage of IERC20(VoteToken).totalSupply()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For another instance, ofc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, this could be calling
votes.getPastTotalSupply(_proposalType, _timepoint)
and calculate a percentage of that, or it could also combine with the total supply to ensure that a minimum threshold of voting power is being tracked for new proposal types.