Core.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Register.sol";
import "./Snap.sol";
import "./Vote.sol";
import "../../setting/Rule.sol";
import "../../library/Authority.sol";
contract Core is Register, Snap, Vote, Rule, Authority {
constructor(address tokenAddress, uint256 threshold, uint256 fee)
Vote(tokenAddress)
Rule(threshold, fee)
Authority()
{}
function setDefaultToken(address newTokenAddress) public override onlyOwner {
super.setDefaultToken(newTokenAddress);
}
function setThreshold(uint256 threshold) public override onlyOwner {
super.setThreshold(threshold);
}
function setProposalFee(uint256 fee) public override onlyOwner {
super.setProposalFee(fee);
}
function setRule(address dao, uint256 nDelay, uint256 nRgstPeriod, uint256 nVotePeriod, uint256 nSnapTime, uint256 nTimelock, uint256 nTurnout, uint256 nQuorum)
public override onlyOwner returns (bool)
{
bool success = super.setRule(dao, nDelay, nRgstPeriod, nVotePeriod, nSnapTime, nTimelock, nTurnout, nQuorum);
return success;
}
function setRuleByProposal(address proposer, bytes32 proposalId) public override onlyOwner {
super.setRuleByProposal(proposer, proposalId);
}
function register(bytes32 proposalId, address delegator, address delegatee) public onlyOwner {
require(getDefaultTokenAmount(delegator) > 0, "Core failed: not enough token balance");
bool success = registerRights(proposalId, delegator, delegatee);
require(success, "Core failed: Can't register voting right");
}
function castVote(bytes32 proposalId, address voter, uint8 support) public override onlyOwner returns (bool) {
require(support > 0 && support <= 3, "Core failed: the number is great than allowable value or zero");
// Only those who have registered voting-power for themselves can participate in the vote.
require(voter == getRegister(proposalId, voter), "Core failed: an registered account");
bool success = super.castVote(proposalId, voter, support);
return success;
}
function snapBefore(bytes32 proposalId) public onlyOwner returns (bool) {
address[] memory voters = getAllRegisters(proposalId);
uint256 _totalPower = 0;
uint256 countVoters = voters.length;
for( uint256 i = 0 ; i < countVoters ; i++ ){
address _voter = voters[i];
uint256 _power = getDefaultTokenAmount(_voter);
setSnapBefore(proposalId, _voter, _power);
_totalPower += _power;
}
uint256 _totalSupply = getTotalSupply();
uint256 _registeredTurnout = _totalPower * 100 / _totalSupply;
uint256 _turnout = getRuleByProposal(proposalId).turnout;
// less than turnout
if(_registeredTurnout < _turnout){
return false;
}
return true;
}
function snapAfter(bytes32 proposalId) public onlyOwner returns (bool) {
address[] memory voters = getAllRegisters(proposalId);
// total turnout
uint256 _totalTurnout = 0;
/** @dev
* It takes the amount of voting rights from the list and stores the min value in _powerAfter of Snap.sol,
* compared to the amount of tokens it currently holds.
*/
uint256 countVoters = voters.length;
uint256 maxAccumulatedValue = 0;
for( uint256 i = 0 ; i < countVoters ; i++ ){
address _delegator = voters[i];
address _delegatee = getRegister(proposalId, _delegator);
// If the delegated account did not participate in the vote
if(!hasVoted(proposalId, _delegatee)) continue;
uint256 _curPower = getDefaultTokenAmount(_delegator);
uint256 _prePower = getPower(proposalId, _delegator);
// compare.(min value)
if(_curPower > _prePower) {
_curPower = _prePower;
}
setSnapAfter(proposalId, _delegator, _curPower);
_totalTurnout += _curPower;
// adjustment of voting power
uint8 _support = getSupport(proposalId, _delegatee);
uint256 _accumulated = setResult(proposalId, _support, _curPower);
if(_support == 1) {
maxAccumulatedValue = _accumulated;
}
}
// compare to turnout
uint256 _totalSupply = getTotalSupply();
uint256 _votingTurnout = _totalTurnout * 100 / _totalSupply;
uint256 _minTurnout = getRuleByProposal(proposalId).turnout;
if(_votingTurnout < _minTurnout) {
return false;
}
// compare to quorum
// search a option that satisfies the quorum
uint256 _maxRate = maxAccumulatedValue * 100 / _totalTurnout;
uint256 _minQuorum = getRuleByProposal(proposalId).quorum;
if(_maxRate < _minQuorum) {
return false;
}
return true;
}
}
Register.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Register {
mapping(bytes32 => mapping(address => address)) _delegations;
mapping(bytes32 => address[]) _registers;
function getAllRegisters(bytes32 proposalId) public view returns (address[] memory) {
return _registers[proposalId];
}
function hasRegistered(bytes32 proposalId, address voter) public view returns (bool) {
return _delegations[proposalId][voter] != address(0x0);
}
function getRegister(bytes32 proposalId, address voter) public view returns (address) {
return _delegations[proposalId][voter];
}
function registerRights(bytes32 proposalId, address delegator, address delegatee) internal returns (bool) {
require(!hasRegistered(proposalId, delegator), "Register failed: has registered already");
_delegations[proposalId][delegator] = delegatee;
_registers[proposalId].push(delegator);
return true;
}
}
Vote.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../token/IERC20.sol";
contract Vote {
IERC20 _origin;
mapping(bytes32 => mapping(uint8 => uint256)) _result;
mapping(bytes32 => mapping(address => uint8)) _supports;
constructor(address tokenAddress){
_setDefaultToken(IERC20(tokenAddress));
}
function setDefaultToken(address newToken) public virtual {
require(newToken != address(0x0), "Vote failed: This is zero address");
_setDefaultToken(IERC20(newToken));
}
function _setDefaultToken(IERC20 newToken) private {
_origin = newToken;
}
function getDefaultToken() public view returns (IERC20) {
return _origin;
}
function getDefaultTokenAmount(address account) public view returns (uint256) {
return _origin.balanceOf(account);
}
function getTotalSupply() public view returns (uint256) {
return _origin.totalSupply();
}
function hasVoted(bytes32 proposalId, address voter) public view returns (bool) {
return _supports[proposalId][voter] != 0;
}
function castVote(bytes32 proposalId, address voter, uint8 support) public virtual returns (bool) {
require(!hasVoted(proposalId, voter), "Vote failed: has voted already");
_supports[proposalId][voter] = support;
return true;
}
function getSupport(bytes32 proposalId, address voter) public view returns (uint8) {
require(_supports[proposalId][voter] != 0, "Vote failed: has not voted yet");
return _supports[proposalId][voter];
}
function setResult(bytes32 proposalId, uint8 support, uint256 power) internal returns (uint256) {
_result[proposalId][support] += power;
return _result[proposalId][support];
}
function getResult(bytes32 proposalId) public view returns (uint256[] memory) {
/** @dev (just using interger but enum)
* voting options
* 1 : agree
* 2 : disagree
* 3 : abstain
*/
uint256[] memory v = new uint256[](4);
// The data of index '0' is dummy
for(uint8 i = 1 ; i <= 4 ; i++ ){
v[i] = _result[proposalId][i];
}
return v;
}
}
Snap.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Snap {
mapping(bytes32 => mapping(address => uint256)) _powerBefore;
mapping(bytes32 => mapping(address => uint256)) _powerAfter;
function setSnapBefore(bytes32 proposalId, address voter, uint256 power) internal {
_powerBefore[proposalId][voter] = power;
}
function setSnapAfter(bytes32 proposalId, address voter, uint256 power) internal {
_powerAfter[proposalId][voter] = power;
}
function getPower(bytes32 proposalId, address voter) public view returns (uint256) {
if(_powerAfter[proposalId][voter] != 0){
return _powerAfter[proposalId][voter];
}
return _powerBefore[proposalId][voter];
}
}
Rule.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../library/TypeStruct.sol";
contract Rule is TypeStruct {
uint256 _threshold;
uint256 _proposalFee;
mapping(address => Rules) _userRules;
mapping(bytes32 => Rules) _proposalRules;
mapping(address => bool) _isInitialized;
constructor(uint256 threshold, uint256 fee) {
_setThreshold(threshold);
_setProposalFee(fee);
}
function setThreshold(uint256 threshold) public virtual {
_setThreshold(threshold);
}
function _setThreshold(uint256 threshold) private {
_threshold = threshold;
}
function getThreshold() public view returns (uint256) {
return _threshold;
}
function setProposalFee(uint256 fee) public virtual {
_setProposalFee(fee);
}
function _setProposalFee(uint256 fee) private {
_proposalFee = fee;
}
function getProposalFee() public view returns (uint256) {
return _proposalFee;
}
function setRule(address proposer, uint256 nDelay, uint256 nRgstPeriod, uint256 nVotePeriod, uint256 nSnapTime, uint256 nTimelock, uint256 nTurnout, uint256 nQuorum)
public virtual returns (bool)
{
Rules memory r = Rules(nDelay, nRgstPeriod, nVotePeriod, nSnapTime, nTimelock, nTurnout, nQuorum);
_userRules[proposer] = r;
if (!_isInitialized[proposer]) {
_isInitialized[proposer] = true;
}
return true;
}
function setRuleByProposal(address proposer, bytes32 proposalId) public virtual {
require(_isInitialized[proposer], "Rule failed: have to initialize user rules");
Rules memory r = getRuleByAccount(proposer);
_proposalRules[proposalId] = r;
}
function getRuleByAccount(address account) public view returns (Rules memory) {
return _userRules[account];
}
function getRuleByProposal(bytes32 proposalId) public view returns (Rules memory) {
return _proposalRules[proposalId];
}
}