Professional Documents
Culture Documents
Parameterizer
Parameterizer
Parameterizer
0;
import "./PLCR/PLCRVoting.sol";
import "../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol";
contract Parameterizer {
// ------
// EVENTS
// ------
// ------
// DATA STRUCTURES
// ------
struct ParamProposal {
uint256 appExpiry;
uint256 challengeID;
uint256 deposit;
string name;
address owner;
uint256 processBy;
uint256 value;
}
struct Challenge {
uint256 rewardPool; // (remaining) pool of tokens distributed
amongst winning voters
address challenger; // owner of Challenge
bool resolved; // indication of if challenge is resolved
uint256 stake; // number of tokens at risk for either party
during challenge
uint256 winningTokens; // (remaining) amount of tokens used for voting
by the winning side
mapping(address => bool) tokenClaims;
}
// ------
// STATE
// ------
// Global Variables
IERC20 public token;
PLCRVoting public voting;
uint256 public PROCESSBY = 604800; // 7 days
/**
@dev Initializer Can only be called once
@param _token The address where the ERC20 token contract is deployed
@param _plcr address of a PLCR voting contract for the provided
token
@notice _parameters array of canonical parameters
*/
function init(
address _token,
address _plcr,
uint256[] calldata _parameters
) public {
require(_token != address(0) && address(token) == address(0));
require(_plcr != address(0) && address(voting) == address(0));
token = IERC20(_token);
voting = PLCRVoting(_plcr);
// -----------------------
// TOKEN HOLDER INTERFACE
// -----------------------
/**
@notice propose a reparamaterization of the key _name's value to _value.
@param _name the name of the proposed param to be set
@param _value the proposed value to set the param to be set
*/
function proposeReparameterization(string calldata _name, uint256 _value)
public returns (bytes32) {
uint256 deposit = get("pMinDeposit");
bytes32 propID = keccak256(abi.encodePacked(_name, _value));
if (keccak256(abi.encodePacked(_name)) ==
keccak256(abi.encodePacked("dispensationPct")) ||
keccak256(abi.encodePacked(_name)) ==
keccak256(abi.encodePacked("pDispensationPct"))) {
require(_value <= 100);
}
//start poll
uint256 pollID = voting.startPoll(
get("pVoteQuorum"),
get("pCommitStageLen"),
get("pRevealStageLen")
);
/**
@notice for the provided proposal ID, set it, resolve its
challenge, or delete it depending on whether it can be set, has a challenge which
can be resolved, or if its "process by" date has passed
@param _propID the proposal ID to make a determination and state
transition for
*/
function processProposal(bytes32 _propID) public {
ParamProposal storage prop = proposals[_propID];
address propOwner = prop.owner;
uint256 propDeposit = prop.deposit;
// Before any token transfers, deleting the proposal will ensure that if
reentrancy occurs the
// prop.owner and prop.deposit will be 0, thereby preventing theft
if (canBeSet(_propID)) {
// There is no challenge against the proposal. The processBy date for
the proposal has not
// passed, but the proposal's appExpirty date has passed.
set(prop.name, prop.value);
emit _ProposalAccepted(_propID, prop.name, prop.value);
delete proposals[_propID];
require(token.transfer(propOwner, propDeposit));
} else if (challengeCanBeResolved(_propID)) {
// There is a challenge against the proposal.
resolveChallenge(_propID);
} else if (block.timestamp > prop.processBy) {
// There is no challenge against the proposal, but the processBy date
has passed.
emit _ProposalExpired(_propID);
delete proposals[_propID];
require(token.transfer(propOwner, propDeposit));
} else {
// There is no challenge against the proposal, and neither the
appExpiry date nor the
// processBy date has passed.
revert();
}
// verify that future proposal appExpiry and processBy times will not
overflow
block.timestamp.add(get("pApplyStageLen"))
.add(get("pCommitStageLen"))
.add(get("pRevealStageLen"))
.add(PROCESSBY);
delete proposals[_propID];
}
/**
@notice Claim the tokens owed for the msg.sender in the
provided challenge
@param _challengeID the challenge ID to claim tokens for
*/
function claimReward(uint256 _challengeID) public {
Challenge storage challenge = challenges[_challengeID];
// ensure voter has not already claimed tokens and challenge results have
been processed
require(challenge.tokenClaims[msg.sender] == false);
require(challenge.resolved == true);
/**
@dev Called by a voter to claim their rewards for each
completed vote.
Someone must call updateStatus() before this can be
called.
@param _challengeIDs The PLCR pollIDs of the challenges rewards are being
claimed for
*/
function claimRewards(uint256[] calldata _challengeIDs) public {
// loop through arrays, claiming each individual vote reward
for (uint256 i = 0; i < _challengeIDs.length; i++) {
claimReward(_challengeIDs[i]);
}
}
// --------
// GETTERS
// --------
/**
@dev Calculates the provided voter's token reward for the given
poll.
@param _voter The address of the voter whose reward balance is to be
returned
@param _challengeID The ID of the challenge the voter's reward is being
calculated for
@return The uint256 indicating the voter's reward
*/
function voterReward(address _voter, uint256 _challengeID)
public view returns (uint256) {
uint256 winningTokens = challenges[_challengeID].winningTokens;
uint256 rewardPool = challenges[_challengeID].rewardPool;
uint256 voterTokens = voting.getNumPassingTokens(_voter, _challengeID);
return (voterTokens * rewardPool) / winningTokens;
}
/**
@notice Determines whether a proposal passed its application stage without a
challenge
@param _propID The proposal ID for which to determine whether its application
stage passed without a challenge
*/
function canBeSet(bytes32 _propID) view public returns (bool) {
ParamProposal memory prop = proposals[_propID];
/**
@notice Determines whether a proposal exists for the provided proposal ID
@param _propID The proposal ID whose existance is to be determined
*/
function propExists(bytes32 _propID) view public returns (bool) {
return proposals[_propID].processBy > 0;
}
/**
@notice Determines whether the provided proposal ID has a challenge which can
be resolved
@param _propID The proposal ID whose challenge to inspect
*/
function challengeCanBeResolved(bytes32 _propID) view public returns (bool) {
ParamProposal storage prop = proposals[_propID];
Challenge storage challenge = challenges[prop.challengeID];
/**
@notice Determines the number of tokens to awarded to the winning party in a
challenge
@param _challengeID The challengeID to determine a reward for
*/
function challengeWinnerReward(uint256 _challengeID) public view returns
(uint256) {
if(voting.getTotalNumberOfTokensForWinningOption(_challengeID) == 0) {
// Edge case, nobody voted, give all tokens to the challenger.
return 2 * challenges[_challengeID].stake;
}
return (2 * challenges[_challengeID].stake) -
challenges[_challengeID].rewardPool;
}
/**
@notice gets the parameter keyed by the provided name value from the params
mapping
@param _name the key whose value is to be determined
*/
function get(string memory _name) public view returns (uint256 value) {
return params[keccak256(abi.encodePacked(_name))];
}
/**
@dev Getter for Challenge tokenClaims mappings
@param _challengeID The challengeID to query
@param _voter The voter whose claim status to query for the provided
challengeID
*/
function tokenClaims(uint256 _challengeID, address _voter) public view returns
(bool) {
return challenges[_challengeID].tokenClaims[_voter];
}
// ----------------
// PRIVATE FUNCTIONS
// ----------------
/**
@dev resolves a challenge for the provided _propID. It must be checked in
advance whether the _propID has a challenge on it
@param _propID the proposal ID whose challenge is to be resolved.
*/
function resolveChallenge(bytes32 _propID) private {
ParamProposal memory prop = proposals[_propID];
Challenge storage challenge = challenges[prop.challengeID];
challenge.winningTokens =
voting.getTotalNumberOfTokensForWinningOption(prop.challengeID);
challenge.resolved = true;
/**
@dev sets the param keted by the provided name to the provided value
@param _name the name of the param to be set
@param _value the value to set the param to be set
*/
function set(string memory _name, uint256 _value) private {
params[keccak256(abi.encodePacked(_name))] = _value;
}
}