Governance Beta (Obsolete)

Note token governance we disabled

KP4R Governance contract is live on Test Net (Ropsten) and is a work in progress.

Kp4rGovV1 is the initial KP4R token governance contract. It purpose is to allow holders greater control over the supply and governance of their token.

At a glance:

  • This Contract is in BETA.
  • This Version of governance is managed by chairperson role.
  • Only the chairperson can create proposals.
  • Chairperson cannot force proposals through without community support.
  • This acts as a community veto, for all chairperson proposals.
  • In future versions we hope to have no privileged roles.


Beta Governance UI coming soon.

Chairperson role

The chairperson is a safeguard against any malicous proposals while the project is in its infantacy. Chairperson is the only party able to create new proposals, but is limited in that it cannot force proposals through without community support.

Limitations and Safeguards

There are three main safeguards for any change on the contract. First the change must be proposed by the chairperson (Keep4r team). Second the change must recieve support from token holders. Finaly a week must pass after the end of the voting period from any proposal, before the action is taken, giving token holders ample time before any change.

Beta Contact

  • Network:

    Ropsten

  • Kp4rGovV1 Beta:

    0x857E7372388F864c579F58c8F17C0A4a83E932B1

  • TestKP4R:

    0x7E18AcdA010E853279701b8D42B200A70a8EF869

  • PlaceholderStaking:

    0xfB68C69329078a4B0D49E04C04fe8Bed6bb2F20E


Source

/* updated – 1/11/2020 */
pragma solidity 0.6.6;

library SafeMath {
    function add(uint a, uint b) internal pure returns (uint) {
        uint c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }
    function sub(uint a, uint b) internal pure returns (uint) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }
    function sub(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
        require(b <= a, errorMessage);
        uint c = a - b;

        return c;
    }
    function mul(uint a, uint b) internal pure returns (uint) {
        if (a == 0) {
            return 0;
        }

        uint c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }
    function div(uint a, uint b) internal pure returns (uint) {
        return div(a, b, "SafeMath: division by zero");
    }
    function div(uint a, uint b, string memory errorMessage) internal pure returns (uint) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint c = a / b;

        return c;
    }
}

interface GovernanceInterface {
    function acceptGovernance() external;
}

interface StakingInterface {
    function votingPowerOf(address acc, uint256 until) external view returns(uint256);
}

interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address account) external view returns (uint);
    function transfer(address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

interface KP4RInterface is IERC20 {
    function transferGovernance(address _governance) external;
    function acceptGovernance() external;
    function addMinter(address _minter) external;
    function removeMinter(address _minter) external;
}

contract Keep4rGovV1 is GovernanceInterface {

    using SafeMath for uint256;

    // chairperson is the only address than create proposals
    address public chairperson;
    address public _pendingChairperson;

    // Each proposal must last 7 days
    uint256 constant DURATION = 7 days;
    // Each action can only be called 7 days after proposal date
    uint256 constant ACTION_DELAY = 7 days;
    // cooldown 
    uint256 constant COOLDOWN = 1 days;

    KP4RInterface public kp4r;
    StakingInterface public staking;

    enum PROPOSAL_TYPE {
        BLANK,                  // For holders opinions   
        UPGRADE_GOVERNANCE,     // Upgrade the governance contract
        UPGRADE_STAKING,        // Change staking address
        ADD_MINTER,             // Approve a minter
        REMOVE_MINTER           // Remove an approved miner
    }

    // proposal
    struct Proposal {
        address target;
        PROPOSAL_TYPE proposalType;

        uint256 yay; // yay == for == yes
        uint256 nay; // nay == against == no
        mapping(address => bool) voted;

        uint256 endDate;
        bool vetoed; // proposal may be vetoed by chair, cancelling it.
        bool executed; // proposal may only be executed once
    }

    // map of all past and present proposals
    mapping(uint256 => Proposal) public proposals;
    uint256 public proposalCount;
    uint256 public lastProposalDate;

    // while isSetup is false, the chairperson can directly set
    // governance
    bool public isSetup = false;

    //EVENTS
    event ProposalCreated (
        uint256 indexed id,
        PROPOSAL_TYPE proposalType,
        address target,
        uint256 endDate
    );

    constructor(address _kp4r, address _staking) public  {
        kp4r = KP4RInterface(_kp4r);
        staking = StakingInterface(_staking);
        chairperson = msg.sender;
    }

    // only chairperson
    modifier onlyChairperson {
        require(
            msg.sender == chairperson,
            "Only owner can call this function."
        );
        _;
    }

    function transferChairpersonRole(address acc) public onlyChairperson {
        _pendingChairperson = acc;
    }

    function veto(uint256 id) public onlyChairperson {
        require(block.timestamp < proposals[id].endDate);
        proposals[id].vetoed = true;
    }

    function createProposal(address _target, PROPOSAL_TYPE _type, uint256 _endDate) public onlyChairperson {
        require(lastProposalDate + COOLDOWN < block.timestamp, "still on cooldown");
        require(block.timestamp + DURATION < _endDate, "endDate");
        lastProposalDate = block.timestamp;

        uint256 id = proposalCount;
        proposals[id].target = _target;
        proposals[id].proposalType = _type;
        proposals[id].yay = 0;
        proposals[id].nay = 0;
        proposals[id].endDate = _endDate;

        proposalCount++;

        emit ProposalCreated(id, _type, _target, _endDate);
    }

    // actions 
    function acceptGovernance() public override {
        kp4r.acceptGovernance();
    }

    // internal actions
    /** @notice transfer via a proposal */
    function _upgradeGovernance(address target) internal {
        kp4r.transferGovernance(target);
    }

    /** @notice transfer via a proposal */
    function _upgradeStaking(address target) internal {
        staking = StakingInterface(target);
    }

    /** @notice addminter */
    function _addMinter(address target) internal {
        kp4r.addMinter(target);
    }

    /** @notice removeminter */
    function _removeMinter(address target) internal {
        kp4r.removeMinter(target);
    }

    /** @notice execute a proposal can be called by anyone on a previous unexeuted proposal */
    function executeProposal(uint256 id) public {
        require(proposals[id].executed == false, "already executed");
        require(block.timestamp + ACTION_DELAY < proposals[id].endDate, "proposal cannot be executed yet");
        require(proposals[id].yay > proposals[id].nay, "proposal did not recieve enough favour");

        proposals[id].executed = true;

        PROPOSAL_TYPE _type = proposals[id].proposalType;   
        address _target = proposals[id].target;

        if (_type == PROPOSAL_TYPE.BLANK) {
            return;
        }

        if (_type == PROPOSAL_TYPE.UPGRADE_GOVERNANCE) {
            _upgradeGovernance(_target);
            return;
        }

        if (_type == PROPOSAL_TYPE.UPGRADE_STAKING) {
            _upgradeStaking(_target);
            return;
        }

        if (_type == PROPOSAL_TYPE.ADD_MINTER) {
            _addMinter(_target);
            return;
        }

        if (_type == PROPOSAL_TYPE.REMOVE_MINTER) {
            _removeMinter(_target);
            return;
        }

    }

    /** @notice vote yay (in-favor-of), or  nay(not-in-favor-of) a given proposal */
    function vote(uint256 id, bool yay) public  {
        require(proposals[id].endDate > block.timestamp, "proposal not active");
        require(!proposals[id].voted[msg.sender], "already voted");
        require(!proposals[id].vetoed, "has been vetoed");

        uint256 _votingPower = staking.votingPowerOf(msg.sender, proposals[id].endDate);
        proposals[id].voted[msg.sender] = true;

        if (yay) {
            proposals[id].yay = proposals[id].yay.add(_votingPower);
        } else {
            proposals[id].nay = proposals[id].nay.add(_votingPower);
        }
    }

    function acceptChairpersonRole() public {
        require(msg.sender == _pendingChairperson, "only new chairperson can accept the role");
        chairperson = _pendingChairperson;
    }

}

results matching ""

    No results matching ""