I'm currently relearning Solidity to consolidate some details and write a 'WTF Solidity QuickStart' for newbies to use (programming experts can find other tutorials), with 1-3 updates per week.
Welcome to follow my Twitter: @0xAA_Science
Welcome to join the WTF Scientist community, where you can find instructions to join our WeChat group: link
All code and tutorials are open-sourced on GitHub (course certification with 1024 stars, community NFT with 2048 stars): github.com/AmazingAng/WTF-Solidity
In this lecture, we will briefly introduce the digital signature ECDSA
in Ethereum and how to use it to issue an NFT
whitelist. The ECDSA
library used in the code is simplified from the library of the same name from OpenZeppelin
.
Digital Signature
If you have traded NFT
on opensea
, you are no stranger to signatures. The following picture shows the window that pops up when the metamask
wallet signs, which can prove that you own the private key without exposing it to the public.

The digital signature algorithm used in Ethereum is called the Elliptic Curve Digital Signature Algorithm (ECDSA
), which is a digital signature algorithm based on the "private-public key" pair of elliptic curves. It mainly plays three roles:
- Identity authentication: Prove that the signer is the holder of the private key.
- Non-repudiation: The sender cannot deny having sent the message.
- Integrity: The message cannot be modified during transmission.
ECDSA
Contract
The ECDSA
standard consists of two parts:
- The signer uses the
private key
(private) to create asignature
(public) for themessage
(public). - Others use the
message
(public) andsignature
(public) to recover the signer'spublic key
(public) and verify the signature.
We will work together with the ECDSA
library to explain these two parts. The private key
, public key
, message
, Ethereum signed message
, and signature
used in this tutorial are shown below:
Creating a signature
1. Packing the message: In the Ethereum ECDSA
standard, the message
being signed is the keccak256
hash of a set of data, which is of type bytes32
. We can pack any content we want to sign using the abi.encodePacked()
function, and then use keccak256()
to calculate the hash as the message
. In our example, the message
is obtained from a 'uint256type variable and an
address` type variable.

2. Calculate Ethereum Signature Message: The message
can be an executable transaction or anything else. In order to prevent users from signing malicious transactions by mistake, EIP191
recommends adding the "\x19Ethereum Signed Message:\n32"
character before the message
, and then doing another keccak256
hash to create the Ethereum Signature Message
. The message processed by the toEthSignedMessageHash()
function cannot be used to execute transactions.
The processed message is:

3-1. Sign with wallet: In daily operations, most users sign messages using this method. After obtaining the message that needs to be signed, we need to use the Metamask
wallet to sign it. The personal_sign
method of Metamask
will automatically convert the message
into an Ethereum signed message
and then initiate the signature. So we only need to input the message
and the signer wallet account
. It should be noted that the input signer wallet account
needs to be consistent with the account currently connected by Metamask
.
Therefore, you need to first import the private key
in the example into the Foxlet wallet
, and then open the console
page of the browser: Chrome menu-more tools-developer tools-Console
. Under the status of connecting to the wallet (such as connecting to OpenSea, otherwise an error will occur), enter the following instructions step by step to sign:
The created signature can be seen in the returned result (PromiseResult
). Different accounts have different private keys, and the created signature values are also different. The signature created using the tutorial's private key is shown below:

3-2. Signing with web3.py: When it comes to batch calling, signing with code is preferred. The following is an implementation based on web3.py.
This is Python code that uses the web3
library and eth_account
module to sign a message using a given private key and Ethereum address. It connects to the Ankr ETH RPC endpoint and prints the keccak hash of the message and the resulting signature.
The result of the execution is shown below. The calculated message, signature, and earlier examples are consistent.
Verify Signature
To verify the signature, the verifier needs to have the message
, signature
, and the public key
used to sign the message. We can verify the signature because only the holder of the private key
can generate such a signature for the transaction, and nobody else can.
4. Recover Public Key from Signature and Message: The signature
is generated by a mathematical algorithm. Here we use the rsv signature
, which contains information about r, s, v
. Then, we can obtain the public key
from r, s, v
, and the Ethereum signature message
. The recoverSigner()
function below implements the above steps. It recovers the public key
from the Ethereum signature message _msgHash
and the signature _signature
(using simple inline assembly):
The parameters are:

5. Compare public keys and verify the signature: Next, we just need to compare the recovered public key
with the signer's public key _signer
to determine if they are equal: if they are, the signature is valid; otherwise, the signature is invalid.
These are parameters:

Using Signatures to Issue Whitelist for NFTs
The NFT
project can use the feature of ECDSA
to issue a whitelist. Since the signature is off-chain and does not require gas
, this whitelist issuance mode is more economical than the Merkle Tree
mode. The method is very simple. The project uses the project account to sign the whitelist issuance address (can add the tokenId
that the address can mint). Then, when minting
, use ECDSA
to check if the signature is valid. If it is valid, give it mint
.
The SignatureNFT
contract implements the issuance of NFT
whitelist using signatures.
State Variables
There are two state variables in the contract:
signer
:public key
, the project signature address.mintedAddress
is amapping
, which records the addresses that have already beenminted
.
Functions
There are four functions in the contract:
- The constructor initializes the name and symbol of the
NFT
, and thesigner
address ofECDSA
signature. - The
mint()
function accepts three parameters: the addressaddress
,tokenId
, and_signature
, verifies whether the signature is valid: if it is valid, theNFT
oftokenId
is minted to theaddress
address, and it is recorded inmintedAddress
. It calls thegetMessageHash()
,ECDSA.toEthSignedMessageHash()
, andverify()
functions. - The
getMessageHash()
function combines themint
address (address
type) andtokenId
(uint256
type) into amessage
. - The
verify()
function calls theverify()
function of theECDSA
library to performECDSA
signature verification.
remix
Verification
-
Sign the
signature
off-chain on Ethereum, and whitelist the_account
address withtokenId = 0
. See the <ECDSA
Contract> section for the data used. -
Deploy the
SignatureNFT
contract with the following parameters:
Deploying the SignatureNFT contract.
Calling the mint()
function to sign and mint the contract using ECDSA verification, with the following parameter:

- By calling the
ownerOf()
function, we can see thattokenId = 0
has been successfully minted to the address_account
, indicating that the contract has been executed successfully!

Summary
In this section, we introduced the digital signature ECDSA
in Ethereum, how to create and verify signatures using ECDSA
, and ECDSA
contracts, and how to distribute NFT
whitelists using them. The ECDSA
library in the code is simplified from the same library of OpenZeppelin
.
- Since the signature is off-chain and does not require
gas
, this whitelist distribution model is more cost-effective than theMerkle Tree
model; - However, since users need to request a centralized interface to obtain the signature, a certain degree of decentralization is inevitably sacrificed;
- Another advantage is that the whitelist can be dynamically changed, rather than being hardcoded in the contract in advance because the central backend interface of the project can accept requests from any new address and provide whitelist signatures.