Lazy minting with a voucher

I am trying to lazy mint NFTs with a voucher. I am mostly using the codes from this GitHub repo: https://github.com/ipfs-shipyard/nft-school-examples/tree/main/lazy-minting . I have decided to add two new parameters (a uint256 parameter royaltyAmount and an address royaltyReceiver) inside the NFTVoucher (in LazyMinter class). I also described new parameters inside the smart contract's _hash function. After adding this I started receiving the wrong address from the smart contract's _verify function. Can someone help me?

Below is how I added new parameters to the createVoucher function:

/**
   * Creates a new NFTVoucher object and signs it using this LazyMinter's signing key.
   *
   * @param {ethers.BigNumber | number} tokenId the id of the un-minted NFT
   * @param {string} uri the metadata URI to associate with this NFT
   * @param {ethers.utils.getAddress | address} royaltyReceiver the address of which the royalty fee will be sent
   * @param {ethers.BigNumber | number} royaltyAmount the percentage of royalty fee
   * @param {ethers.BigNumber | number} minPrice the minimum price (in wei) that the creator will accept to redeem this NFT. defaults to zero
   *
   * @returns {NFTVoucher}
   */
  async createVoucher(tokenId, uri, royaltyReceiver, royaltyAmount, minPrice = 0) {
    const voucher = { tokenId, minPrice, uri, royaltyReceiver, royaltyAmount}
    const domain = await this._signingDomain()
    const types = {
      NFTVoucher: [
        {name: "tokenId", type: "uint256"},
        {name: "minPrice", type: "uint256"},
        {name: "uri", type: "string"},
        {name: "royaltyReceiver", type: "address"},
        {name: "royaltyAmount", type: "uint256"}
      ]
    }

and in my smart contract, I changed the _hash like this:

// @notice Returns a hash of the given NFTVoucher, prepared using EIP712 typed data hashing rules.
  /// @param voucher An NFTVoucher to hash.
  function _hash(NFTVoucher calldata voucher) internal view returns (bytes32) {
    return _hashTypedDataV4(keccak256(abi.encode(
      keccak256("NFTVoucher(uint256 tokenId,uint256 minPrice,string uri, address royaltyReceiver, uint256 royaltyAmount)"),
      voucher.tokenId,
      voucher.minPrice,
      keccak256(bytes(voucher.uri)),
      voucher.royaltyReceiver,
      voucher.royaltyAmount
    )));
  }

I also changed the NFTVoucher struct like this:

/// @notice Represents an un-minted NFT, which has not yet been recorded into the blockchain. A signed voucher can be redeemed for a real NFT using the redeem function.
  struct NFTVoucher {
    /// @notice The id of the token to be redeemed. Must be unique - if another token with this ID already exists, the redeem function will revert.
    uint256 tokenId;

    /// @notice The minimum price (in wei) that the NFT creator is willing to accept for the initial sale of this NFT.
    uint256 minPrice;

    /// @notice The metadata URI to associate with this token.
    string uri;

    /// @notice The address of which the royalty fee will be sent.
    address royaltyReceiver;

    /// @notice The percentage of royalty fee
    uint256 royaltyAmount;

    /// @notice the EIP-712 signature of all other fields in the NFTVoucher struct. For a voucher to be valid, it must be signed by an account with the MINTER_ROLE.
    bytes signature;
  }
1 Like

brother did you find any solution

I did but I don't know how or why? What I've done is this: I have reverted back everything to its original place. Then I added my two parameters at the end of the "everything" (NFTVoucher struct in the smart contract, at the end of the _hash function, and at the end of the NFTVoucher object in createVoucher, etc). And it worked. I really don't understand why it didn't work in the first place and I don't know why it works now. Weird...

i am using etherjs (_signTypedData) to generate a signature and sending it to contract to be verified. but the decoded address in the contract comes out to be different. in the frontend , using the etherjs library, address comes out to be correct

Having the same problem here. The contract outputted by _verify is completely random.

what can be the solution. can you please tell me why do we need to do abi.encode and keccak256 before using hashSignTypedDataV4 in in order to get the digest so that ECDSA.recover can be used.

I am also not sure. You should open a new question for this.

I also noticed something. For some reason the output of the async createVoucher function wasn't returning in the same order (tokenId, minPrice, uri, royaltyReceiver, royaltyAmount). I had to manually change the order so it matched with the order of struct NFTVoucher in my smart contract.

Hi everyone,
I tried to pass the voucher data in ordered object like
const voucherOrdered = {
tokenId: voucher.tokenId,
price: voucher.price,
uri: voucher.uri,
artistAddress: voucher.artistAddress, -- this is my newly added parameter
signature: voucher.signature,
};
but it did not help, the _verify in the contract still returns the random signer

it was solved with removing space in the _hashTypedDataV4 in the smart contract


thanks a lot @caffeinum for the catch!

1 Like

hello i am trying to add lazy mint for my NFT contract in remixIDE, so i am not knowing how to generate this signature value. can anyone help me