# TokenId in lazy minting

**URL:** <https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229>\
**Category:** Contracts\
**Tags:** erc721, etherscan-verify\
**Created:** [November 4, 2021, 7:50pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229 "2021-11-04T19:50:20Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ankit\_pal](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/ankit_pal/32/10113_2.png) [@Ankit\_pal](https://forum.openzeppelin.com/u/Ankit_pal)\
**Post date:** [November 4, 2021, 7:50pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/1 "2021-11-04T19:50:20Z")

</div>

I was following this tutorial from openzeppelin on lazy minting  
[link to the video](https://www.youtube.com/watch?v=SF-XOwWIwRo)[youtube link]  
by means of which a voucher is generated without modifying the state of the blockchain and hence making it gasless. What i don't understand is how is the tokenId of the voucher being kept track of(since making a counter and using that to make tokens would imply gas fees which is what i'm trying to avoid for the token creator)

```auto
    const { voucher, signature } = await lazyMinter.createVoucher(
      1,
      "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
      minPrice
    );

```

the number 1 is the tokenId that i have to provide in my tests which i'm not sure how to unload to the contract gas-lessly

This is the function used to create and redeem the voucher

```auto
const ethers = require("ethers");
const { TypedDataUtils } = require("ethers-eip712");

const SIGNING_DOMAIN_NAME = "LazyNFT-Voucher";
const SIGNING_DOMAIN_VERSION = "1";

class LazyMinter {
  constructor({ contractAddress, signer }) {
    this.contractAddress = contractAddress;
    this.signer = signer;

    this.types = {
      EIP712Domain: [
        { name: "name", type: "string" },
        { name: "version", type: "string" },
        { name: "chainId", type: "uint256" },
        { name: "verifyingContract", type: "address" },
      ],
      NFTVoucher: [
        { name: "tokenId", type: "uint256" },
        { name: "minPrice", type: "uint256" },
        { name: "uri", type: "string" },
      ],
    };
  }

  async _signingDomain() {
    if (this._domain != null) {
      return this._domain;
    }
    const chainId = await this.signer.getChainId();
    this._domain = {
      name: SIGNING_DOMAIN_NAME,
      version: SIGNING_DOMAIN_VERSION,
      verifyingContract: this.contractAddress,
      chainId,
    };
    return this._domain;
  }

  async _formatVoucher(voucher) {
    const domain = await this._signingDomain();
    return {
      domain,
      types: this.types,
      primaryType: "NFTVoucher",
      message: voucher,
    };
  }

  async createVoucher(tokenId, uri, minPrice = 0) {
    const voucher = { tokenId, uri, minPrice };
    const typedData = await this._formatVoucher(voucher);
    const digest = TypedDataUtils.encodeDigest(typedData);
    const signature = await this.signer.signMessage(digest);
    return {
      voucher,
      signature,
      digest,
    };
  }
}

module.exports = {
  LazyMinter,
};

```

The contract code to verify this is

```auto
  function _hash(NFTVoucher calldata voucher) internal view returns (bytes32) {

    return _hashTypedDataV4(keccak256(abi.encode(
      keccak256("NFTVoucher(uint256 tokenId,uint256 minPrice,string uri)"),
      //voucher.tokenId,
      curr_tokenId,
      voucher.minPrice,
      keccak256(bytes(voucher.uri))
    )));
  }

  /// @notice Verifies the signature for a given NFTVoucher, returning the address of the signer.
  /// @dev Will revert if the signature is invalid. Does not verify that the signer is authorized to mint NFTs.
  /// @param voucher An NFTVoucher describing an unminted NFT.
  /// @param signature An EIP712 signature of the given voucher.
  function _verify(NFTVoucher calldata voucher, bytes memory signature) internal view returns (address) {
    bytes32 digest = _hash(voucher);
    return digest.toEthSignedMessageHash().recover(signature);
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override (AccessControl, ERC721) returns (bool) {
    return ERC721.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
  }

```

If i understand correctly we have to store this voucher id in a centralised location off chain and retrieve as and when needed? I am new to web3 so pardon me if i missed out on any necessary details i'll be happy to provide them when asked for.

---

<div class="post-metadata">

**Author:** ![frangio](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/frangio/32/15251_2.png) [@frangio](https://forum.openzeppelin.com/u/frangio)\
**Post date:** [November 5, 2021, 1:19pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/2 "2021-11-05T13:19:37Z")

</div>

The token creator needs to generate a signed voucher, and the token buyer needs to receive both the voucher and the signature. The token buyer then has the tokenId (as it is part of the voucher) and they are the ones who have to submit it to the smart contract.

Does this answer the question?

---

<div class="post-metadata">

**Author:** ![loks2cool\_A](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/loks2cool_a/32/13281_2.png) [@loks2cool\_A](https://forum.openzeppelin.com/u/loks2cool_A)\
**Post date:** [February 17, 2022, 10:48am UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/3 "2022-02-17T10:48:13Z")

</div>

Hi Team, I have a follow-up question here. For instance, we need to increment the counter to obtain the token\_id which will definitely change the state of the blockchain and it required gas fee..

In EIP712 implementation, when seller sign the voucher with the following metadata (tokenid, minprice, uri). We need tokenid to generate upfront and keep track of it. While doing that, a gas fee is required.

Can someone please provide direction here, how to tackle this and enable gasless transaction for signing the voucher with tokenid.. Also, how to handle this tokenid generation upfront?.

Thank you so much for your support..

thanks,

---

<div class="post-metadata">

**Author:** ![frangio](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/frangio/32/15251_2.png) [@frangio](https://forum.openzeppelin.com/u/frangio)\
**Post date:** [February 18, 2022, 3:31pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/4 "2022-02-18T15:31:26Z")

</div>

If you have lazy minting using EIP712 signed vouchers then your contract would not really have a counter for token ids. It's the seller that has to keep a counter off chain and make sure to generate vouchers for new token ids.

Note also that the buyer will have no guarantees that a token id is unique and theirs until the mint is confirmed on chain.

---

<div class="post-metadata">

**Author:** ![loks2cool\_A](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/loks2cool_a/32/13281_2.png) [@loks2cool\_A](https://forum.openzeppelin.com/u/loks2cool_A)\
**Post date:** [February 18, 2022, 5:53pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/5 "2022-02-18T17:53:17Z")

</div>

> [@frangio](#):
>
> e to submit it to the smart contract.

Thank you so much, it helps!!.

---

<div class="post-metadata">

**Author:** ![DoDzilla](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/dodzilla/32/11642_2.png) [@DoDzilla](https://forum.openzeppelin.com/u/DoDzilla)\
**Post date:** [March 22, 2022, 9:21am UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/6 "2022-03-22T09:21:58Z")

</div>

I am also following the same tutorial and I have an issue. I added an extra unit 256 parameter to the NFTVoucher (in class LazyMinter). Adding this single value resulted in \_verify function to return wrong address. It was returning the minter's address not it returns an arbitrary address. I don't understand why?!

---

<div class="post-metadata">

**Author:** ![frangio](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/frangio/32/15251_2.png) [@frangio](https://forum.openzeppelin.com/u/frangio)\
**Post date:** [March 25, 2022, 1:01pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/7 "2022-03-25T13:01:33Z")

</div>

Please show some code so we can try to understand the error.

---

<div class="post-metadata">

**Author:** ![DoDzilla](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/dodzilla/32/11642_2.png) [@DoDzilla](https://forum.openzeppelin.com/u/DoDzilla)\
**Post date:** [March 28, 2022, 7:40am UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/8 "2022-03-28T07:40:03Z")

</div>

The code and the question is here [Lazy minting with a voucher](https://forum.openzeppelin.com/t/lazy-minting-with-a-voucher/26549)

---

<div class="post-metadata">

**Author:** ![Adarsh\_Raj1](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/adarsh_raj1/32/14284_2.png) [@Adarsh\_Raj1](https://forum.openzeppelin.com/u/Adarsh_Raj1)\
**Post date:** [April 5, 2022, 5:10pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/9 "2022-04-05T17:10:46Z")

</div>

i am stuck with the same thing I dont understand where to find the solution from. gone through roofs

---

<div class="post-metadata">

**Author:** ![Adarsh\_Raj1](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/adarsh_raj1/32/14284_2.png) [@Adarsh\_Raj1](https://forum.openzeppelin.com/u/Adarsh_Raj1)\
**Post date:** [April 5, 2022, 5:17pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/10 "2022-04-05T17:17:14Z")

</div>

please let me know of how you fixed this issue

---

<div class="post-metadata">

**Author:** ![marie0901](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/marie0901/32/14473_2.png) [@marie0901](https://forum.openzeppelin.com/u/marie0901)\
**Post date:** [April 25, 2022, 6:30pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/11 "2022-04-25T18:30:17Z")

</div>

hi guys, any news here?  
wrong address after adding any new parameter to the NFTvoucher

---

<div class="post-metadata">

**Author:** ![Adarsh\_Raj1](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/adarsh_raj1/32/14284_2.png) [@Adarsh\_Raj1](https://forum.openzeppelin.com/u/Adarsh_Raj1)\
**Post date:** [May 1, 2022, 3:35pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/12 "2022-05-01T15:35:32Z")

</div>

can we schedule a meeting for just five minutes?

---

<div class="post-metadata">

**Author:** ![frangio](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/frangio/32/15251_2.png) [@frangio](https://forum.openzeppelin.com/u/frangio)\
**Post date:** [May 3, 2022, 5:07pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/13 "2022-05-03T17:07:06Z")

</div>

Closed on May 3, 2022, 5:07pm UTC



---

<div class="post-metadata">

**Author:** ![frangio](https://sea2.discourse-cdn.com/flex022/user_avatar/forum.openzeppelin.com/frangio/32/15251_2.png) [@frangio](https://forum.openzeppelin.com/u/frangio)\
**Post date:** [May 3, 2022, 5:07pm UTC](https://forum.openzeppelin.com/t/tokenid-in-lazy-minting/18229/14 "2022-05-03T17:07:24Z")

</div>

For further questions please create a new post following the guidelines:

> [@How do I ask a good question?](https://forum.openzeppelin.com/t/how-do-i-ask-a-good-question/9375):
>
> If you were redirected here, your post did not meet quality standards. Please review this page and edit or create a new topic according to the guidelines. Asking good questions is important in this community. It will improve your chances of getting an answer from the OpenZeppelin team or community members, and of finding out how to solve your problem. Users are expected to put in a minimum level of effort. Topics will be closed unanswered if moderators consider that this was not the case. Re…
