Skip to content

Commit

Permalink
Tidy and optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
Vectorized committed Aug 21, 2024
1 parent e24ee8c commit 6cba3fa
Showing 1 changed file with 53 additions and 62 deletions.
115 changes: 53 additions & 62 deletions contracts/ERC721A.sol
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ contract ERC721A is IERC721A {
* @dev Initializes the ownership slot minted at `index` for efficiency purposes.
*/
function _initializeOwnershipAt(uint256 index) internal virtual {
if (_packedOwnerships[index] == 0) {
if (_packedOwnerships[index] == uint256(0)) {
_packedOwnerships[index] = _packedOwnershipOf(index);
}
}
Expand All @@ -396,7 +396,7 @@ contract ERC721A is IERC721A {
}

// If the data at the starting slot does not exist, start the scan.
if (packed == 0) {
if (packed == uint256(0)) {
if (tokenId >= _currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
// Invariant:
// There will always be an initialized ownership slot
Expand All @@ -411,8 +411,8 @@ contract ERC721A is IERC721A {
unchecked {
packed = _packedOwnerships[--tokenId];
}
if (packed == 0) continue;
if (packed & _BITMASK_BURNED == 0) return packed;
if (packed == uint256(0)) continue;
if (packed & _BITMASK_BURNED == uint256(0)) return packed;
// Otherwise, the token is burned, and we must revert.
// This handles the case of batch burned tokens, where only the burned bit
// of the starting slot is set, and remaining slots are left uninitialized.
Expand All @@ -423,7 +423,7 @@ contract ERC721A is IERC721A {
// This is possible because we have already achieved the target condition.
// This saves 2143 gas on transfers of initialized tokens.
// If the token is not burned, return `packed`. Otherwise, revert.
if (packed & _BITMASK_BURNED == 0) return packed;
if (packed & _BITMASK_BURNED == uint256(0)) return packed;
}
_revert(OwnerQueryForNonexistentToken.selector);
}
Expand Down Expand Up @@ -527,8 +527,8 @@ contract ERC721A is IERC721A {

if (tokenId < _currentIndex) {
uint256 packed;
while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
result = packed & _BITMASK_BURNED == 0;
while ((packed = _packedOwnerships[tokenId]) == uint256(0)) --tokenId;
result = packed & _BITMASK_BURNED == uint256(0);
}
}
}
Expand All @@ -548,33 +548,28 @@ contract ERC721A is IERC721A {
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
uint256 approvedAddressValue,
uint256 ownerMasked,
uint256 msgSenderMasked
) private pure returns (bool result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
msgSender := and(msgSender, _BITMASK_ADDRESS)
// `msgSender == owner || msgSender == approvedAddress`.
result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
result := or(eq(msgSenderMasked, ownerMasked), eq(msgSenderMasked, approvedAddressValue))
}
}

/**
* @dev Returns the storage slot and value for the approved address of `tokenId`.
* @dev Returns the storage slot and value for the approved address of `tokenId` casted to a uint256.
*/
function _getApprovedSlotAndAddress(uint256 tokenId)
function _getApprovedSlotAndValue(uint256 tokenId)
private
view
returns (uint256 approvedAddressSlot, address approvedAddress)
returns (uint256 approvedAddressSlot, uint256 approvedAddressValue)
{
TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
// The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
// The following is equivalent to `approvedAddressValue = uint160(_tokenApprovals[tokenId].value)`.
assembly {
approvedAddressSlot := tokenApproval.slot
approvedAddress := sload(approvedAddressSlot)
approvedAddressValue := sload(approvedAddressSlot)
}
}

Expand All @@ -601,25 +596,21 @@ contract ERC721A is IERC721A {
uint256 tokenId
) public payable virtual override {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
uint256 fromMasked = uint160(from);

// Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
if (uint160(prevOwnershipPacked) != fromMasked) _revert(TransferFromIncorrectOwner.selector);

if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);

(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
(uint256 approvedAddressSlot, uint256 approvedAddressValue) = _getApprovedSlotAndValue(tokenId);

// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!_isSenderApprovedOrOwner(approvedAddressValue, fromMasked, uint160(_msgSenderERC721A())))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);

_beforeTokenTransfers(from, to, tokenId, 1);

// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
if approvedAddressValue {
sstore(approvedAddressSlot, 0) // Equivalent to `delete _tokenApprovals[tokenId]`.
}
}

Expand All @@ -642,10 +633,10 @@ contract ERC721A is IERC721A {
);

// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == uint256(0)) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
if (_packedOwnerships[nextTokenId] == uint256(0)) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
Expand All @@ -655,20 +646,20 @@ contract ERC721A is IERC721A {
}
}

// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
// Mask to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint160(to);
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
from, // `from`.
fromMasked, // `from`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
if (toMasked == 0) _revert(TransferToZeroAddress.selector);
if (toMasked == uint256(0)) _revert(TransferToZeroAddress.selector);

_afterTokenTransfers(from, to, tokenId, 1);
}
Expand Down Expand Up @@ -746,14 +737,15 @@ contract ERC721A is IERC721A {
address to,
uint256[] memory tokenIds
) internal virtual {
uint256 fromMasked = uint256(uint160(from));
uint256 toMasked = uint256(uint160(to));
uint256 fromMasked = uint160(from);
uint256 toMasked = uint160(to);
uint256 byMasked = uint160(by);
// Disallow transfer to zero address.
if (toMasked == uint256(0)) _revert(TransferToZeroAddress.selector);
// Early return if `tokenIds` is empty.
if (tokenIds.length == uint256(0)) return;
// Whether we need to check the individual token approvals.
bool approvalCheck = by != address(0) && by != from && !isApprovedForAll(from, by);
bool approvalCheck = byMasked != uint256(0) && byMasked != fromMasked && !isApprovedForAll(from, by);
// The next `tokenId` to be minted (i.e. `_nextTokenId()`).
uint256 end = _currentIndex;
// Pointer to start and end (exclusive) of `tokenIds`.
Expand Down Expand Up @@ -799,12 +791,12 @@ contract ERC721A is IERC721A {
// - `nextInitialized` to `false`.
_packedOwnerships[tokenId] = _packOwnershipData(to, _nextExtraData(from, to, prevOwnershipPacked));
do {
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
(uint256 approvedAddressSlot, uint256 approvedAddressValue) = _getApprovedSlotAndValue(tokenId);
// Revert if the sender is not authorized to transfer the token.
if (approvalCheck)
if (by != approvedAddress) _revert(TransferCallerNotOwnerNorApproved.selector);
if (byMasked != approvedAddressValue) _revert(TransferCallerNotOwnerNorApproved.selector);
assembly {
if approvedAddress {
if approvedAddressValue {
sstore(approvedAddressSlot, 0) // Equivalent to `delete _tokenApprovals[tokenId]`.
}
// Emit the `Transfer` event.
Expand Down Expand Up @@ -962,7 +954,7 @@ contract ERC721A is IERC721A {
) {
return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
if (reason.length == uint256(0)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
assembly {
Expand All @@ -987,7 +979,7 @@ contract ERC721A is IERC721A {
*/
function _mint(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (quantity == 0) _revert(MintZeroQuantity.selector);
if (quantity == uint256(0)) _revert(MintZeroQuantity.selector);

_beforeTokenTransfers(address(0), to, startTokenId, quantity);

Expand All @@ -1012,10 +1004,10 @@ contract ERC721A is IERC721A {
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);

// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
// Mask to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint160(to);

if (toMasked == 0) _revert(MintToZeroAddress.selector);
if (toMasked == uint256(0)) _revert(MintToZeroAddress.selector);

uint256 end = startTokenId + quantity;
uint256 tokenId = startTokenId;
Expand Down Expand Up @@ -1067,7 +1059,7 @@ contract ERC721A is IERC721A {
function _mintERC2309(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (to == address(0)) _revert(MintToZeroAddress.selector);
if (quantity == 0) _revert(MintZeroQuantity.selector);
if (quantity == uint256(0)) _revert(MintZeroQuantity.selector);
if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);

_beforeTokenTransfers(address(0), to, startTokenId, quantity);
Expand Down Expand Up @@ -1184,10 +1176,10 @@ contract ERC721A is IERC721A {
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;

// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
// Mask to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint160(to);

if (toMasked == 0) _revert(MintToZeroAddress.selector);
if (toMasked == uint256(0)) _revert(MintToZeroAddress.selector);

assembly {
// Emit the `Transfer` event.
Expand Down Expand Up @@ -1313,23 +1305,22 @@ contract ERC721A is IERC721A {
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

address from = address(uint160(prevOwnershipPacked));
uint256 fromMasked = uint160(prevOwnershipPacked);
address from = address(uint160(fromMasked));

(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
(uint256 approvedAddressSlot, uint256 approvedAddressValue) = _getApprovedSlotAndValue(tokenId);

if (approvalCheck) {
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!_isSenderApprovedOrOwner(approvedAddressValue, fromMasked, uint160(_msgSenderERC721A())))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
}

_beforeTokenTransfers(from, address(0), tokenId, 1);

// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
if approvedAddressValue {
sstore(approvedAddressSlot, 0) // Equivalent to `delete _tokenApprovals[tokenId]`.
}
}

Expand All @@ -1356,10 +1347,10 @@ contract ERC721A is IERC721A {
);

// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == uint256(0)) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
if (_packedOwnerships[nextTokenId] == uint256(0)) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
Expand Down Expand Up @@ -1387,7 +1378,7 @@ contract ERC721A is IERC721A {
*/
function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
uint256 packed = _packedOwnerships[index];
if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
if (packed == uint256(0)) _revert(OwnershipNotInitializedForExtraData.selector);
uint256 extraDataCasted;
// Cast `extraData` with assembly to avoid redundant masking.
assembly {
Expand Down

0 comments on commit 6cba3fa

Please sign in to comment.