@@ -324,7 +324,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
324
324
bytes memory _data
325
325
) public virtual override {
326
326
_transfer (from, to, tokenId);
327
- if (! _checkOnERC721Received (from, to, tokenId, _data)) {
327
+ if (to. isContract () && ! _checkContractOnERC721Received (from, to, tokenId, _data)) {
328
328
revert TransferToNonERC721ReceiverImplementer ();
329
329
}
330
330
}
@@ -396,15 +396,22 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
396
396
_ownerships[startTokenId].startTimestamp = uint64 (block .timestamp );
397
397
398
398
uint256 updatedIndex = startTokenId;
399
+ uint256 end = updatedIndex + quantity;
399
400
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);
406
414
}
407
-
408
415
_currentIndex = updatedIndex;
409
416
}
410
417
_afterTokenTransfers (address (0 ), to, startTokenId, quantity);
@@ -534,35 +541,30 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
534
541
}
535
542
536
543
/**
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.
539
545
*
540
546
* @param from address representing the previous owner of the given token ID
541
547
* @param to target address that will receive the tokens
542
548
* @param tokenId uint256 ID of the token to be transferred
543
549
* @param _data bytes optional data to send along with the call
544
550
* @return bool whether the call correctly returned the expected magic value
545
551
*/
546
- function _checkOnERC721Received (
552
+ function _checkContractOnERC721Received (
547
553
address from ,
548
554
address to ,
549
555
uint256 tokenId ,
550
556
bytes memory _data
551
557
) 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))
562
566
}
563
567
}
564
- } else {
565
- return true ;
566
568
}
567
569
}
568
570
0 commit comments