diff --git a/.gas-snapshot b/.gas-snapshot index 8485e98a07..c64ff83e28 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -227,7 +227,7 @@ ERC4337FactoryTest:test__codesize() (gas: 13520) ERC4337Test:testCdFallback() (gas: 443989) ERC4337Test:testCdFallback2() (gas: 1140699) ERC4337Test:testDelegateExecute() (gas: 369570) -ERC4337Test:testDelegateExecute(uint256) (runs: 256, μ: 352449, ~: 344555) +ERC4337Test:testDelegateExecute(uint256) (runs: 256, μ: 355685, ~: 344555) ERC4337Test:testDelegateExecuteRevertsIfOwnerSlotValueChanged() (gas: 319282) ERC4337Test:testDepositFunctions() (gas: 502955) ERC4337Test:testDirectStorage() (gas: 70413) @@ -235,17 +235,17 @@ ERC4337Test:testDisableInitializerForImplementation() (gas: 1177324) ERC4337Test:testETHReceived() (gas: 16584) ERC4337Test:testExecute() (gas: 382786) ERC4337Test:testExecuteBatch() (gas: 692605) -ERC4337Test:testExecuteBatch(uint256) (runs: 256, μ: 511473, ~: 368287) +ERC4337Test:testExecuteBatch(uint256) (runs: 256, μ: 514103, ~: 368298) ERC4337Test:testInitializer() (gas: 285192) ERC4337Test:testIsValidSignature() (gas: 111663) ERC4337Test:testIsValidSignaturePersonalSign() (gas: 96270) ERC4337Test:testIsValidSignatureWrapped() (gas: 406706) ERC4337Test:testOnERC1155BatchReceived() (gas: 1393788) ERC4337Test:testOnERC1155Received() (gas: 1391111) -ERC4337Test:testOnERC721Received() (gas: 1378408) +ERC4337Test:testOnERC721Received() (gas: 1372839) ERC4337Test:testOwnerRecovery() (gas: 486105) ERC4337Test:testValidateUserOp() (gas: 491555) -ERC4337Test:test__codesize() (gas: 54837) +ERC4337Test:test__codesize() (gas: 54809) ERC4626Test:testDepositWithNoApprovalReverts() (gas: 16371) ERC4626Test:testDepositWithNotEnoughApprovalReverts() (gas: 89884) ERC4626Test:testDifferentialFullMulDiv(uint256,uint256,uint256) (runs: 256, μ: 3325, ~: 3185) @@ -265,22 +265,22 @@ ERC4626Test:testWithdrawWithNoUnderlyingAmountReverts() (gas: 13102) ERC4626Test:testWithdrawWithNotEnoughUnderlyingAmountReverts() (gas: 144074) ERC4626Test:testWithdrawZero() (gas: 52807) ERC4626Test:test__codesize() (gas: 41067) -ERC6551Test:testCdFallback() (gas: 894557) -ERC6551Test:testDeployERC6551(uint256) (runs: 256, μ: 170994, ~: 168739) +ERC6551Test:testCdFallback() (gas: 894601) +ERC6551Test:testDeployERC6551(uint256) (runs: 256, μ: 171354, ~: 168782) ERC6551Test:testDeployERC6551Proxy() (gas: 80751) -ERC6551Test:testExecute() (gas: 507855) -ERC6551Test:testExecuteBatch() (gas: 816977) -ERC6551Test:testExecuteBatch(uint256) (runs: 256, μ: 589408, ~: 483186) -ERC6551Test:testInitializeERC6551ProxyImplementation() (gas: 189801) -ERC6551Test:testIsValidSignature() (gas: 187612) -ERC6551Test:testOnERC1155BatchReceived() (gas: 1526542) -ERC6551Test:testOnERC1155Received() (gas: 1523898) -ERC6551Test:testOnERC721Received() (gas: 1515109) -ERC6551Test:testOnERC721ReceivedCycles() (gas: 1711044) -ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 256, μ: 449262, ~: 455913) -ERC6551Test:testSupportsInterface() (gas: 169387) -ERC6551Test:testUpgrade() (gas: 1154845) -ERC6551Test:test__codesize() (gas: 48097) +ERC6551Test:testExecute() (gas: 507965) +ERC6551Test:testExecuteBatch() (gas: 817065) +ERC6551Test:testExecuteBatch(uint256) (runs: 256, μ: 601662, ~: 483230) +ERC6551Test:testInitializeERC6551ProxyImplementation() (gas: 189823) +ERC6551Test:testIsValidSignature() (gas: 187678) +ERC6551Test:testOnERC1155BatchReceived() (gas: 1526564) +ERC6551Test:testOnERC1155Received() (gas: 1523920) +ERC6551Test:testOnERC721Received() (gas: 1509583) +ERC6551Test:testOnERC721ReceivedCycles() (gas: 1714722) +ERC6551Test:testOnERC721ReceivedCyclesWithDifferentChainIds(uint256) (runs: 256, μ: 449035, ~: 454283) +ERC6551Test:testSupportsInterface() (gas: 169409) +ERC6551Test:testUpgrade() (gas: 1154933) +ERC6551Test:test__codesize() (gas: 48069) ERC6909Test:testApprove() (gas: 36771) ERC6909Test:testApprove(address,uint256,uint256) (runs: 256, μ: 36480, ~: 37413) ERC6909Test:testBurn() (gas: 40676) @@ -317,57 +317,57 @@ ERC6909Test:testTransferInsufficientBalanceReverts(address,uint256,uint256,uint2 ERC6909Test:testTransferOverMaxUintReverts() (gas: 63438) ERC6909Test:testTransferOverMaxUintReverts(address,uint256,uint256,uint256) (runs: 256, μ: 63957, ~: 63967) ERC6909Test:test__codesize() (gas: 26802) -ERC721HooksTest:testERC721Hooks() (gas: 2948024) -ERC721HooksTest:test__codesize() (gas: 10061) -ERC721Test:testApprove(uint256) (runs: 256, μ: 108048, ~: 108093) -ERC721Test:testApproveAll(uint256) (runs: 256, μ: 47496, ~: 40243) -ERC721Test:testApproveBurn(uint256) (runs: 256, μ: 86755, ~: 86771) -ERC721Test:testApproveNonExistentReverts(uint256,address) (runs: 256, μ: 33661, ~: 33621) -ERC721Test:testApproveUnauthorizedReverts(uint256) (runs: 256, μ: 83350, ~: 82576) -ERC721Test:testAuthorizedEquivalence(address,bool,bool) (runs: 256, μ: 683, ~: 678) -ERC721Test:testAux(uint256) (runs: 256, μ: 192072, ~: 193158) -ERC721Test:testBurn(uint256) (runs: 256, μ: 82594, ~: 93970) -ERC721Test:testBurnNonExistentReverts(uint256) (runs: 256, μ: 10761, ~: 10761) -ERC721Test:testCannotExceedMaxBalance() (gas: 149912) -ERC721Test:testDoubleBurnReverts(uint256) (runs: 256, μ: 63486, ~: 63432) -ERC721Test:testDoubleMintReverts(uint256) (runs: 256, μ: 81033, ~: 81065) -ERC721Test:testEverything(uint256) (runs: 256, μ: 310827, ~: 303159) -ERC721Test:testExtraData(uint256) (runs: 256, μ: 99225, ~: 99378) -ERC721Test:testExtraData2(uint256,uint256) (runs: 256, μ: 54384, ~: 54033) -ERC721Test:testIsApprovedOrOwner(uint256) (runs: 256, μ: 135467, ~: 135452) -ERC721Test:testMint(uint256) (runs: 256, μ: 82830, ~: 82851) -ERC721Test:testMintAndSetExtraData(uint256) (runs: 256, μ: 84492, ~: 84450) -ERC721Test:testMintAndSetExtraDataWithOverwrite(uint256,uint96) (runs: 256, μ: 83908, ~: 83818) -ERC721Test:testMintToZeroReverts(uint256) (runs: 256, μ: 10346, ~: 10346) -ERC721Test:testOwnerOfNonExistent(uint256) (runs: 256, μ: 33405, ~: 33360) -ERC721Test:testSafeMintToEOA(uint256) (runs: 256, μ: 83495, ~: 83531) -ERC721Test:testSafeMintToERC721Recipient(uint256) (runs: 256, μ: 409465, ~: 410593) -ERC721Test:testSafeMintToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 470885, ~: 460023) -ERC721Test:testSafeMintToERC721RecipientWithWrongReturnData(uint256) (runs: 256, μ: 170030, ~: 170030) -ERC721Test:testSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256,bytes) (runs: 256, μ: 171246, ~: 171193) -ERC721Test:testSafeMintToNonERC721RecipientReverts(uint256) (runs: 256, μ: 100470, ~: 100470) -ERC721Test:testSafeMintToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 101740, ~: 101687) -ERC721Test:testSafeMintToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 203149, ~: 203149) -ERC721Test:testSafeMintToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 204364, ~: 204311) -ERC721Test:testSafeTransferFromToEOA(uint256) (runs: 256, μ: 121999, ~: 122108) -ERC721Test:testSafeTransferFromToERC721Recipient(uint256) (runs: 256, μ: 470972, ~: 472096) -ERC721Test:testSafeTransferFromToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 532406, ~: 521752) -ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataReverts(uint256) (runs: 256, μ: 200853, ~: 200900) -ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataWithDataReverts(uint256,bytes) (runs: 256, μ: 202151, ~: 202142) -ERC721Test:testSafeTransferFromToNonERC721RecipientReverts(uint256) (runs: 256, μ: 131272, ~: 131220) -ERC721Test:testSafeTransferFromToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 132624, ~: 132621) -ERC721Test:testSafeTransferFromToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 233951, ~: 234003) -ERC721Test:testSafeTransferFromToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 235262, ~: 235260) -ERC721Test:testSafetyOfCustomStorage(uint256,uint256) (runs: 256, μ: 1085, ~: 735) -ERC721Test:testTransferFrom() (gas: 85744) -ERC721Test:testTransferFrom(uint256) (runs: 256, μ: 113989, ~: 112537) -ERC721Test:testTransferFromApproveAll(uint256) (runs: 256, μ: 119344, ~: 119308) -ERC721Test:testTransferFromNotExistentReverts(address,address,uint256) (runs: 256, μ: 34015, ~: 33992) -ERC721Test:testTransferFromNotOwner(uint256) (runs: 256, μ: 84740, ~: 84713) -ERC721Test:testTransferFromSelf(uint256) (runs: 256, μ: 92775, ~: 92764) -ERC721Test:testTransferFromToZeroReverts(uint256) (runs: 256, μ: 79052, ~: 79023) -ERC721Test:testTransferFromWrongFromReverts(address,uint256) (runs: 256, μ: 80417, ~: 80414) -ERC721Test:test__codesize() (gas: 43776) +ERC721HooksTest:testERC721Hooks() (gas: 2945944) +ERC721HooksTest:test__codesize() (gas: 10033) +ERC721Test:testApprove(uint256) (runs: 256, μ: 108117, ~: 108141) +ERC721Test:testApproveAll(uint256) (runs: 256, μ: 47505, ~: 40334) +ERC721Test:testApproveBurn(uint256) (runs: 256, μ: 86810, ~: 86860) +ERC721Test:testApproveNonExistentReverts(uint256,address) (runs: 256, μ: 33635, ~: 33571) +ERC721Test:testApproveUnauthorizedReverts(uint256) (runs: 256, μ: 83253, ~: 82370) +ERC721Test:testAuthorizedEquivalence(address,bool,bool) (runs: 256, μ: 748, ~: 743) +ERC721Test:testAux(uint256) (runs: 256, μ: 191960, ~: 193100) +ERC721Test:testBurn(uint256) (runs: 256, μ: 83077, ~: 94049) +ERC721Test:testBurnNonExistentReverts(uint256) (runs: 256, μ: 10783, ~: 10783) +ERC721Test:testCannotExceedMaxBalance() (gas: 149932) +ERC721Test:testDoubleBurnReverts(uint256) (runs: 256, μ: 63588, ~: 63538) +ERC721Test:testDoubleMintReverts(uint256) (runs: 256, μ: 79180, ~: 79211) +ERC721Test:testEverything(uint256) (runs: 256, μ: 311696, ~: 378404) +ERC721Test:testExtraData(uint256) (runs: 256, μ: 99104, ~: 99180) +ERC721Test:testExtraData2(uint256,uint256) (runs: 256, μ: 54285, ~: 53934) +ERC721Test:testIsApprovedOrOwner(uint256) (runs: 256, μ: 135193, ~: 135205) +ERC721Test:testMint(uint256) (runs: 256, μ: 82833, ~: 82756) +ERC721Test:testMintAndSetExtraDataUnchecked(uint256) (runs: 256, μ: 84364, ~: 84400) +ERC721Test:testMintAndSetExtraDataUncheckedWithOverwrite(uint256,uint96) (runs: 256, μ: 83617, ~: 83531) +ERC721Test:testMintToZeroReverts(uint256) (runs: 256, μ: 10327, ~: 10327) +ERC721Test:testOwnerOfNonExistent(uint256) (runs: 256, μ: 33394, ~: 33338) +ERC721Test:testSafeMintToEOA(uint256) (runs: 256, μ: 83361, ~: 83403) +ERC721Test:testSafeMintToERC721Recipient(uint256) (runs: 256, μ: 409324, ~: 410440) +ERC721Test:testSafeMintToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 470749, ~: 459869) +ERC721Test:testSafeMintToERC721RecipientWithWrongReturnData(uint256) (runs: 256, μ: 169942, ~: 169942) +ERC721Test:testSafeMintToERC721RecipientWithWrongReturnDataWithData(uint256,bytes) (runs: 256, μ: 171157, ~: 171104) +ERC721Test:testSafeMintToNonERC721RecipientReverts(uint256) (runs: 256, μ: 100404, ~: 100404) +ERC721Test:testSafeMintToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 101651, ~: 101598) +ERC721Test:testSafeMintToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 203061, ~: 203061) +ERC721Test:testSafeMintToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 204297, ~: 204244) +ERC721Test:testSafeTransferFromToEOA(uint256) (runs: 256, μ: 121877, ~: 122025) +ERC721Test:testSafeTransferFromToERC721Recipient(uint256) (runs: 256, μ: 470923, ~: 472054) +ERC721Test:testSafeTransferFromToERC721RecipientWithData(uint256,bytes) (runs: 256, μ: 532348, ~: 521614) +ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataReverts(uint256) (runs: 256, μ: 200897, ~: 200952) +ERC721Test:testSafeTransferFromToERC721RecipientWithWrongReturnDataWithDataReverts(uint256,bytes) (runs: 256, μ: 202134, ~: 202148) +ERC721Test:testSafeTransferFromToNonERC721RecipientReverts(uint256) (runs: 256, μ: 131304, ~: 131242) +ERC721Test:testSafeTransferFromToNonERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 132615, ~: 132624) +ERC721Test:testSafeTransferFromToRevertingERC721RecipientReverts(uint256) (runs: 256, μ: 233972, ~: 234030) +ERC721Test:testSafeTransferFromToRevertingERC721RecipientWithDataReverts(uint256,bytes) (runs: 256, μ: 235285, ~: 235290) +ERC721Test:testSafetyOfCustomStorage(uint256,uint256) (runs: 256, μ: 1063, ~: 713) +ERC721Test:testTransferFrom() (gas: 85788) +ERC721Test:testTransferFrom(uint256) (runs: 256, μ: 114217, ~: 112500) +ERC721Test:testTransferFromApproveAll(uint256) (runs: 256, μ: 119313, ~: 119288) +ERC721Test:testTransferFromNotExistentReverts(address,address,uint256) (runs: 256, μ: 34038, ~: 34014) +ERC721Test:testTransferFromNotOwner(uint256) (runs: 256, μ: 84776, ~: 84735) +ERC721Test:testTransferFromSelf(uint256) (runs: 256, μ: 92733, ~: 92714) +ERC721Test:testTransferFromToZeroReverts(uint256) (runs: 256, μ: 79086, ~: 79067) +ERC721Test:testTransferFromWrongFromReverts(address,uint256) (runs: 256, μ: 80462, ~: 80467) +ERC721Test:test__codesize() (gas: 43506) FixedPointMathLibTest:testAbs() (gas: 578) FixedPointMathLibTest:testAbs(int256) (runs: 256, μ: 516, ~: 485) FixedPointMathLibTest:testAbsEdgeCases() (gas: 410) @@ -918,7 +918,7 @@ OwnableTest:test__codesize() (gas: 12253) ReceiverTest:testETHReceived() (gas: 9621) ReceiverTest:testOnERC1155BatchReceived() (gas: 48975) ReceiverTest:testOnERC1155Received() (gas: 46717) -ReceiverTest:testOnERC721Received() (gas: 64108) +ReceiverTest:testOnERC721Received() (gas: 64152) ReceiverTest:test__codesize() (gas: 3310) RedBlackTreeLibTest:testRedBlackTreeBenchUint160() (gas: 3433600) RedBlackTreeLibTest:testRedBlackTreeBenchUint256() (gas: 5847065) diff --git a/src/tokens/ERC721.sol b/src/tokens/ERC721.sol index 3832c608bd..ef8bece111 100644 --- a/src/tokens/ERC721.sol +++ b/src/tokens/ERC721.sol @@ -490,15 +490,16 @@ abstract contract ERC721 { _afterTokenTransfer(address(0), to, id); } - /// @dev Mints token `id` to `to` and update the `extraData`. + /// @dev Mints token `id` to `to` and update the `extraData`, + /// but without checking if the token `id` already exists, + /// as `id` will usually be from an auto-incrementing counter. /// /// Requirements: /// - /// - Token `id` must not exist. /// - `to` cannot be the zero address. /// /// Emits a {Transfer} event. - function _mintAndSetExtraData(address to, uint256 id, uint96 value) internal virtual { + function _mintAndSetExtraDataUnchecked(address to, uint256 id, uint96 value) internal virtual { _beforeTokenTransfer(address(0), to, id); /// @solidity memory-safe-assembly assembly { @@ -509,18 +510,10 @@ abstract contract ERC721 { mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`. revert(0x1c, 0x04) } - // Load the ownership data. mstore(0x00, id) mstore(0x1c, _ERC721_MASTER_SLOT_SEED) - let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20))) - let ownershipPacked := sload(ownershipSlot) - // Revert if the token already exists. - if shl(96, ownershipPacked) { - mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`. - revert(0x1c, 0x04) - } // Update with the owner and extra data. - sstore(ownershipSlot, or(shl(160, value), to)) + sstore(add(id, add(id, keccak256(0x00, 0x20))), or(shl(160, value), to)) // Increment the balance of the owner. { mstore(0x00, to) diff --git a/test/ERC721.t.sol b/test/ERC721.t.sol index 053bf28a3c..a4ded07bc0 100644 --- a/test/ERC721.t.sol +++ b/test/ERC721.t.sol @@ -335,25 +335,25 @@ contract ERC721Test is SoladyTest { assertEq(_ownerOf(id), owner); } - function testMintAndSetExtraData(uint256 id) public { + function testMintAndSetExtraDataUnchecked(uint256 id) public { address owner = _randomNonZeroAddress(); _expectMintEvent(owner, id); - token.mintWithExtraData(owner, id, _extraData(id)); + token.mintWithExtraDataUnchecked(owner, id, _extraData(id)); assertEq(token.balanceOf(owner), 1); assertEq(_ownerOf(id), owner); assertEq(token.getExtraData(id), _extraData(id)); } - function testMintAndSetExtraDataWithOverwrite(uint256 id, uint96 random) public { + function testMintAndSetExtraDataUncheckedWithOverwrite(uint256 id, uint96 random) public { address owner = _randomNonZeroAddress(); token.setExtraData(id, random); assertEq(token.getExtraData(id), random); _expectMintEvent(owner, id); - token.mintWithExtraData(owner, id, _extraData(id)); + token.mintWithExtraDataUnchecked(owner, id, _extraData(id)); assertEq(token.getExtraData(id), _extraData(id)); } @@ -835,7 +835,7 @@ contract ERC721Test is SoladyTest { token.mint(address(0), id); vm.expectRevert(ERC721.TransferToZeroAddress.selector); - token.mintWithExtraData(address(0), id, _extraData(id)); + token.mintWithExtraDataUnchecked(address(0), id, _extraData(id)); } function testDoubleMintReverts(uint256 id) public { @@ -844,9 +844,6 @@ contract ERC721Test is SoladyTest { token.mint(to, id); vm.expectRevert(ERC721.TokenAlreadyExists.selector); token.mint(to, id); - - vm.expectRevert(ERC721.TokenAlreadyExists.selector); - token.mintWithExtraData(to, id, _extraData(id)); } function testBurnNonExistentReverts(uint256 id) public { diff --git a/test/utils/mocks/MockERC721.sol b/test/utils/mocks/MockERC721.sol index 411730ad59..29fc6f05be 100644 --- a/test/utils/mocks/MockERC721.sol +++ b/test/utils/mocks/MockERC721.sol @@ -28,8 +28,8 @@ contract MockERC721 is ERC721 { _mint(_brutalized(to), id); } - function mintWithExtraData(address to, uint256 id, uint96 value) public virtual { - _mintAndSetExtraData(_brutalized(to), id, _brutalized(value)); + function mintWithExtraDataUnchecked(address to, uint256 id, uint96 value) public virtual { + _mintAndSetExtraDataUnchecked(_brutalized(to), id, _brutalized(value)); } function burn(uint256 id) public virtual {