文件 1 的 7:Address.sol
pragma solidity ^0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 7:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 3 的 7:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 4 的 7:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_setOwner(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 5 的 7:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 6 的 7:SafeMath.sol
pragma solidity >=0.8.4;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a + b) >= b, "SafeMath: Add Overflow");
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a - b) <= a, "SafeMath: Underflow");
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "SafeMath: Mul Overflow");
}
function to128(uint256 a) internal pure returns (uint128 c) {
require(a <= type(uint128).max, "SafeMath: uint128 Overflow");
c = uint128(a);
}
function to64(uint256 a) internal pure returns (uint64 c) {
require(a <= type(uint64).max, "SafeMath: uint64 Overflow");
c = uint64(a);
}
function to32(uint256 a) internal pure returns (uint32 c) {
require(a <= type(uint32).max, "SafeMath: uint32 Overflow");
c = uint32(a);
}
}
library SafeMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a + b) >= b, "SafeMath: Add Overflow");
}
function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a - b) <= a, "SafeMath: Underflow");
}
}
library SafeMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a + b) >= b, "SafeMath: Add Overflow");
}
function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a - b) <= a, "SafeMath: Underflow");
}
}
library SafeMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a + b) >= b, "SafeMath: Add Overflow");
}
function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a - b) <= a, "SafeMath: Underflow");
}
}
文件 7 的 7:TokenVesting.sol
pragma solidity >=0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./libraries/SafeMath.sol";
contract TokenVesting is Ownable {
using SafeMath for uint256;
using SafeMath128 for uint128;
using SafeMath64 for uint64;
using SafeMath32 for uint32;
using SafeERC20 for IERC20;
struct VestingSchedule {
uint128 amount;
uint128 claimed;
uint64 startTime;
uint32 vestingPeriod;
uint32 cliff;
uint128 initialRelease;
}
IERC20 private immutable _token;
uint256 private _totalAlloc;
uint256 private _totalClaimed;
bool private _claimAllowed;
mapping(address => VestingSchedule) private _vestingSchedules;
event VestingAdded(address payee, uint256 amount);
event TokensClaimed(address payee, uint256 amount);
event VestingRevoked(address payee);
constructor(address token) {
_token = IERC20(token);
_claimAllowed = false;
}
function vestingSchedule(address _payee)
public
view
returns (
uint128,
uint128,
uint64,
uint32,
uint32,
uint128
)
{
VestingSchedule memory v = _vestingSchedules[_payee];
return (v.amount, v.claimed, v.startTime, v.vestingPeriod, v.cliff, v.initialRelease);
}
function totalAlloc() public view returns (uint256) {
return _totalAlloc;
}
function totalClaimed() public view returns (uint256) {
return _totalClaimed;
}
function claimAllowed() public view returns (bool) {
return _claimAllowed;
}
function setClaimAllowed(bool allowed) external onlyOwner {
_claimAllowed = allowed;
}
function addVestingSchedules(
address[] calldata _payees,
uint256[] calldata _amounts,
uint64[] calldata _startTimes,
uint32[] calldata _vestingPeriods,
uint32[] calldata _cliffs,
uint128[] calldata _initialReleases
) external onlyOwner {
require(_payees.length == _amounts.length, "TokenVesting: payees and amounts length mismatch");
require(_payees.length == _startTimes.length, "TokenVesting: payees and startTimes length mismatch");
require(_payees.length == _vestingPeriods.length, "TokenVesting: payees and vestingPeriods length mismatch");
require(_payees.length == _cliffs.length, "TokenVesting: payees and cliffs length mismatch");
require(_payees.length == _initialReleases.length, "TokenVesting: payees and initialReleases length mismatch");
for (uint256 i = 0; i < _payees.length; i++) {
_addVestingSchedule(
_payees[i],
_amounts[i],
_startTimes[i],
_vestingPeriods[i],
_cliffs[i],
_initialReleases[i]
);
}
}
function _addVestingSchedule(
address _payee,
uint256 _amount,
uint64 _startTime,
uint32 _vestingPeriod,
uint32 _cliff,
uint128 _initialRelease
) private {
require(_payee != address(0), "TokenVesting: payee is the zero address");
require(_amount > 0, "TokenVesting: amount is 0");
require(_vestingSchedules[_payee].amount == 0, "TokenVesting: payee already has a vesting schedule");
require(_vestingPeriod > 0, "TokenVesting: total period is 0");
require(_cliff <= _vestingPeriod, "TokenVesting: vestingPeriod is less than cliff");
require(_initialRelease < _amount, "TokenVesting: initial release is larger than total alloc");
require(
_initialRelease < (_amount * _cliff) / _vestingPeriod,
"TokenVesting: initial release is larger than cliff alloc"
);
_vestingSchedules[_payee] = VestingSchedule({
amount: _amount.to128(),
claimed: 0,
startTime: _startTime,
vestingPeriod: _vestingPeriod,
cliff: _cliff,
initialRelease: _initialRelease
});
_totalAlloc = _totalAlloc.add(_amount);
emit VestingAdded(_payee, _amount);
}
function revokeVestingSchedule(address _payee) external onlyOwner {
VestingSchedule memory v = _vestingSchedules[_payee];
require(v.amount > 0, "TokenVesting: payee does not exist");
uint256 remainingAmount = v.amount.sub(v.claimed);
_totalAlloc = _totalAlloc.sub(remainingAmount);
delete _vestingSchedules[_payee];
emit VestingRevoked(_payee);
}
function claim(uint256 _amount) external {
require(_claimAllowed == true, "TokenVesting: claim is disabled");
require(_amount <= _token.balanceOf(address(this)), "TokenVesting: contract does not have enough funds");
address payee = msg.sender;
VestingSchedule storage v = _vestingSchedules[payee];
require(v.amount > 0, "TokenVesting: not vested address");
uint256 claimableTokens = claimableAmount(payee);
require(claimableTokens > 0, "TokenVesting: no vested funds");
require(_amount <= claimableTokens, "TokenVesting: cannot claim larger than total vested amount");
v.claimed = v.claimed.add(_amount.to128());
_totalClaimed = _totalClaimed.add(_amount);
_token.safeTransfer(payee, _amount);
emit TokensClaimed(payee, _amount);
}
function claimableAmount(address _payee) public view returns (uint256) {
VestingSchedule memory v = _vestingSchedules[_payee];
if (block.timestamp < v.startTime) {
return 0;
}
uint256 vestedPeriod = block.timestamp.sub(v.startTime);
uint256 vestedAmount;
if (vestedPeriod < v.cliff) {
vestedAmount = v.initialRelease;
} else if (vestedPeriod > v.vestingPeriod) {
vestedAmount = v.amount;
} else {
vestedAmount = (v.amount * vestedPeriod) / v.vestingPeriod;
}
if (vestedAmount < v.claimed) {
return 0;
}
return vestedAmount.sub(v.claimed);
}
function withdraw(uint256 _amount) external onlyOwner {
require(_amount < _token.balanceOf(address(this)), "TokenVesting: withdraw amount larger than balance");
_token.safeTransfer(owner(), _amount);
}
function withdrawAll() external onlyOwner {
_token.safeTransfer(owner(), _token.balanceOf(address(this)));
}
}
{
"compilationTarget": {
"contracts/TokenVesting.sol": "TokenVesting"
},
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payee","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payee","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VestingAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payee","type":"address"}],"name":"VestingRevoked","type":"event"},{"inputs":[{"internalType":"address[]","name":"_payees","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"uint64[]","name":"_startTimes","type":"uint64[]"},{"internalType":"uint32[]","name":"_vestingPeriods","type":"uint32[]"},{"internalType":"uint32[]","name":"_cliffs","type":"uint32[]"},{"internalType":"uint128[]","name":"_initialReleases","type":"uint128[]"}],"name":"addVestingSchedules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_payee","type":"address"}],"name":"claimableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_payee","type":"address"}],"name":"revokeVestingSchedule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setClaimAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAlloc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_payee","type":"address"}],"name":"vestingSchedule","outputs":[{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]