Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a ERC1363Utils helper similar to existing ERC721Utils and ERC1155Utils #5133

Merged
merged 3 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}}