From b693f7f9d13e24e6df7556f4b10fccae8a47f5df Mon Sep 17 00:00:00 2001 From: Kfish Date: Fri, 10 Mar 2023 11:11:43 -0300 Subject: [PATCH] first consecutive id --- .../ERC721/extensions/ERC721Consecutive.sol | 7 ++- .../ERC721/extensions/ERC721Consecutive.t.sol | 53 +++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/contracts/token/ERC721/extensions/ERC721Consecutive.sol b/contracts/token/ERC721/extensions/ERC721Consecutive.sol index 9451c8c59c6..454d66a4e1f 100644 --- a/contracts/token/ERC721/extensions/ERC721Consecutive.sol +++ b/contracts/token/ERC721/extensions/ERC721Consecutive.sol @@ -141,8 +141,13 @@ abstract contract ERC721Consecutive is IERC2309, ERC721 { super._afterTokenTransfer(from, to, firstTokenId, batchSize); } + /** + * @dev Used to offset the first token id in {_totalConsecutiveSupply} + */ + function _firstConsecutiveId() internal view virtual returns (uint96) { return 0; } + function _totalConsecutiveSupply() private view returns (uint96) { (bool exists, uint96 latestId, ) = _sequentialOwnership.latestCheckpoint(); - return exists ? latestId + 1 : 0; + return exists ? latestId + 1 : _firstConsecutiveId(); } } diff --git a/test/token/ERC721/extensions/ERC721Consecutive.t.sol b/test/token/ERC721/extensions/ERC721Consecutive.t.sol index 3cc8689298c..09e4370011b 100644 --- a/test/token/ERC721/extensions/ERC721Consecutive.t.sol +++ b/test/token/ERC721/extensions/ERC721Consecutive.t.sol @@ -15,8 +15,10 @@ function toSingleton(address account) pure returns (address[] memory) { contract ERC721ConsecutiveTarget is StdUtils, ERC721Consecutive { uint256 public totalMinted = 0; + uint96 public firstConsecutiveId = 0; - constructor(address[] memory receivers, uint256[] memory batches) ERC721("", "") { + constructor(address[] memory receivers, uint256[] memory batches, uint96 startingId) ERC721("", "") { + firstConsecutiveId = startingId; for (uint256 i = 0; i < batches.length; i++) { address receiver = receivers[i % receivers.length]; uint96 batchSize = uint96(bound(batches[i], 0, _maxBatchSize())); @@ -28,36 +30,43 @@ contract ERC721ConsecutiveTarget is StdUtils, ERC721Consecutive { function burn(uint256 tokenId) public { _burn(tokenId); } + + function _firstConsecutiveId() internal view virtual override returns (uint96) { + return firstConsecutiveId; + } } contract ERC721ConsecutiveTest is Test { - function test_balance(address receiver, uint256[] calldata batches) public { + function test_balance(address receiver, uint256[] calldata batches, uint96 startingId) public { vm.assume(receiver != address(0)); + vm.assume(startingId < type(uint96).max - 5000); - ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); + ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches, startingId); assertEq(token.balanceOf(receiver), token.totalMinted()); } - function test_ownership(address receiver, uint256[] calldata batches, uint256[2] calldata unboundedTokenId) public { + function test_ownership(address receiver, uint256[] calldata batches, uint256[2] calldata unboundedTokenId, uint96 startingId) public { vm.assume(receiver != address(0)); + vm.assume(startingId < type(uint96).max - 5000); - ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); + ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches, startingId); if (token.totalMinted() > 0) { - uint256 validTokenId = bound(unboundedTokenId[0], 0, token.totalMinted() - 1); + uint256 validTokenId = bound(unboundedTokenId[0], startingId, startingId + token.totalMinted() - 1); assertEq(token.ownerOf(validTokenId), receiver); } - uint256 invalidTokenId = bound(unboundedTokenId[1], token.totalMinted(), type(uint256).max); + uint256 invalidTokenId = bound(unboundedTokenId[1], startingId + token.totalMinted(), startingId + token.totalMinted() + 1); vm.expectRevert(); token.ownerOf(invalidTokenId); } - function test_burn(address receiver, uint256[] calldata batches, uint256 unboundedTokenId) public { + function test_burn(address receiver, uint256[] calldata batches, uint256 unboundedTokenId, uint96 startingId) public { vm.assume(receiver != address(0)); + vm.assume(startingId < type(uint96).max - 5000); - ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); + ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches, startingId); // only test if we minted at least one token uint256 supply = token.totalMinted(); @@ -93,7 +102,7 @@ contract ERC721ConsecutiveTest is Test { batches[0] = bound(unboundedBatches[0], 1, 5000); batches[1] = bound(unboundedBatches[1], 1, 5000); - ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(receivers, batches); + ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(receivers, batches, 0); uint256 tokenId0 = bound(unboundedTokenId[0], 0, batches[0] - 1); uint256 tokenId1 = bound(unboundedTokenId[1], 0, batches[1] - 1) + batches[0]; @@ -119,4 +128,28 @@ contract ERC721ConsecutiveTest is Test { assertEq(token.balanceOf(accounts[0]), batches[0]); assertEq(token.balanceOf(accounts[1]), batches[1]); } + + function test_start_consecutive_id( + address receiver, + uint256[2] calldata unboundedBatches, + uint256[2] calldata unboundedTokenId, + uint96 startingId + ) public { + vm.assume(receiver != address(0)); + uint256 startingTokenId = bound(startingId, 1, 5000); + + // We assume _maxBatchSize is 5000 (the default). This test will break otherwise. + uint256[] memory batches = new uint256[](2); + batches[0] = bound(unboundedBatches[0], startingTokenId, 5000); + batches[1] = bound(unboundedBatches[1], startingTokenId, 5000); + + ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches, uint96(startingTokenId)); + + uint256 tokenId0 = bound(unboundedTokenId[0], startingTokenId, batches[0]); + uint256 tokenId1 = bound(unboundedTokenId[1], startingTokenId, batches[1]); + + assertEq(token.ownerOf(tokenId0), receiver); + assertEq(token.ownerOf(tokenId1), receiver); + assertEq(token.balanceOf(receiver), batches[0] + batches[1]); + } }