// File: /contracts/OLETokenLock.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.6;


import "@openzeppelin/contracts/math/SafeMath.sol";
import "./gov/OLEToken.sol";


/// @title OLE token Locked
/// @author OpenLeverage
/// @notice Release OLE to beneficiaries linearly.
contract OLETokenLock {
    using SafeMath for uint256;
    OLEToken public token;
    mapping(address => ReleaseVar) public releaseVars;

    event Release(address beneficiary, uint amount);
    event TransferTo(address beneficiary, address to, uint amount);

    struct ReleaseVar {
        uint256 amount;
        uint128 startTime;
        uint128 endTime;
        uint128 lastUpdateTime;
    }

    constructor(OLEToken token_, address[] memory beneficiaries, uint256[] memory amounts, uint128[] memory startTimes, uint128[] memory endTimes) {
        require(beneficiaries.length == amounts.length
        && beneficiaries.length == startTimes.length
            && beneficiaries.length == endTimes.length, "Array length must be same");
        token = token_;
        for (uint i = 0; i < beneficiaries.length; i++) {
            require(endTimes[i] > startTimes[i], "StartTime must be earlier than endTime");
            address beneficiary = beneficiaries[i];
            releaseVars[beneficiary] = ReleaseVar(amounts[i], startTimes[i], endTimes[i], startTimes[i]);
        }
    }

    function release() external {
        releaseInternal(msg.sender);
    }

    function releaseInternal(address beneficiary) internal {
        uint256 amount = token.balanceOf(address(this));
        require(amount > 0, "no amount available");
        uint256 releaseAmount = releaseAbleAmount(beneficiary);
        // The transfer out limit exceeds the available limit of the account
        require(amount >= releaseAmount, "transfer out limit exceeds ");
        releaseVars[beneficiary].lastUpdateTime = uint128(block.timestamp > releaseVars[beneficiary].endTime ? releaseVars[beneficiary].endTime : block.timestamp);
        token.transfer(beneficiary, releaseAmount);
        emit Release(beneficiary, releaseAmount);
    }

    function transferTo(address to, uint amount) external {
        address beneficiary = msg.sender;
        require(releaseVars[beneficiary].amount > 0, 'beneficiary does not exist');
        require(releaseVars[to].amount == 0, 'to is exist');
        require(to != beneficiary, 'same address');
        // release firstly
        releaseInternal(beneficiary);
        // calc locked left amount
        uint lockedLeftAmount = lockedAmount(beneficiary);
        require(lockedLeftAmount >= amount, 'Not enough');
        releaseVars[beneficiary].amount = lockedLeftAmount.sub(amount);
        uint128 startTime = uint128(releaseVars[beneficiary].startTime > block.timestamp ? releaseVars[beneficiary].startTime : block.timestamp);
        releaseVars[beneficiary].startTime = startTime;
        releaseVars[to] = ReleaseVar(amount, startTime, releaseVars[beneficiary].endTime, startTime);
        emit TransferTo(beneficiary, to, amount);
    }

    function releaseAbleAmount(address beneficiary) public view returns (uint256){
        ReleaseVar memory releaseVar = releaseVars[beneficiary];
        require(block.timestamp >= releaseVar.startTime, "not time to unlock");
        require(releaseVar.amount > 0, "beneficiary does not exist");
        uint256 calTime = block.timestamp > releaseVar.endTime ? releaseVar.endTime : block.timestamp;
        return calTime.sub(releaseVar.lastUpdateTime).mul(releaseVar.amount)
        .div(releaseVar.endTime - releaseVar.startTime);
    }

    function lockedAmount(address beneficiary) public view returns (uint256){
        ReleaseVar memory releaseVar = releaseVars[beneficiary];
        require(releaseVar.endTime >= block.timestamp, 'locked end');
        return releaseVar.amount.mul(releaseVar.endTime - releaseVar.lastUpdateTime)
        .div(releaseVar.endTime - releaseVar.startTime);
    }

}
// File: /contracts/gov/OLEToken.sol



// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.7.6;

pragma experimental ABIEncoderV2;

import "../Adminable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";

/// @dev Admin of this contract is the address of Timelock. 
contract OLEToken is Adminable {
    using SafeMath for uint;

    // EIP-20 token name for this token
    string public  name;

    // EIP-20 token symbol for this token
    string public  symbol;

    // EIP-20 token decimals for this token
    uint8 public constant decimals = 18;

    // Total number of tokens in circulation
    uint public totalSupply = 1000000000e18; // 1 billion OLE

    // Allowance amounts on behalf of others
    mapping(address => mapping(address => uint)) internal allowances;

    // Official record of token balances for each account
    mapping(address => uint) internal balances;

    // The standard EIP-20 transfer event
    event Transfer(address indexed from, address indexed to, uint256 amount);

    // The standard EIP-20 approval event
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /**
     * Construct a new OpenLev token
     * @param initAccount The initial account to grant all the tokens
     */
    constructor(address initAccount, address payable _admin, string memory _name, string memory _symbol)  {
        admin = _admin;
        balances[initAccount] = totalSupply;
        name = _name;
        symbol = _symbol;
        emit Transfer(address(0), initAccount, totalSupply);
    }

    function mint(address account, uint amount) external onlyAdmin {
        require(account != address(0), "OLE: mint to the zero address");
        totalSupply = totalSupply.add(amount);
        balances[account] = balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

    function burn(uint amount) external {
        balances[msg.sender] = balances[msg.sender].sub(amount);
        totalSupply = totalSupply.sub(amount);
        emit Transfer(msg.sender, address(0), amount);
    }
    /**
     * Get the number of tokens `spender` is approved to spend on behalf of `account`
     * @param account The address of the account holding the funds
     * @param spender The address of the account spending the funds
     * @return The number of tokens approved
     */
    function allowance(address account, address spender) external view returns (uint) {
        return allowances[account][spender];
    }


    /**
     * Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @return Whether or not the approval succeeded
     */
    function approve(address spender, uint amount) external returns (bool) {
        allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    /**
     * Get the number of tokens held by the `account`
     * @param account The address of the account to get the balance of
     * @return The number of tokens held
     */
    function balanceOf(address account) external view returns (uint) {
        return balances[account];
    }

    /**
     * Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @return Whether or not the transfer succeeded
     */
    function transfer(address dst, uint amount) external returns (bool) {
        _transferTokens(msg.sender, dst, amount);
        return true;
    }

    /**
     * Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @return Whether or not the transfer succeeded
     */
    function transferFrom(address src, address dst, uint amount) external returns (bool) {
        address spender = msg.sender;
        uint spenderAllowance = allowances[src][spender];

        if (spender != src && spenderAllowance != uint(- 1)) {
            allowances[src][spender] = spenderAllowance.sub(amount);
            emit Approval(src, spender, allowances[src][spender]);
        }

        _transferTokens(src, dst, amount);
        return true;
    }


    function _transferTokens(address src, address dst, uint amount) internal {
        require(src != address(0), "Zero src address");
        require(dst != address(0), "Zero dst address");

        balances[src] = balances[src].sub(amount);
        balances[dst] = balances[dst].add(amount);
        emit Transfer(src, dst, amount);
    }

}

// File: /contracts/Adminable.sol



// SPDX-License-Identifier: BUSL-1.1


pragma solidity 0.7.6;

abstract contract Adminable {
    address payable public admin;
    address payable public pendingAdmin;
    address payable public developer;

    event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

    event NewAdmin(address oldAdmin, address newAdmin);
    constructor () {
        developer = msg.sender;
    }

    modifier onlyAdmin() {
        require(msg.sender == admin, "caller must be admin");
        _;
    }
    modifier onlyAdminOrDeveloper() {
        require(msg.sender == admin || msg.sender == developer, "caller must be admin or developer");
        _;
    }

    function setPendingAdmin(address payable newPendingAdmin) external virtual onlyAdmin {
        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;
        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;
        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
    }

    function acceptAdmin() external virtual {
        require(msg.sender == pendingAdmin, "only pendingAdmin can accept admin");
        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;
        // Store admin with value pendingAdmin
        admin = pendingAdmin;
        // Clear the pending value
        pendingAdmin = address(0);
        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
    }

}

// File: @openzeppelin/contracts/math/SafeMath.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}