Contract Diff Checker

Contract Name:
RIS3Gov

Contract Source Code:

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.4;

import "./interfaces/IRIS3Store.sol";
import "./utils/Ownable.sol";
import "./utils/Presenter.sol";
import "./utils/Maintainable.sol";
import "./interfaces/IERC20.sol";

// BE is presenter of gov
contract RIS3Gov is Presenter, Maintainable {
  IRIS3Store public store;
  //---
  // - Case: staking day
  //+ Only allow: start staking by admin -> reset cycle info -> allow stake/unstake for citizen
  //- Case: production days
  //+ Only allow: start production by admin -> mint reward by time, calculate and collect reward
  //- Case: voting day
  //+ Only allow: start votingP1 (50% time) by admin -> collect reward, vote for next gov; start votingP2 (the rest 50% time) by admin -> Display result or in case dictator set law
  //---
  ///@dev Cycle config
  uint256 public stageDurationStaking = 10 minutes;
  uint256 public stageDurationProduction = 10 minutes;
  uint256 public stageDurationVoting1 = 5 minutes;
  uint256 public stageDurationVoting2 = 5 minutes;

  ///@dev Cycle state
  uint256 public stageIndex = 3; // 0: staking, 1: production, 2: voting1, 3: voting2
  uint256 public stageStartTime = 0; // Save start time of current stage
  uint256 public stageEndTime = 0; // Save end time of current stage

  event Restore(address oldGov, address newGov);

  /**
   * @dev get pending reward of user
   *
   * Requirements
   *
   * - `_type` type of stake, 0 is for farm, 1 for factory, 2 for citizen
   *
   * Access
   *
   * - User
   */
  function getPendingReward(address _user, uint _type) public view returns (uint256, uint256){
    if(store.getAlreadyCollectedReward(_user, _type, stageStartTime, stageEndTime)) {
      return (0, 0);
    }

    return store.getPendingReward(_user, _type);
  }

  /**
   * @dev restore a goverment to new government
   *
   * Emits a Restore event
   * Requirements
   *
   * - `_gov` old gov address
   *
   * Access
   *
   * - ADMIN
   */
  function restore(address _gov) external onlyOwner {
    ifMaintenance();
    RIS3Gov oldGov = RIS3Gov(_gov);
    stageDurationStaking = oldGov.stageDurationStaking();
    stageDurationProduction = oldGov.stageDurationProduction();
    stageDurationVoting1 = oldGov.stageDurationVoting1();
    stageDurationVoting2 = oldGov.stageDurationVoting2();

    stageIndex = oldGov.stageIndex();
    stageStartTime = oldGov.stageStartTime();
    stageEndTime = oldGov.stageEndTime();
    
    emit Restore((_gov), address(this));
  }

  /**
   * @dev set store token address
   *
   * Requirements
   *
   * - `_store` address of store.
   *
   * Access
   *
   * - Owner
   */
  function setStore(address _store) external onlyOwner {
    store = IRIS3Store(_store);
  }

  /**
   * @dev set ris3Token and lp token address
   *
   * Requirements
   *
   * - `_ris3Token` address of store.
   * - `_ris3BNBLP` address of lp token.
   *
   * Access
   *
   * - Owner
   */
  function setTokens(address _ris3Token, address _ris3BNBLP) external onlyOwner {
    store.setTokens(_ris3Token, _ris3BNBLP);
  }

  /**
   * @dev user can stake tokens by sending `_amount` and `type`
   *
   *
   * Requirements
   *
   * - `_amount` amount of tokens.
   * - `_type` type of stake, 0 is for farm, 1 for factory, 2 for citizen
   *
   * Access
   *
   * - User
   */
  function deposit(uint256 _amount, uint _type) external {
    ifNotMaintenance();
    require(_amount > 0, "Cannot stake 0");
    require(_isStageLive(0), "No staking");

    store.deposit(_amount, _type, msg.sender);
  }

  /**
   * @dev user can unstake tokens by sending `_amount` and only citizen can withdraw staking on during staking time
   *
   * Requirements
   *
   * - `_amount` amount of tokens.
   *
   * Access
   *
   * - User
   */
  function withdraw(uint256 _amount) external {
    ifNotMaintenance();
    require(_amount > 0, "> Zero");
    require(_isStageLive(0), "no staking");

    store.withdraw(_amount, msg.sender);
  } // citizen withdraw

  /**
   * @dev to start the staking
   * Access
   *
   * - Admin
   */
  function startStaking() external onlyAdmin {

    bool isIntialValue = stageIndex == 0 && stageStartTime == 0 && stageEndTime == 0;
    require(isIntialValue || _isStageFinished(3), "can't set staking");

    // reset cycle
    stageIndex = 0;
    stageStartTime = block.timestamp;
    stageEndTime = stageStartTime + stageDurationStaking;
    store.resetCycle();
  }

  ///@notice STAGE 2: PRODUCTION TIME
  /**
   * @dev to start production time
   *
   * Access
   *
   * - ADMIN
   */
  function startProduction() external onlyAdmin {
    ifNotMaintenance();
    require(_isStageFinished(0), "cant set production");

    stageIndex = 1;
    stageStartTime = block.timestamp;
    stageEndTime = stageStartTime + stageDurationProduction;

    //add factory and farm tokens
    store.setRewards();
  }

  /**
   * @dev user want to withdraw the rewards
   *
   * Requirements
   *
   * - `_type` type of stake, 0 is for farm, 1 for factory, 2 for citizen
   *
   * Access
   *
   * - User
   */
  function collectReward(uint _type) public {
    ifNotMaintenance();
    require(_isStageLive(1), "No production");
    require(store.getAlreadyCollectedReward(msg.sender, _type, stageStartTime, stageEndTime) == false, "already claimed");
    //claim Rewards from store
    store.claimRewards(msg.sender, _type);
  }

  /**
   * @dev STAGE 3: USER VOTING TIME
   *
   * Requirements
   *
   * Access
   *
   * - ADMIN
   */
  function startVoting1() external onlyAdmin {
    ifNotMaintenance();

    require(_isStageFinished(1), "can't set voting 1");

    stageIndex = 2;
    stageStartTime = block.timestamp;
    stageEndTime = stageStartTime + stageDurationVoting1;
  }

  /**
   * @dev user will cast the votes
   *
   * Requirements
   * - `_governmentIndex` type of government
   * - `_taxRates` tax rates law
   * - `_prodRates` prod rates law
   * - `_taxPoolUses` tax pool uses law
   * - `_dictatorAddress` dictatorAddresses in case dictatorship
   * Access
   *
   * - User
   */
  function castVote(uint256 _governmentIndex, uint256 _taxRates, uint256 _prodRates, uint256 _taxPoolUses, address _dictatorAddress) public {
    ifNotMaintenance();
    require(_isStageLive(2), "No voting 1");

    // 0 is for Socialism, 1 is for Democracy and 2 is for Dictatorship
    require(_governmentIndex < 3, "Wrong government type");
    require(_taxRates < 3, "Wrong tax rates");
    require(_prodRates < 3, "Wrong prod rates");
    require(_taxPoolUses < 3, "Wrong tax pool uses");

    store.saveUserVote(_governmentIndex, _taxRates, _prodRates, _taxPoolUses, _dictatorAddress, msg.sender);
  }

  /**
   * @dev this function will be call to finish the election and calculate voting result
   *
   * Access
   *
   * - ADMIN
   */
  function startVoting2() external onlyAdmin {
    ifNotMaintenance();
    require(_isStageFinished(2), "can't set voting 2");

    stageIndex = 3;
    stageStartTime = block.timestamp;
    stageEndTime = stageStartTime + stageDurationVoting2;

    store.saveElection();
  }

  /**
   * @dev set laws by dictator
   *
   * Requirements
   *
   * - `_taxRates` tax rates
   * - `_prodRates` prod rates
   * - `_taxPoolUses` tax pool uses
   *
   * Access
   *
   * - User
   */
  function dictatorVote(uint256 _taxRates, uint256 _prodRates, uint256 _taxPoolUses) public {
    require(_isStageLive(3), "only allow in voting 2");
    store.saveDictatorVote(_taxRates, _prodRates, _taxPoolUses, msg.sender);
  }

  /**
   * @dev set cycle durations
   *
   * Requirements
   *
   * - `_stageDurationStaking` set staking duration
   * - `_stageDurationProduction` set Production duration
   * - `_stageDurationVoting1` set VOTING 1 duration
   * - `_stageDurationVoting2` set VOTING 2 duration
   *
   * Access
   *
   * - ADMIN
   */
  function setStageDuration(uint256 _stageDurationStaking,
    uint256 _stageDurationProduction,
    uint256 _stageDurationVoting1,
    uint256 _stageDurationVoting2) external onlyAdmin {
    stageDurationStaking = _stageDurationStaking;
    stageDurationProduction = _stageDurationProduction;
    stageDurationVoting1 = _stageDurationVoting1;
    stageDurationVoting2 = _stageDurationVoting2;
  }

  /**
  * @dev check if stage is live
  *
  * Requirements
  *
  * - `_stage` current stage
  *
  * Access
  *
  * - INTERNAL
  */
  function _isStageLive(uint256 _stage) internal view returns (bool){
    return stageIndex == _stage && block.timestamp >= stageStartTime && block.timestamp <= stageEndTime;
  }

  /**
  * @dev check if stage is finished
  *
  * Requirements
  *
  * - `_stage` current stage
  *
  * Access
  *
  * - INTERNAL
  */
  function _isStageFinished(uint256 _stage) internal view returns (bool){
    return stageIndex == _stage && block.timestamp > stageEndTime;
  }
}

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.4;

interface IERC20 {
  /**
   * @dev Returns the amount of tokens in existence.
   */
  function totalSupply() external view returns (uint256);

  /**
   * @dev Returns the amount of tokens owned by `account`.
   */
  function balanceOf(address account) external view returns (uint256);

  /**
   * @dev Moves `amount` tokens from the caller's account to `recipient`.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transfer(address recipient, uint256 amount) external returns (bool);

  /**
   * @dev Returns the remaining number of tokens that `spender` will be
   * allowed to spend on behalf of `owner` through {transferFrom}. This is
   * zero by default.
   *
   * This value changes when {approve} or {transferFrom} are called.
   */
  function allowance(address owner, address spender) external view returns (uint256);

  /**
   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * IMPORTANT: Beware that changing an allowance with this method brings the risk
   * that someone may use both the old and the new allowance by unfortunate
   * transaction ordering. One possible solution to mitigate this race
   * condition is to first reduce the spender's allowance to 0 and set the
   * desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   *
   * Emits an {Approval} event.
   */
  function approve(address spender, uint256 amount) external returns (bool);

  /**
   * @dev Moves `amount` tokens from `sender` to `recipient` using the
   * allowance mechanism. `amount` is then deducted from the caller's
   * allowance.
   *
   * Returns a boolean value indicating whether the operation succeeded.
   *
   * Emits a {Transfer} event.
   */
  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);


  /**
   * @dev Emitted when `value` tokens are moved from one account (`from`) to
   * another (`to`).
   *
   * Note that `value` may be zero.
   */
  event Transfer(address indexed from, address indexed to, uint256 value);

  /**
   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
   * a call to {approve}. `value` is the new allowance.
   */
  event Approval(address indexed owner, address indexed spender, uint256 value);

  // Calculate fee
  function calculateAmountsAfterFee(
    address sender,
    uint256 amount
  ) external view returns (uint256 transferToAmount, uint256 transferToFeeDistributorAmount);

  // Mint by only owner
  function mint(uint256 amount) external;

  // Burn
  function burn(uint256 amount) external;
}

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.4;

interface IRIS3Store {
  function getRis3Address() external view returns (address);

  function getTotalTaxCollected() external view returns (uint256);

  function getAlreadyCollectedReward(address _user, uint _type, uint256 _startTime, uint256 _endTime) external view returns (bool);

  function getPendingReward(address _user, uint _type) external view returns(uint256, uint256);

  function addTax(uint256 _tax) external;

  function resetRewards() external;

  function setRewards() external;

  function claimRewards(address _user, uint _type) external;

  function setTokens(address _ris3, address _ris3BNBLP) external;

  function resetCycle() external;

  function deposit(uint256 _amount, uint _type, address sender) external;

  function withdraw(uint256 _amount, address _sender) external;

  function saveUserVote(uint256 _governmentIndex, uint256 _taxRates, uint256 _prodRates, uint256 _taxPoolUses, address _dictatorAddress, address _user) external;

  function saveElection() external;

  function saveDictatorVote(uint256 _taxRates, uint256 _prodRates, uint256 _taxPoolUses, address _user) external;

}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

abstract contract Context {
  function _msgSender() internal view virtual returns (address payable) {
    return payable(msg.sender);
  }

  function _msgData() internal view virtual returns (bytes memory) {
    this;
    // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
    return msg.data;
  }
}

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.4;

import "./Ownable.sol";

contract Maintainable is Ownable {

  bool public isMaintenance;
  bool public isOutdated;

  // Check if contract is not in maintenance
  function ifNotMaintenance() internal view {
    require(!isMaintenance, "Maintenance");
    require(!isOutdated, "Outdated");
  }

  // Check if contract on maintenance for restore
  function ifMaintenance() internal view {
    require(isMaintenance, "!Maintenance");
  }

  // Enable maintenance
  function enableMaintenance(bool status) onlyOwner public {
    isMaintenance = status;
  }

  // Enable outdated
  function enableOutdated(bool status) onlyOwner public {
    isOutdated = status;
  }

}

// SPDX-License-Identifier: MIT

pragma solidity 0.8.4;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
contract Ownable is Context {
  address private _owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev Initializes the contract setting the deployer as the initial owner.
   */
  constructor () {
    address msgSender = _msgSender();
    _owner = msgSender;
    emit OwnershipTransferred(address(0), msgSender);
  }

  /**
   * @dev Returns the address of the current owner.
   */
  function owner() public view returns (address) {
    return _owner;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(_owner == _msgSender(), "Ownable: caller is not the owner");
    _;
  }

  /**
   * @dev Transfers ownership of the contract to a new account (`newOwner`).
   * Can only be called by the current owner.
   */
  function transferOwnership(address newOwner) public virtual onlyOwner {
    require(newOwner != address(0), "Ownable: new owner is the zero address");
    emit OwnershipTransferred(_owner, newOwner);
    _owner = newOwner;
  }
}

// SPDX-License-Identifier: UNLICENSED

pragma solidity 0.8.4;

import "./Context.sol";
import "./Ownable.sol";

contract Presenter is Ownable {

  address public presenter;

  function setPresenter(address _presenter) onlyOwner public {
    presenter = _presenter;
  }

  function isPresenter() public view returns (bool) {
    return presenter == _msgSender();
  }

  modifier onlyAdmin() {
    require(owner() == _msgSender() || isPresenter(), "Caller is not the admin");
    _;
  }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):