编译器
0.8.13+commit.abaa5c0e
文件 1 的 4:FixedVesting.sol
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./TransferHelper.sol";
import "./IFixedCreator.sol";
contract FixedVesting{
address public immutable creator = msg.sender;
address public owner = tx.origin;
bool private initialized;
bool public isPaused;
uint128 public vestingLength;
uint128 public sold;
address public token;
address[] public buyers;
struct Detail{
uint128 datetime;
uint128 ratio_d2;
}
struct Bought{
uint128 buyerIndex;
uint128 purchased;
uint128 completed_d2;
uint128 claimed;
}
mapping(address => Bought) public invoice;
mapping(uint128 => Detail) public vesting;
modifier onlyOwner{
require(msg.sender == owner, "!owner");
_;
}
function initialize(
address _token,
uint128[] calldata _datetime,
uint128[] calldata _ratio_d2
) external {
require(!initialized, "Initialized");
require(msg.sender == creator, "!creator");
_setToken(_token);
_newVesting(_datetime, _ratio_d2);
initialized = true;
}
function getBuyerLength() external view returns (uint){
return buyers.length;
}
function vestingRunning() public view returns(uint128 round, uint128 totalPercent_d2){
uint128 vestingSize = vestingLength;
uint128 total;
for(uint128 i=1; i<=vestingSize; i++){
Detail memory temp = vesting[i];
total += temp.ratio_d2;
if( (temp.datetime <= block.timestamp && block.timestamp <= vesting[i+1].datetime) ||
(i == vestingSize && block.timestamp >= temp.datetime)
){
round = i;
totalPercent_d2 = total;
break;
}
}
}
function claimToken() external {
(uint128 round, uint128 totalPercent_d2) = vestingRunning();
Bought memory temp = invoice[msg.sender];
require(!isPaused && round > 0 && token != address(0), "!started");
require(temp.purchased > 0, "!buyer");
require(temp.completed_d2 < totalPercent_d2, "claimed");
uint128 amountToClaim;
if(temp.completed_d2 == 0){
amountToClaim = (temp.purchased * totalPercent_d2) / 10000;
} else{
amountToClaim = ((temp.claimed * totalPercent_d2) / temp.completed_d2) - temp.claimed;
}
require(IERC20(token).balanceOf(address(this)) >= amountToClaim && amountToClaim > 0, "insufficient");
invoice[msg.sender].completed_d2 = totalPercent_d2;
invoice[msg.sender].claimed = temp.claimed + amountToClaim;
TransferHelper.safeTransfer(address(token), msg.sender, amountToClaim);
}
function _setToken(address _token) private {
token = _token;
}
function _newVesting(
uint128[] calldata _datetime,
uint128[] calldata _ratio_d2
) private {
require(_datetime.length == _ratio_d2.length, "!good");
uint128 vestingSize = vestingLength;
for(uint128 i=0; i<_datetime.length; i++){
if(i != _datetime.length-1) require(_datetime[i] < _datetime[i+1], "!good");
vestingSize += 1;
vesting[vestingSize] = Detail(_datetime[i], _ratio_d2[i]);
}
vestingLength = vestingSize;
}
function newBuyers(address[] calldata _buyer, uint128[] calldata _purchased) external onlyOwner {
require(_buyer.length == _purchased.length, "!good");
for(uint16 i=0; i<_buyer.length; i++){
if(_buyer[i] == address(0) || _purchased[i] == 0) continue;
Bought memory temp = invoice[_buyer[i]];
if(temp.purchased == 0){
buyers.push(_buyer[i]);
invoice[_buyer[i]].buyerIndex = uint128(buyers.length - 1);
}
invoice[_buyer[i]].purchased = temp.purchased + _purchased[i];
sold += _purchased[i];
}
}
function replaceBuyers(address[] calldata _oldBuyer, address[] calldata _newBuyer) external onlyOwner {
require(_oldBuyer.length == _newBuyer.length && buyers.length > 0, "!good");
for(uint16 i=0; i<_oldBuyer.length; i++){
Bought memory temp = invoice[_oldBuyer[i]];
if( temp.purchased == 0 ||
_oldBuyer[i] == address(0) ||
_newBuyer[i] == address(0)
) continue;
buyers[temp.buyerIndex] = _newBuyer[i];
invoice[_newBuyer[i]] = temp;
delete invoice[_oldBuyer[i]];
}
}
function removeBuyers(address[] calldata _buyer) external onlyOwner {
require(buyers.length > 0, "!good");
for(uint16 i=0; i<_buyer.length; i++){
Bought memory temp = invoice[_buyer[i]];
if(temp.purchased == 0 || _buyer[i] == address(0)) continue;
sold -= temp.purchased;
address addressToMove = buyers[buyers.length-1];
buyers[temp.buyerIndex] = addressToMove;
invoice[addressToMove].buyerIndex = temp.buyerIndex;
buyers.pop();
delete invoice[_buyer[i]];
}
}
function replacePurchases(address[] calldata _buyer, uint128[] calldata _newPurchased) external onlyOwner {
require(_buyer.length == _newPurchased.length && buyers.length > 0, "!good");
for(uint16 i=0; i<_buyer.length; i++){
Bought memory temp = invoice[_buyer[i]];
if( temp.purchased == 0 ||
temp.completed_d2 > 0 ||
_buyer[i] == address(0) ||
_newPurchased[i] == 0) continue;
sold = sold - temp.purchased + _newPurchased[i];
invoice[_buyer[i]].purchased = _newPurchased[i];
}
}
function updateVestingDatetimes(uint128[] calldata _vestingRound, uint128[] calldata _newDatetime) external onlyOwner {
require(_vestingRound.length == _newDatetime.length, "!good");
(uint128 round, ) = vestingRunning();
uint128 vestingSize = vestingLength;
for(uint128 i=0; i<_vestingRound.length; i++){
if( _vestingRound[i] > vestingSize ||
round >= _vestingRound[i]) continue;
vesting[_vestingRound[i]].datetime = _newDatetime[i];
}
}
function updateVestingRatios(uint128[] calldata _vestingRound, uint128[] calldata _newRatio_d2) external onlyOwner {
require(_vestingRound.length == _newRatio_d2.length, "!good");
(uint128 round, ) = vestingRunning();
uint128 vestingSize = vestingLength;
for(uint128 i=0; i<_vestingRound.length; i++){
if(_vestingRound[i] > vestingSize ||
round >= _vestingRound[i]) continue;
vesting[_vestingRound[i]].ratio_d2 = _newRatio_d2[i];
}
}
function newVesting(
uint128[] calldata _datetime,
uint128[] calldata _ratio_d2
) external onlyOwner {
_newVesting(_datetime, _ratio_d2);
}
function removeLastVestingRound() external onlyOwner {
uint128 vestingSizeTarget = vestingLength-1;
delete vesting[vestingSizeTarget];
vestingLength = vestingSizeTarget;
}
function emergencyWithdraw(address _target, uint128 _amount) external onlyOwner {
require(_target != address(0), "!good");
uint128 contractBalance = uint128(IERC20(token).balanceOf(address(this)));
if(_amount > contractBalance) _amount = contractBalance;
TransferHelper.safeTransfer(address(token), _target, _amount);
}
function setToken(address _token) external onlyOwner {
_setToken(_token);
}
function togglePause() external onlyOwner {
isPaused = !isPaused;
}
function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "!good");
owner = _newOwner;
}
}
文件 2 的 4: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 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);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 3 的 4:IFixedCreator.sol
pragma solidity ^0.8.13;
interface IFixedCreator{
event VestingCreated(address indexed vesting, uint index);
function owner() external view returns (address);
function allVestingsLength() external view returns(uint);
function allVestings(uint) external view returns(address);
function createVesting(address, uint128[] calldata, uint128[] calldata) external returns (address);
function transferOwnership(address) external;
}
文件 4 的 4:TransferHelper.sol
pragma solidity ^0.8.13;
library TransferHelper {
function safeApprove(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: APPROVE_FAILED");
}
function safeTransfer(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FAILED");
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "TransferHelper: TRANSFER_FROM_FAILED");
}
function safeTransferETH(address to, uint value) internal {
(bool success,) = to.call{value:value}(new bytes(0));
require(success, "TransferHelper: ETH_TRANSFER_FAILED");
}
}
{
"compilationTarget": {
"contracts/FixedVesting.sol": "FixedVesting"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"buyers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"uint128","name":"_amount","type":"uint128"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBuyerLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint128[]","name":"_datetime","type":"uint128[]"},{"internalType":"uint128[]","name":"_ratio_d2","type":"uint128[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"invoice","outputs":[{"internalType":"uint128","name":"buyerIndex","type":"uint128"},{"internalType":"uint128","name":"purchased","type":"uint128"},{"internalType":"uint128","name":"completed_d2","type":"uint128"},{"internalType":"uint128","name":"claimed","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_buyer","type":"address[]"},{"internalType":"uint128[]","name":"_purchased","type":"uint128[]"}],"name":"newBuyers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128[]","name":"_datetime","type":"uint128[]"},{"internalType":"uint128[]","name":"_ratio_d2","type":"uint128[]"}],"name":"newVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_buyer","type":"address[]"}],"name":"removeBuyers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"removeLastVestingRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_oldBuyer","type":"address[]"},{"internalType":"address[]","name":"_newBuyer","type":"address[]"}],"name":"replaceBuyers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_buyer","type":"address[]"},{"internalType":"uint128[]","name":"_newPurchased","type":"uint128[]"}],"name":"replacePurchases","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"setToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sold","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128[]","name":"_vestingRound","type":"uint128[]"},{"internalType":"uint128[]","name":"_newDatetime","type":"uint128[]"}],"name":"updateVestingDatetimes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128[]","name":"_vestingRound","type":"uint128[]"},{"internalType":"uint128[]","name":"_newRatio_d2","type":"uint128[]"}],"name":"updateVestingRatios","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"","type":"uint128"}],"name":"vesting","outputs":[{"internalType":"uint128","name":"datetime","type":"uint128"},{"internalType":"uint128","name":"ratio_d2","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingLength","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingRunning","outputs":[{"internalType":"uint128","name":"round","type":"uint128"},{"internalType":"uint128","name":"totalPercent_d2","type":"uint128"}],"stateMutability":"view","type":"function"}]