Ability to set starting token id for ERC721Consecutive (#4097)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
Co-authored-by: ernestognw <ernestognw@gmail.com>
This commit is contained in:
kfishnchips
2023-05-26 12:07:16 -04:00
committed by GitHub
parent 09329f8a18
commit 5420879d9b
5 changed files with 263 additions and 168 deletions

View File

@ -11,13 +11,18 @@ import "../../token/ERC721/extensions/draft-ERC721Votes.sol";
* @title ERC721ConsecutiveMock
*/
contract ERC721ConsecutiveMock is ERC721Consecutive, ERC721Pausable, ERC721Votes {
uint96 private immutable _offset;
constructor(
string memory name,
string memory symbol,
uint96 offset,
address[] memory delegates,
address[] memory receivers,
uint96[] memory amounts
) ERC721(name, symbol) EIP712(name, "1") {
_offset = offset;
for (uint256 i = 0; i < delegates.length; ++i) {
_delegate(delegates[i], delegates[i]);
}
@ -27,6 +32,10 @@ contract ERC721ConsecutiveMock is ERC721Consecutive, ERC721Pausable, ERC721Votes
}
}
function _firstConsecutiveId() internal view virtual override returns (uint96) {
return _offset;
}
function _ownerOf(uint256 tokenId) internal view virtual override(ERC721, ERC721Consecutive) returns (address) {
return super._ownerOf(tokenId);
}

View File

@ -20,8 +20,8 @@ import "../../../utils/structs/BitMaps.sol";
* regained after construction. During construction, only batch minting is allowed.
*
* IMPORTANT: This extension bypasses the hooks {_beforeTokenTransfer} and {_afterTokenTransfer} for tokens minted in
* batch. When using this extension, you should consider the {_beforeConsecutiveTokenTransfer} and
* {_afterConsecutiveTokenTransfer} hooks in addition to {_beforeTokenTransfer} and {_afterTokenTransfer}.
* batch. The hooks will be only called once per batch, so you should take `batchSize` parameter into consideration
* when relying on hooks.
*
* IMPORTANT: When overriding {_afterTokenTransfer}, be careful about call ordering. {ownerOf} may return invalid
* values during the {_afterTokenTransfer} execution if the super call is not called first. To be safe, execute the
@ -56,7 +56,7 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
address owner = super._ownerOf(tokenId);
// If token is owned by the core, or beyond consecutive range, return base value
if (owner != address(0) || tokenId > type(uint96).max) {
if (owner != address(0) || tokenId > type(uint96).max || tokenId < _firstConsecutiveId()) {
return owner;
}
@ -82,7 +82,7 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
* Emits a {IERC2309-ConsecutiveTransfer} event.
*/
function _mintConsecutive(address to, uint96 batchSize) internal virtual returns (uint96) {
uint96 first = _totalConsecutiveSupply();
uint96 next = _nextConsecutiveId();
// minting a batch of size 0 is a no-op
if (batchSize > 0) {
@ -91,29 +91,29 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
require(batchSize <= _maxBatchSize(), "ERC721Consecutive: batch too large");
// hook before
_beforeTokenTransfer(address(0), to, first, batchSize);
_beforeTokenTransfer(address(0), to, next, batchSize);
// push an ownership checkpoint & emit event
uint96 last = first + batchSize - 1;
uint96 last = next + batchSize - 1;
_sequentialOwnership.push(last, uint160(to));
// The invariant required by this function is preserved because the new sequentialOwnership checkpoint
// is attributing ownership of `batchSize` new tokens to account `to`.
__unsafe_increaseBalance(to, batchSize);
emit ConsecutiveTransfer(first, last, address(0), to);
emit ConsecutiveTransfer(next, last, address(0), to);
// hook after
_afterTokenTransfer(address(0), to, first, batchSize);
_afterTokenTransfer(address(0), to, next, batchSize);
}
return first;
return next;
}
/**
* @dev See {ERC721-_mint}. Override version that restricts normal minting to after construction.
*
* Warning: Using {ERC721Consecutive} prevents using {_mint} during construction in favor of {_mintConsecutive}.
* WARNING: Using {ERC721Consecutive} prevents using {_mint} during construction in favor of {_mintConsecutive}.
* After construction, {_mintConsecutive} is no longer available and {_mint} becomes available.
*/
function _mint(address to, uint256 tokenId) internal virtual override {
@ -132,17 +132,30 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 {
) internal virtual override {
if (
to == address(0) && // if we burn
firstTokenId < _totalConsecutiveSupply() && // and the tokenId was minted in a batch
!_sequentialBurn.get(firstTokenId) // and the token was never marked as burnt
) {
firstTokenId >= _firstConsecutiveId() &&
firstTokenId < _nextConsecutiveId() &&
!_sequentialBurn.get(firstTokenId)
) // and the token was never marked as burnt
{
require(batchSize == 1, "ERC721Consecutive: batch burn not supported");
_sequentialBurn.set(firstTokenId);
}
super._afterTokenTransfer(from, to, firstTokenId, batchSize);
}
function _totalConsecutiveSupply() private view returns (uint96) {
/**
* @dev Used to offset the first token id in {_nextConsecutiveId}
*/
function _firstConsecutiveId() internal view virtual returns (uint96) {
return 0;
}
/**
* @dev Returns the next tokenId to mint using {_mintConsecutive}. It will return {_firstConsecutiveId}
* if no consecutive tokenId has been minted before.
*/
function _nextConsecutiveId() private view returns (uint96) {
(bool exists, uint96 latestId, ) = _sequentialOwnership.latestCheckpoint();
return exists ? latestId + 1 : 0;
return exists ? latestId + 1 : _firstConsecutiveId();
}
}