-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request from GHSA-878m-3g6q-594q
* Test batch minting of 1 * Fix balance tracking * fix lint * add changeset * rename UNSAFE -> unsafe * fix docs * fix changeset * grammar * add explanation of preserved invariant * add fuzz tests * rename variable * improve property definition * add burn * add test ownership multiple batches * refactor fuzz tests * change ownership test for better probability * typo * reorder comment * update changelog notes * edit changelog * lint * Update CHANGELOG.md --------- Co-authored-by: Francisco Giordano <fg@frang.io>
- Loading branch information
Showing
6 changed files
with
149 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'openzeppelin-solidity': patch | ||
--- | ||
|
||
`ERC721Consecutive`: Fixed a bug when `_mintConsecutive` is used for batches of size 1 that could lead to balance overflow. Refer to the breaking changes section in the changelog for a note on the behavior of `ERC721._beforeTokenTransfer`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../../../../contracts/token/ERC721/extensions/ERC721Consecutive.sol"; | ||
import "forge-std/Test.sol"; | ||
|
||
function toSingleton(address account) pure returns (address[] memory) { | ||
address[] memory accounts = new address[](1); | ||
accounts[0] = account; | ||
return accounts; | ||
} | ||
|
||
contract ERC721ConsecutiveTarget is StdUtils, ERC721Consecutive { | ||
uint256 public totalMinted = 0; | ||
|
||
constructor(address[] memory receivers, uint256[] memory batches) ERC721("", "") { | ||
for (uint256 i = 0; i < batches.length; i++) { | ||
address receiver = receivers[i % receivers.length]; | ||
uint96 batchSize = uint96(bound(batches[i], 0, _maxBatchSize())); | ||
_mintConsecutive(receiver, batchSize); | ||
totalMinted += batchSize; | ||
} | ||
} | ||
|
||
function burn(uint256 tokenId) public { | ||
_burn(tokenId); | ||
} | ||
} | ||
|
||
contract ERC721ConsecutiveTest is Test { | ||
function test_balance(address receiver, uint256[] calldata batches) public { | ||
vm.assume(receiver != address(0)); | ||
|
||
ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); | ||
|
||
assertEq(token.balanceOf(receiver), token.totalMinted()); | ||
} | ||
|
||
function test_ownership(address receiver, uint256[] calldata batches, uint256[2] calldata unboundedTokenId) public { | ||
vm.assume(receiver != address(0)); | ||
|
||
ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); | ||
|
||
if (token.totalMinted() > 0) { | ||
uint256 validTokenId = bound(unboundedTokenId[0], 0, token.totalMinted() - 1); | ||
assertEq(token.ownerOf(validTokenId), receiver); | ||
} | ||
|
||
uint256 invalidTokenId = bound(unboundedTokenId[1], token.totalMinted(), type(uint256).max); | ||
vm.expectRevert(); | ||
token.ownerOf(invalidTokenId); | ||
} | ||
|
||
function test_burn(address receiver, uint256[] calldata batches, uint256 unboundedTokenId) public { | ||
vm.assume(receiver != address(0)); | ||
|
||
ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(toSingleton(receiver), batches); | ||
|
||
// only test if we minted at least one token | ||
uint256 supply = token.totalMinted(); | ||
vm.assume(supply > 0); | ||
|
||
// burn a token in [0; supply[ | ||
uint256 tokenId = bound(unboundedTokenId, 0, supply - 1); | ||
token.burn(tokenId); | ||
|
||
// balance should have decreased | ||
assertEq(token.balanceOf(receiver), supply - 1); | ||
|
||
// token should be burnt | ||
vm.expectRevert(); | ||
token.ownerOf(tokenId); | ||
} | ||
|
||
function test_transfer( | ||
address[2] calldata accounts, | ||
uint256[2] calldata unboundedBatches, | ||
uint256[2] calldata unboundedTokenId | ||
) public { | ||
vm.assume(accounts[0] != address(0)); | ||
vm.assume(accounts[1] != address(0)); | ||
vm.assume(accounts[0] != accounts[1]); | ||
|
||
address[] memory receivers = new address[](2); | ||
receivers[0] = accounts[0]; | ||
receivers[1] = accounts[1]; | ||
|
||
// We assume _maxBatchSize is 5000 (the default). This test will break otherwise. | ||
uint256[] memory batches = new uint256[](2); | ||
batches[0] = bound(unboundedBatches[0], 1, 5000); | ||
batches[1] = bound(unboundedBatches[1], 1, 5000); | ||
|
||
ERC721ConsecutiveTarget token = new ERC721ConsecutiveTarget(receivers, batches); | ||
|
||
uint256 tokenId0 = bound(unboundedTokenId[0], 0, batches[0] - 1); | ||
uint256 tokenId1 = bound(unboundedTokenId[1], 0, batches[1] - 1) + batches[0]; | ||
|
||
assertEq(token.ownerOf(tokenId0), accounts[0]); | ||
assertEq(token.ownerOf(tokenId1), accounts[1]); | ||
assertEq(token.balanceOf(accounts[0]), batches[0]); | ||
assertEq(token.balanceOf(accounts[1]), batches[1]); | ||
|
||
vm.prank(accounts[0]); | ||
token.transferFrom(accounts[0], accounts[1], tokenId0); | ||
|
||
assertEq(token.ownerOf(tokenId0), accounts[1]); | ||
assertEq(token.ownerOf(tokenId1), accounts[1]); | ||
assertEq(token.balanceOf(accounts[0]), batches[0] - 1); | ||
assertEq(token.balanceOf(accounts[1]), batches[1] + 1); | ||
|
||
vm.prank(accounts[1]); | ||
token.transferFrom(accounts[1], accounts[0], tokenId1); | ||
|
||
assertEq(token.ownerOf(tokenId0), accounts[1]); | ||
assertEq(token.ownerOf(tokenId1), accounts[0]); | ||
assertEq(token.balanceOf(accounts[0]), batches[0]); | ||
assertEq(token.balanceOf(accounts[1]), batches[1]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters