contract GasDaoTokenLock {
    ERC20 public immutable token;
    uint256 public immutable unlockBegin;
    uint256 public immutable unlockCliff;
    uint256 public immutable unlockEnd;

    mapping(address=>uint256) public lockedAmounts;
    mapping(address=>uint256) public claimedAmounts;

    event Locked(address indexed sender, address indexed recipient, uint256 amount);
    event Claimed(address indexed owner, address indexed recipient, uint256 amount);

    /**
     * @dev Constructor.
     * @param _token The token this contract will lock.l
     * @param _unlockBegin The time at which unlocking of tokens will begin.
     * @param _unlockCliff The first time at which tokens are claimable.
     * @param _unlockEnd The time at which the last token will unlock.
     */
    constructor(ERC20 _token, uint256 _unlockBegin, uint256 _unlockCliff, uint256 _unlockEnd) {
        require(_unlockCliff >= _unlockBegin, "ERC20Locked: Unlock cliff must not be before unlock begin");
        require(_unlockEnd >= _unlockCliff, "ERC20Locked: Unlock end must not be before unlock cliff");
        token = _token;
        unlockBegin = _unlockBegin;
        unlockCliff = _unlockCliff;
        unlockEnd = _unlockEnd;
    }

    /**
     * @dev Returns the maximum number of tokens currently claimable by `owner`.
     * @param owner The account to check the claimable balance of.
     * @return The number of tokens currently claimable.
     */
    function claimableBalance(address owner) public view returns(uint256) {
        if(block.timestamp < unlockCliff) {
            return 0;
        }

        uint256 locked = lockedAmounts[owner];
        uint256 claimed = claimedAmounts[owner];
        if(block.timestamp >= unlockEnd) {
            return locked - claimed;
        }
        return (locked * (block.timestamp - unlockBegin)) / (unlockEnd - unlockBegin) - claimed;
    }

    /**
     * @dev Transfers tokens from the caller to the token lock contract and locks them for benefit of `recipient`.
     *      Requires that the caller has authorised this contract with the token contract.
     * @param recipient The account the tokens will be claimable by.
     * @param amount The number of tokens to transfer and lock.
     */
    function lock(address recipient, uint256 amount) public {
        require(block.timestamp < unlockEnd, "TokenLock: Unlock period already complete");
        lockedAmounts[recipient] += amount;
        require(token.transferFrom(msg.sender, address(this), amount), "TokenLock: Transfer failed");
        emit Locked(msg.sender, recipient, amount);
    }

    /**
     * @dev Claims the caller's tokens that have been unlocked, sending them to `recipient`.
     * @param recipient The account to transfer unlocked tokens to.
     * @param amount The amount to transfer. If greater than the claimable amount, the maximum is transferred.
     */
    function claim(address recipient, uint256 amount) public {
        uint256 claimable = claimableBalance(msg.sender);
        if(amount > claimable) {
            amount = claimable;
        }
        claimedAmounts[msg.sender] += amount;
        require(token.transfer(recipient, amount), "TokenLock: Transfer failed");
        emit Claimed(msg.sender, recipient, amount);
    }
}
/**
 * @dev Time-locks tokens according to an unlock schedule.
 */
function claimableBalance(address owner) public view returns(uint256) {
        if(block.timestamp < unlockCliff) {
            return 0;
        }

        uint256 locked = lockedAmounts[owner];
        uint256 claimed = claimedAmounts[owner];
        if(block.timestamp >= unlockEnd) {
            return locked - claimed;
        }
        return (locked * (block.timestamp - unlockBegin)) / (unlockEnd - unlockBegin) - claimed;
    }
/**
 * @dev Returns the maximum number of tokens currently claimable by `owner`.
 * @param owner The account to check the claimable balance of.
 * @return The number of tokens currently claimable.
 */