You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ERC721合约中设置白名单发售与公开发售的双供应量上限?

How to Separate Max Supply for Whitelist and Public NFT Sales

Hey James, this is a super common requirement for NFT launches, and you’re totally right that a single maxSupply variable can’t handle splitting mint limits between phases. Let’s walk through the implementation logic first, then jump into a Solidity code example that covers all your needs.

Core Implementation Logic

The key is to track minted counts separately for each phase, plus add guards for per-wallet limits and overall total supply. Here’s the breakdown:

  • Track phase-specific minted amounts: Use two state variables to keep tabs on how many NFTs have been minted during the whitelist phase and public phase respectively.
  • Define phase limits: Set a hard cap for the whitelist phase (1500 in your case) and calculate the public phase limit as totalMaxSupply - whitelistMaxSupply (which would be 8500 here).
  • Per-wallet mint limit: Use a mapping to track how many NFTs each wallet has minted, and enforce that this never exceeds your per-wallet cap (10).
  • Phase state control: Add a state variable to track which sale phase is active (whitelist, public, or closed) so you can restrict minting to the correct phase.

Solidity Code Example

Here’s a complete example based on OpenZeppelin’s ERC721 implementation—this covers all your requirements:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract PhasedNFTSale is ERC721, Ownable {
    // Total maximum supply of NFTs
    uint256 public constant totalMaxSupply = 10000;
    // Maximum NFTs allowed to be minted during whitelist phase
    uint256 public constant whitelistMaxSupply = 1500;
    // Maximum NFTs a single wallet can mint in total
    uint256 public constant perWalletMax = 10;

    // Track minted counts per phase
    uint256 public whitelistMinted;
    uint256 public publicMinted;

    // Whitelist storage (could also use a Merkle tree for gas efficiency with large whitelists)
    mapping(address => bool) public isWhitelisted;

    // Sale phase enum
    enum SalePhase {
        Closed,
        Whitelist,
        Public
    }
    SalePhase public currentPhase;

    // Mapping to track how many NFTs each wallet has minted
    mapping(address => uint256) public walletMintedCount;

    constructor() ERC721("PhasedNFT", "PNFT") Ownable(msg.sender) {
        currentPhase = SalePhase.Closed;
    }

    // Owner-only function to add addresses to whitelist
    function addToWhitelist(address[] calldata addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
            isWhitelisted[addresses[i]] = true;
        }
    }

    // Owner-only function to switch sale phases
    function setSalePhase(SalePhase newPhase) external onlyOwner {
        currentPhase = newPhase;
    }

    // Whitelist mint function
    function whitelistMint(uint256 quantity) external payable {
        // Check if we're in whitelist phase
        require(currentPhase == SalePhase.Whitelist, "Whitelist sale not active");
        // Check if sender is whitelisted
        require(isWhitelisted[msg.sender], "Not whitelisted");
        // Check per-wallet limit
        require(walletMintedCount[msg.sender] + quantity <= perWalletMax, "Exceeds per-wallet limit");
        // Check whitelist phase supply limit
        require(whitelistMinted + quantity <= whitelistMaxSupply, "Whitelist supply exhausted");
        // Check total supply limit
        require(whitelistMinted + publicMinted + quantity <= totalMaxSupply, "Total supply exhausted");

        // Update mint counts
        walletMintedCount[msg.sender] += quantity;
        whitelistMinted += quantity;

        // Mint the NFTs
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = whitelistMinted + publicMinted - quantity + i;
            _safeMint(msg.sender, tokenId);
        }
    }

    // Public mint function
    function publicMint(uint256 quantity) external payable {
        // Check if we're in public phase
        require(currentPhase == SalePhase.Public, "Public sale not active");
        // Check per-wallet limit
        require(walletMintedCount[msg.sender] + quantity <= perWalletMax, "Exceeds per-wallet limit");
        // Check public phase supply limit (total max minus whitelist max)
        require(publicMinted + quantity <= totalMaxSupply - whitelistMaxSupply, "Public supply exhausted");
        // Check total supply limit
        require(whitelistMinted + publicMinted + quantity <= totalMaxSupply, "Total supply exhausted");

        // Update mint counts
        walletMintedCount[msg.sender] += quantity;
        publicMinted += quantity;

        // Mint the NFTs
        for (uint256 i = 0; i < quantity; i++) {
            uint256 tokenId = whitelistMinted + publicMinted - quantity + i;
            _safeMint(msg.sender, tokenId);
        }
    }
}

Key Details to Note

  1. Whitelist Storage: The example uses a simple mapping for whitelisting, which works fine for 2000 addresses. For larger whitelists, consider using a Merkle Tree to save gas (you can verify a proof instead of storing every address on-chain).
  2. Phase Control: The setSalePhase function lets you toggle between closed, whitelist, and public phases—only the contract owner can call this, so you have full control over when each phase starts/ends.
  3. Per-Wallet Limit: The walletMintedCount mapping tracks total mints per wallet across both phases, so a user can’t mint 10 in whitelist and another 10 in public.
  4. Redundant Supply Checks: We add checks for total supply alongside phase-specific limits to ensure we never exceed the 10000 total cap, even if there’s a bug in phase logic.

This setup gives you full control over each sale phase’s supply, enforces per-wallet limits, and stays within your total supply cap. Let me know if you need help adapting this to your specific stack!

内容的提问来源于stack exchange,提问作者JamesL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:27:37