signature-mint-erc721

Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨

Stars
20

ERC721 - Signature minting

Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨

How it works

Lazy minting or Signature minting is a mechanism to mint NFTs (or any kind of Token) just in time, without paying the gas fees upfront, and let the user pay for it along with the price of the NFT, if any.

In traditional minting, the token is minted to a specific address, or transferred to it after minting. While for lazy minting, an authorized contract admin approves an external request/voucher and enables them to mint the token in the admin's contract.

A mint voucher or request is a struct that needs to be signed by the authorized contract admin. It has all the parameters for the NFT to be minted, aswell as additional metadata such as the price, the currency to be used.

The voucher is then hashed using the EIP712 standard and signed by the authorized contract admin. Once signed, the signer can be retrieved any time by recovering it using ECDSA algorithm from the signature. This can be used to ensure that the voucher is not tampered with, and is signed by someone who is authorized to mint tokens.

How is the voucher validity checked?

Here, we use 2 parameters to check if a specific voucher is valid.

  • If the uuid specified in the voucher has been minted. We use a mapping for UUID with their minted
    status to verify (mapping(bytes32 => bool) private minted;)
  • If the signer is someone who is authorized to mint the token. In this case, I check if the signer has the
    MINTER_ROLE role in the contract, as we use AccessControlEnumerable from OpenZeppelin library. This can
    be the owner, the admin for the contract aswell, as you want.

How does the signature-mint process work?

The whole process is divided into 3 main steps.

  1. The mint voucher payload generation. The contract admin creates a payload containing the details based on the parameters of the voucher. The parameters are validated, and the price is converted from the native token or the ERC20 token (if specified in the currency) into gwei (1e9 wei). And then it's ready to be signed.

  2. The voucher signing by the authorized contract admin. The payload generated is then signed using the ECDSA algorithm(ECDSA.toTypedDataHash), and the signature is returned.

  3. Minting tokens using the signed voucher. Once the signature is generated, it's forwarded to the external account trying to mint the token. Using the signatureMint function (or whatever you name it), the signature is validated first, then the NFT is minted to the specified address. The price is deducted if specified from the native tokens/ERC20, whichever is specified.

The Mint voucher

This is the struct that holds the parameters for the voucher.

struct MintVoucher {
  bytes32 uuid;
  address to;
  string uri;
  uint256 price;
  address currency;
  address paymentReceiver;
}

The parameters can be described as so:

Parameter Type Usage
uuid bytes The UUID generated for having unique signatures. This is autogenerated.
to address The address where the NFT will be sent to. Defaults to address(0)
uri string The URI containing all the metadata for the NFT to be minted.
price integer The price for the NFT to be minted. Defaults to 0.
currency address The address of the currency to be paid in. Defaults to the native currency address.
paymentReceiver address The creator of the NFT or the address, who receives the payment of the price for the NFT.

The voucher can be verified with the function provided in the SignatureMint contract.

function verify(MintVoucher calldata voucher, bytes calldata signature)
  public
  view;

This function uses the 2 validation steps mentioned above to ensure that the voucher is valid.

And, the signed voucher can be redeemed using the following function in the NFT contract.

function signatureMint(MintVoucher calldata voucher, bytes calldata signature)
  external
  payable
  nonReentrant;

This uses the verify function internally to ensure it's valid. If it is, it processes the payment and mints the NFT as specified.

Usage

Hardhat tasks are provided with the contracts in order to test the signature mint process and make it easy for anyone to use it.

Firstly, install the dependencies to get started.

npm install -D
# OR
yarn

Next, build the contracts.

yarn run build

Every task is run on the local network. If you want to use a different network, use the --network option as --network mumbai. You can check the networks supported in the hardhat config file.

Deploying a contract

You can deploy the contract using the following task:

npx hardhat deploy:NFT --name "My NFT contract" --symbol MNFT

The flags for this task are:

Flag Optional Usage
name NO The name of the NFT contract
symbol NO The symbol to be used for the tokens by the contract

Generating a signature

This task allows you to generate a signed voucher.

npx hardhat nft:generate-signature

The flags for this task are:

Flag Optional Usage
contract NO The address of the deployed NFT contract
metadata NO The URI containing metadata for the NFT to be minted
paymentreceiver NO The address which receives the payment for the NFT
to Yes. Defaults to Zero address. The address which receives the NFT.
price Yes. Defaults to zero. The price for the NFT.

Verifying a signature

This task allows you to verify the signed voucher for validity.

npx hardhat nft:verify-signature

The flags for this task are:

Flag Optional Usage
contract NO The address of the deployed NFT contract
signedpayload NO The signed payload to be verified for validity generated from generate-signature task

Minting using a signed voucher

This task allows you to verify the signed voucher for validity.

npx hardhat nft:signature-mint

The flags for this task are:

Flag Optional Usage
contract NO The address of the deployed NFT contract
signedpayload NO The signed payload to be verified for validity generated from generate-signature task

Contributing

Contributions, issues and feature requests are welcome. After cloning & setting up project locally, you can just submit a PR to this repo and it will be deployed once it's accepted.

⚠️ It’s good to have descriptive commit messages, or PR titles so that other contributors can understand about your commit or the PR Created. Read conventional commits before making the commit message.

Show your support

We love people's support in growing and improving. Be sure to leave a ⭐️ if you like the project and also be sure to contribute, if you're interested!