From aec36ddd6addbce37bc9780dade636262320571a Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 2 Aug 2024 21:06:42 +0200 Subject: [PATCH] Create a ERC1363Utils helper similar to existing ERC721Utils and ERC1155Utils (#5133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ernesto GarcĂ­a Co-authored-by: cairo --- .changeset/tricky-bats-pretend.md | 5 + contracts/token/ERC1155/README.adoc | 2 + contracts/token/ERC20/README.adoc | 2 + contracts/token/ERC20/extensions/ERC1363.sol | 82 +---------------- contracts/token/ERC20/utils/ERC1363Utils.sol | 96 ++++++++++++++++++++ contracts/token/ERC721/README.adoc | 2 + 6 files changed, 111 insertions(+), 78 deletions(-) create mode 100644 .changeset/tricky-bats-pretend.md create mode 100644 contracts/token/ERC20/utils/ERC1363Utils.sol diff --git a/.changeset/tricky-bats-pretend.md b/.changeset/tricky-bats-pretend.md new file mode 100644 index 00000000000..2809d329357 --- /dev/null +++ b/.changeset/tricky-bats-pretend.md @@ -0,0 +1,5 @@ +--- +'openzeppelin-solidity': minor +--- + +`ERC1363Utils`: Add helper similar to the existing `ERC721Utils` and `ERC1155Utils` diff --git a/contracts/token/ERC1155/README.adoc b/contracts/token/ERC1155/README.adoc index d911d785d85..f8bf958f623 100644 --- a/contracts/token/ERC1155/README.adoc +++ b/contracts/token/ERC1155/README.adoc @@ -39,3 +39,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel == Utilities {{ERC1155Holder}} + +{{ERC1155Utils}} diff --git a/contracts/token/ERC20/README.adoc b/contracts/token/ERC20/README.adoc index faaacc57667..bfbe6790b67 100644 --- a/contracts/token/ERC20/README.adoc +++ b/contracts/token/ERC20/README.adoc @@ -71,3 +71,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel == Utilities {{SafeERC20}} + +{{ERC1363Utils}} diff --git a/contracts/token/ERC20/extensions/ERC1363.sol b/contracts/token/ERC20/extensions/ERC1363.sol index 1852ffa810a..253e443d6b1 100644 --- a/contracts/token/ERC20/extensions/ERC1363.sol +++ b/contracts/token/ERC20/extensions/ERC1363.sol @@ -4,10 +4,8 @@ pragma solidity ^0.8.20; import {ERC20} from "../ERC20.sol"; import {IERC165, ERC165} from "../../../utils/introspection/ERC165.sol"; - import {IERC1363} from "../../../interfaces/IERC1363.sol"; -import {IERC1363Receiver} from "../../../interfaces/IERC1363Receiver.sol"; -import {IERC1363Spender} from "../../../interfaces/IERC1363Spender.sol"; +import {ERC1363Utils} from "../utils/ERC1363Utils.sol"; /** * @title ERC1363 @@ -16,18 +14,6 @@ import {IERC1363Spender} from "../../../interfaces/IERC1363Spender.sol"; * {ERC1363-transferFromAndCall} methods while calls after approvals can be made with {ERC1363-approveAndCall} */ abstract contract ERC1363 is ERC20, ERC165, IERC1363 { - /** - * @dev Indicates a failure with the token `receiver`. Used in transfers. - * @param receiver Address to which tokens are being transferred. - */ - error ERC1363InvalidReceiver(address receiver); - - /** - * @dev Indicates a failure with the token `spender`. Used in approvals. - * @param spender Address that may be allowed to operate on tokens without being their owner. - */ - error ERC1363InvalidSpender(address spender); - /** * @dev Indicates a failure within the {transfer} part of a transferAndCall operation. * @param receiver Address to which tokens are being transferred. @@ -80,7 +66,7 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363 { if (!transfer(to, value)) { revert ERC1363TransferFailed(to, value); } - _checkOnTransferReceived(_msgSender(), to, value, data); + ERC1363Utils.checkOnERC1363TransferReceived(_msgSender(), _msgSender(), to, value, data); return true; } @@ -112,7 +98,7 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363 { if (!transferFrom(from, to, value)) { revert ERC1363TransferFromFailed(from, to, value); } - _checkOnTransferReceived(from, to, value, data); + ERC1363Utils.checkOnERC1363TransferReceived(_msgSender(), from, to, value, data); return true; } @@ -139,67 +125,7 @@ abstract contract ERC1363 is ERC20, ERC165, IERC1363 { if (!approve(spender, value)) { revert ERC1363ApproveFailed(spender, value); } - _checkOnApprovalReceived(spender, value, data); + ERC1363Utils.checkOnERC1363ApprovalReceived(_msgSender(), spender, value, data); return true; } - - /** - * @dev Performs a call to {IERC1363Receiver-onTransferReceived} on a target address. - * - * Requirements: - * - * - The target has code (i.e. is a contract). - * - The target `to` must implement the {IERC1363Receiver} interface. - * - The target must return the {IERC1363Receiver-onTransferReceived} selector to accept the transfer. - */ - function _checkOnTransferReceived(address from, address to, uint256 value, bytes memory data) private { - if (to.code.length == 0) { - revert ERC1363InvalidReceiver(to); - } - - try IERC1363Receiver(to).onTransferReceived(_msgSender(), from, value, data) returns (bytes4 retval) { - if (retval != IERC1363Receiver.onTransferReceived.selector) { - revert ERC1363InvalidReceiver(to); - } - } catch (bytes memory reason) { - if (reason.length == 0) { - revert ERC1363InvalidReceiver(to); - } else { - /// @solidity memory-safe-assembly - assembly { - revert(add(32, reason), mload(reason)) - } - } - } - } - - /** - * @dev Performs a call to {IERC1363Spender-onApprovalReceived} on a target address. - * - * Requirements: - * - * - The target has code (i.e. is a contract). - * - The target `spender` must implement the {IERC1363Spender} interface. - * - The target must return the {IERC1363Spender-onApprovalReceived} selector to accept the approval. - */ - function _checkOnApprovalReceived(address spender, uint256 value, bytes memory data) private { - if (spender.code.length == 0) { - revert ERC1363InvalidSpender(spender); - } - - try IERC1363Spender(spender).onApprovalReceived(_msgSender(), value, data) returns (bytes4 retval) { - if (retval != IERC1363Spender.onApprovalReceived.selector) { - revert ERC1363InvalidSpender(spender); - } - } catch (bytes memory reason) { - if (reason.length == 0) { - revert ERC1363InvalidSpender(spender); - } else { - /// @solidity memory-safe-assembly - assembly { - revert(add(32, reason), mload(reason)) - } - } - } - } } diff --git a/contracts/token/ERC20/utils/ERC1363Utils.sol b/contracts/token/ERC20/utils/ERC1363Utils.sol new file mode 100644 index 00000000000..e6af49de5d3 --- /dev/null +++ b/contracts/token/ERC20/utils/ERC1363Utils.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {IERC1363Receiver} from "../../../interfaces/IERC1363Receiver.sol"; +import {IERC1363Spender} from "../../../interfaces/IERC1363Spender.sol"; + +/** + * @dev Library that provides common ERC-1363 utility functions. + * + * See https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. + */ +library ERC1363Utils { + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1363InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the token `spender`. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1363InvalidSpender(address spender); + + /** + * @dev Performs a call to {IERC1363Receiver-onTransferReceived} on a target address. + * + * Requirements: + * + * - The target has code (i.e. is a contract). + * - The target `to` must implement the {IERC1363Receiver} interface. + * - The target must return the {IERC1363Receiver-onTransferReceived} selector to accept the transfer. + */ + function checkOnERC1363TransferReceived( + address operator, + address from, + address to, + uint256 value, + bytes memory data + ) internal { + if (to.code.length == 0) { + revert ERC1363InvalidReceiver(to); + } + + try IERC1363Receiver(to).onTransferReceived(operator, from, value, data) returns (bytes4 retval) { + if (retval != IERC1363Receiver.onTransferReceived.selector) { + revert ERC1363InvalidReceiver(to); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + revert ERC1363InvalidReceiver(to); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } + + /** + * @dev Performs a call to {IERC1363Spender-onApprovalReceived} on a target address. + * + * Requirements: + * + * - The target has code (i.e. is a contract). + * - The target `spender` must implement the {IERC1363Spender} interface. + * - The target must return the {IERC1363Spender-onApprovalReceived} selector to accept the approval. + */ + function checkOnERC1363ApprovalReceived( + address operator, + address spender, + uint256 value, + bytes memory data + ) internal { + if (spender.code.length == 0) { + revert ERC1363InvalidSpender(spender); + } + + try IERC1363Spender(spender).onApprovalReceived(operator, value, data) returns (bytes4 retval) { + if (retval != IERC1363Spender.onApprovalReceived.selector) { + revert ERC1363InvalidSpender(spender); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + revert ERC1363InvalidSpender(spender); + } else { + /// @solidity memory-safe-assembly + assembly { + revert(add(32, reason), mload(reason)) + } + } + } + } +} diff --git a/contracts/token/ERC721/README.adoc b/contracts/token/ERC721/README.adoc index 0f87916f60a..5554720d699 100644 --- a/contracts/token/ERC721/README.adoc +++ b/contracts/token/ERC721/README.adoc @@ -65,3 +65,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel == Utilities {{ERC721Holder}} + +{{ERC721Utils}}