From e03a3774699da45b63295cb843ec9152095d1fbc Mon Sep 17 00:00:00 2001 From: Vectorized Date: Sun, 27 Feb 2022 04:18:46 +0800 Subject: [PATCH] Mint re-entrancy guard with optimizations. (#131) * Added reentrancy guard to _mint * Optimized _mint * Added more optimizations * Added missing isContract check in safeTransferFrom * Moved updatedIndex++ for gas savings * Convert while to do while --- contracts/ERC721A.sol | 48 ++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/contracts/ERC721A.sol b/contracts/ERC721A.sol index 9c5d5e00c..af376b338 100644 --- a/contracts/ERC721A.sol +++ b/contracts/ERC721A.sol @@ -324,7 +324,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { bytes memory _data ) public virtual override { _transfer(from, to, tokenId); - if (!_checkOnERC721Received(from, to, tokenId, _data)) { + if (to.isContract() && !_checkContractOnERC721Received(from, to, tokenId, _data)) { revert TransferToNonERC721ReceiverImplementer(); } } @@ -396,15 +396,22 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { _ownerships[startTokenId].startTimestamp = uint64(block.timestamp); uint256 updatedIndex = startTokenId; + uint256 end = updatedIndex + quantity; - for (uint256 i; i < quantity; i++) { - emit Transfer(address(0), to, updatedIndex); - if (safe && !_checkOnERC721Received(address(0), to, updatedIndex, _data)) { - revert TransferToNonERC721ReceiverImplementer(); - } - updatedIndex++; + if (safe && to.isContract()) { + do { + emit Transfer(address(0), to, updatedIndex); + if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) { + revert TransferToNonERC721ReceiverImplementer(); + } + } while (updatedIndex != end); + // Reentrancy protection + if (_currentIndex != startTokenId) revert(); + } else { + do { + emit Transfer(address(0), to, updatedIndex++); + } while (updatedIndex != end); } - _currentIndex = updatedIndex; } _afterTokenTransfers(address(0), to, startTokenId, quantity); @@ -534,8 +541,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { } /** - * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. - * The call is not executed if the target address is not a contract. + * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens @@ -543,26 +549,22 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ - function _checkOnERC721Received( + function _checkContractOnERC721Received( address from, address to, uint256 tokenId, bytes memory _data ) private returns (bool) { - if (to.isContract()) { - try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { - return retval == IERC721Receiver(to).onERC721Received.selector; - } catch (bytes memory reason) { - if (reason.length == 0) { - revert TransferToNonERC721ReceiverImplementer(); - } else { - assembly { - revert(add(32, reason), mload(reason)) - } + try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { + return retval == IERC721Receiver(to).onERC721Received.selector; + } catch (bytes memory reason) { + if (reason.length == 0) { + revert TransferToNonERC721ReceiverImplementer(); + } else { + assembly { + revert(add(32, reason), mload(reason)) } } - } else { - return true; } }