编译器
0.8.17+commit.8df45f5f
文件 1 的 6: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;
}
}
文件 2 的 6:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, 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 from,
address to,
uint256 amount
) external returns (bool);
}
文件 3 的 6:ITokenLockup.sol
pragma solidity ^0.8.17;
interface ITokenLockup {
struct Schedule {
uint256 endTime;
uint256 portion;
}
event Fund(
address indexed recipient,
uint256 amount
);
event Claim(
address indexed recipient,
uint256 claimed
);
event Reclaim(
address indexed recipient,
uint256 claimed
);
event ChangeRecipient(
address indexed oldRecipient,
address indexed newRecipient
);
function token() external view returns (address);
function startTime() external view returns (uint256);
function claimDelay() external view returns (uint256);
function schedule(uint256 index) external view returns (uint256, uint256);
function initialLocked(address account) external view returns (uint256);
function totalClaimed(address account) external view returns (uint256);
function initialLockedSupply() external view returns (uint256);
function unallocatedSupply() external view returns (uint256);
function addTokens(uint256 amount) external;
function fund(address[] calldata recipients, uint256[] calldata amounts) external;
function claim() external;
function changeRecipient(address newRecipient) external;
function unlockedSupply() external view returns (uint256);
function lockedSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function unlockedOf(address account) external view returns (uint256);
function lockedOf(address account) external view returns (uint256);
}
文件 4 的 6:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1);
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
文件 5 的 6: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() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 6 的 6:TokenLockup.sol
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./interfaces/ITokenLockup.sol";
contract TokenLockup is Ownable, ITokenLockup {
address public token;
uint256 public startTime;
uint256 public claimDelay;
Schedule[] public schedule;
mapping(address => uint256) public initialLocked;
mapping(address => uint256) public totalClaimed;
uint256 public initialLockedSupply;
uint256 public unallocatedSupply;
uint256 private constant INVERSE_BASIS_POINTS = 10_000;
constructor(
address _token,
uint256 _startTime,
uint256 _claimDelay,
Schedule[] memory _schedule
) {
require(_startTime >= block.timestamp, 'Invalid startTime');
token = _token;
startTime = _startTime;
claimDelay = _claimDelay;
uint256 scheduleLength = _schedule.length;
uint256 totalPortion;
for (uint256 i; i < scheduleLength; i++) {
totalPortion += _schedule[i].portion;
if (i != 0) {
require(_schedule[i-1].endTime < _schedule[i].endTime, 'Invalid schedule times');
}
schedule.push(_schedule[i]);
}
require(totalPortion == INVERSE_BASIS_POINTS, 'Invalid schedule portions');
}
function addTokens(uint256 amount) external onlyOwner {
require(IERC20(token).transferFrom(msg.sender, address(this), amount));
unallocatedSupply += amount;
}
function fund(address[] calldata recipients, uint256[] calldata amounts)
external
onlyOwner
{
require(recipients.length == amounts.length);
uint256 _totalAmount = 0;
for (uint i; i < amounts.length; ++i) {
uint256 amount = amounts[i];
address recipient = recipients[i];
if (recipient == address(0)) {
break;
}
_totalAmount += amount;
initialLocked[recipient] += amount;
emit Fund(recipient, amount);
}
initialLockedSupply += _totalAmount;
unallocatedSupply -= _totalAmount;
}
function claim() external {
require(block.timestamp > startTime + claimDelay, "Claiming is not available yet");
uint256 claimable = _totalUnlockedOf(msg.sender) - totalClaimed[msg.sender];
totalClaimed[msg.sender] += claimable;
require(IERC20(token).transfer(msg.sender, claimable));
emit Claim(msg.sender, claimable);
}
function changeRecipient(address newRecipient) external {
require(newRecipient != msg.sender, "newRecipient must not be msg.sender");
uint256 _initialLocked = initialLocked[msg.sender];
uint256 _totalClaimed = totalClaimed[msg.sender];
initialLocked[msg.sender] = 0;
totalClaimed[msg.sender] = 0;
initialLocked[newRecipient] += _initialLocked;
totalClaimed[newRecipient] += _totalClaimed;
emit ChangeRecipient(msg.sender, newRecipient);
}
function balanceOf(address account) external view returns (uint256) {
return _totalUnlockedOf(account) - totalClaimed[account];
}
function unlockedOf(address account) external view returns (uint256) {
return _totalUnlockedOf(account);
}
function lockedOf(address account) external view returns (uint256) {
return initialLocked[account] - _totalUnlockedOf(account);
}
function unlockedSupply() external view returns (uint256) {
return _totalUnlocked();
}
function lockedSupply() external view returns (uint256) {
return initialLockedSupply - _totalUnlocked();
}
function _totalUnlockedOf(address account) internal view returns (uint256) {
uint256 locked = initialLocked[account];
return _computeUnlocked(locked, block.timestamp);
}
function _totalUnlocked() internal view returns (uint256) {
uint256 locked = initialLockedSupply;
return _computeUnlocked(locked, block.timestamp);
}
function _computeUnlocked(uint256 locked, uint256 time) internal view returns (uint256) {
uint256 start = startTime;
if (time < start) {
return 0;
}
uint256 unlocked;
uint256 scheduleLength = schedule.length;
for (uint i; i < scheduleLength; i++) {
uint256 portion = schedule[i].portion;
uint256 end = schedule[i].endTime;
if (time < end) {
unlocked += locked * (time - start) * portion / ((end - start) * INVERSE_BASIS_POINTS);
break;
} else {
unlocked += locked * portion / INVERSE_BASIS_POINTS;
start = end;
}
}
return unlocked;
}
}
{
"compilationTarget": {
"contracts/TokenLockup.sol": "TokenLockup"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_startTime","type":"uint256"},{"internalType":"uint256","name":"_claimDelay","type":"uint256"},{"components":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"portion","type":"uint256"}],"internalType":"struct ITokenLockup.Schedule[]","name":"_schedule","type":"tuple[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newRecipient","type":"address"}],"name":"ChangeRecipient","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimed","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Fund","type":"event"},{"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":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimed","type":"uint256"}],"name":"Reclaim","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newRecipient","type":"address"}],"name":"changeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"recipients","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"fund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"initialLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialLockedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"lockedOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedSupply","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":"uint256","name":"","type":"uint256"}],"name":"schedule","outputs":[{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"portion","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"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":[],"name":"unallocatedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unlockedOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlockedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]