Acropolis vs Masonry
266 लाइनें
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./utils/ContractGuard.sol";
import "./utils/ContractGuard.sol";
import "./interfaces/IBasisAsset.sol";
import "./interfaces/IBasisAsset.sol";
import "./interfaces/ITreasury.sol";
import "./interfaces/ITreasury.sol";
contract ShareWrapper {
contract ShareWrapper {
    using SafeMath for uint256;
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    using SafeERC20 for IERC20;
    IERC20 public share;
    IERC20 public share;
    uint256 private _totalSupply;
    uint256 private _totalSupply;
    mapping(address => uint256) private _balances;
    mapping(address => uint256) private _balances;
    function totalSupply() public view returns (uint256) {
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
        return _totalSupply;
    }
    }
    function balanceOf(address account) public view returns (uint256) {
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
        return _balances[account];
    }
    }
    function stake(uint256 amount) public virtual {
    function stake(uint256 amount) public virtual {
        _totalSupply = _totalSupply.add(amount);
        _totalSupply = _totalSupply.add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        _balances[msg.sender] = _balances[msg.sender].add(amount);
        share.safeTransferFrom(msg.sender, address(this), amount);
        share.safeTransferFrom(msg.sender, address(this), amount);
    }
    }
    function withdraw(uint256 amount) public virtual {
    function withdraw(uint256 amount) public virtual {
        uint256 masonShare = _balances[msg.sender];
        uint256 andrasShare = _balances[msg.sender];
        require(masonShare >= amount, "Masonry: withdraw request greater than staked amount");
        require(andrasShare >= amount, "Acropolis: withdraw request greater than staked amount");
        _totalSupply = _totalSupply.sub(amount);
        _totalSupply = _totalSupply.sub(amount);
        _balances[msg.sender] = masonShare.sub(amount);
        _balances[msg.sender] = andrasShare.sub(amount);
        share.safeTransfer(msg.sender, amount);
        share.safeTransfer(msg.sender, amount);
    }
    }
}
}
/*
/*
  ______                __       _______
__________                             .___   ___________.__
 /_  __/___  ____ ___  / /_     / ____(_)___  ____ _____  ________
\______   \_____     ______  ____    __| _/   \_   _____/|__|  ____  _____     ____    ____   ____
  / / / __ \/ __ `__ \/ __ \   / /_  / / __ \/ __ `/ __ \/ ___/ _ \
 |    |  _/\__  \   /  ___/_/ __ \  / __ |     |    __)  |  | /    \ \__  \   /    \ _/ ___\_/ __ \
 / / / /_/ / / / / / / /_/ /  / __/ / / / / / /_/ / / / / /__/  __/
 |    |   \ / __ \_ \___ \ \  ___/ / /_/ |     |     \   |  ||   |  \ / __ \_|   |  \\  \___\  ___/
/_/  \____/_/ /_/ /_/_.___/  /_/   /_/_/ /_/\__,_/_/ /_/\___/\___/
 |______  /(____  //____  > \___  >\____ |     \___  /   |__||___|  /(____  /|___|  / \___  >\___  >
        \/      \/      \/      \/      \/         \/             \/      \/      \/      \/     \/
    http://tomb.finance
*/
*/
contract Masonry is ShareWrapper, ContractGuard {
contract Acropolis is ShareWrapper, ContractGuard {
    using SafeERC20 for IERC20;
    using SafeERC20 for IERC20;
    using Address for address;
    using Address for address;
    using SafeMath for uint256;
    using SafeMath for uint256;
    /* ========== DATA STRUCTURES ========== */
    /* ========== DATA STRUCTURES ========== */
    struct Masonseat {
    struct Ecclesiaseat {
        uint256 lastSnapshotIndex;
        uint256 lastSnapshotIndex;
        uint256 rewardEarned;
        uint256 rewardEarned;
        uint256 epochTimerStart;
        uint256 epochTimerStart;
    }
    }
    struct MasonrySnapshot {
    struct AcropolisSnapshot {
        uint256 time;
        uint256 time;
        uint256 rewardReceived;
        uint256 rewardReceived;
        uint256 rewardPerShare;
        uint256 rewardPerShare;
    }
    }
    /* ========== STATE VARIABLES ========== */
    /* ========== STATE VARIABLES ========== */
    // governance
    // governance
    address public operator;
    address public operator;
    // flags
    // flags
    bool public initialized = false;
    bool public initialized = false;
    IERC20 public tomb;
    IERC20 public based;
    ITreasury public treasury;
    ITreasury public treasury;
    mapping(address => Masonseat) public masons;
    mapping(address => Ecclesiaseat) public demos;
    MasonrySnapshot[] public masonryHistory;
    AcropolisSnapshot[] public acropolisHistory;
    uint256 public withdrawLockupEpochs;
    uint256 public withdrawLockupEpochs;
    uint256 public rewardLockupEpochs;
    uint256 public rewardLockupEpochs;
    /* ========== EVENTS ========== */
    /* ========== EVENTS ========== */
    event Initialized(address indexed executor, uint256 at);
    event Initialized(address indexed executor, uint256 at);
    event Staked(address indexed user, uint256 amount);
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);
    event RewardPaid(address indexed user, uint256 reward);
    event RewardAdded(address indexed user, uint256 reward);
    event RewardAdded(address indexed user, uint256 reward);
    /* ========== Modifiers =============== */
    /* ========== Modifiers =============== */
    modifier onlyOperator() {
    modifier onlyOperator() {
        require(operator == msg.sender, "Masonry: caller is not the operator");
        require(operator == msg.sender, "Acropolis: caller is not the operator");
        _;
        _;
    }
    }
    modifier masonExists {
    modifier andrasExists {
        require(balanceOf(msg.sender) > 0, "Masonry: The mason does not exist");
        require(balanceOf(msg.sender) > 0, "Acropolis: The andras does not exist");
        _;
        _;
    }
    }
    modifier updateReward(address mason) {
    modifier updateReward(address andras) {
        if (mason != address(0)) {
        if (andras != address(0)) {
            Masonseat memory seat = masons[mason];
            Ecclesiaseat memory seat = demos[andras];
            seat.rewardEarned = earned(mason);
            seat.rewardEarned = earned(andras);
            seat.lastSnapshotIndex = latestSnapshotIndex();
            seat.lastSnapshotIndex = latestSnapshotIndex();
            masons[mason] = seat;
            demos[andras] = seat;
        }
        }
        _;
        _;
    }
    }
    modifier notInitialized {
    modifier notInitialized {
        require(!initialized, "Masonry: already initialized");
        require(!initialized, "Acropolis: already initialized");
        _;
        _;
    }
    }
    /* ========== GOVERNANCE ========== */
    /* ========== GOVERNANCE ========== */
    function initialize(
    function initialize(
        IERC20 _tomb,
        IERC20 _based,
        IERC20 _share,
        IERC20 _share,
        ITreasury _treasury
        ITreasury _treasury
    ) public notInitialized {
    ) public notInitialized {
        tomb = _tomb;
        based = _based;
        share = _share;
        share = _share;
        treasury = _treasury;
        treasury = _treasury;
        MasonrySnapshot memory genesisSnapshot = MasonrySnapshot({time : block.number, rewardReceived : 0, rewardPerShare : 0});
        AcropolisSnapshot memory genesisSnapshot = AcropolisSnapshot({time : block.number, rewardReceived : 0, rewardPerShare : 0});
        masonryHistory.push(genesisSnapshot);
        acropolisHistory.push(genesisSnapshot);
        withdrawLockupEpochs = 6; // Lock for 6 epochs (36h) before release withdraw
        withdrawLockupEpochs = 4; // Lock for 6 epochs (36h) before release withdraw
        rewardLockupEpochs = 3; // Lock for 3 epochs (18h) before release claimReward
        rewardLockupEpochs = 2; // Lock for 3 epochs (18h) before release claimReward    !!! THIS IS ALTERED TO SMALLER PERIODS
        initialized = true;
        initialized = true;
        operator = msg.sender;
        operator = msg.sender;
        emit Initialized(msg.sender, block.number);
        emit Initialized(msg.sender, block.number);
    }
    }
    function setOperator(address _operator) external onlyOperator {
    function setOperator(address _operator) external onlyOperator {
        operator = _operator;
        operator = _operator;
    }
    }
    function setLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
    function setLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
        require(_withdrawLockupEpochs >= _rewardLockupEpochs && _withdrawLockupEpochs <= 56, "_withdrawLockupEpochs: out of range"); // <= 2 week
        require(_withdrawLockupEpochs >= _rewardLockupEpochs && _withdrawLockupEpochs <= 56, "_withdrawLockupEpochs: out of range"); // <= 2 week
        withdrawLockupEpochs = _withdrawLockupEpochs;
        withdrawLockupEpochs = _withdrawLockupEpochs;
        rewardLockupEpochs = _rewardLockupEpochs;
        rewardLockupEpochs = _rewardLockupEpochs;
    }
    }
    /* ========== VIEW FUNCTIONS ========== */
    /* ========== VIEW FUNCTIONS ========== */
    // =========== Snapshot getters
    // =========== Snapshot getters =========== //
    function latestSnapshotIndex() public view returns (uint256) {
    function latestSnapshotIndex() public view returns (uint256) {
        return masonryHistory.length.sub(1);
        return acropolisHistory.length.sub(1);
    }
    }
    function getLatestSnapshot() internal view returns (MasonrySnapshot memory) {
    function getLatestSnapshot() internal view returns (AcropolisSnapshot memory) {
        return masonryHistory[latestSnapshotIndex()];
        return acropolisHistory[latestSnapshotIndex()];
    }
    }
    function getLastSnapshotIndexOf(address mason) public view returns (uint256) {
    function getLastSnapshotIndexOf(address andras) public view returns (uint256) {
        return masons[mason].lastSnapshotIndex;
        return demos[andras].lastSnapshotIndex;
    }
    }
    function getLastSnapshotOf(address mason) internal view returns (MasonrySnapshot memory) {
    function getLastSnapshotOf(address andras) internal view returns (AcropolisSnapshot memory) {
        return masonryHistory[getLastSnapshotIndexOf(mason)];
        return acropolisHistory[getLastSnapshotIndexOf(andras)];
    }
    }
    function canWithdraw(address mason) external view returns (bool) {
    function canWithdraw(address andras) external view returns (bool) {
        return masons[mason].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch();
        return demos[andras].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch();
    }
    }
    function canClaimReward(address mason) external view returns (bool) {
    function canClaimReward(address andras) external view returns (bool) {
        return masons[mason].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch();
        return demos[andras].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch();
    }
    }
    function epoch() external view returns (uint256) {
    function epoch() external view returns (uint256) {
        return treasury.epoch();
        return treasury.epoch();
    }
    }
    function nextEpochPoint() external view returns (uint256) {
    function nextEpochPoint() external view returns (uint256) {
        return treasury.nextEpochPoint();
        return treasury.nextEpochPoint();
    }
    }
    function getTombPrice() external view returns (uint256) {
    function getBasedPrice() external view returns (uint256) {
        return treasury.getTombPrice();
        return treasury.getBasedPrice();
    }
    }
    // =========== Mason getters
    // =========== Andras getters =========== //
    function rewardPerShare() public view returns (uint256) {
    function rewardPerShare() public view returns (uint256) {
        return getLatestSnapshot().rewardPerShare;
        return getLatestSnapshot().rewardPerShare;
    }
    }
    function earned(address mason) public view returns (uint256) {
    function earned(address andras) public view returns (uint256) {
        uint256 latestRPS = getLatestSnapshot().rewardPerShare;
        uint256 latestRPS = getLatestSnapshot().rewardPerShare;
        uint256 storedRPS = getLastSnapshotOf(mason).rewardPerShare;
        uint256 storedRPS = getLastSnapshotOf(andras).rewardPerShare;
        return balanceOf(mason).mul(latestRPS.sub(storedRPS)).div(1e18).add(masons[mason].rewardEarned);
        return balanceOf(andras).mul(latestRPS.sub(storedRPS)).div(1e18).add(demos[andras].rewardEarned);
    }
    }
    /* ========== MUTATIVE FUNCTIONS ========== */
    /* ========== MUTATIVE FUNCTIONS ========== */
    function stake(uint256 amount) public override onlyOneBlock updateReward(msg.sender) {
    function stake(uint256 amount) public override onlyOneBlock updateReward(msg.sender) {
        require(amount > 0, "Masonry: Cannot stake 0");
        require(amount > 0, "Acropolis: Cannot stake 0");
        super.stake(amount);
        super.stake(amount);
        masons[msg.sender].epochTimerStart = treasury.epoch(); // reset timer
        demos[msg.sender].epochTimerStart = treasury.epoch(); // reset timer
        emit Staked(msg.sender, amount);
        emit Staked(msg.sender, amount);
    }
    }
    function withdraw(uint256 amount) public override onlyOneBlock masonExists updateReward(msg.sender) {
    function withdraw(uint256 amount) public override onlyOneBlock andrasExists updateReward(msg.sender) {
        require(amount > 0, "Masonry: Cannot withdraw 0");
        require(amount > 0, "Acropolis: Cannot withdraw 0");
        require(masons[msg.sender].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch(), "Masonry: still in withdraw lockup");
        require(demos[msg.sender].epochTimerStart.add(withdrawLockupEpochs) <= treasury.epoch(), "Acropolis: still in withdraw lockup");
        claimReward();
        claimReward();
        super.withdraw(amount);
        super.withdraw(amount);
        emit Withdrawn(msg.sender, amount);
        emit Withdrawn(msg.sender, amount);
    }
    }
    function exit() external {
    function exit() external {
        withdraw(balanceOf(msg.sender));
        withdraw(balanceOf(msg.sender));
    }
    }
    function claimReward() public updateReward(msg.sender) {
    function claimReward() public updateReward(msg.sender) {
        uint256 reward = masons[msg.sender].rewardEarned;
        uint256 reward = demos[msg.sender].rewardEarned;
        if (reward > 0) {
        if (reward > 0) {
            require(masons[msg.sender].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch(), "Masonry: still in reward lockup");
            require(demos[msg.sender].epochTimerStart.add(rewardLockupEpochs) <= treasury.epoch(), "Acropolis: still in reward lockup");
            masons[msg.sender].epochTimerStart = treasury.epoch(); // reset timer
            demos[msg.sender].epochTimerStart = treasury.epoch(); // reset timer
            masons[msg.sender].rewardEarned = 0;
            demos[msg.sender].rewardEarned = 0;
            tomb.safeTransfer(msg.sender, reward);
            based.safeTransfer(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
        }
        }
    }
    }
    function allocateSeigniorage(uint256 amount) external onlyOneBlock onlyOperator {
    function allocateSeigniorage(uint256 amount) external onlyOneBlock onlyOperator {
        require(amount > 0, "Masonry: Cannot allocate 0");
        require(amount > 0, "Acropolis: Cannot allocate 0");
        require(totalSupply() > 0, "Masonry: Cannot allocate when totalSupply is 0");
        require(totalSupply() > 0, "Acropolis: Cannot allocate when totalSupply is 0");
        // Create & add new snapshot
        // Create & add new snapshot
        uint256 prevRPS = getLatestSnapshot().rewardPerShare;
        uint256 prevRPS = getLatestSnapshot().rewardPerShare;
        uint256 nextRPS = prevRPS.add(amount.mul(1e18).div(totalSupply()));
        uint256 nextRPS = prevRPS.add(amount.mul(1e18).div(totalSupply()));
        MasonrySnapshot memory newSnapshot = MasonrySnapshot({
        AcropolisSnapshot memory newSnapshot = AcropolisSnapshot({
            time: block.number,
            time: block.number,
            rewardReceived: amount,
            rewardReceived: amount,
            rewardPerShare: nextRPS
            rewardPerShare: nextRPS
        });
        });
        masonryHistory.push(newSnapshot);
        acropolisHistory.push(newSnapshot);
        tomb.safeTransferFrom(msg.sender, address(this), amount);
        based.safeTransferFrom(msg.sender, address(this), amount);
        emit RewardAdded(msg.sender, amount);
        emit RewardAdded(msg.sender, amount);
    }
    }
    function governanceRecoverUnsupported(IERC20 _token, uint256 _amount, address _to) external onlyOperator {
    function governanceRecoverUnsupported(IERC20 _token, uint256 _amount, address _to) external onlyOperator {
        // do not allow to drain core tokens
        // do not allow to drain core tokens
        require(address(_token) != address(tomb), "tomb");
        require(address(_token) != address(based), "based");
        require(address(_token) != address(share), "share");
        require(address(_token) != address(share), "share");
        _token.safeTransfer(_to, _amount);
        _token.safeTransfer(_to, _amount);
    }
    }
}
}