Skip to content

Commit e03a377

Browse files
authored
Mint re-entrancy guard with optimizations. (chiru-labs#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
1 parent 6064c6a commit e03a377

File tree

1 file changed

+25
-23
lines changed

1 file changed

+25
-23
lines changed

contracts/ERC721A.sol

+25-23
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)