Skip to content

Commit

Permalink
Mint re-entrancy guard with optimizations. (#131)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Vectorized authored Feb 26, 2022
1 parent 6064c6a commit e03a377
Showing 1 changed file with 25 additions and 23 deletions.
48 changes: 25 additions & 23 deletions contracts/ERC721A.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -534,35 +541,30 @@ 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
* @param tokenId uint256 ID of the token to be transferred
* @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;
}
}

Expand Down

0 comments on commit e03a377

Please sign in to comment.