Skip to content

Commit

Permalink
first consecutive id
Browse files Browse the repository at this point in the history
  • Loading branch information
kfishnchips committed Mar 10, 2023
1 parent 4fb6833 commit b693f7f
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
7 changes: 6 additions & 1 deletion contracts/token/ERC721/extensions/ERC721Consecutive.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
53 changes: 43 additions & 10 deletions test/token/ERC721/extensions/ERC721Consecutive.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand All @@ -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();
Expand Down Expand Up @@ -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];
Expand All @@ -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]);
}
}

0 comments on commit b693f7f

Please sign in to comment.