contract TokenTimeLock {
    /**
     * Create new Token Time Lock with given donation address.
     *
     * @param _donationAddress donation address
     */
    function TokenTimeLock (address _donationAddress) public {
        donationAddress = _donationAddress;
    }

    /**
     * Lock given amount of given EIP-20 tokens until given time arrives, after
     * this time allow the tokens to be transferred to given beneficiary.  This
     * contract should be allowed to transfer at least given amount of tokens
     * from msg.sender.
     *
     * @param _token EIP-20 token contract managing tokens to be locked
     * @param _beneficiary beneficiary to receive tokens after unlock time
     * @param _amount amount of tokens to be locked
     * @param _unlockTime unlock time
     *
     * @return time lock ID
     */
    function lock (
        Token _token, address _beneficiary, uint256 _amount,
        uint256 _unlockTime) public returns (uint256) {
        require (_amount > 0);

        uint256 id = nextLockID++;

        TokenTimeLockInfo storage lockInfo = locks [id];

        lockInfo.token = _token;
        lockInfo.beneficiary = _beneficiary;
        lockInfo.amount = _amount;
        lockInfo.unlockTime = _unlockTime;

        Lock (id, _token, _beneficiary, _amount, _unlockTime);

        require (_token.transferFrom (msg.sender, this, _amount));

        return id;
    }

    /**
     * Unlock tokens locked under time lock with given ID and transfer them to
     * corresponding beneficiary.
     *
     * @param _id time lock ID to unlock tokens locked under
     */
    function unlock (uint256 _id) public {
        TokenTimeLockInfo memory lockInfo = locks [_id];
        delete locks [_id];

        require (lockInfo.amount > 0);
        require (lockInfo.unlockTime <= block.timestamp);

        Unlock (_id);

        require (
            lockInfo.token.transfer (
                lockInfo.beneficiary, lockInfo.amount));
    }

    /**
     * If you like this contract, you may send some ether to this address and
     * it will be used to develop more useful contracts available to everyone.
     */
    address public donationAddress;

    /**
     * Next time lock ID to be used.
     */
    uint256 private nextLockID = 0;

    /**
     * Maps time lock ID to TokenTimeLockInfo structure encapsulating time lock
     * information.
     */
    mapping (uint256 => TokenTimeLockInfo) public locks;

    /**
     * Encapsulates information abount time lock.
     */
    struct TokenTimeLockInfo {
        /**
         * EIP-20 token contract managing locked tokens.
         */
        Token token;

        /**
         * Beneficiary to receive tokens once they are unlocked.
         */
        address beneficiary;

        /**
         * Amount of locked tokens.
         */
        uint256 amount;

        /**
         * Unlock time.
         */
        uint256 unlockTime;
    }

    /**
     * Logged when tokens were time locked.
     *
     * @param id time lock ID
     * @param token EIP-20 token contract managing locked tokens
     * @param beneficiary beneficiary to receive tokens once they are unlocked
     * @param amount amount of locked tokens
     * @param unlockTime unlock time
     */
    event Lock (
        uint256 indexed id, Token indexed token, address indexed beneficiary,
        uint256 amount, uint256 unlockTime);

    /**
     * Logged when tokens were unlocked and sent to beneficiary.
     *
     * @param id time lock ID
     */
    event Unlock (uint256 indexed id);
}
/**
 * Allows one to lock EIP-20 tokens until certain time arrives.
 * Copyright © 2018 by ABDK Consulting https://abdk.consulting/
 * Author: Mikhail Vladimirov <mikhail.vladimirov[at]gmail.com>
 */
function unlock (uint256 _id) public {
    TokenTimeLockInfo memory lockInfo = locks [_id];
    delete locks [_id];

    require (lockInfo.amount > 0);
    require (lockInfo.unlockTime <= block.timestamp);

    Unlock (_id);

    require (
        lockInfo.token.transfer (
            lockInfo.beneficiary, lockInfo.amount));
}

/**
 * Unlock tokens locked under time lock with given ID and transfer them to
 * corresponding beneficiary.
 *
 * @param _id time lock ID to unlock tokens locked under
 */