NFTMintSaleMultiple is a contract that handles the sale and minting of NFTs with multiple pricing tiers. It allows users to buy NFTs using a specified payment token and mints the NFTs using a VibeERC721 contract. The contract also handles the distribution of sale proceeds and fees.

Inherited Contracts


NFTMintSale inherits OpenZeppelin's Ownable contract, which provides basic authorization control functions such as onlyOwner modifier and transferring contract ownership.

Key Functions

The init function initializes the NFTMintSaleMultiple contract with the necessary parameters such as sale start and end times, tier information, and payment token. It also sets the VibeFees for the sale.

function init(bytes calldata data) external {
    (address proxy, uint32 beginTime_, uint32 endTime_, TierInfo[] memory tiers_, IERC20 paymentToken_, address owner_) = abi.decode(data, (address, uint32, uint32, TierInfo[], IERC20, address));
    require(nft == VibeERC721(address(0)), "Already initialized");


The setVibeFees function allows setting the fees for the sale, including the Vibe treasury address and the fee percentage. It can only be called by the master contract owner.

function setVibeFees(address vibeTreasury_, uint96 feeTake_) external onlyMasterContractOwner {
    fees = VibeFees(vibeTreasury_, feeTake_);

The _preBuyCheck internal function can be overridden in derived contracts to add custom checks before minting an NFT for a specific tier.

function _preBuyCheck(address recipient, uint256 tier) internal virtual {}

The buyNFT function allows a user to buy and mint an NFT for a specified recipient from a specific tier. It performs a pre-buy check and requires that the current time is within the sale period and the tier's current ID is within the tier's range. It transfers the required payment amount from the user to the contract and mints the NFT with the specific ID.

function buyNFT(address recipient, uint256 tier) public {
    _preBuyCheck(recipient, tier);
    TierInfo memory tierInfo = tiers[tier];
    uint256 id = uint256(tierInfo.currentId);
    require(block.timestamp >= beginTime && block.timestamp <= endTime && id <= tierInfo.endId);
    paymentToken.safeTransferFrom(msg.sender, address(this), uint256(tierInfo.price));
    nft.mintWithId(recipient, id);
    emit LogNFTBuy(recipient, id, tier);

The buyMultipleNFT function allows a user to buy and mint multiple NFTs for a specified recipient from multiple tiers. It calls the buyNFT function for each tier specified in the input array.

function buyMultipleNFT(address recipient, uint256[] calldata tiersToBuy) external {
    for (uint i; i < tiersToBuy.length; i++) {
        buyNFT(recipient, tiersToBuy[i]);

    emit LogNFTBuyMultiple(recipient, tiersToBuy);

The removeTokensAndReclaimOwnership function allows the owner to end the sale early or after the sale has ended, distribute the proceeds and fees, and renounce minter role of the NFT contract. It also checks if the proceeds recipient is a contract that supports the IDistributor interface and calls the distribute function if it does.

function removeTokensAndReclaimOwnership(address proceedRecipient) external onlyOwner {
    if(block.timestamp < endTime){
        endTime = uint32(block.timestamp);
        emit SaleEndedEarly();
    } else {
        emit SaleEnded();
    uint256 total = paymentToken.balanceOf(address(this));
    uint256 fee = total * uint256(fees.feeTake) / BPS;
    paymentToken.safeTransfer(proceedRecipient, total - fee);
    paymentToken.safeTransfer(fees.vibeTreasury, fee);

    if (proceedRecipient.code.length > 0) {
        (bool success, bytes memory result) ="supportsInterface(bytes4)", type(IDistributor).interfaceId));
        if (success) {
            (bool distribute) = abi.decode(result, (bool));
            if (distribute) {
                IDistributor(proceedRecipient).distribute(paymentToken, total - fee);

    emit TokensClaimed(total, fee, proceedRecipient);

The extendEndTime function allows the owner to extend the sale end time. It requires that the new end time is in the future.

function extendEndTime(uint32 newEndTime) external onlyOwner {
    require(newEndTime > block.timestamp);
    endTime = newEndTime;

    emit SaleExtended(endTime);


The Created event is emitted when the contract is initialized with the sale parameters.

The LogNFTBuy event is emitted when an NFT is bought and minted from a specific tier. It includes the recipient address, the tokenId, and the tier.

LogNFTBuyMultiple The LogNFTBuyMultiple event is emitted when multiple NFTs are bought and minted from multiple tiers. It includes the recipient address and an array of tiers.

The SaleExtended event is emitted when the sale end time is extended. It includes the new end time.

The SaleEnded event is emitted when the sale ends as per the original schedule.

The SaleEndedEarly event is emitted when the sale is ended early by the contract owner.

The TokensClaimed event is emitted when the sale proceeds are claimed. It includes the total amount, fee, and proceed recipient address.


To use NFTMintSaleMultiple, deploy the contract and initialize it with the necessary parameters such as sale start and end times, tier information, and payment token. Users can then buy NFTs using the buyNFT.