question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ERC721 optimised for sequential id and no burn

See original GitHub issue

🧐 Motivation

The high values for gas fees when minting has been a big issue. There have been some suggestions on how to lower these when using the ERC721 contract but then these are limited to the implementation of this same contract.

The ERC721 contract is rightfully a general implementation but there’s one case where it should be possible to considerably reduce the gas usage. I believe this is a very common case.

šŸ“ Details

For the case when using sequential ids and there’s no support for burning tokens the following optimizations for ERC721 are possible:

  1. _owners can be an array instead of a mapping.

https://github.com/aalmada/openzeppelin-contracts/blob/44d55cfe3298d563a63df7b5d67711312810112b/contracts/token/ERC721/ERC721Lite.sol#L32

  1. exists() can just check if the tokenIdis smaller than _owners.length

https://github.com/aalmada/openzeppelin-contracts/blob/44d55cfe3298d563a63df7b5d67711312810112b/contracts/token/ERC721/ERC721Lite.sol#L233-L235

  1. mint() can generate the tokenId from the value of _owners.length and _owners can be updated by using _owners.push(to)

https://github.com/aalmada/openzeppelin-contracts/blob/44d55cfe3298d563a63df7b5d67711312810112b/contracts/token/ERC721/ERC721Lite.sol#L289-L302

ERC721Enumerable can also be optimized:

  1. _allTokens and _allTokensIndex can be removed.

  2. totalSupply() can return the _owners.length from ERC721 exposed as an internal _totalSupply() method.

https://github.com/aalmada/openzeppelin-contracts/blob/44d55cfe3298d563a63df7b5d67711312810112b/contracts/token/ERC721/extensions/ERC721LiteEnumerable.sol#L40-L42

  1. tokenByIndex() can simply return the index value

https://github.com/aalmada/openzeppelin-contracts/blob/44d55cfe3298d563a63df7b5d67711312810112b/contracts/token/ERC721/extensions/ERC721LiteEnumerable.sol#L47-L50

Conclusions

My tests show that gas usage for minting reduces from around 161k to around 101k. I believe this can be considered a significant improvement for a very common scenario.

I’d like to know your opinions and if I should consider submitting a PR.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
Amxxcommented, Jan 25, 2022

shameless advertisement for my generic subgraph

It indexes all ERC721 contracts on mainnet, and you can easily build equivalent instances for other chains using our @openzeppelin/subgraphs toolkit.

1reaction
Amxxcommented, Jan 24, 2022

Hello @aalmada

There is a lot of discussion around the gas usage of ERC721 lately, and we appreciate the clarity of your approach. We understand that in very specific cases, our implementation is not the most gas effective. Sequential tokens minting with no burn is definitely one of these specific cases.

We care a lot about the versatility and backward compatibility of our codebase. For this reason, we don’t think it would be reasonable to deprecate our current proposal. AFAIK, nobody is asking for that anyway.

If we were to provide an implementation optimized for this specific case, we would be splitting our codebase, with unclear compatibility with ERC721 modules (voting being the last one). I don’t think this is optimal.

An opinion that we have shared, and that we will continue to share, is that ERC721Enumerable should not be used unless absolutely necessary. I have personally never seen any situation where it was actually needed. If you don’t include Enumerability, our (generic) implementation is as cheap as any other (that is focused on ā€œbatch-mintingā€) within the margin or error.

My answer to your issue would then be:

  • We strongly encourage the community to build and distribute contracts that are optimized for specific requirements (that our generic contracts are not optimized for)
  • Please please please, don’t use enumerability unless you really need to. And if you don’t need it, then benchmark other implementations against our ā€œcoreā€ erc721 contract.
Read more comments on GitHub >

github_iconTop Results From Across the Web

ERC721Enumerable gas optimization - OpenZeppelin Forum
When you are minting sequentially and never burn, totalSupply and tokenByIndex are trivial function (the first one is your "counter", the secondĀ ...
Read more >
ERC721 vs. ERC721A: Batch Minting NFTs - Alchemy
An alternative implementation of the ERC721 standard created by the Azuki team. Useful for optimizing batch mints to create NFTs.
Read more >
A journey towards a gas efficient ERC721
We strongly believe that you can have a gas efficient ERC721 and maintain compatibility, composability and future proofing. The bottom line is that...
Read more >
How to Mint NFTs Using the ERC721A Implementation
1. If you are interested in creating an NFT contract that can mint multiple tokens at a time, you may wa... 2. To...
Read more >
erc 721 - ERC721 Token - Burn and mint again
An implementation which allows re-mint of a burned token will be considered a valid implementation of the ERC-721 standard.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found