文件 1 的 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 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);
}
文件 2 的 4:IStripToken.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IStripToken is IERC20 {
function decimals() external view returns (uint256);
function setMultiSigAdminAddress(address) external;
function recoverERC20(address, uint256) external;
}
文件 3 的 4:PrivateVesting.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./interfaces/IStripToken.sol";
contract PrivateVesting {
using SafeMath for uint256;
struct VestingSchedule {
uint256 totalAmount;
uint256 amountWithdrawn;
uint256 startTime;
}
address private owner;
address private presaleContract;
address payable public multiSigAdmin;
mapping(address => VestingSchedule) public recipients;
uint256 constant MAX_UINT256 = type(uint256).max;
uint256 constant TOTAL_SUPPLY = 500e27;
uint256 constant UNLOCK_UNIT = 10;
uint256 constant INITIAL_LOCK_PERIOD = 45 days;
uint256 public vestingAllocation;
uint256 private totalAllocated;
event VestingScheduleRegistered(address registeredAddress, uint256 totalAmount);
event VestingSchedulesRegistered(address[] registeredAddresses, uint256[] totalAmounts);
event MultiSigAdminUpdated(address _multiSigAdmin);
IStripToken public stripToken;
modifier onlyOwner() {
require(owner == msg.sender, "Requires Owner Role");
_;
}
modifier onlyMultiSigAdmin() {
require(msg.sender == multiSigAdmin || presaleContract == msg.sender, "Should be multiSig contract");
_;
}
constructor(
address _stripToken,
address _presaleContract,
address payable _multiSigAdmin
) {
owner = msg.sender;
stripToken = IStripToken(_stripToken);
presaleContract = _presaleContract;
multiSigAdmin = _multiSigAdmin;
vestingAllocation = TOTAL_SUPPLY;
stripToken.approve(presaleContract, MAX_UINT256);
}
function setMultiSigAdminAddress(address payable _multiSigAdmin) external onlyOwner {
require(_multiSigAdmin != address(0x00));
multiSigAdmin = _multiSigAdmin;
emit MultiSigAdminUpdated(multiSigAdmin);
}
function setPresaleContractAddress(address _presaleContract) external onlyOwner {
require(_presaleContract != address(0x00));
presaleContract = _presaleContract;
stripToken.approve(_presaleContract, MAX_UINT256);
}
function setVestingAllocation(uint256 _newAlloc) external onlyOwner {
require(_newAlloc <= TOTAL_SUPPLY, "setVestingAllocation: Exceeds total supply");
vestingAllocation = _newAlloc;
}
function addRecipient(
address _recipient,
uint256 _totalAmount,
bool isPresaleBuyer
) private {
require(_recipient != address(0x00), "addRecipient: Invalid recipient address");
require(_totalAmount > 0, "addRecipient: Cannot vest 0");
require(!isPresaleBuyer || (isPresaleBuyer && recipients[_recipient].totalAmount == 0), "addRecipient: Already allocated");
require(
totalAllocated.sub(recipients[_recipient].totalAmount).add(_totalAmount) <= vestingAllocation,
"addRecipient: Total Allocation Overflow"
);
totalAllocated = totalAllocated.sub(recipients[_recipient].totalAmount).add(_totalAmount);
recipients[_recipient] = VestingSchedule({totalAmount: _totalAmount, amountWithdrawn: 0, startTime: block.timestamp});
}
function addNewRecipient(
address _newRecipient,
uint256 _totalAmount,
bool isPresaleBuyer
) external onlyMultiSigAdmin {
addRecipient(_newRecipient, _totalAmount, isPresaleBuyer);
emit VestingScheduleRegistered(_newRecipient, _totalAmount);
}
function addNewRecipients(
address[] memory _newRecipients,
uint256[] memory _totalAmounts,
bool isPresaleBuyer
) external onlyMultiSigAdmin {
for (uint256 i = 0; i < _newRecipients.length; i++) {
addRecipient(_newRecipients[i], _totalAmounts[i], isPresaleBuyer);
}
emit VestingSchedulesRegistered(_newRecipients, _totalAmounts);
}
function getLocked(address beneficiary) external view returns (uint256) {
return recipients[beneficiary].totalAmount.sub(getVested(beneficiary));
}
function getWithdrawable(address beneficiary) public view returns (uint256) {
return getVested(beneficiary).sub(recipients[beneficiary].amountWithdrawn);
}
function withdrawToken(address _recipient) external returns (uint256) {
VestingSchedule storage _vestingSchedule = recipients[msg.sender];
if (_vestingSchedule.totalAmount == 0) return 0;
uint256 _vested = getVested(msg.sender);
uint256 _withdrawable = _vested.sub(recipients[msg.sender].amountWithdrawn);
_vestingSchedule.amountWithdrawn = _vested;
require(_withdrawable > 0, "withdraw: Nothing to withdraw");
require(stripToken.transfer(_recipient, _withdrawable));
return _withdrawable;
}
function getVested(address beneficiary) public view virtual returns (uint256 _amountVested) {
require(beneficiary != address(0x00), "getVested: Invalid address");
VestingSchedule memory _vestingSchedule = recipients[beneficiary];
if (
(_vestingSchedule.totalAmount == 0) ||
(block.timestamp < _vestingSchedule.startTime) ||
(block.timestamp < _vestingSchedule.startTime.add(INITIAL_LOCK_PERIOD))
) {
return 0;
}
uint256 vestedPercent = 0;
uint256 firstVestingPoint = _vestingSchedule.startTime.add(INITIAL_LOCK_PERIOD);
uint256 vestingPeriod = 270 days;
uint256 secondVestingPoint = firstVestingPoint.add(vestingPeriod);
if (block.timestamp > firstVestingPoint && block.timestamp <= secondVestingPoint) {
vestedPercent = 10 + (block.timestamp - firstVestingPoint).mul(90).div(vestingPeriod);
} else if (block.timestamp > secondVestingPoint) {
vestedPercent = 100;
}
uint256 vestedAmount = _vestingSchedule.totalAmount.mul(vestedPercent).div(100);
if (vestedAmount > _vestingSchedule.totalAmount) {
return _vestingSchedule.totalAmount;
}
return vestedAmount;
}
}
文件 4 的 4:SafeMath.sol
pragma solidity ^0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
{
"compilationTarget": {
"contracts/PrivateVesting.sol": "PrivateVesting"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_stripToken","type":"address"},{"internalType":"address","name":"_presaleContract","type":"address"},{"internalType":"address payable","name":"_multiSigAdmin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_multiSigAdmin","type":"address"}],"name":"MultiSigAdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"registeredAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"VestingScheduleRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"registeredAddresses","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"totalAmounts","type":"uint256[]"}],"name":"VestingSchedulesRegistered","type":"event"},{"inputs":[{"internalType":"address","name":"_newRecipient","type":"address"},{"internalType":"uint256","name":"_totalAmount","type":"uint256"},{"internalType":"bool","name":"isPresaleBuyer","type":"bool"}],"name":"addNewRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_newRecipients","type":"address[]"},{"internalType":"uint256[]","name":"_totalAmounts","type":"uint256[]"},{"internalType":"bool","name":"isPresaleBuyer","type":"bool"}],"name":"addNewRecipients","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"getLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"getVested","outputs":[{"internalType":"uint256","name":"_amountVested","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"}],"name":"getWithdrawable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiSigAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"recipients","outputs":[{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"_multiSigAdmin","type":"address"}],"name":"setMultiSigAdminAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_presaleContract","type":"address"}],"name":"setPresaleContractAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newAlloc","type":"uint256"}],"name":"setVestingAllocation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stripToken","outputs":[{"internalType":"contract IStripToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vestingAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"withdrawToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]