How do I implement allowlist verification?
If you're using Spearmint, we do the heavy lifting for you (check out our API) so that you don't need to worry about building your own allowlist infrastructure. But If you're curious about how all of this works under the hood, read on!
The options
If you have a list of addresses that you would like to allow to call a function on your smart contract, you have the following popular options. Each have their pros and cons, which are discussed below. The summaries here assume some level of prior knowledge on the topics covered.
📚 On-chain dictionary
This is arguably the easiest way to implement your allowlist, but also the most expensive. You simply keep a mapping of addresses to booleans (or a variation of it) in your smart contract, and expose an access-controlled method that updates the mapping. Whenever you need to add addresses to it, you can do so. In the function you care about, you can check whether the sender of the transaction has a valid value in the mapping before continuing.
We would NOT recommend doing this, as the gas required to update the mapping is proportional to the number of addresses you want to allow, and this can grow to a significant amount especially under periods of network congestion.
✍️ Digital signatures
This is another way to implement allowlists, thanks to the properties of public key cryptography that blockchains like Ethereum afford.
On Ethereum, when Alice signs a message with her private key, anyone is able to recover Alice's (public) address provided that they have both the original message and the signature. In this way, we are able to prove that Alice did indeed sign the original message.
Smart contracts on Ethereum have access to the ecrecover
EVM opcode which uses Ethereum's ECDSA signature verification algorithm to verify signed messages. This can be used within smart contracts to grant access to callers who can provide a digital signature signed by an authoritative party.
To implement allowlist verification using this approach, you can provide each address on the allowlist as the message to sign, and sign those messages using a secure private key. These digital signatures can then be used by each address as a "key" to unlock functionality on your smart contract. On the contract side, msg.sender
should be used as the message to verify along with the signature. Note that you can choose to encode more information (not just the address) into the message as needed.
The great thing about this option is that you do not need to make any on-chain changes if you need to add additional addresses to the allowlist. All generated signatures will be verified against the same signer address. The one minor downside is that it requires keeping the signing private key safe.
🌲 Merkle proofs
Merkle proofs are a popular way to implement allowlists, taking advantage of the properties of Merkle trees.

An example of a Merkle tree.
In a Merkle tree, hashes of each piece of data are at the leaves, with branch nodes storing a hash of their children. This creates a unique property that allows you to verify that a certain piece of data was included in the tree with a given root node hash by only providing the hashes of neighboring nodes from the data in question up to the root node. This list of hashes is what's called a Merkle proof.

In this example, the Merkle proof for leaf 64 is "1FXq", "ec20", and "8f74" (highlighted in yellow).
For allowlist verification, you would need construct a Merkle tree using hashes of the addresses as the leaf nodes. The resulting root node hash is the critical piece you will rely on to be able to verify Merkle proofs, so you'd want to store this on your smart contract. Users calling your contract need to provide Merkle proofs associated with their address (or node in the tree), so this would need to be stored somewhere and served to the users. You can choose to implement this either on the frontend (if publicly exposing the list of allowlisted addresses is not an issue) or your own backend service.
This option has the advantage of not requiring a private key or necessarily requiring a backend. However, it has the downside of not being as amenable to changes in the allowlist. Each time an address is added or removed, the entire Merkle tree needs to be regenerated, and a new root hash must be written to the smart contract on-chain. Also, as the number of addresses in the tree increases, the size of the Merkle proof increases logarithmically, which marginally increases gas spent.
Using Spearmint
Spearmint's Attestations API supports both digital signatures and Merkle proofs. We think that the digital signatures approach provides a better experience overall after weighing the pros and cons listed above, but we offer both methods so that you can choose what works best for your use case!
Updated about 1 month ago