Skip to content

Commit

Permalink
Create a ERC1363Utils helper similar to existing ERC721Utils and ERC1…
Browse files Browse the repository at this point in the history
…155Utils (OpenZeppelin#5133)

Co-authored-by: Ernesto García <ernestognw@gmail.com>
Co-authored-by: cairo <cairoeth@protonmail.com>
Signed-off-by: Hadrien Croubois <hadrien.croubois@gmail.com>
  • Loading branch information
3 people committed Aug 2, 2024
1 parent 0d2d72a commit a726d5d
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 78 deletions.
5 changes: 5 additions & 0 deletions .changeset/tricky-bats-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`ERC1363Utils`: Add helper similar to the existing `ERC721Utils` and `ERC1155Utils`
2 changes: 2 additions & 0 deletions contracts/token/ERC1155/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
== Utilities

{{ERC1155Holder}}

{{ERC1155Utils}}
2 changes: 2 additions & 0 deletions contracts/token/ERC20/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
== Utilities

{{SafeERC20}}

{{ERC1363Utils}}
82 changes: 4 additions & 78 deletions contracts/token/ERC20/extensions/ERC1363.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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))
}
}
}
}
}
96 changes: 96 additions & 0 deletions contracts/token/ERC20/utils/ERC1363Utils.sol
Original file line number Diff line number Diff line change
@@ -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))
}
}
}
}
}
2 changes: 2 additions & 0 deletions contracts/token/ERC721/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,5 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel
== Utilities

{{ERC721Holder}}

{{ERC721Utils}}

0 comments on commit a726d5d

Please sign in to comment.