账户
0xe8...addf
0xe8...addf

0xe8...addf

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.7.4+commit.3f05b770
语言
Solidity
合同源代码
文件 1 的 1:UniformTimeVault.sol
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity 0.7.4;

library SafeMathLib {
  function times(uint a, uint b) public pure returns (uint) {
    uint c = a * b;
    require(a == 0 || c / a == b, 'Overflow detected');
    return c;
  }

  function minus(uint a, uint b) public pure returns (uint) {
    require(b <= a, 'Underflow detected');
    return a - b;
  }

  function plus(uint a, uint b) public pure returns (uint) {
    uint c = a + b;
    require(c>=a && c>=b, 'Overflow detected');
    return c;
  }

}


/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract UniformTimeVault {
    using SafeMathLib for uint;

    IERC20 public token;
    uint public numTranches = 0;
    uint public startTime = 0;
    uint public endTime = 0;
    uint public vestingPeriodLength = 0;
    uint public totalAllocation = 0;

    struct Tranche {
        uint id;
        address destination;
        uint totalCoins;
        uint currentCoins;
        uint coinsPerSecond;
        uint lastWithdrawalTime;
    }

    mapping (uint => Tranche) public tranches;

    event WithdrawalOccurred(uint trancheId, uint numTokens, uint tokensLeft);
    event TrancheAdded(uint id, address destination, uint totalCoins);

    constructor(address tokenAddr, address[] memory destinations, uint[] memory tokenAllocations, uint _endTime, uint _startTime) {
        token = IERC20(tokenAddr);
        endTime = _endTime;
        startTime = _startTime;
        vestingPeriodLength = endTime - startTime;
        require(vestingPeriodLength > 0 , 'start time must be before end time');

        for (uint i = 0; i < destinations.length; i++)  {
            uint trancheId = i + 1;
            uint coinsPerSecond = tokenAllocations[i] / vestingPeriodLength;
            totalAllocation = totalAllocation.plus(tokenAllocations[i]);
            tranches[trancheId] = Tranche(
                trancheId,
                destinations[i],
                tokenAllocations[i],
                tokenAllocations[i],
                coinsPerSecond,
                _startTime
            );
            emit TrancheAdded(trancheId, destinations[i], tokenAllocations[i]);
        }
        numTranches = destinations.length;
    }

    function withdraw(uint trancheId) external {
        Tranche storage tranche = tranches[trancheId];
        require(tranche.currentCoins >  0, 'No coins left to withdraw');
        uint currentWithdrawal = 0;

        // if after vesting period ends, give them the remaining coins
        if (block.timestamp >= endTime) {
            currentWithdrawal = tranche.currentCoins;
        } else {
            // compute allowed withdrawal
            currentWithdrawal = (block.timestamp.minus(tranche.lastWithdrawalTime)).times(tranche.coinsPerSecond);
        }

        // update struct
        tranche.currentCoins = tranche.currentCoins.minus(currentWithdrawal);
        tranche.lastWithdrawalTime = block.timestamp;

        // transfer the tokens, brah
        token.transfer(tranche.destination, currentWithdrawal);
        emit WithdrawalOccurred(trancheId, currentWithdrawal, tranche.currentCoins);
    }

    function changeDestination(uint trancheId, address newDestination) external {
        Tranche storage tranche = tranches[trancheId];
        require(tranche.destination == msg.sender, 'Can only change destination if you are the destination');
        tranche.destination = newDestination;
    }
}
设置
{
  "compilationTarget": {
    "UniformTimeVault.sol": "UniformTimeVault"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"tokenAddr","type":"address"},{"internalType":"address[]","name":"destinations","type":"address[]"},{"internalType":"uint256[]","name":"tokenAllocations","type":"uint256[]"},{"internalType":"uint256","name":"_endTime","type":"uint256"},{"internalType":"uint256","name":"_startTime","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"destination","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalCoins","type":"uint256"}],"name":"TrancheAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"trancheId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numTokens","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokensLeft","type":"uint256"}],"name":"WithdrawalOccurred","type":"event"},{"inputs":[{"internalType":"uint256","name":"trancheId","type":"uint256"},{"internalType":"address","name":"newDestination","type":"address"}],"name":"changeDestination","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTranches","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tranches","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"address","name":"destination","type":"address"},{"internalType":"uint256","name":"totalCoins","type":"uint256"},{"internalType":"uint256","name":"currentCoins","type":"uint256"},{"internalType":"uint256","name":"coinsPerSecond","type":"uint256"},{"internalType":"uint256","name":"lastWithdrawalTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingPeriodLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"trancheId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]