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

Add ERC1363 implementation #4631

Merged
merged 96 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
84fe15d
Add ERC1363 contracts
vittominacori Sep 27, 2023
67d6fcd
Add ERC1363 tests
vittominacori Sep 27, 2023
4cbb703
Add changeset
vittominacori Sep 27, 2023
fdd322b
Improve documentation
vittominacori Sep 27, 2023
f8bd003
Add ERC1363Holder
vittominacori Sep 27, 2023
381387c
Update changeset
vittominacori Sep 27, 2023
83baa53
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Sep 29, 2023
a07c77c
Move mint out of test behavior and rename const to avoid confusion
vittominacori Sep 29, 2023
cf3c377
migrate Ownable tests to ethers
Amxx Oct 5, 2023
673ff79
fix lint
Amxx Oct 5, 2023
c9b4e8d
Update hardhat.config.js
Amxx Oct 5, 2023
c46a45e
add Ownable2Step
Amxx Oct 5, 2023
328a344
remove deploy helper
Amxx Oct 5, 2023
7c12232
add deprecation notices
Amxx Oct 5, 2023
517ad87
up
Amxx Oct 5, 2023
9002e5f
up
Amxx Oct 5, 2023
16731a9
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Oct 6, 2023
89ab4c9
Rename wrong file
vittominacori Oct 6, 2023
74558f8
remove .address when doing ethers call that support addressable
Amxx Oct 6, 2023
0e6e84c
Update pragma to 0.8.20
vittominacori Oct 6, 2023
9ea2db1
Fix flaky test in ERC2981.behavior
ernestognw Oct 10, 2023
fd7100a
Update test/access/Ownable.test.js
Amxx Oct 10, 2023
1a9a046
Update test/access/Ownable.test.js
Amxx Oct 10, 2023
b569ca8
Fix lint
ernestognw Oct 10, 2023
b24fec4
Fix upgradeable tests
ernestognw Oct 10, 2023
5e96021
redesign signers/fixture
Amxx Oct 10, 2023
f382329
Fix vanilla tests by overriding require and readArtifact with sync an…
ernestognw Oct 10, 2023
95be46d
Revert "redesign signers/fixture"
Amxx Oct 10, 2023
55b0406
Optimize ethers.getSigners call by passing a promise to use within fi…
ernestognw Oct 10, 2023
e45e482
Slice ethers accounts
ernestognw Oct 11, 2023
b391c2f
rename
Amxx Oct 11, 2023
74bab81
override hre.ethers.getSigners
Amxx Oct 11, 2023
98e5ecd
unify coding style/naming between env-contracts.js and env-artifacts.js
Amxx Oct 11, 2023
4db1800
Attempt to fix tests by avoid overriding `this` in Truffle require
ernestognw Oct 12, 2023
0a8a69d
Revert "Attempt to fix tests by avoid overriding `this` in Truffle re…
Amxx Oct 12, 2023
ec3bfc5
Merge branch 'master' into test/migration/ownable
Amxx Oct 12, 2023
5d5a150
Force compile on upgradeable and coverage workflows
Amxx Oct 12, 2023
300fa1e
use cached getSigners() in fixtures
Amxx Oct 13, 2023
ae4381e
use describe instead of contract for ethers test that don't need the …
Amxx Oct 13, 2023
b02770e
add enviornment sanity check
Amxx Oct 16, 2023
4e6a1ca
skip signer slice when running coverage
Amxx Oct 16, 2023
69b91a0
up
Amxx Oct 16, 2023
5162e98
Move non standardized error definition to the implementation file
Amxx Oct 17, 2023
2668934
Add "relaxed" helpers for ERC1363 in SafeERC20
Amxx Oct 17, 2023
5b84827
Merge branch 'test/migration/ownable' into feat/eip1363-v5.x
Amxx Oct 17, 2023
da0d4ff
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Oct 17, 2023
f269fa7
rewrite tests using ethers
Amxx Oct 17, 2023
8a4cae5
Merge branch 'master' into feat/eip1363-v5.x
Amxx Oct 17, 2023
46858a7
Remove ERC1363 Spender and Receiver interface from introspection test
vittominacori Oct 17, 2023
c07c414
addapt erc165 verification to ethers
Amxx Oct 17, 2023
24e8e89
Add a test to check that events are emitted
vittominacori Oct 17, 2023
f781db4
minor fixes
Amxx Oct 17, 2023
17b697b
Merge branch 'feat/eip1363-v5.x' of https://github.com/vittominacori/…
Amxx Oct 17, 2023
80930e8
migrate ERC1363Holder test to ethers + lint:fix
Amxx Oct 17, 2023
a844207
improve SupportInterface.behavior.js
Amxx Oct 17, 2023
178ff7d
codespell
Amxx Oct 17, 2023
34a7a8d
test SafeERC20 relaxed functions
Amxx Oct 17, 2023
7fde72d
fix lint
Amxx Oct 17, 2023
5ec39cb
Apply suggestions from code review
Amxx Oct 17, 2023
eb0fb78
remove ERC1363Holder
Amxx Oct 17, 2023
b893eec
add changeset
Amxx Oct 17, 2023
e137121
Fix documentation reference
vittominacori Oct 18, 2023
07bdbfd
Use external instead of public in mock (as it is in interface)
vittominacori Oct 20, 2023
4440345
Typo
vittominacori Oct 26, 2023
3dff6e2
Update contracts/interfaces/IERC1363Spender.sol
vittominacori Oct 27, 2023
c2f27c8
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Oct 27, 2023
81d191f
Apply suggestions from code review
Amxx Oct 27, 2023
c3fa314
test coverage
Amxx Oct 27, 2023
09049d3
lint
Amxx Oct 27, 2023
0ac39e4
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Nov 14, 2023
341a687
Merge branch 'master' of https://github.com/OpenZeppelin/openzeppelin…
vittominacori Dec 8, 2023
938a92b
Merge branch 'master' into feat/eip1363-v5.x
Amxx Dec 15, 2023
924b50d
re-enable shouldBehaveLikeERC20 + minor test cleanup
Amxx Dec 15, 2023
7d5e66e
document the 1363 functions not passing the standardized tests
Amxx Dec 15, 2023
f0d3a17
fix lint
Amxx Dec 15, 2023
7629147
Merge branch 'master' into feat/eip1363-v5.x
Amxx Jan 2, 2024
8f7c2bf
fix merge
Amxx Jan 2, 2024
feb824e
fix lint
Amxx Jan 2, 2024
af285c6
add notices
Amxx Jan 2, 2024
08c8eff
Merge branch 'master' into feat/eip1363-v5.x
Amxx Jan 18, 2024
d9cba07
forge update
Amxx Jan 18, 2024
ed594f0
Update .changeset/friendly-nails-push.md
Amxx Jan 18, 2024
72b0271
Merge branch 'master' into feat/eip1363-v5.x
ernestognw Jan 18, 2024
ab19eff
Optionally evaluate return value when calling ERC1363 using `transfer…
ernestognw Jan 18, 2024
5efabe6
Apply review comments
ernestognw Jan 18, 2024
07f1e4d
Update test/token/ERC20/utils/SafeERC20.test.js
Amxx Jan 19, 2024
fb78a86
Update test/token/ERC20/utils/SafeERC20.test.js
Amxx Jan 19, 2024
8639885
check values
Amxx Jan 23, 2024
10f4d1d
test SafeERC20 against non compliant ERC1363 tokens
Amxx Jan 23, 2024
327aca5
lint
Amxx Jan 23, 2024
cba6a94
Nits
ernestognw Jan 23, 2024
6747cb8
Add note for usdt-like approval requirements in approveAndCallRelaxed
ernestognw Jan 23, 2024
96551b0
Moar nits
ernestognw Jan 23, 2024
54f06e7
Improve ERC1363 tests
ernestognw Jan 23, 2024
12e2952
Fix codespell
ernestognw Jan 23, 2024
2c85474
final cleanup (minimize changes)
Amxx Jan 24, 2024
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/friendly-nails-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`ERC1363`: Add implementation of the token payable standard allowing execution of contract code after transfers and approvals.
5 changes: 5 additions & 0 deletions .changeset/nice-paws-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`SafeERC20`: Add "relaxed" function for interacting with ERC-1363 functions in a way that is compatible with EOAs.
86 changes: 46 additions & 40 deletions contracts/interfaces/IERC1363.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
* @dev Interface of an ERC-1363 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1363[ERC].
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines a interface for ERC-20 tokens that supports executing recipient
* code after `transfer` or `transferFrom`, or spender code after `approve`.
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC165, IERC20 {
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
Expand All @@ -26,55 +26,61 @@ interface IERC1363 is IERC165, IERC20 {
*/

/**
* @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
* @param to address The address which you want to transfer to
* @param amount uint256 The amount of tokens to be transferred
* @return true unless throwing
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 amount) external returns (bool);
function transferAndCall(address to, uint256 value) external returns (bool);

/**
* @dev Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
* @param to address The address which you want to transfer to
* @param amount uint256 The amount of tokens to be transferred
* @param data bytes Additional data with no specified format, sent in call to `to`
* @return true unless throwing
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 amount, bytes memory data) external returns (bool);
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

/**
* @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param amount uint256 The amount of tokens to be transferred
* @return true unless throwing
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 amount) external returns (bool);
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

/**
* @dev Transfer tokens from one address to another and then call `onTransferReceived` on receiver
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param amount uint256 The amount of tokens to be transferred
* @param data bytes Additional data with no specified format, sent in call to `to`
* @return true unless throwing
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 amount, bytes memory data) external returns (bool);
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
* and then call `onApprovalReceived` on spender.
* @param spender address The address which will spend the funds
* @param amount uint256 The amount of tokens to be spent
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 amount) external returns (bool);
function approveAndCall(address spender, uint256 value) external returns (bool);

/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
* and then call `onApprovalReceived` on spender.
* @param spender address The address which will spend the funds
* @param amount uint256 The amount of tokens to be spent
* @param data bytes Additional data with no specified format, sent in call to `spender`
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 amount, bytes memory data) external returns (bool);
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
37 changes: 17 additions & 20 deletions contracts/interfaces/IERC1363Receiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,29 @@
pragma solidity ^0.8.20;

/**
* @dev Interface for any contract that wants to support {IERC1363-transferAndCall}
* or {IERC1363-transferFromAndCall} from {ERC1363} token contracts.
* @title IERC1363Receiver
* @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall`
* from ERC-1363 token contracts.
*/
interface IERC1363Receiver {
/*
* Note: the ERC-165 identifier for this interface is 0x88a7ca5c.
* 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
*/

/**
* @notice Handle the receipt of ERC-1363 tokens
* @dev Any ERC-1363 smart contract calls this function on the recipient
* after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the
* transfer. Return of other than the magic value MUST result in the
* transaction being reverted.
* Note: the token contract address is always the message sender.
* @param operator address The address which called `transferAndCall` or `transferFromAndCall` function
* @param from address The address which are token transferred from
* @param amount uint256 The amount of tokens transferred
* @param data bytes Additional data with no specified format
* @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` unless throwing
* @dev Whenever ERC-1363 tokens are transferred to this contract via `transferAndCall` or `transferFromAndCall`
* by `operator` from `from`, this function is called.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
* (i.e. 0x88a7ca5c, or its own function selector).
*
* @param operator The address which called `transferAndCall` or `transferFromAndCall` function.
* @param from The address which are tokens transferred from.
* @param value The amount of tokens transferred.
* @param data Additional data with no specified format.
* @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))` if transfer is allowed unless throwing.
*/
function onTransferReceived(
address operator,
address from,
uint256 amount,
bytes memory data
uint256 value,
bytes calldata data
) external returns (bytes4);
}
33 changes: 15 additions & 18 deletions contracts/interfaces/IERC1363Spender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,23 @@
pragma solidity ^0.8.20;

/**
* @dev Interface for any contract that wants to support {IERC1363-approveAndCall}
* from {ERC1363} token contracts.
* @title ERC1363Spender
* @dev Interface for any contract that wants to support `approveAndCall`
* from ERC-1363 token contracts.
*/
interface IERC1363Spender {
/*
* Note: the ERC-165 identifier for this interface is 0x7b04a2d0.
* 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
*/

/**
* @notice Handle the approval of ERC-1363 tokens
* @dev Any ERC-1363 smart contract calls this function on the recipient
* after an `approve`. This function MAY throw to revert and reject the
* approval. Return of other than the magic value MUST result in the
* transaction being reverted.
* Note: the token contract address is always the message sender.
* @param owner address The address which called `approveAndCall` function
* @param amount uint256 The amount of tokens to be spent
* @param data bytes Additional data with no specified format
* @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`unless throwing
* @dev Whenever an ERC-1363 token `owner` approves this contract via `approveAndCall`
* to spend their tokens, this function is called.
*
* NOTE: To accept the approval, this must return
* `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
* (i.e. 0x7b04a2d0, or its own function selector).
*
* @param owner The address which called `approveAndCall` function and previously owned the tokens.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format.
* @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))` if approval is allowed unless throwing.
*/
function onApprovalReceived(address owner, uint256 amount, bytes memory data) external returns (bytes4);
function onApprovalReceived(address owner, uint256 value, bytes calldata data) external returns (bytes4);
}
52 changes: 52 additions & 0 deletions contracts/mocks/token/ERC1363ReceiverMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC1363Receiver} from "../../interfaces/IERC1363Receiver.sol";

contract ERC1363ReceiverMock is IERC1363Receiver {
enum RevertType {
None,
RevertWithoutMessage,
RevertWithMessage,
RevertWithCustomError,
Panic
}

bytes4 private _retval;
RevertType private _error;

event Received(address operator, address from, uint256 value, bytes data);
error CustomError(bytes4);

constructor() {
_retval = IERC1363Receiver.onTransferReceived.selector;
_error = RevertType.None;
}

function setUp(bytes4 retval, RevertType error) public {
_retval = retval;
_error = error;
ernestognw marked this conversation as resolved.
Show resolved Hide resolved
}

function onTransferReceived(
address operator,
address from,
uint256 value,
bytes calldata data
) external override returns (bytes4) {
if (_error == RevertType.RevertWithoutMessage) {
revert();
} else if (_error == RevertType.RevertWithMessage) {
revert("ERC1363ReceiverMock: reverting");
} else if (_error == RevertType.RevertWithCustomError) {
revert CustomError(_retval);
} else if (_error == RevertType.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}

emit Received(operator, from, value, data);
return _retval;
}
}
47 changes: 47 additions & 0 deletions contracts/mocks/token/ERC1363SpenderMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC1363Spender} from "../../interfaces/IERC1363Spender.sol";

contract ERC1363SpenderMock is IERC1363Spender {
enum RevertType {
None,
RevertWithoutMessage,
RevertWithMessage,
RevertWithCustomError,
Panic
}

bytes4 private _retval;
RevertType private _error;

event Approved(address owner, uint256 value, bytes data);
error CustomError(bytes4);

constructor() {
_retval = IERC1363Spender.onApprovalReceived.selector;
_error = RevertType.None;
}

function setUp(bytes4 retval, RevertType error) public {
_retval = retval;
_error = error;
}

function onApprovalReceived(address owner, uint256 value, bytes calldata data) external override returns (bytes4) {
if (_error == RevertType.RevertWithoutMessage) {
revert();
} else if (_error == RevertType.RevertWithMessage) {
revert("ERC1363SpenderMock: reverting");
} else if (_error == RevertType.RevertWithCustomError) {
revert CustomError(_retval);
} else if (_error == RevertType.Panic) {
uint256 a = uint256(0) / uint256(0);
a;
}

emit Approved(owner, value, data);
return _retval;
}
}
3 changes: 3 additions & 0 deletions contracts/token/ERC20/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Additionally there are multiple custom extensions, including:
* {ERC20FlashMint}: token level support for flash loans through the minting and burning of ephemeral tokens (standardized as ERC-3156).
* {ERC20Votes}: support for voting and vote delegation.
* {ERC20Wrapper}: wrapper to create an ERC-20 backed by another ERC-20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}.
* {ERC1363}: support for calling the target of a transfer or approval, enabling code execution on the receiver within a single transaction.
* {ERC4626}: tokenized vault that manages shares (represented as ERC-20) that are backed by assets (another ERC-20).

Finally, there are some utilities to interact with ERC-20 contracts in various ways:
Expand Down Expand Up @@ -60,6 +61,8 @@ NOTE: This core set of contracts is designed to be unopinionated, allowing devel

{{ERC20FlashMint}}

{{ERC1363}}

{{ERC4626}}

== Utilities
Expand Down
Loading
Loading