Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-sequential minting (a.k.a spot-minting) support #479

Merged
merged 24 commits into from
Feb 22, 2024

Conversation

Vectorized
Copy link
Collaborator

@Vectorized Vectorized commented Feb 2, 2024

This PR gives ERC721A the ability to mint tokens in non-sequential order.

Devs will need to override a new _sequentialUpTo() function to return a value less than 2**256 - 1 to activate this feature.

About:

  • Sequential mint: _startTokenId() <= tokenId <= _sequentialUpTo()
  • Spot (i.e. non-sequential) mint: _sequentialUpTo() < tokenId.

Zero-cost abstraction:

  • If _sequentialUpTo() == type(uint256).max (by default), all expressions of x > _sequentialUpTo(), x != _sequentialUpTo() will evaluate to false on compile time, and the dead code will be removed. If you don't override _sequentialUpTo(), you won't pay any extra gas.

Notes:

  • "Non-sequential" is a mouthful to type. ChatGPT suggests that "spot" is a short word that conveys similar meaning as non-sequential.
  • The new storage variable for the total number of spot mints is placed at the end of all the storage variables, so that we can maintain storage compatibility with the current ERC721AUpgradeable.
  • Most functionality of ERC721AQueryable can still be used with spot mints. The tokensOfOwner function is disabled if spot mints is enabled, as it would need to scan the entire uint256 range, which is unfeasible. The tokensOfOwnerIn function, however, is still available for use with spot mints, as the search range can be manually restricted. Added some minor optimizations to the scanning logic.

Todo:

  • Reason really hard about the code.

@Vectorized Vectorized marked this pull request as ready for review February 2, 2024 07:29
@mvillere
Copy link

mvillere commented Feb 2, 2024

Clever approach to implement this I think. I went through all the changes and didn't see any red flags. The only obvious thing that popped into my head is whether it makes sense to package spot + sequential mint functionality together in a single contract as I don't think a lot of mints will need both together. That being said, I do like having the capability now, and it added a lot less code to the base contract than I expected. Nicely done.

@Vectorized
Copy link
Collaborator Author

@mvillere Thanks so much for your review.

One possible use case is multi-chain NFTs.
L1 minting can be sequential, while NFTs bridged from L2 -> L1 are spot minted.

Copy link
Collaborator

@cygaar cygaar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall approach looks sound to me, however it does seem like this would be better off as an extension rather than be placed in the base ERC721A contract. The usage of this contract seems pretty niche as it's pretty rare to find a contract that needs to mint sequentially and at specific spots

contracts/ERC721A.sol Show resolved Hide resolved
@Vectorized
Copy link
Collaborator Author

Was initially thinking of a subclass that wraps the ERC721A's functions.

Decided on the current approach after hard marinating over a week:

  • Ease of use. Just override one function. This logic is intended to be used with burning exposed (via ERC721ABurnable). If we were to make a subclass, we can face Solidity inheritance hell.
  • Reuse common logic and storage variables, leading to smaller bytecode and lower runtime gas. Also easier to visually inspect.

Copy link

@daejunpark daejunpark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I focused on reviewing the integrity of _packedOwnerships data structure, and it looked good to me. My observations supporting the integirty are as follows:

  • _packedOwnerships is divided into two parts for sequential and spot mintings. While the latter part doesn't hold some sequential properties of the former, they are used differently by identifying which part a given token id belongs to.
  • _currentIndex - 1 stays within _sequentialUpTo, ensuring that sequential minting data doesn't spill over into the spot minting area.
  • The next slot initialization in transferFrom() and burn() doesn't occur for spot-minted tokens, as their _BITMASK_NEXT_INITIALIZED flag is always set true.

Minor comments are added below.

contracts/ERC721A.sol Show resolved Hide resolved
contracts/ERC721A.sol Show resolved Hide resolved
contracts/ERC721A.sol Show resolved Hide resolved
contracts/ERC721A.sol Show resolved Hide resolved
@Vectorized Vectorized merged commit b3517b0 into chiru-labs:main Feb 22, 2024
3 checks passed
@dcapitator
Copy link

Could we consider implementing batch spot minting as a highly anticipated use case?

Nice work.

@lak02
Copy link

lak02 commented Feb 27, 2024

could be less fee for NFTs redeem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants