@@ -324,7 +324,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
324324 bytes memory _data
325325 ) public virtual override {
326326 _transfer (from, to, tokenId);
327- if (! _checkOnERC721Received (from, to, tokenId, _data)) {
327+ if (to. isContract () && ! _checkContractOnERC721Received (from, to, tokenId, _data)) {
328328 revert TransferToNonERC721ReceiverImplementer ();
329329 }
330330 }
@@ -396,15 +396,22 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
396396 _ownerships[startTokenId].startTimestamp = uint64 (block .timestamp );
397397
398398 uint256 updatedIndex = startTokenId;
399+ uint256 end = updatedIndex + quantity;
399400
400- for (uint256 i; i < quantity; i++ ) {
401- emit Transfer (address (0 ), to, updatedIndex);
402- if (safe && ! _checkOnERC721Received (address (0 ), to, updatedIndex, _data)) {
403- revert TransferToNonERC721ReceiverImplementer ();
404- }
405- updatedIndex++ ;
401+ if (safe && to.isContract ()) {
402+ do {
403+ emit Transfer (address (0 ), to, updatedIndex);
404+ if (! _checkContractOnERC721Received (address (0 ), to, updatedIndex++ , _data)) {
405+ revert TransferToNonERC721ReceiverImplementer ();
406+ }
407+ } while (updatedIndex != end);
408+ // Reentrancy protection
409+ if (_currentIndex != startTokenId) revert ();
410+ } else {
411+ do {
412+ emit Transfer (address (0 ), to, updatedIndex++ );
413+ } while (updatedIndex != end);
406414 }
407-
408415 _currentIndex = updatedIndex;
409416 }
410417 _afterTokenTransfers (address (0 ), to, startTokenId, quantity);
@@ -534,35 +541,30 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
534541 }
535542
536543 /**
537- * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
538- * The call is not executed if the target address is not a contract.
544+ * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
539545 *
540546 * @param from address representing the previous owner of the given token ID
541547 * @param to target address that will receive the tokens
542548 * @param tokenId uint256 ID of the token to be transferred
543549 * @param _data bytes optional data to send along with the call
544550 * @return bool whether the call correctly returned the expected magic value
545551 */
546- function _checkOnERC721Received (
552+ function _checkContractOnERC721Received (
547553 address from ,
548554 address to ,
549555 uint256 tokenId ,
550556 bytes memory _data
551557 ) private returns (bool ) {
552- if (to.isContract ()) {
553- try IERC721Receiver (to).onERC721Received (_msgSender (), from, tokenId, _data) returns (bytes4 retval ) {
554- return retval == IERC721Receiver (to).onERC721Received.selector ;
555- } catch (bytes memory reason ) {
556- if (reason.length == 0 ) {
557- revert TransferToNonERC721ReceiverImplementer ();
558- } else {
559- assembly {
560- revert (add (32 , reason), mload (reason))
561- }
558+ try IERC721Receiver (to).onERC721Received (_msgSender (), from, tokenId, _data) returns (bytes4 retval ) {
559+ return retval == IERC721Receiver (to).onERC721Received.selector ;
560+ } catch (bytes memory reason ) {
561+ if (reason.length == 0 ) {
562+ revert TransferToNonERC721ReceiverImplementer ();
563+ } else {
564+ assembly {
565+ revert (add (32 , reason), mload (reason))
562566 }
563567 }
564- } else {
565- return true ;
566568 }
567569 }
568570
0 commit comments