A tutorial that teaches how to create a signature mint, in which minters pay their own gas, but must first be given a valid signed authorization.
Ownable
to assign an owner to this contract. You could instead just save an address for the authorized signer if you aren’t going to add any functionality only the owner can invoke.
public
-facing mint function, create a function called mintTo
that accepts an address
for the _recipient
.
msg.sender
. This practice is falling out of favor. Allowing the recipient to be different than the sender gives greater flexibility. Doing so is also necessary to assign the right NFT owner in the event the user is using a smart contract wallet, paymaster, or other form of account abstraction.json
file on the traditional internet, you can put your metadata directly in the contract. To do so, first import some helper libraries:
override
the functions for _baseURI
and tokenURI
to return base 64 encoded json metadata with the information you supplied in the constructor:
override
the _update
function.
_beforeTransfer
function. Current versions of OpenZeppelin’s ERC-721 implementation have replaced that function with _update
."\x19Ethereum Signed Message:\n32"
to the message. You must also do this when creating the signed message!
Add a function to validateSignature
:
_signature
yet. The function has two inputs:
address
of the _recipient
_signature
, or signed message provided by the user claiming they have been given permission to mint the NFTmessageHash.toEthSignedMessageHash
prepends the bytes representation of "\x19Ethereum Signed Message:\n32"
to the message, then hashes the result.
Finally, calling recover
with ethSignedMessageHash
and the _signature
attempts to recover the signing address
from the _signature
using the independently constructed message data.
If the recovered address matches the expected address, in this case, the contract owner, then the provided _signature
is valid. If the addresses do not match, then the _signature
is not valid.
Update your mintTo
function to make use of the validation:
signer
address is random, rather than the first address in the list of default Hardhat accounts.
The reason for this is that while signMessage
does follow the previously mentioned standards, prepends "\x19Ethereum Signed Message:\n32"
to the message, and correctly signs it, it does not prepare the data to be signed in exactly the same way as the smart contract converts the address
to bytes32
.
To fix this, first import some helper functions from viem:
encodePacked
and keccak256
hash your variables and turn them into bytes
, just like you did in the contract in validateSignature
:
signMessage
function with the newly assembled messageBytes
. You’ll need to mark the data representation as raw
:
privateKeyToAccount
expects that your key starts with 0x
. You may need to manually add that depending on the tool you exported it from.