Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some minor optimizations and refactors #337

Merged
merged 12 commits into from
Jun 14, 2022
68 changes: 46 additions & 22 deletions contracts/ERC721A.sol
Original file line number Diff line number Diff line change
Expand Up @@ -580,15 +580,36 @@ contract ERC721A is IERC721A {
}

/**
* @dev Zeroes out _tokenApprovals[tokenId]
* @dev Returns the storage slot and value for the approved address of `tokenId`.
*/
function _removeTokenApproval(uint256 tokenId) private {
mapping(uint256 => address) storage tokenApprovalPtr = _tokenApprovals;
function _getApprovedAddress(
uint256 tokenId
) private view returns (uint256 approvedAddressSlot, address approvedAddress) {
mapping(uint256 => address) storage tokenApprovalsPtr = _tokenApprovals;
// The following is equivalent to `approvedAddress[tokenId]`.
assembly {
// Compute the slot.
mstore(0x00, tokenId)
mstore(0x20, tokenApprovalPtr.slot)
let hash := keccak256(0, 0x40)
sstore(hash, 0)
mstore(0x20, tokenApprovalsPtr.slot)
approvedAddressSlot := keccak256(0x00, 0x40)
// Load the slot's value from storage.
approvedAddress := sload(approvedAddressSlot)
}
}

/**
* @dev Returns whether the `approvedAddress` is equals to `from` or `msgSender`.
*/
function _isOwnerOrApproved(
address approvedAddress,
address from,
address msgSender
Vectorized marked this conversation as resolved.
Show resolved Hide resolved
) private pure returns (bool result) {
assembly {
// Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
Vectorized marked this conversation as resolved.
Show resolved Hide resolved
from := and(from, BITMASK_ADDRESS)
// `msgSender == from || msgSender == approvedAddress`.
result := or(eq(msgSender, from), eq(msgSender, approvedAddress))
}
}

Expand All @@ -611,20 +632,22 @@ contract ERC721A is IERC721A {

if (address(uint160(prevOwnershipPacked)) != from) revert TransferFromIncorrectOwner();

address approvedAddress = _tokenApprovals[tokenId];
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedAddress(tokenId);

bool isApprovedOrOwner = (_msgSenderERC721A() == from ||
isApprovedForAll(from, _msgSenderERC721A()) ||
approvedAddress == _msgSenderERC721A());
if (!_isOwnerOrApproved(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A()))
revert TransferCallerNotOwnerNorApproved();

if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
if (to == address(0)) revert TransferToZeroAddress();

_beforeTokenTransfers(from, to, tokenId, 1);

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

// Underflow of the sender's balance is impossible because we check for
Expand Down Expand Up @@ -684,21 +707,22 @@ contract ERC721A is IERC721A {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);

address from = address(uint160(prevOwnershipPacked));
address approvedAddress = _tokenApprovals[tokenId];

if (approvalCheck) {
bool isApprovedOrOwner = (_msgSenderERC721A() == from ||
isApprovedForAll(from, _msgSenderERC721A()) ||
approvedAddress == _msgSenderERC721A());
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedAddress(tokenId);

if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
}
if (approvalCheck)
if (!_isOwnerOrApproved(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A()))
revert TransferCallerNotOwnerNorApproved();

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

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

// Underflow of the sender's balance is impossible because we check for
Expand Down