generated from defi-wonderland/solidity-foundry-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add wonderGovernor and wonderVotes contract examples
Signed-off-by: 0xRaccoon <[email protected]>
- Loading branch information
Showing
6 changed files
with
180 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters