I've been revisiting ethers.js recently to refresh my understanding of the details and to write a simple tutorial called "WTF Ethers" for beginners.
Twitter: @0xAA_Science
Community: Website wtf.academy | WTF Solidity | discord | WeChat Group Application
All the code and tutorials are open-sourced on GitHub: github.com/WTFAcademy/WTF-Ethers
In this chapter, we will introduce a method of using off-chain signatures as a whitelist for NFTs. If you are not familiar with the ECDSA contract, please refer to WTF Solidity 37: Digital Signature.
Digital Signature
If you have used opensea to trade NFTs, you will be familiar with signatures. The image below shows the window that pops up when signing with the small fox (Metamask) wallet. It proves that you own the private key without needing to disclose it publicly.

The digital signature algorithm used by Ethereum is called Elliptic Curve Digital Signature Algorithm (ECDSA), based on the digital signature algorithm of elliptic curve "private key - public key" pairs. It serves three main purposes:
- Identity Authentication: Proves that the signer is the holder of the private key.
- Non-Repudiation: The sender cannot deny sending the message.
- Integrity: The message cannot be modified during transmission.
Digital Signature Contract Overview
The SignatureNFT contract in the WTF Solidity 37: Digital Signature uses ECDSA to validate whitelist addresses and mint NFTs. Let's discuss two important functions:
-
Constructor: Initializes the name, symbol, and signing public key
signerof the NFT. -
mint(): Validates the whitelist address usingECDSAand mints the NFT. The parameters are the whitelist addressaccount, thetokenIdto be minted, and the signature.
Generating a Digital Signature
-
Message Packaging: According to the
ECDSAstandard in Ethereum, themessageto be signed is thekeccak256hash of a set of data, represented asbytes32. We can use thesolidityKeccak256()function provided byethers.jsto pack and compute the hash of any content we want to sign. It is equivalent tokeccak256(abi.encodePacked())in Solidity.In the code below, we pack an
addressvariable and auint256variable, and calculate the hash to obtain themessage: -
Signing: To prevent users from mistakenly signing malicious transactions,
EIP191advocates adding the"\x19Ethereum Signed Message:\n32"character at the beginning of themessage, hashing it again withkeccak256to obtain theEthereum signed message, and then signing it. The wallet class inethers.jsprovides thesignMessage()function for signing according to theEIP191standard. Note that if themessageis of typestring, it needs to be processed using thearrayify()function. Example:
Off-Chain Signature Whitelist Minting of NFTs
-
Create a
providerandwallet, wherewalletis the wallet used for signing. -
Generate the
messageand sign it based on the whitelist addresses and thetokenIdthey can mint.
-
Create a contract factory to prepare for deploying the NFT contract.
-
Deploy the NFT contract using the contract factory.

-
Call the
mint()function of theNFTcontract, use off-chain signature to verify the whitelist, and mint anNFTfor theaccountaddress.
For Production
To use off-chain signature verification whitelisting to issue NFT in a production environment, follow these steps:
- Determine the whitelist.
- Maintain the private key of the signing wallet in the backend to generate the
messageandsignaturefor whitelisted addresses. - Deploy the
NFTcontract and save the public key of the signing wallet (signer) in the contract. - When a user wants to mint, request the
signaturecorresponding to the address from the backend. - Use the
mint()function to mint theNFT.
Summary
In this lesson, we introduced how to use ethers.js together with smart contracts to verify whitelisting using off-chain digital signatures for NFTs. Merkle Tree and off-chain digital signatures are currently the most popular and cost-effective ways to distribute whitelists. If the whitelist is already determined during contract deployment, we recommend using the Merkle Tree approach. If the whitelist needs to be constantly updated after contract deployment, such as in the case of the Galaxy Project's OAT, we recommend using the off-chain signature verification approach, otherwise, the root of the Merkle Tree in the contract needs to be constantly updated, which costs alot of gas.