编译器
0.8.18+commit.87f61d96
文件 1 的 22:AccessProtected.sol
pragma solidity 0.8.18;
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
abstract contract AccessProtected is Context {
mapping(address => bool) private _admins;
uint256 public adminCount;
event AdminAccessSet(address indexed _admin, bool _enabled);
constructor() {
_admins[_msgSender()] = true;
adminCount = 1;
emit AdminAccessSet(_msgSender(), true);
}
modifier onlyAdmin() {
require(_admins[_msgSender()], "ADMIN_ACCESS_REQUIRED");
_;
}
function isAdmin(address _addressToCheck) external view returns (bool) {
return _admins[_addressToCheck];
}
function setAdmin(address admin, bool isEnabled) public onlyAdmin {
require(admin != address(0), "INVALID_ADDRESS");
require(
_admins[admin] != isEnabled,
"FLAG_ALREADY_PRESENT_FOR_ADDRESS"
);
if (isEnabled) {
adminCount++;
} else {
require(adminCount > 1, "AT_LEAST_ONE_ADMIN_REQUIRED");
adminCount--;
}
_admins[admin] = isEnabled;
emit AdminAccessSet(admin, isEnabled);
}
}
文件 2 的 22:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 3 的 22: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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 4 的 22:Create2.sol
pragma solidity ^0.8.0;
library Create2 {
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
}
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
assembly {
let ptr := mload(0x40)
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer)
let start := add(ptr, 0x0b)
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
}
文件 5 的 22:IERC20.sol
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
文件 6 的 22:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 7 的 22:IMilestoneManager.sol
pragma solidity 0.8.18;
import "../MilestoneManager.sol";
interface IMilestoneManager {
event MilestoneCreated(
bytes32 indexed milestoneId,
address indexed recipient
);
event MilestoneApproved(bytes32 indexed milestoneId, uint256 step);
event MilestoneClaimed(
bytes32 indexed milestoneId,
address indexed recipient,
uint256 amount
);
event AdminWithdrawn(address indexed recipient, uint256 amountRequested);
error MilestoneAlreadyExists(bytes32 milestoneId);
error MilestoneNotActive();
error InsufficientManagerBalance();
error NotMilestoneOwner();
error InvalidParameters();
error invalidAddress();
error invalidZeroAddress();
error arrayLengthMismatch();
error insufficientBalance();
error milestoneNotExists();
error invalidStepIndex();
error stepAlreadyApproved();
error invalidToken();
function createMilestone(
address recipient,
uint256[] memory allocations,
bool[] memory approvals,
string[] memory externalRefs
) external returns (bytes32);
function claim(bytes32 milestoneId) external;
function approveMilestone(bytes32 milestoneId, uint256 step) external;
function getMilestoneById(
bytes32 milestoneId
) external view returns (MilestoneManager.Milestone memory);
function getAllRecipients() external view returns (address[] memory);
function getRecipientMilestones(
address recipient
) external view returns (bytes32[] memory);
function isRecipient(address recipient) external view returns (bool);
}
文件 8 的 22:IMilestoneManagerFactory.sol
pragma solidity 0.8.18;
import { MilestoneManager } from "../MilestoneManager.sol";
interface IMilestoneManagerFactory {
event MilestoneManagerCreated(MilestoneManager milestone);
function newMilestoneManager(
address tokenAddress
) external returns (MilestoneManager);
}
文件 9 的 22:ITokenVestingManager.sol
pragma solidity 0.8.18;
import "../libraries/TokenVestingLib.sol";
interface ITokenVestingManager {
struct CreateVestingBatchParams {
address[] _recipients;
uint32[] _startTimestamps;
uint32[] _endTimestamps;
uint32[] _timelocks;
uint256[] _initialUnlocks;
uint32[] _cliffReleaseTimestamps;
uint256[] _cliffAmounts;
uint32[] _releaseIntervalSecs;
uint256[] _linearVestAmounts;
}
event VestingCreated(
address indexed recipient,
bytes32 vestingId,
TokenVestingLib.Vesting vesting
);
event Claimed(
address indexed recipient,
uint256 withdrawalAmount,
bytes32 vestingId
);
event VestingRevoked(
address indexed recipient,
uint256 numTokensWithheld,
TokenVestingLib.Vesting vesting
);
event AdminWithdrawn(address indexed recipient, uint256 amountRequested);
error vestingAlreadyExists(bytes32 vestingId);
error vestingNotActive();
error insuficientManagerBalance();
error notVestingOwner();
error invalidZeroAddress();
error insufficientBalance();
error arrayLengthMismatch();
error allVestedAmountAlreadyClaimed();
error invalidToken();
function createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) external returns (bytes32 vestingId);
function createVestingBatch(
CreateVestingBatchParams memory params
) external returns (bytes32[] memory);
function claim(bytes32 vestingId) external;
function revokeVesting(bytes32 vestingId) external;
function withdrawAdmin(uint256 amountRequested) external;
function withdrawOtherToken(address otherTokenAddress) external;
function amountAvailableToWithdrawByAdmin() external view returns (uint256);
function getVestingInfo(
bytes32 _vestingId
) external view returns (TokenVestingLib.Vesting memory);
function getAllRecipients() external view returns (address[] memory);
function isRecipient(address recipient) external view returns (bool);
}
文件 10 的 22:ITokenVestingManagerFactory.sol
pragma solidity 0.8.18;
import { TokenVestingManager } from "../TokenVestingManager.sol";
interface ITokenVestingManagerFactory {
event TokenVestingManagerCreated(TokenVestingManager tokenVestingManager);
function newTokenVestingManager(
address tokenAddress
) external returns (TokenVestingManager);
}
文件 11 的 22:IVestedMilestoneManager.sol
pragma solidity 0.8.18;
interface IVestedMilestoneManager {
event VestedMilestoneCreated(
bytes32 indexed vestedMilestoneId,
address indexed recipient
);
event VestedMilestoneStepApproved(
bytes32 indexed vestedMilestoneId,
uint256 stepIndex
);
event VestingInitialized(bytes32 indexed milestoneId, uint256 stepIndex);
event Claimed(address indexed recipient, uint256 amount, uint256 stepIndex);
event AdminWithdrawn(address indexed recipient, uint256 amountRequested);
error insufficientBalance();
error arrayLengthMismatch();
error MilestoneAlreadyExists(bytes32 milestoneId);
error milestoneNotExists();
error invalidStepIndex();
error notVestingOwner();
error invalidZeroAddress();
error stepNotApproved();
error stepAlreadyApproved();
error invalidCliffReleaseTimestamp();
error invalidUnlockTimestamp();
error startTimestampNotReached();
error invalidToken();
function createMilestone(
address _recipient,
uint256[] memory _allocations,
string[] memory _externalRefs
) external returns (bytes32);
function approveMilestoneStep(
bytes32 _milestoneId,
uint256 _stepIndex,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) external;
function claim(bytes32 milestoneId, uint256 stepIndex) external;
function isRecipient(address recipient) external view returns (bool);
}
文件 12 的 22:IVestedMilestoneManagerFactory.sol
pragma solidity 0.8.18;
import { VestedMilestoneManager } from "../VestedMilestoneManager.sol";
interface IVestedMilestoneManagerFactory {
event VestedMilestoneManagerCreated(VestedMilestoneManager vestedMilestone);
function newVestedMilestoneManager(
address tokenAddress
) external returns (VestedMilestoneManager);
}
文件 13 的 22:MilestoneManager.sol
pragma solidity 0.8.18;
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessProtected } from "./libraries/AccessProtected.sol";
import { IMilestoneManager } from "./interfaces/IMilestoneManager.sol";
contract MilestoneManager is AccessProtected, IMilestoneManager {
using SafeERC20 for IERC20;
address public immutable tokenAddress;
uint256 public numTokensReservedForMilestones;
uint256 private milestoneIdNonce;
struct Milestone {
address recipient;
uint256 claimedAmount;
MilestoneStep[] steps;
}
struct MilestoneStep {
uint256 allocation;
bool isApproved;
string externalRef;
}
mapping(address => bytes32[]) public recipientMilestones;
mapping(bytes32 => Milestone) public milestonesById;
address[] public recipients;
constructor(address _tokenAddress) {
if (_tokenAddress == address(0)) {
revert invalidAddress();
}
tokenAddress = _tokenAddress;
}
function createMilestone(
address recipient,
uint256[] calldata _allocations,
bool[] calldata _approvals,
string[] calldata _externalRefs
) public onlyAdmin returns (bytes32) {
if (recipient == address(0)) {
revert invalidZeroAddress();
}
uint256 length = _allocations.length;
if (_approvals.length != length || _externalRefs.length != length) {
revert arrayLengthMismatch();
}
bytes32 milestoneId = bytes32(milestoneIdNonce++);
Milestone storage milestone = milestonesById[milestoneId];
milestone.recipient = recipient;
uint256 totalExpectedAmount;
for (uint256 i; i < _allocations.length; ) {
milestone.steps.push(
MilestoneStep({
allocation: _allocations[i],
isApproved: _approvals[i],
externalRef: _externalRefs[i]
})
);
totalExpectedAmount += _allocations[i];
unchecked {
++i;
}
}
milestonesById[milestoneId] = milestone;
if (!isRecipient(recipient)) {
recipients.push(recipient);
}
recipientMilestones[recipient].push(milestoneId);
numTokensReservedForMilestones += totalExpectedAmount;
emit MilestoneCreated(milestoneId, recipient);
IERC20(tokenAddress).safeTransferFrom(
msg.sender,
address(this),
totalExpectedAmount
);
return milestoneId;
}
function createMilestoneBatch(
address[] calldata recipient,
uint256[][] calldata _allocations,
bool[][] calldata _approvals,
string[][] calldata _externalRefs
) external onlyAdmin returns (bytes32[] memory) {
uint256 length = recipient.length;
bytes32[] memory milestonesId = new bytes32[](length);
if (
_allocations.length != length ||
_approvals.length != length ||
_externalRefs.length != length
) {
revert arrayLengthMismatch();
}
for (uint256 i; i < length; ) {
milestonesId[i] = createMilestone(
recipient[i],
_allocations[i],
_approvals[i],
_externalRefs[i]
);
unchecked {
++i;
}
}
return milestonesId;
}
function claim(bytes32 milestoneId) external {
Milestone storage milestone = milestonesById[milestoneId];
if (milestone.recipient != msg.sender) {
revert NotMilestoneOwner();
}
uint256 claimableAmount = calculateClaimable(milestoneId);
if (claimableAmount == 0) {
revert insufficientBalance();
}
milestone.claimedAmount += claimableAmount;
numTokensReservedForMilestones -= claimableAmount;
emit MilestoneClaimed(milestoneId, msg.sender, claimableAmount);
IERC20(tokenAddress).safeTransfer(msg.sender, claimableAmount);
}
function calculateClaimable(
bytes32 milestoneId
) internal view returns (uint256) {
Milestone memory milestone = milestonesById[milestoneId];
uint256 totalClaimable;
for (uint256 i; i < milestone.steps.length; ) {
if (milestone.steps[i].isApproved) {
totalClaimable += milestone.steps[i].allocation;
}
unchecked {
++i;
}
}
return totalClaimable - milestone.claimedAmount;
}
function approveMilestone(
bytes32 _milestoneId,
uint256 _stepIndex
) external onlyAdmin {
Milestone storage milestone = milestonesById[_milestoneId];
if (milestone.recipient == address(0)) {
revert milestoneNotExists();
}
if (_stepIndex >= milestone.steps.length) {
revert invalidStepIndex();
}
MilestoneStep storage step = milestone.steps[_stepIndex];
if (step.isApproved) {
revert stepAlreadyApproved();
}
step.isApproved = true;
emit MilestoneApproved(_milestoneId, _stepIndex);
}
function withdrawAdmin(uint256 _amountRequested) external onlyAdmin {
uint256 amountRemaining = amountAvailableToWithdrawByAdmin();
if (amountRemaining < _amountRequested) {
revert insufficientBalance();
}
emit AdminWithdrawn(msg.sender, _amountRequested);
IERC20(tokenAddress).safeTransfer(msg.sender, _amountRequested);
}
function withdrawOtherToken(address _otherTokenAddress) external onlyAdmin {
if (_otherTokenAddress == tokenAddress) {
revert invalidToken();
}
uint256 balance = IERC20(_otherTokenAddress).balanceOf(address(this));
IERC20(_otherTokenAddress).safeTransfer(msg.sender, balance);
}
function amountAvailableToWithdrawByAdmin() public view returns (uint256) {
return
IERC20(tokenAddress).balanceOf(address(this)) -
numTokensReservedForMilestones;
}
function getMilestoneById(
bytes32 milestoneId
) external view returns (Milestone memory) {
return milestonesById[milestoneId];
}
function getAllRecipients() external view returns (address[] memory) {
return recipients;
}
function getAllRecipientsSliced(
uint256 _from,
uint256 _to
) external view returns (address[] memory) {
address[] memory recipientsSliced = new address[](_to - _from);
for (uint256 i = _from; i < _to; ) {
recipientsSliced[i - _from] = recipients[i];
unchecked {
++i;
}
}
return recipientsSliced;
}
function getAllRecipientsLength() external view returns (uint256) {
return recipients.length;
}
function getRecipientMilestones(
address _recipient
) external view returns (bytes32[] memory) {
return recipientMilestones[_recipient];
}
function getRecipientMilestonesLength(
address _recipient
) external view returns (uint256) {
return recipientMilestones[_recipient].length;
}
function getRecipientMilestonesSliced(
uint256 _from,
uint256 _to,
address _recipient
) external view returns (bytes32[] memory) {
bytes32[] memory recipientMilestonesSliced = new bytes32[](_to - _from);
for (uint256 i = _from; i < _to; ) {
recipientMilestonesSliced[i - _from] = recipientMilestones[
_recipient
][i];
unchecked {
++i;
}
}
return recipientMilestonesSliced;
}
function isRecipient(address recipient) public view returns (bool) {
return recipientMilestones[recipient].length != 0;
}
}
文件 14 的 22:MilestoneManagerFactory.sol
pragma solidity 0.8.18;
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { IMilestoneManagerFactory } from "../interfaces/IMilestoneManagerFactory.sol";
import { MilestoneManager } from "../MilestoneManager.sol";
contract MilestoneManagerFactory is IMilestoneManagerFactory {
function newMilestoneManager(
address tokenAddress
) external override returns (MilestoneManager) {
MilestoneManager milestoneManager = new MilestoneManager(tokenAddress);
emit MilestoneManagerCreated(milestoneManager);
milestoneManager.setAdmin(msg.sender, true);
milestoneManager.setAdmin(address(this), false);
return milestoneManager;
}
}
文件 15 的 22: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);
}
}
文件 16 的 22:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 17 的 22:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 18 的 22:TokenVestingLib.sol
pragma solidity 0.8.18;
library TokenVestingLib {
error invalidAddress();
error invalidVestedAmount();
error invalidStartTimestamp();
error invalidEndTimestamp();
error invalidReleaseInterval();
error invalidIntervalLength();
error invalidCliffRelease();
error invalidCliffAmount();
error timelockEnabled();
struct Vesting {
address recipient;
uint32 startTimestamp;
uint32 endTimestamp;
uint32 deactivationTimestamp;
uint32 timelock;
uint32 releaseIntervalSecs;
uint32 cliffReleaseTimestamp;
uint256 initialUnlock;
uint256 cliffAmount;
uint256 linearVestAmount;
uint256 claimedAmount;
}
function calculateVestedAmount(
Vesting memory _vesting,
uint32 _referenceTimestamp
) internal pure returns (uint256) {
if (_vesting.deactivationTimestamp != 0) {
if (_referenceTimestamp > _vesting.deactivationTimestamp) {
_referenceTimestamp = _vesting.deactivationTimestamp;
}
}
uint256 vestingAmount;
if (_referenceTimestamp > _vesting.endTimestamp) {
_referenceTimestamp = _vesting.endTimestamp;
}
if (_referenceTimestamp >= _vesting.cliffReleaseTimestamp) {
vestingAmount += _vesting.cliffAmount;
}
if (_vesting.initialUnlock > 0) {
vestingAmount += _vesting.initialUnlock;
}
uint256 startTimestamp;
if (_vesting.cliffReleaseTimestamp != 0) {
startTimestamp = _vesting.cliffReleaseTimestamp;
} else {
startTimestamp = _vesting.startTimestamp;
}
if (_referenceTimestamp > startTimestamp) {
uint256 currentVestingDurationSecs = _referenceTimestamp -
startTimestamp;
uint256 truncatedCurrentVestingDurationSecs = (currentVestingDurationSecs /
_vesting.releaseIntervalSecs) *
_vesting.releaseIntervalSecs;
uint256 finalVestingDurationSecs = _vesting.endTimestamp -
startTimestamp;
uint256 linearVestAmount = (_vesting.linearVestAmount *
truncatedCurrentVestingDurationSecs) / finalVestingDurationSecs;
vestingAmount += linearVestAmount;
}
return vestingAmount;
}
function generateVestingId(
Vesting memory _vesting
) internal pure returns (bytes32) {
return keccak256(abi.encode(_vesting));
}
}
文件 19 的 22:TokenVestingManager.sol
pragma solidity 0.8.18;
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessProtected } from "./libraries/AccessProtected.sol";
import { TokenVestingLib } from "./libraries/TokenVestingLib.sol";
import { ITokenVestingManager } from "./interfaces/ITokenVestingManager.sol";
contract TokenVestingManager is AccessProtected, ITokenVestingManager {
using TokenVestingLib for TokenVestingLib.Vesting;
using SafeERC20 for IERC20;
address public immutable tokenAddress;
uint256 public numTokensReservedForVesting;
mapping(address => bytes32[]) public recipientVestings;
mapping(bytes32 => TokenVestingLib.Vesting) public vestingById;
address[] public recipients;
constructor(address _tokenAddress) {
if (_tokenAddress == address(0)) {
revert TokenVestingLib.invalidAddress();
}
tokenAddress = _tokenAddress;
}
modifier isVestingActive(bytes32 _vestingId) {
if (getVestingInfo(_vestingId).deactivationTimestamp != 0) {
revert vestingNotActive();
}
_;
}
function createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) external onlyAdmin returns (bytes32 vestingId) {
if (_recipient == address(0)) {
revert TokenVestingLib.invalidAddress();
}
uint256 totalExpectedAmount = _initialUnlock +
_cliffAmount +
_linearVestAmount;
numTokensReservedForVesting += totalExpectedAmount;
vestingId = _createVesting(
_recipient,
_startTimestamp,
_endTimestamp,
_timelock,
_initialUnlock,
_cliffReleaseTimestamp,
_cliffAmount,
_releaseIntervalSecs,
_linearVestAmount
);
IERC20(tokenAddress).safeTransferFrom(
msg.sender,
address(this),
totalExpectedAmount
);
return vestingId;
}
function createVestingBatch(
CreateVestingBatchParams calldata params
) external onlyAdmin returns (bytes32[] memory) {
uint256 length = params._recipients.length;
bytes32[] memory vestingIds = new bytes32[](length);
uint256 totalExpectedAmount;
{
if (
params._startTimestamps.length != length ||
params._endTimestamps.length != length ||
params._timelocks.length != length ||
params._initialUnlocks.length != length ||
params._cliffAmounts.length != length ||
params._cliffReleaseTimestamps.length != length ||
params._releaseIntervalSecs.length != length ||
params._linearVestAmounts.length != length
) {
revert arrayLengthMismatch();
}
}
for (uint256 i; i < params._recipients.length; ) {
vestingIds[i] = _createVesting(
params._recipients[i],
params._startTimestamps[i],
params._endTimestamps[i],
params._timelocks[i],
params._initialUnlocks[i],
params._cliffReleaseTimestamps[i],
params._cliffAmounts[i],
params._releaseIntervalSecs[i],
params._linearVestAmounts[i]
);
totalExpectedAmount +=
uint256(params._initialUnlocks[i]) +
uint256(params._cliffAmounts[i]) +
params._linearVestAmounts[i];
unchecked {
++i;
}
}
numTokensReservedForVesting += totalExpectedAmount;
IERC20(tokenAddress).safeTransferFrom(
msg.sender,
address(this),
totalExpectedAmount
);
return vestingIds;
}
function claim(bytes32 _vestingId) external {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
if (msg.sender != vesting.recipient) {
revert notVestingOwner();
}
if (vesting.timelock > uint32(block.timestamp)) {
revert TokenVestingLib.timelockEnabled();
}
uint256 vested = TokenVestingLib.calculateVestedAmount(
vesting,
uint32(block.timestamp)
);
uint256 claimable = vested - vesting.claimedAmount;
if (claimable == 0) {
revert insufficientBalance();
}
vesting.claimedAmount += claimable;
numTokensReservedForVesting -= claimable;
emit Claimed(msg.sender, claimable, _vestingId);
IERC20(tokenAddress).safeTransfer(msg.sender, claimable);
}
function revokeVesting(
bytes32 _vestingId
) external onlyAdmin isVestingActive(_vestingId) {
TokenVestingLib.Vesting storage vesting = vestingById[_vestingId];
uint256 finalVestAmount = TokenVestingLib.calculateVestedAmount(
vesting,
vesting.endTimestamp
);
if (vesting.claimedAmount == finalVestAmount) {
revert allVestedAmountAlreadyClaimed();
}
vesting.deactivationTimestamp = uint32(block.timestamp);
uint256 vestedAmountNow = TokenVestingLib.calculateVestedAmount(
vesting,
uint32(block.timestamp)
);
uint256 amountRemaining = finalVestAmount - vestedAmountNow;
numTokensReservedForVesting -= amountRemaining;
emit VestingRevoked(vesting.recipient, amountRemaining, vesting);
}
function _createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) internal returns (bytes32) {
if (_recipient == address(0)) {
revert TokenVestingLib.invalidAddress();
}
if (_linearVestAmount + _cliffAmount == 0) {
revert TokenVestingLib.invalidVestedAmount();
}
if (_startTimestamp == 0) {
revert TokenVestingLib.invalidStartTimestamp();
}
if (_endTimestamp == 0) {
revert TokenVestingLib.invalidEndTimestamp();
}
if (_startTimestamp > _endTimestamp) {
revert TokenVestingLib.invalidEndTimestamp();
}
if (_releaseIntervalSecs == 0) {
revert TokenVestingLib.invalidReleaseInterval();
}
if (_cliffReleaseTimestamp == 0) {
if (_cliffAmount != 0) {
revert TokenVestingLib.invalidCliffAmount();
}
if ((_endTimestamp - _startTimestamp) % _releaseIntervalSecs != 0) {
revert TokenVestingLib.invalidIntervalLength();
}
} else {
if (
((_startTimestamp > _cliffReleaseTimestamp) ||
(_cliffReleaseTimestamp >= _endTimestamp))
) {
revert TokenVestingLib.invalidCliffRelease();
}
if (_cliffAmount == 0) {
revert TokenVestingLib.invalidCliffAmount();
}
if (
(_endTimestamp - _cliffReleaseTimestamp) %
_releaseIntervalSecs !=
0
) {
revert TokenVestingLib.invalidIntervalLength();
}
}
TokenVestingLib.Vesting memory vesting = TokenVestingLib.Vesting({
recipient: _recipient,
startTimestamp: _startTimestamp,
endTimestamp: _endTimestamp,
deactivationTimestamp: 0,
timelock: _timelock,
initialUnlock: _initialUnlock,
cliffReleaseTimestamp: _cliffReleaseTimestamp,
cliffAmount: _cliffAmount,
releaseIntervalSecs: _releaseIntervalSecs,
linearVestAmount: _linearVestAmount,
claimedAmount: 0
});
bytes32 vestingId = TokenVestingLib.generateVestingId(vesting);
if (getVestingInfo(vestingId).startTimestamp != 0) {
revert vestingAlreadyExists(vestingId);
}
if (!isRecipient(_recipient)) {
recipients.push(_recipient);
}
vestingById[vestingId] = vesting;
recipientVestings[_recipient].push(vestingId);
emit VestingCreated(_recipient, vestingId, vesting);
return vestingId;
}
function withdrawAdmin(uint256 _amountRequested) external onlyAdmin {
uint256 amountRemaining = amountAvailableToWithdrawByAdmin();
if (amountRemaining < _amountRequested) {
revert insufficientBalance();
}
emit AdminWithdrawn(msg.sender, _amountRequested);
IERC20(tokenAddress).safeTransfer(msg.sender, _amountRequested);
}
function withdrawOtherToken(address _otherTokenAddress) external onlyAdmin {
if (_otherTokenAddress == tokenAddress) {
revert invalidToken();
}
uint256 balance = IERC20(_otherTokenAddress).balanceOf(address(this));
IERC20(_otherTokenAddress).safeTransfer(msg.sender, balance);
}
function amountAvailableToWithdrawByAdmin() public view returns (uint256) {
return
IERC20(tokenAddress).balanceOf(address(this)) -
numTokensReservedForVesting;
}
function getVestingInfo(
bytes32 _vestingId
) public view returns (TokenVestingLib.Vesting memory) {
return vestingById[_vestingId];
}
function getAllRecipients() external view returns (address[] memory) {
return recipients;
}
function isRecipient(address recipient) public view returns (bool) {
return recipientVestings[recipient].length != 0;
}
function getAllRecipientsLength() external view returns (uint256) {
return recipients.length;
}
function getAllRecipientsSliced(
uint256 _from,
uint256 _to
) external view returns (address[] memory) {
address[] memory recipientsSliced = new address[](_to - _from);
for (uint256 i = _from; i < _to; ) {
recipientsSliced[i - _from] = recipients[i];
unchecked {
++i;
}
}
return recipientsSliced;
}
function getAllRecipientVestings(
address _recipient
) external view returns (bytes32[] memory) {
return recipientVestings[_recipient];
}
function getAllRecipientVestingsSliced(
uint256 _from,
uint256 _to,
address _recipient
) external view returns (bytes32[] memory) {
bytes32[] memory recipientVestingsSliced = new bytes32[](_to - _from);
for (uint256 i = _from; i < _to; ) {
recipientVestingsSliced[i - _from] = recipientVestings[_recipient][
i
];
unchecked {
++i;
}
}
return recipientVestingsSliced;
}
function getAllRecipientVestingsLength(
address _recipient
) external view returns (uint256) {
return recipientVestings[_recipient].length;
}
}
文件 20 的 22:TokenVestingManagerFactory.sol
pragma solidity 0.8.18;
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { ITokenVestingManagerFactory } from "../interfaces/ITokenVestingManagerFactory.sol";
import { TokenVestingManager } from "../TokenVestingManager.sol";
contract TokenVestingManagerFactory is ITokenVestingManagerFactory {
function newTokenVestingManager(
address tokenAddress
) external override returns (TokenVestingManager) {
TokenVestingManager tokenVestingManager = new TokenVestingManager(
tokenAddress
);
emit TokenVestingManagerCreated(tokenVestingManager);
tokenVestingManager.setAdmin(msg.sender, true);
tokenVestingManager.setAdmin(address(this), false);
return tokenVestingManager;
}
}
文件 21 的 22:VestedMilestoneManager.sol
pragma solidity 0.8.18;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./libraries/AccessProtected.sol";
import "./libraries/TokenVestingLib.sol";
import { IVestedMilestoneManager } from "./interfaces/IVestedMilestoneManager.sol";
contract VestedMilestoneManager is
AccessProtected,
ReentrancyGuard,
IVestedMilestoneManager
{
using SafeERC20 for IERC20;
using TokenVestingLib for TokenVestingLib.Vesting;
address public immutable tokenAddress;
uint256 public numTokensReservedForVesting;
uint256 private milestoneIdNonce;
struct Milestone {
address recipient;
uint256 claimedAmount;
MilestoneStep[] steps;
}
struct MilestoneStep {
uint256 allocation;
bool isApproved;
string externalRef;
TokenVestingLib.Vesting vesting;
}
mapping(address => bytes32[]) public recipientMilestones;
mapping(bytes32 => Milestone) public milestonesById;
address[] public recipients;
constructor(address _tokenAddress) {
if (_tokenAddress == address(0)) {
revert TokenVestingLib.invalidAddress();
}
tokenAddress = _tokenAddress;
}
function createMilestone(
address _recipient,
uint256[] calldata _allocations,
string[] calldata _externalRefs
) public onlyAdmin returns (bytes32) {
if (_recipient == address(0)) {
revert invalidZeroAddress();
}
uint256 length = _allocations.length;
if (_externalRefs.length != length) {
revert arrayLengthMismatch();
}
bytes32 milestoneId = bytes32(milestoneIdNonce++);
Milestone storage milestone = milestonesById[milestoneId];
TokenVestingLib.Vesting memory emptyVesting;
milestone.recipient = _recipient;
uint256 totalExpectedAmount;
for (uint256 i; i < _allocations.length; ) {
milestone.steps.push(
MilestoneStep({
allocation: _allocations[i],
isApproved: false,
externalRef: _externalRefs[i],
vesting: emptyVesting
})
);
totalExpectedAmount += _allocations[i];
unchecked {
++i;
}
}
milestonesById[milestoneId] = milestone;
if (!isRecipient(_recipient)) {
recipients.push(_recipient);
}
recipientMilestones[_recipient].push(milestoneId);
numTokensReservedForVesting += totalExpectedAmount;
emit VestedMilestoneCreated(milestoneId, _recipient);
IERC20(tokenAddress).safeTransferFrom(
msg.sender,
address(this),
totalExpectedAmount
);
return milestoneId;
}
function createMilestoneBatch(
address[] calldata recipient,
uint256[][] calldata _allocations,
string[][] calldata _externalRefs
) external onlyAdmin returns (bytes32[] memory) {
uint256 length = recipient.length;
bytes32[] memory milestonesId = new bytes32[](length);
if (_allocations.length != length || _externalRefs.length != length) {
revert arrayLengthMismatch();
}
for (uint256 i; i < length; ) {
milestonesId[i] = createMilestone(
recipient[i],
_allocations[i],
_externalRefs[i]
);
unchecked {
++i;
}
}
return milestonesId;
}
function approveMilestoneStep(
bytes32 _milestoneId,
uint256 _stepIndex,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) external onlyAdmin {
Milestone storage milestone = milestonesById[_milestoneId];
if (milestone.recipient == address(0)) {
revert milestoneNotExists();
}
if (_stepIndex >= milestone.steps.length) {
revert invalidStepIndex();
}
MilestoneStep storage step = milestone.steps[_stepIndex];
if (step.isApproved) {
revert stepAlreadyApproved();
}
step.vesting = _createVesting(
milestone.recipient,
_startTimestamp,
_endTimestamp,
_timelock,
_initialUnlock,
_cliffReleaseTimestamp,
_cliffAmount,
_releaseIntervalSecs,
_linearVestAmount
);
if (
_cliffAmount + _linearVestAmount + _initialUnlock != step.allocation
) {
revert TokenVestingLib.invalidVestedAmount();
}
step.isApproved = true;
emit VestedMilestoneStepApproved(_milestoneId, _stepIndex);
emit VestingInitialized(_milestoneId, _stepIndex);
}
function claim(bytes32 milestoneId, uint256 stepIndex) external {
Milestone storage milestone = milestonesById[milestoneId];
if (msg.sender != milestone.recipient) {
revert notVestingOwner();
}
MilestoneStep storage step = milestone.steps[stepIndex];
if (!milestone.steps[stepIndex].isApproved) {
revert stepNotApproved();
}
if (
milestone.steps[stepIndex].vesting.timelock >
uint32(block.timestamp) &&
milestone.steps[stepIndex].vesting.timelock != 0
) {
revert TokenVestingLib.timelockEnabled();
}
if (
milestone.steps[stepIndex].vesting.startTimestamp >
uint32(block.timestamp)
) {
revert startTimestampNotReached();
}
uint256 claimableAmount = TokenVestingLib.calculateVestedAmount(
step.vesting,
uint32(block.timestamp)
);
if (claimableAmount == 0) {
revert insufficientBalance();
}
uint256 claimable = claimableAmount - step.vesting.claimedAmount;
milestone.claimedAmount += claimable;
step.vesting.claimedAmount += claimable;
numTokensReservedForVesting -= claimable;
emit Claimed(msg.sender, claimable, stepIndex);
IERC20(tokenAddress).safeTransfer(msg.sender, claimable);
}
function _createVesting(
address _recipient,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint32 _timelock,
uint256 _initialUnlock,
uint32 _cliffReleaseTimestamp,
uint256 _cliffAmount,
uint32 _releaseIntervalSecs,
uint256 _linearVestAmount
) internal pure returns (TokenVestingLib.Vesting memory) {
if (_linearVestAmount + _cliffAmount == 0) {
revert TokenVestingLib.invalidVestedAmount();
}
if (_startTimestamp == 0) {
revert TokenVestingLib.invalidStartTimestamp();
}
if (_startTimestamp > _endTimestamp) {
revert TokenVestingLib.invalidEndTimestamp();
}
if (_releaseIntervalSecs == 0) {
revert TokenVestingLib.invalidReleaseInterval();
}
if (_cliffReleaseTimestamp != 0 && _cliffAmount == 0) {
revert TokenVestingLib.invalidVestedAmount();
}
if (_cliffReleaseTimestamp == 0 && _cliffAmount != 0) {
revert TokenVestingLib.invalidVestedAmount();
}
if (_cliffReleaseTimestamp != 0) {
if (
(_endTimestamp - _cliffReleaseTimestamp) %
_releaseIntervalSecs !=
0
) {
revert TokenVestingLib.invalidIntervalLength();
}
} else {
if ((_endTimestamp - _startTimestamp) % _releaseIntervalSecs != 0) {
revert TokenVestingLib.invalidIntervalLength();
}
}
if (
((_startTimestamp > _cliffReleaseTimestamp) ||
(_cliffReleaseTimestamp >= _endTimestamp)) && _cliffAmount != 0
) {
revert invalidCliffReleaseTimestamp();
}
if ((_startTimestamp > _timelock && _timelock != 0)) {
revert invalidUnlockTimestamp();
}
TokenVestingLib.Vesting memory vesting = TokenVestingLib.Vesting({
recipient: _recipient,
startTimestamp: _startTimestamp,
endTimestamp: _endTimestamp,
deactivationTimestamp: 0,
timelock: _timelock,
initialUnlock: _initialUnlock,
cliffReleaseTimestamp: _cliffReleaseTimestamp,
cliffAmount: _cliffAmount,
releaseIntervalSecs: _releaseIntervalSecs,
linearVestAmount: _linearVestAmount,
claimedAmount: 0
});
return vesting;
}
function withdrawAdmin(uint256 _amountRequested) external onlyAdmin {
uint256 amountRemaining = amountAvailableToWithdrawByAdmin();
if (amountRemaining < _amountRequested) {
revert insufficientBalance();
}
emit AdminWithdrawn(msg.sender, _amountRequested);
IERC20(tokenAddress).safeTransfer(msg.sender, _amountRequested);
}
function withdrawOtherToken(address _otherTokenAddress) external onlyAdmin {
if (_otherTokenAddress == tokenAddress) {
revert invalidToken();
}
uint256 balance = IERC20(_otherTokenAddress).balanceOf(address(this));
IERC20(_otherTokenAddress).safeTransfer(msg.sender, balance);
}
function amountAvailableToWithdrawByAdmin() public view returns (uint256) {
return
IERC20(tokenAddress).balanceOf(address(this)) -
numTokensReservedForVesting;
}
function isRecipient(address recipient) public view returns (bool) {
return recipientMilestones[recipient].length != 0;
}
function getAllRecipients() external view returns (address[] memory) {
return recipients;
}
function getAllRecipientsSliced(
uint256 _from,
uint256 _to
) external view returns (address[] memory) {
address[] memory recipientsSliced = new address[](_to - _from);
for (uint256 i = _from; i < _to; ) {
recipientsSliced[i - _from] = recipients[i];
unchecked {
++i;
}
}
return recipientsSliced;
}
function getAllRecipientsLength() external view returns (uint256) {
return recipients.length;
}
function getMilestoneById(
bytes32 milestoneId
) external view returns (Milestone memory) {
return milestonesById[milestoneId];
}
function getMilestoneStep(
bytes32 milestoneId,
uint256 stepIndex
) external view returns (MilestoneStep memory) {
return milestonesById[milestoneId].steps[stepIndex];
}
function getAllMilestoneSteps(
bytes32 milestoneId
) external view returns (MilestoneStep[] memory) {
return milestonesById[milestoneId].steps;
}
function getAllMilestoneStepsLength(
bytes32 milestoneId
) external view returns (uint256) {
return milestonesById[milestoneId].steps.length;
}
function getAllMilestoneStepsSliced(
bytes32 milestoneId,
uint256 _from,
uint256 _to
) external view returns (MilestoneStep[] memory) {
MilestoneStep[] memory stepsSliced = new MilestoneStep[](_to - _from);
for (uint256 i = _from; i < _to; ) {
stepsSliced[i - _from] = milestonesById[milestoneId].steps[i];
unchecked {
++i;
}
}
return stepsSliced;
}
function getRecipientMilestones(
address _recipient
) external view returns (bytes32[] memory) {
return recipientMilestones[_recipient];
}
function getRecipientMilestonesLength(
address _recipient
) external view returns (uint256) {
return recipientMilestones[_recipient].length;
}
function getRecipientMilestonesSliced(
address _recipient,
uint256 _from,
uint256 _to
) external view returns (bytes32[] memory) {
bytes32[] memory milestonesSliced = new bytes32[](_to - _from);
for (uint256 i = _from; i < _to; ) {
milestonesSliced[i - _from] = recipientMilestones[_recipient][i];
unchecked {
++i;
}
}
return milestonesSliced;
}
}
文件 22 的 22:VestedMilestoneManagerFactory.sol
pragma solidity 0.8.18;
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
import { IVestedMilestoneManagerFactory } from "../interfaces/IVestedMilestoneManagerFactory.sol";
import { VestedMilestoneManager } from "../VestedMilestoneManager.sol";
contract VestedMilestoneManagerFactory is IVestedMilestoneManagerFactory {
function newVestedMilestoneManager(
address tokenAddress
) external override returns (VestedMilestoneManager) {
VestedMilestoneManager vestedMilestoneManager = new VestedMilestoneManager(
tokenAddress
);
emit VestedMilestoneManagerCreated(vestedMilestoneManager);
vestedMilestoneManager.setAdmin(msg.sender, true);
vestedMilestoneManager.setAdmin(address(this), false);
return vestedMilestoneManager;
}
}
{
"compilationTarget": {
"contracts/TokenVestingManager.sol": "TokenVestingManager"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"allVestedAmountAlreadyClaimed","type":"error"},{"inputs":[],"name":"arrayLengthMismatch","type":"error"},{"inputs":[],"name":"insufficientBalance","type":"error"},{"inputs":[],"name":"insuficientManagerBalance","type":"error"},{"inputs":[],"name":"invalidAddress","type":"error"},{"inputs":[],"name":"invalidCliffAmount","type":"error"},{"inputs":[],"name":"invalidCliffRelease","type":"error"},{"inputs":[],"name":"invalidEndTimestamp","type":"error"},{"inputs":[],"name":"invalidIntervalLength","type":"error"},{"inputs":[],"name":"invalidReleaseInterval","type":"error"},{"inputs":[],"name":"invalidStartTimestamp","type":"error"},{"inputs":[],"name":"invalidToken","type":"error"},{"inputs":[],"name":"invalidVestedAmount","type":"error"},{"inputs":[],"name":"invalidZeroAddress","type":"error"},{"inputs":[],"name":"notVestingOwner","type":"error"},{"inputs":[],"name":"timelockEnabled","type":"error"},{"inputs":[{"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"name":"vestingAlreadyExists","type":"error"},{"inputs":[],"name":"vestingNotActive","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_admin","type":"address"},{"indexed":false,"internalType":"bool","name":"_enabled","type":"bool"}],"name":"AdminAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRequested","type":"uint256"}],"name":"AdminWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawalAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"bytes32","name":"vestingId","type":"bytes32"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"indexed":false,"internalType":"struct TokenVestingLib.Vesting","name":"vesting","type":"tuple"}],"name":"VestingCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"numTokensWithheld","type":"uint256"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"indexed":false,"internalType":"struct TokenVestingLib.Vesting","name":"vesting","type":"tuple"}],"name":"VestingRevoked","type":"event"},{"inputs":[],"name":"adminCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amountAvailableToWithdrawByAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint32","name":"_startTimestamp","type":"uint32"},{"internalType":"uint32","name":"_endTimestamp","type":"uint32"},{"internalType":"uint32","name":"_timelock","type":"uint32"},{"internalType":"uint256","name":"_initialUnlock","type":"uint256"},{"internalType":"uint32","name":"_cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"_cliffAmount","type":"uint256"},{"internalType":"uint32","name":"_releaseIntervalSecs","type":"uint32"},{"internalType":"uint256","name":"_linearVestAmount","type":"uint256"}],"name":"createVesting","outputs":[{"internalType":"bytes32","name":"vestingId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"_recipients","type":"address[]"},{"internalType":"uint32[]","name":"_startTimestamps","type":"uint32[]"},{"internalType":"uint32[]","name":"_endTimestamps","type":"uint32[]"},{"internalType":"uint32[]","name":"_timelocks","type":"uint32[]"},{"internalType":"uint256[]","name":"_initialUnlocks","type":"uint256[]"},{"internalType":"uint32[]","name":"_cliffReleaseTimestamps","type":"uint32[]"},{"internalType":"uint256[]","name":"_cliffAmounts","type":"uint256[]"},{"internalType":"uint32[]","name":"_releaseIntervalSecs","type":"uint32[]"},{"internalType":"uint256[]","name":"_linearVestAmounts","type":"uint256[]"}],"internalType":"struct ITokenVestingManager.CreateVestingBatchParams","name":"params","type":"tuple"}],"name":"createVestingBatch","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestings","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestingsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"getAllRecipientVestingsSliced","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRecipients","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllRecipientsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_from","type":"uint256"},{"internalType":"uint256","name":"_to","type":"uint256"}],"name":"getAllRecipientsSliced","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"getVestingInfo","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"internalType":"struct TokenVestingLib.Vesting","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_addressToCheck","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"isRecipient","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numTokensReservedForVesting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"recipientVestings","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"recipients","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_vestingId","type":"bytes32"}],"name":"revokeVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"bool","name":"isEnabled","type":"bool"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"vestingById","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"deactivationTimestamp","type":"uint32"},{"internalType":"uint32","name":"timelock","type":"uint32"},{"internalType":"uint32","name":"releaseIntervalSecs","type":"uint32"},{"internalType":"uint32","name":"cliffReleaseTimestamp","type":"uint32"},{"internalType":"uint256","name":"initialUnlock","type":"uint256"},{"internalType":"uint256","name":"cliffAmount","type":"uint256"},{"internalType":"uint256","name":"linearVestAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountRequested","type":"uint256"}],"name":"withdrawAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_otherTokenAddress","type":"address"}],"name":"withdrawOtherToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]