账户
0xc9...0c30
0xC9...0c30

0xC9...0c30

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.17+commit.8df45f5f
语言
Solidity
合同源代码
文件 1 的 11:Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
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 的 11:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
合同源代码
文件 3 的 11:IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
合同源代码
文件 4 的 11:IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
合同源代码
文件 5 的 11:IERC721Enumerable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}
合同源代码
文件 6 的 11:IPresaleCvgSeed.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface IPresaleCvgSeed is IERC721Enumerable {
    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            ENUMS & STRUCTS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    enum SaleState {
        NOT_ACTIVE,
        PRESEED,
        SEED,
        OVER
    }

    struct PresaleInfo {
        uint256 vestingType; // Define the presaler type
        uint256 cvgAmount; // Total CVG amount claimable for the nft owner
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            SETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function setSaleState(SaleState _saleState) external;

    function grantPreseed(address _wallet, uint256 _amount) external;

    function grantSeed(address _wallet, uint256 _amount) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            EXTERNALS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function investMint(bool _isDai) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            GETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function presaleInfoTokenId(uint256 _tokenId) external view returns (PresaleInfo memory);

    function saleState() external view returns (SaleState);

    function tokenOfOwnerByIndex(address owner, uint256 index) external view override returns (uint256);

    function getTokenIdAndType(
        address _wallet,
        uint256 _index
    ) external view returns (uint256 tokenId, uint256 typeVesting);

    function getTokenIdsForWallet(address _wallet) external view returns (uint256[] memory);

    function getTotalCvg() external view returns (uint256);

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                        WITHDRAW OWNER
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function withdrawFunds() external;

    function withdrawToken(address _token) external;
}
合同源代码
文件 7 的 11:IPresaleCvgWl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface IPresaleCvgWl is IERC721Enumerable {
    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            ENUMS & STRUCTS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    enum SaleState {
        NOT_ACTIVE,
        WL,
        OVER
    }

    struct PresaleInfo {
        uint256 vestingType;
        uint256 cvgAmount;
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            SETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function setSaleState(SaleState _saleState) external;

    function setMerkleRootWlS(bytes32 _newMerkleRootWlS) external;

    function setMerkleRootWlM(bytes32 _newMerkleRootWlM) external;

    function setMerkleRootWlL(bytes32 _newMerkleRootWlL) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            EXTERNALS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function investMint(bytes32[] calldata _merkleProof, uint256 _amount) external;

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            GETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function presaleInfos(uint256 _tokenId) external view returns (PresaleInfo memory);

    function getAmountCvgForVesting() external view returns (uint256);

    function getTotalCvg() external view returns (uint256);

    function saleState() external view returns (SaleState);

    function tokenOfOwnerByIndex(address owner, uint256 index) external view override returns (uint256);

    function getTokenIdAndType(
        address _wallet,
        uint256 _index
    ) external view returns (uint256 tokenId, uint256 typeVesting);

    function getTokenIdsForWallet(address _wallet) external view returns (uint256[] memory);

    function MAX_SUPPLY_PRESALE() external view returns (uint256);

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                        WITHDRAW OWNER
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    function withdrawFunds() external;

    function withdrawToken(address _token) external;
}
合同源代码
文件 8 的 11:IboInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";

interface IboInterface is IERC721Enumerable {
    function totalCvgPerToken(uint256 tokenId) external view returns (uint256);

    function iboStartTimestamp() external view returns (uint256);

    function getTokenIdsForWallet(address _wallet) external view returns (uint256[] memory);

    function getTotalCvgDue() external view returns (uint256);
}
合同源代码
文件 9 的 11:Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
合同源代码
文件 10 的 11:Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}
合同源代码
文件 11 的 11:VestingCvg.sol
// SPDX-License-Identifier: MIT
/**
 _____
/  __ \
| /  \/ ___  _ ____   _____ _ __ __ _  ___ _ __   ___ ___
| |    / _ \| '_ \ \ / / _ \ '__/ _` |/ _ \ '_ \ / __/ _ \
| \__/\ (_) | | | \ V /  __/ | | (_| |  __/ | | | (_|  __/
 \____/\___/|_| |_|\_/ \___|_|  \__, |\___|_| |_|\___\___|
                                 __/ |
                                |___/
 */
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IPresaleCvgWl.sol";
import "../interfaces/IPresaleCvgSeed.sol";
import "../interfaces/IboInterface.sol";

contract VestingCvg is Ownable2Step {
    /// @dev Enum about vesting types
    enum VestingType {
        SEED,
        WL,
        IBO,
        TEAM,
        DAO
    }
    /// @dev Enum about the state of the vesting
    enum State {
        NOT_ACTIVE,
        SET,
        OPEN
    }
    /// @dev Struct Info about VestingSchedules
    struct VestingSchedule {
        uint80 daysBeforeCliff;
        uint80 daysAfterCliff;
        uint96 dropCliff;
        uint256 totalAmount;
        uint256 totalReleased;
    }
    struct InfoVestingTokenId {
        uint256 amountReleasable;
        uint256 totalCvg;
        uint256 amountRedeemed;
    }
    /// @dev Max supply TEAM & DAO
    uint256 public constant MAX_SUPPLY_TEAM = 12_750_000 * 10 ** 18;
    uint256 public constant MAX_SUPPLY_DAO = 14_250_000 * 10 ** 18;

    uint256 public constant ONE_DAY = 1 days;
    uint256 public constant ONE_GWEI = 10 ** 9;

    State public state;

    IPresaleCvgWl public presaleWl;
    IPresaleCvgSeed public presaleSeed;
    IboInterface public ibo;
    IERC20 public cvg;

    address public whitelistedTeam;
    address public whitelistedDao;

    /// @dev Timestamp shared between all vestingSchedules to mark the beginning of the vesting
    uint256 public startTimestamp;

    /// @dev VestingType associated to the vesting schedule info
    mapping(VestingType => VestingSchedule) public vestingSchedules;

    mapping(uint256 => uint256) public amountReleasedIdSeed; // tokenId => amountReleased
    mapping(uint256 => uint256) public amountReleasedIdWl; // tokenId => amountReleased
    mapping(uint256 => uint256) public amountReleasedIdIbo; // tokenId => amountReleased

    constructor(IPresaleCvgWl _presaleWl, IPresaleCvgSeed _presaleSeed, IboInterface _ibo) {
        presaleWl = _presaleWl;
        presaleSeed = _presaleSeed;
        ibo = _ibo;
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            MODIFIERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */

    modifier onlyOwnerOfSeed(uint256 _tokenId) {
        require(presaleSeed.ownerOf(_tokenId) == msg.sender, "NOT_OWNED");
        _;
    }

    modifier onlyOwnerOfWl(uint256 _tokenId) {
        require(presaleWl.ownerOf(_tokenId) == msg.sender, "NOT_OWNED");
        _;
    }

    modifier onlyOwnerOfIbo(uint256 _tokenId) {
        require(ibo.ownerOf(_tokenId) == msg.sender, "NOT_OWNED");
        _;
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            SETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */

    function setWhitelistTeam(address newWhitelistedTeam) external onlyOwner {
        whitelistedTeam = newWhitelistedTeam;
    }

    function setWhitelistDao(address newWhitelistedDao) external onlyOwner {
        whitelistedDao = newWhitelistedDao;
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            GETTERS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */

    function getTotalReleasedScheduleId(VestingType _vestingType) external view returns (uint256) {
        return (vestingSchedules[_vestingType].totalReleased);
    }

    function getInfoVestingTokenId(
        uint256 _tokenId,
        VestingType _vestingType
    ) external view returns (InfoVestingTokenId memory) {
        (uint256 amountReleasable, uint256 totalCvg, uint256 amountRedeemed) = _computeReleaseAmount(
            _tokenId,
            _vestingType
        );
        return
            InfoVestingTokenId({
                amountReleasable: amountReleasable,
                totalCvg: totalCvg,
                amountRedeemed: amountRedeemed
            });
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            EXTERNALS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */
    /// @notice Set vesting with current timestamp, creates vestingSchedules for all vesting types (only callable by owner)
    /// @dev Sum of all vesting schedule must be distributed in the contract before intializing it the vesting
    function setVesting(IERC20 _cvg) external onlyOwner {
        require(state == State.NOT_ACTIVE, "VESTING_ALREADY_SET");
        state = State.SET;

        require(
            presaleSeed.saleState() == IPresaleCvgSeed.SaleState.OVER &&
                presaleWl.saleState() == IPresaleCvgWl.SaleState.OVER,
            "PRESALE_ROUND_NOT_FINISHED"
        );

        startTimestamp = block.timestamp;

        /// @dev SEED SCHEDULE
        uint256 seedAmount = presaleSeed.getTotalCvg();
        vestingSchedules[VestingType.SEED] = VestingSchedule({
            totalAmount: seedAmount,
            totalReleased: 0,
            daysBeforeCliff: 4 * 30,
            daysAfterCliff: 15 * 30,
            dropCliff: 50
        });

        /// @dev WL SCHEDULE
        uint256 wlAmount = presaleWl.MAX_SUPPLY_PRESALE();
        vestingSchedules[VestingType.WL] = VestingSchedule({
            totalAmount: wlAmount,
            totalReleased: 0,
            daysBeforeCliff: 0,
            daysAfterCliff: 3 * 30,
            dropCliff: 330
        });

        /// @dev IBO SCHEDULE
        uint256 iboAmount = ibo.getTotalCvgDue();
        vestingSchedules[VestingType.IBO] = VestingSchedule({
            totalAmount: iboAmount,
            totalReleased: 0,
            daysBeforeCliff: 0,
            daysAfterCliff: 2 * 30,
            dropCliff: 0
        });

        /// @dev TEAM SCHEDULE
        uint256 teamAmount = MAX_SUPPLY_TEAM;
        vestingSchedules[VestingType.TEAM] = VestingSchedule({
            totalAmount: teamAmount,
            totalReleased: 0,
            daysBeforeCliff: 180,
            daysAfterCliff: 18 * 30,
            dropCliff: 50
        });

        /// @dev DAO SCHEDULE
        uint256 daoAmount = MAX_SUPPLY_DAO;
        vestingSchedules[VestingType.DAO] = VestingSchedule({
            totalAmount: daoAmount,
            totalReleased: 0,
            daysBeforeCliff: 0,
            daysAfterCliff: 18 * 30,
            dropCliff: 50
        });

        require(address(_cvg) != address(0), "CVG_ZERO");
        cvg = _cvg;
        require(
            _cvg.balanceOf(address(this)) >= seedAmount + wlAmount + iboAmount + teamAmount + daoAmount,
            "NOT_ENOUGH_CVG"
        );
    }

    /// @notice Open vesting for all
    function openVesting() external onlyOwner {
        require(state == State.SET, "VESTING_ALREADY_OPENED");
        state = State.OPEN;
    }

    /**
     * @notice Release CVG token available for SEED nft owner
     * @param _tokenId token Id SEED
     */
    function releaseSeed(uint256 _tokenId) external onlyOwnerOfSeed(_tokenId) {
        require(state == State.OPEN, "VESTING_NOT_OPEN");
        (uint256 amountToRelease, , ) = _computeReleaseAmount(_tokenId, VestingType.SEED);
        require(amountToRelease != 0, "NOT_RELEASABLE");

        //update totalReleased & amountReleasedId
        vestingSchedules[VestingType.SEED].totalReleased += amountToRelease;

        amountReleasedIdSeed[_tokenId] += amountToRelease;

        //transfer Cvg amount to release
        cvg.transfer(msg.sender, amountToRelease);
    }

    /**
     * @notice Release CVG token available for WL nft owner
     * @param _tokenId token Id WL
     */
    function releaseWl(uint256 _tokenId) external onlyOwnerOfWl(_tokenId) {
        require(state == State.OPEN, "VESTING_NOT_OPEN");
        (uint256 amountToRelease, , ) = _computeReleaseAmount(_tokenId, VestingType.WL);
        require(amountToRelease != 0, "NOT_RELEASABLE");

        //update totalReleased & amountReleasedIdSeed
        vestingSchedules[VestingType.WL].totalReleased += amountToRelease;

        amountReleasedIdWl[_tokenId] += amountToRelease;

        //transfer Cvg amount to release
        cvg.transfer(msg.sender, amountToRelease);
    }

    /**
     * @notice Release CVG token available for IBO nft owner
     * @param _tokenId token Id IBO
     */
    function releaseIbo(uint256 _tokenId) external onlyOwnerOfIbo(_tokenId) {
        require(state == State.OPEN, "VESTING_NOT_OPEN");
        (uint256 amountToRelease, , ) = _computeReleaseAmount(_tokenId, VestingType.IBO);
        require(amountToRelease != 0, "NOT_RELEASABLE");

        //update totalReleased & amountReleasedIdSeed
        vestingSchedules[VestingType.IBO].totalReleased += amountToRelease;

        amountReleasedIdIbo[_tokenId] += amountToRelease;

        //transfer Cvg amount to release
        cvg.transfer(msg.sender, amountToRelease);
    }

    /// @notice Release CVG token available for whitelisted address TEAM or DAO
    function releaseTeamOrDao(bool _isTeam) external {
        uint256 amountToRelease;
        VestingType _vestingType;

        if (_isTeam) {
            require(msg.sender == whitelistedTeam, "NOT_TEAM");
            _vestingType = VestingType.TEAM;
        } else {
            require(msg.sender == whitelistedDao, "NOT_DAO");
            _vestingType = VestingType.DAO;
        }

        (amountToRelease, , ) = _computeReleaseAmount(0, _vestingType);
        require(amountToRelease != 0, "NOT_RELEASABLE");

        vestingSchedules[_vestingType].totalReleased += amountToRelease;

        /// @dev transfer Cvg amount to release
        cvg.transfer(msg.sender, amountToRelease);
    }

    /* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=
                            INTERNALS
    =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= */

    function _computeReleaseAmount(
        uint256 _tokenId,
        VestingType _vestingType
    ) internal view returns (uint256 amountToRelease, uint256 totalAmount, uint256 totalAmountReleased) {
        if (_vestingType == VestingType.SEED) {
            totalAmountReleased = amountReleasedIdSeed[_tokenId];
            totalAmount = presaleSeed.presaleInfoTokenId(_tokenId).cvgAmount;
        } else if (_vestingType == VestingType.WL) {
            totalAmountReleased = amountReleasedIdWl[_tokenId];
            totalAmount = presaleWl.presaleInfos(_tokenId).cvgAmount;
        } else if (_vestingType == VestingType.IBO) {
            totalAmountReleased = amountReleasedIdIbo[_tokenId];
            totalAmount = ibo.totalCvgPerToken(_tokenId);
        } else if (_vestingType == VestingType.TEAM) {
            totalAmountReleased = vestingSchedules[_vestingType].totalReleased;
            totalAmount = MAX_SUPPLY_TEAM;
        } else {
            totalAmountReleased = vestingSchedules[_vestingType].totalReleased;
            totalAmount = MAX_SUPPLY_DAO;
        }

        amountToRelease = _calculateRelease(_vestingType, totalAmount, totalAmountReleased);
    }

    /**
     * @dev Calculate the releasable amount in function of the vestingSchedule params, the total amount vested for a tokenId
     * and the total amount already released. Calculated linearly between cliff release and the end of the vesting.
     */
    function _calculateRelease(
        VestingType vestingType,
        uint256 totalAmount,
        uint256 totalAmountReleased
    ) private view returns (uint256 amountToRelease) {
        uint256 cliffTimestamp = startTimestamp + vestingSchedules[vestingType].daysBeforeCliff * ONE_DAY;

        uint256 endVestingTimestamp = cliffTimestamp + vestingSchedules[vestingType].daysAfterCliff * ONE_DAY;

        if (block.timestamp > cliffTimestamp) {
            if (block.timestamp > endVestingTimestamp) {
                amountToRelease = totalAmount - totalAmountReleased;
            } else {
                uint256 ratio = ((endVestingTimestamp - block.timestamp) * ONE_GWEI) /
                    (endVestingTimestamp - cliffTimestamp);

                uint256 amountDroppedAtCliff = (totalAmount * vestingSchedules[vestingType].dropCliff) / 1000;

                uint256 totalAmountAfterCliff = totalAmount - amountDroppedAtCliff;

                amountToRelease =
                    amountDroppedAtCliff +
                    (((ONE_GWEI - ratio) * totalAmountAfterCliff) / ONE_GWEI) -
                    totalAmountReleased;
            }
        }
    }
}
设置
{
  "compilationTarget": {
    "contracts/PresaleVesting/VestingCvg.sol": "VestingCvg"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 250
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"contract IPresaleCvgWl","name":"_presaleWl","type":"address"},{"internalType":"contract IPresaleCvgSeed","name":"_presaleSeed","type":"address"},{"internalType":"contract IboInterface","name":"_ibo","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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"},{"inputs":[],"name":"MAX_SUPPLY_DAO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY_TEAM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_DAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_GWEI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amountReleasedIdIbo","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amountReleasedIdSeed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amountReleasedIdWl","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cvg","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"enum VestingCvg.VestingType","name":"_vestingType","type":"uint8"}],"name":"getInfoVestingTokenId","outputs":[{"components":[{"internalType":"uint256","name":"amountReleasable","type":"uint256"},{"internalType":"uint256","name":"totalCvg","type":"uint256"},{"internalType":"uint256","name":"amountRedeemed","type":"uint256"}],"internalType":"struct VestingCvg.InfoVestingTokenId","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum VestingCvg.VestingType","name":"_vestingType","type":"uint8"}],"name":"getTotalReleasedScheduleId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ibo","outputs":[{"internalType":"contract IboInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"openVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"presaleSeed","outputs":[{"internalType":"contract IPresaleCvgSeed","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"presaleWl","outputs":[{"internalType":"contract IPresaleCvgWl","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"releaseIbo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"releaseSeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_isTeam","type":"bool"}],"name":"releaseTeamOrDao","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"releaseWl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_cvg","type":"address"}],"name":"setVesting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWhitelistedDao","type":"address"}],"name":"setWhitelistDao","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWhitelistedTeam","type":"address"}],"name":"setWhitelistTeam","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"state","outputs":[{"internalType":"enum VestingCvg.State","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum VestingCvg.VestingType","name":"","type":"uint8"}],"name":"vestingSchedules","outputs":[{"internalType":"uint80","name":"daysBeforeCliff","type":"uint80"},{"internalType":"uint80","name":"daysAfterCliff","type":"uint80"},{"internalType":"uint96","name":"dropCliff","type":"uint96"},{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"uint256","name":"totalReleased","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistedDao","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistedTeam","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]