EVM Accesslists

Gate who gets to buy your NFTs

Merkle Tree Based Accesslist

Using a Merkle tree is a little more involved, but more secure and gas efficient.

Here is an example accesslist mint function:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

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

contract Accesslist is ERC721, Ownable {
  bytes32 public merkleRoot;
  uint256 public nextTokenId;
  mapping(address => bool) public claimed;

  constructor() ERC721("ExampleNFT", "NFT") {
    merkleRoot = 0x0;

  function isWhiteListed(bytes32[] memory proof, bytes32 leaf) public view returns(bool) {
        return MerkleProof.verify(proof, merkleRoot, leaf);

  function setMerkleRoot(bytes32 merkleRoot_) external onlyOwner{
    merkleRoot = merkleRoot_;

  function mint(address to, bytes32[] calldata merkleProof) public payable {
    require(claimed[to] == false, "already claimed");
    claimed[to] = true;
    require(isWhiteListed(merkleProof, keccak256(abi.encodePacked(to))), "invalid merkle proof");
    _mint(to, nextTokenId);

Please contact a tech support engineer in our discord to set up your accesslist mint with Crossmint.

After creating a ticket, please provide the following information:

  1. Your Crossmint clientId for the collection
  2. A list of addresses that should be included in the accesslist phase. (Email, SOL, or EVM)


Why do we need a list of addresses from you?

Crossmint users can login with email, metamask, or phantom wallets. That login is then linked to unique user wallets which we will pass to the _to parameter in your mint function and which must be included in the Merkle root so they can mint during the accesslist.

Once an engineer picks up your support ticket we will do the following:

  1. Provide a list of Crossmint custodial wallets we generated for the list you provided.
  2. If applicable we will also provide you with a Merkle root hash generated from the list.

Mapping Based Accesslists

The simplest form of whitelisting involves maintaining a mapping in your smart contract that holds the addresses of whitelisted supporters.

mapping(address => uint256) public whitelistMapping;

The following example maps the address for a user to how many tokens they can mint during the presale phase. The presale mint function ensures the minter is in the mapping and has tokens remaining.

function presale(address _to, uint256 _count) external payable {
    // ensure _to address has tokens left to mint
    require(whitelistMapping[_to] > 0, "no whitelist tokens for user");
    // decrement mapping for user
    whitelistMapping[_to] -= _count;
    // presale minting logic here

Note that this approach consumers higher gas fees, specially if the accesslist is large, and that savvy developers my be able to determine the addresses of the accesslist.