// Contract: SleepToken
// Target function: function released() public view returns (uint256)
 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center];
            if (cp.fromBlock == blockNumber) {
                return cp.votes;
            } else if (cp.fromBlock < blockNumber) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return checkpoints[account][lower].votes;
    }

    function _delegate(address delegator, address delegatee)
    internal
    {
        address currentDelegate = _delegates[delegator];
        uint256 delegatorBalance = balanceOf(delegator); // balance of underlying SLEEPs (not scaled);
        _delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveDelegates(currentDelegate, delegatee, delegatorBalance);
    }

    function _moveDelegates(address srcRep, address dstRep, uint256 amount) internal {
        if (srcRep != dstRep && amount > 0) {
            if (srcRep != address(0)) {
                // decrease old representative
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                uint256 srcRepNew = srcRepOld.sub(amount);
                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
            }

            if (dstRep != address(0)) {
                // increase new representative
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                uint256 dstRepNew = dstRepOld.add(amount);
                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
            }
        }
    }

    function _writeCheckpoint(
        address delegatee,
        uint32 nCheckpoints,
        uint256 oldVotes,
        uint256 newVotes
    )
    internal
    {
        uint32 blockNumber = safe32(block.number, "SLEEP::_writeCheckpoint: block number exceeds 32 bits");

        if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
            checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
        } else {
            checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
            numCheckpoints[delegatee] = nCheckpoints + 1;
        }

        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
    }

    function safe32(uint n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }

    function getChainId() internal pure returns (uint) {
        uint256 chainId;
        assembly { chainId := chainid() }
        return chainId;
    }
}

interface IMigratorChef {
    function migrate(IERC20 token) external returns (IERC20);
}

contract SleepBedroom is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // Info of each user.
    struct UserInfo {
        uint256 amount;     // How many LP tokens the user has provided.
        uint256 rewardDebt; // Reward debt. See explanation below.
        uint256 startStakeTime; // Average start stake time of the staked tokens in pool
    }

    // Info of each pool.
    struct PoolInfo {
        IERC20 lpToken;           // Address of LP token contract.
        uint256 allocPoint;       // How many allocation points assigned to this pool. SLEEPs to distribute per block.
        uint256 lastRewardBlock;  // Last block number that SLEEPs distribution occurs.
        uint256 accSleepPerShare; // Accumulated SLEEPs per share, times 1e12. See below.
    }

    // The SLEEP TOKEN!
    SleepToken public sleep;
    // Dev address.
    address public devaddr;
    // Block number when bonus SLEEP period ends.
    uint256 public bonusEndBlock;
    // SLEEP tokens created per block.
    uint256 public sleepPerBlock;
    // Bonus muliplier for early sleep makers.
    uint256 public constant BONUS_MULTIPLIER = 10; // 3 times of bonus
    // Extra reward increases for maximum 100000 blocks(around 17 days).
    uint256 public constant MAX_EXTRA_REWARD_BLOCK_INTERVAL = 100000;

    // Info of each pool.
    PoolInfo[] public poolInfo;
    // Info of each user that stakes LP tokens.
    mapping (uint256 => mapping (address => UserInfo)) public userInfo;
    // Total allocation points. Must be the sum of all allocation points in all pools.
    uint256 public totalAllocPoint = 0;
    // The block number when SLEEP mining starts.
    uint256 public startBlock;
    // Extra sleep token reward per block for stake time based calculation
    uint256 public extraSleepPerBlock;
    // Maximum time based extra reward per MAX_EXTRA_REWARD_BLOCK_INTERVAL block for each user;
    uint256 public maxExtraReward;
    // The migrator contract. Can only be set through governance (owner).
    IMigratorChef public migrator;

    event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
    event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
    event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);

    constructor(
        SleepToken _sleep,
        address _devaddr,
        uint256 _sleepPerBlock,
        uint256 _startBlock,
        uint256 _bonusEndBlock,
        uint256 _extraSleepPerBlock,
        uint256 _maxExtraReward
    ) public {
        sleep = _sleep;
        devaddr = _devaddr;
        sleepPerBlock = _sleepPerBlock;
        bonusEndBlock = _bonusEndBlock;
        startBlock = _startBlock;
        extraSleepPerBlock = _extraSleepPerBlock;
        maxExtraReward = _maxExtraReward;
    }

    function poolLength() external view returns (uint256) {
        return poolInfo.length;
    }

    function setMaxExtraReward(uint256 _maxExtraReward) external onlyOwner() {
        maxExtraReward = _maxExtraReward;
    }

    // Add a new lp to the pool. Can only be called by the owner.
    // XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
    function add(uint256 _allocPoint, IERC20 _lpToken, bool _withUpdate) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
        totalAllocPoint = totalAllocPoint.add(_allocPoint);
        poolInfo.push(PoolInfo({
        lpToken: _lpToken,
        allocPoint: _allocPoint,
        lastRewardBlock: lastRewardBlock,
        accSleepPerShare: 0
        }));
    }

    // Update the given pool's SLEEP allocation point. Can only be called by the owner.
    function set(uint256 _pid, uint256 _allocPoint, bool _withUpdate) public onlyOwner {
        if (_withUpdate) {
            massUpdatePools();
        }
        totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint);
        poolInfo[_pid].allocPoint = _allocPoint;
    }



    // Return reward multiplier over the given _from to _to block.
    function getMultiplier(uint256 _from, uint256 _to) public view returns (uint256) {
        if (_to <= bonusEndBlock) {
            return _to.sub(_from).mul(BONUS_MULTIPLIER);
        } else if (_from >= bonusEndBlock) {
            return _to.sub(_from);
        } else {
            return bonusEndBlock.sub(_from).mul(BONUS_MULTIPLIER).add(
                _to.sub(bonusEndBlock)
            );
        }
    }

    // View function to see pending SLEEPs on frontend.
    function pendingSleep(uint256 _pid, address _user) external view returns (uint256) {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][_user];
        uint256 accSleepPerShare = pool.accSleepPerShare;
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (block.number > pool.lastRewardBlock && lpSupply != 0) {
            uint256 multiplier = (getMultiplier(pool.lastRewardBlock, block.number));
            uint256 sleepReward = multiplier.mul(sleepPerBlock).mul(pool.allocPoint).div(totalAllocPoint).div(getHalvingFactor());
            accSleepPerShare = accSleepPerShare.add(sleepReward.mul(1e12).div(lpSupply));
        }
        return user.amount.mul(accSleepPerShare).div(1e12).sub(user.rewardDebt);
    }

    // Update reward variables for all pools. Be careful of gas spending!
    function massUpdatePools() public {
        uint256 length = poolInfo.length;
        for (uint256 pid = 0; pid < length; ++pid) {
            updatePool(pid);
        }
    }


    // Update reward variables of the given pool to be up-to-date.
    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        if (block.number <= pool.lastRewardBlock) {
            return;
        }
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            pool.lastRewardBlock = block.number;
            return;
        }
        uint256 multiplier = getMultiplier(pool.lastRewardBlock, block.number);
        uint256 sleepReward = multiplier.mul(sleepPerBlock).mul(pool.allocPoint).div(totalAllocPoint).div(getHalvingFactor());
        sleep.mint(devaddr, sleepReward.div(10));
        sleep.mint(address(this), sleepReward);
        pool.accSleepPerShare = pool.accSleepPerShare.add(sleepReward.mul(1e12).div(lpSupply));
        pool.lastRewardBlock = block.number;
    }

    // Deposit LP tokens to MasterBedroom for SLEEP allocation.
    function deposit(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        updatePool(_pid);
        if (user.amount > 0) {
            uint256 pending = user.amount.mul(pool.accSleepPerShare).div(1e12).sub(user.rewardDebt);
            safeSleepTransfer(msg.sender, pending);
        }
        pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
        user.startStakeTime = ((user.startStakeTime.mul(user.amount)).add(_amount.mul(block.number))).div(user.amount.add(_amount));
        user.amount = user.amount.add(_amount);
        user.rewardDebt = user.amount.mul(pool.accSleepPerShare).div(1e12);
        emit Deposit(msg.sender, _pid, _amount);
    }

    // Withdraw LP tokens from MasterBedroom.
    function withdraw(uint256 _pid, uint256 _amount) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        require(user.amount >= _amount, "withdraw: not good");
        updatePool(_pid);
        uint256 pending = user.amount.mul(pool.accSleepPerShare).div(1e12).sub(user.rewardDebt);
        safeSleepTransfer(msg.sender, pending);
        uint256 extraReward = getExtraReward(_pid, user.startStakeTime, block.number, _amount).div(getHalvingFactor());
        sleep.mint(msg.sender, extraReward); // stake time based reward
        sleep.mint(devaddr, extraReward.div(10));
        user.amount = user.amount.sub(_amount);
        user.rewardDebt = user.amount.mul(pool.accSleepPerShare).div(1e12);
        pool.lpToken.safeTransfer(address(msg.sender), _amount);

        emit Withdraw(msg.sender, _pid, _amount);
    }

    function getHalvingFactor() public view returns (uint256) {
        return 2 ** ((block.number - startBlock) / 100000); // halve per 100000 blocks
    }

    function getExtraRewardForUser(uint256 _pid, address _userAddress) public view returns(uint256) {
        UserInfo storage user = userInfo[_pid][_userAddress];
        return getExtraReward(_pid, user.startStakeTime, block.number, user.amount);
    }

    // get stake time based extra reward
    function getExtraReward(uint256 _pid, uint256 startStakeBlock, uint256 endBlock, uint256 numLPTokens) public view returns(uint256){
        if(startStakeBlock > endBlock) {
            return 0;
        }
        PoolInfo storage pool = poolInfo[_pid];
        uint256 lpSupply = pool.lpToken.balanceOf(address(this));
        if (lpSupply == 0) {
            return 0;
        }
        uint256 extraRewardBlockInterval = endBlock.sub(startStakeBlock) > MAX_EXTRA_REWARD_BLOCK_INTERVAL ? MAX_EXTRA_REWARD_BLOCK_INTERVAL : endBlock.sub(startStakeBlock);
        uint256 extraReward = extraRewardBlockInterval.mul(extraRewardBlockInterval).mul(extraSleepPerBlock).div(poolInfo.length).mul(numLPTokens).div(lpSupply).div(10000).div(getHalvingFactor());
        uint256 maxExtraReward = maxExtraReward.div(getHalvingFactor()).mul(extraRewardBlockInterval).mul(extraRewardBlockInterval).div(MAX_EXTRA_REWARD_BLOCK_INTERVAL).div(MAX_EXTRA_REWARD_BLOCK_INTERVAL);
        return extraReward;
    }

    // get stake time based extra reward
    function getMaxExtraReward(uint256 _pid, uint256 startStakeBlock, uint256 endBlock) public view returns(uint256){
        if(startStakeBlock > endBlock) {
            return 0;
        }
        uint256 extraRewardBlockInterval = endBlock.sub(startStakeBlock) > MAX_EXTRA_REWARD_BLOCK_INTERVAL ? MAX_EXTRA_REWARD_BLOCK_INTERVAL : endBlock.sub(startStakeBlock);
        uint256 maxExtraReward = maxExtraReward.div(getHalvingFactor()).mul(extraRewardBlockInterval).mul(extraRewardBlockInterval).div(MAX_EXTRA_REWARD_BLOCK_INTERVAL).div(MAX_EXTRA_REWARD_BLOCK_INTERVAL);
        return maxExtraReward;
    }

    // Withdraw without caring about rewards. EMERGENCY ONLY.
    function emergencyWithdraw(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        UserInfo storage user = userInfo[_pid][msg.sender];
        pool.lpToken.safeTransfer(address(msg.sender), user.amount);
        emit EmergencyWithdraw(msg.sender, _pid, user.amount);
        user.amount = 0;
        user.rewardDebt = 0;
    }

    // Safe Sleep transfer function, just in case if rounding error causes pool to not have enough SLEEPs.
    function safeSleepTransfer(address _to, uint256 _amount) internal {
        uint256 sleepBal = sleep.balanceOf(address(this));
        if (_amount > sleepBal) {
            sleep.transfer(_to, sleepBal);
        } else {
            sleep.transfer(_to, _amount);
        }
    }

    // Update dev address by the previous dev.
    function dev(address _devaddr) public onlyOwner {
        devaddr = _devaddr;
    }

    // Set the migrator contract. Can only be called by the owner.
    function setMigrator(IMigratorChef _migrator) public onlyOwner {
        migrator = _migrator;
    }

    // Migrate lp token to another lp contract. Can be called by anyone. We trust that migrator contract is good.
    function migrate(uint256 _pid) public {
        require(address(migrator) != address(0), "migrate: no migrator");
        PoolInfo storage pool = poolInfo[_pid];
        IERC20 lpToken = pool.lpToken;
        uint256 bal = lpToken.balanceOf(address(this));
        lpToken.safeApprove(address(migrator), bal);
        IERC20 newLpToken = migrator.migrate(lpToken);
        require(bal == newLpToken.balanceOf(address(this)), "migrate: bad");
        pool.lpToken = newLpToken;
    }
}

/**
* @title SleepVesting
* @dev A token holder contract that can release its token balance gradually like a
* typical vesting scheme, with a vesting period. Optionally revocable by the
* owner.
*/
contract SleepVesting is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for ERC20;
    event TokensVested(address token, uint256 amount);
    address private _beneficiary;
    uint256 private _start;
    uint256 private _duration;
    uint256 private _released;
    // The SLEEP Token!
    ERC20 public sleep;
    constructor(address beneficiary, uint256 start, uint256 duration, SleepToken _sleep) public {
        require(beneficiary != address(0), "SleepVesting: beneficiary is the zero address");
        require(duration > 0, "SleepVesting: duration is 0");
        require(start.add(duration) > block.number, "SleepVesting: final time is before current time");
        _beneficiary = beneficiary;
        _start = start;
        _duration = duration;
        sleep = _sleep;
    }
    /**
     * @return the beneficiary of the tokens.
     */
    function beneficiary() public view returns (address) {
        return _beneficiary;
    }
    /**
    * @return the start time of the token vesting.
    */
    function start() public view returns (uint256) {
        return _start;
    }
    /**
     * @return the duration of the token vesting.
     */
    function duration() public view returns (uint256) {
        return _duration;
    }