Skip to content

Commit

Permalink
Merge branch 'master' into modular-exponentiation-precompile-wrapper-O…
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx authored Feb 2, 2024
2 parents 81e8ba0 + cc431f5 commit 2b72050
Show file tree
Hide file tree
Showing 15 changed files with 744 additions and 155 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-turtles-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Arrays`: deprecate `findUpperBound` in favor of the new `lowerBound`.
5 changes: 5 additions & 0 deletions .changeset/poor-chefs-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`ERC721Utils` and `ERC1155Utils`: Add reusable libraries with functions to perform acceptance checks on `IERC721Receiver` and `IERC1155Receiver` implementers.
5 changes: 5 additions & 0 deletions .changeset/thick-pumpkins-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`Arrays`: add new functions `lowerBound`, `upperBound`, `lowerBoundMemory` and `upperBoundMemory` for lookups in sorted arrays with potential duplicates.
20 changes: 18 additions & 2 deletions contracts/mocks/ArraysMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,24 @@ contract Uint256ArraysMock {
_array = array;
}

function findUpperBound(uint256 element) external view returns (uint256) {
return _array.findUpperBound(element);
function findUpperBound(uint256 value) external view returns (uint256) {
return _array.findUpperBound(value);
}

function lowerBound(uint256 value) external view returns (uint256) {
return _array.lowerBound(value);
}

function upperBound(uint256 value) external view returns (uint256) {
return _array.upperBound(value);
}

function lowerBoundMemory(uint256[] memory array, uint256 value) external pure returns (uint256) {
return array.lowerBoundMemory(value);
}

function upperBoundMemory(uint256[] memory array, uint256 value) external pure returns (uint256) {
return array.upperBoundMemory(value);
}

function unsafeAccess(uint256 pos) external view returns (uint256) {
Expand Down
2 changes: 0 additions & 2 deletions contracts/proxy/ERC1967/ERC1967Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {StorageSlot} from "../../utils/StorageSlot.sol";
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
Expand Down
72 changes: 3 additions & 69 deletions contracts/token/ERC1155/ERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
pragma solidity ^0.8.20;

import {IERC1155} from "./IERC1155.sol";
import {IERC1155Receiver} from "./IERC1155Receiver.sol";
import {IERC1155MetadataURI} from "./extensions/IERC1155MetadataURI.sol";
import {ERC1155Utils} from "./utils/ERC1155Utils.sol";
import {Context} from "../../utils/Context.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {Arrays} from "../../utils/Arrays.sol";
Expand Down Expand Up @@ -203,9 +203,9 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
if (ids.length == 1) {
uint256 id = ids.unsafeMemoryAccess(0);
uint256 value = values.unsafeMemoryAccess(0);
_doSafeTransferAcceptanceCheck(operator, from, to, id, value, data);
ERC1155Utils.checkOnERC1155Received(operator, from, to, id, value, data);
} else {
_doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, values, data);
ERC1155Utils.checkOnERC1155BatchReceived(operator, from, to, ids, values, data);
}
}
}
Expand Down Expand Up @@ -374,72 +374,6 @@ abstract contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI, IER
emit ApprovalForAll(owner, operator, approved);
}

/**
* @dev Performs an acceptance check by calling {IERC1155-onERC1155Received} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}

/**
* @dev Performs a batch acceptance check by calling {IERC1155-onERC1155BatchReceived} on the `to` address
* if it contains code at the moment of execution.
*/
function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) private {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC1155Receiver implementer
revert ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}

/**
* @dev Creates an array in memory with only one value for each of the elements provided.
*/
Expand Down
87 changes: 87 additions & 0 deletions contracts/token/ERC1155/utils/ERC1155Utils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC1155Receiver} from "../IERC1155Receiver.sol";
import {IERC1155Errors} from "../../../interfaces/draft-IERC6093.sol";

/**
* @dev Library that provide common ERC-1155 utility functions.
*
* See https://eips.ethereum.org/EIPS/eip-1155[ERC-1155].
*/
library ERC1155Utils {
/**
* @dev Performs an acceptance check for the provided `operator` by calling {IERC1155-onERC1155Received}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
*
* The acceptance call is not executed and treated as a no-op if the target address is doesn't contain code (i.e. an EOA).
* Otherwise, the recipient must implement {IERC1155Receiver-onERC1155Received} and return the acceptance magic value to accept
* the transfer.
*/
function checkOnERC1155Received(
address operator,
address from,
address to,
uint256 id,
uint256 value,
bytes memory data
) internal {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155Received(operator, from, id, value, data) returns (bytes4 response) {
if (response != IERC1155Receiver.onERC1155Received.selector) {
// Tokens rejected
revert IERC1155Errors.ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC1155Receiver implementer
revert IERC1155Errors.ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}

/**
* @dev Performs a batch acceptance check for the provided `operator` by calling {IERC1155-onERC1155BatchReceived}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
*
* The acceptance call is not executed and treated as a no-op if the target address is doesn't contain code (i.e. an EOA).
* Otherwise, the recipient must implement {IERC1155Receiver-onERC1155Received} and return the acceptance magic value to accept
* the transfer.
*/
function checkOnERC1155BatchReceived(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory values,
bytes memory data
) internal {
if (to.code.length > 0) {
try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, values, data) returns (
bytes4 response
) {
if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
// Tokens rejected
revert IERC1155Errors.ERC1155InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC1155Receiver implementer
revert IERC1155Errors.ERC1155InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}
36 changes: 4 additions & 32 deletions contracts/token/ERC721/ERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
pragma solidity ^0.8.20;

import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {ERC721Utils} from "./utils/ERC721Utils.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
Expand Down Expand Up @@ -158,7 +158,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
transferFrom(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
}

/**
Expand Down Expand Up @@ -311,7 +311,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
*/
function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
_mint(to, tokenId);
_checkOnERC721Received(address(0), to, tokenId, data);
ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data);
}

/**
Expand Down Expand Up @@ -384,7 +384,7 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
_transfer(from, to, tokenId);
_checkOnERC721Received(from, to, tokenId, data);
ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
}

/**
Expand Down Expand Up @@ -452,32 +452,4 @@ abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Er
}
return owner;
}

/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
* recipient doesn't accept the token transfer. The call is not executed if the target address is not a 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
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
revert ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
revert ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}
48 changes: 48 additions & 0 deletions contracts/token/ERC721/utils/ERC721Utils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../IERC721Receiver.sol";
import {IERC721Errors} from "../../../interfaces/draft-IERC6093.sol";

/**
* @dev Library that provide common ERC-721 utility functions.
*
* See https://eips.ethereum.org/EIPS/eip-721[ERC-721].
*/
library ERC721Utils {
/**
* @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received}
* on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
*
* The acceptance call is not executed and treated as a no-op if the target address is doesn't contain code (i.e. an EOA).
* Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept
* the transfer.
*/
function checkOnERC721Received(
address operator,
address from,
address to,
uint256 tokenId,
bytes memory data
) internal {
if (to.code.length > 0) {
try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
if (retval != IERC721Receiver.onERC721Received.selector) {
// Token rejected
revert IERC721Errors.ERC721InvalidReceiver(to);
}
} catch (bytes memory reason) {
if (reason.length == 0) {
// non-IERC721Receiver implementer
revert IERC721Errors.ERC721InvalidReceiver(to);
} else {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
}
}
Loading

0 comments on commit 2b72050

Please sign in to comment.