-
Notifications
You must be signed in to change notification settings - Fork 12.2k
ERC1363 #3017
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
ERC1363 #3017
Changes from all commits
0616203
786fa7c
553002b
65ac716
b88a69b
0208e27
914a6d2
1ec46cf
c24145f
a3ef6b4
ee2b256
eadcad5
08dd234
abccc64
7e2bbfd
b482786
3c0df20
561310c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'openzeppelin-solidity': minor | ||
--- | ||
|
||
`ERC1363`: Add an extension to `ERC20` for performing transferAndCall & approveAndCall operations following ERC-1363. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../../interfaces/IERC1363Receiver.sol"; | ||
import "../../interfaces/IERC1363Spender.sol"; | ||
|
||
contract ERC1363ReceiverMock is IERC1363Receiver, IERC1363Spender { | ||
event TransferReceived(address operator, address from, uint256 value, bytes data); | ||
event ApprovalReceived(address owner, uint256 value, bytes data); | ||
|
||
function onTransferReceived( | ||
address operator, | ||
address from, | ||
uint256 value, | ||
bytes memory data | ||
) external override returns (bytes4) { | ||
if (data.length == 1) { | ||
if (data[0] == 0x00) return bytes4(0); | ||
if (data[0] == 0x01) revert("onTransferReceived revert"); | ||
if (data[0] == 0x02) revert(); | ||
if (data[0] == 0x03) assert(false); | ||
} | ||
emit TransferReceived(operator, from, value, data); | ||
return this.onTransferReceived.selector; | ||
} | ||
|
||
function onApprovalReceived(address owner, uint256 value, bytes memory data) external override returns (bytes4) { | ||
if (data.length == 1) { | ||
if (data[0] == 0x00) return bytes4(0); | ||
if (data[0] == 0x01) revert("onApprovalReceived revert"); | ||
if (data[0] == 0x02) revert(); | ||
if (data[0] == 0x03) assert(false); | ||
} | ||
emit ApprovalReceived(owner, value, data); | ||
return this.onApprovalReceived.selector; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "../ERC20.sol"; | ||
import "../../../interfaces/IERC1363.sol"; | ||
import "../../../interfaces/IERC1363Receiver.sol"; | ||
import "../../../interfaces/IERC1363Spender.sol"; | ||
import "../../../utils/introspection/ERC165.sol"; | ||
import "../../../utils/Address.sol"; | ||
|
||
abstract contract ERC1363 is IERC1363, ERC20, ERC165 { | ||
using Address for address; | ||
|
||
/** | ||
* @dev See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { | ||
return interfaceId == type(IERC1363).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-transferAndCall}. | ||
*/ | ||
function transferAndCall(address to, uint256 value) public override returns (bool) { | ||
return transferAndCall(to, value, bytes("")); | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-transferAndCall}. | ||
*/ | ||
function transferAndCall(address to, uint256 value, bytes memory data) public override returns (bool) { | ||
require(transfer(to, value)); | ||
require( | ||
_checkOnTransferReceived(_msgSender(), _msgSender(), to, value, data), | ||
"ERC1363: transfer to non ERC1363Receiver implementer" | ||
); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-transferFromAndCall}. | ||
*/ | ||
function transferFromAndCall(address from, address to, uint256 value) public override returns (bool) { | ||
return transferFromAndCall(from, to, value, bytes("")); | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-transferFromAndCall}. | ||
*/ | ||
function transferFromAndCall( | ||
address from, | ||
address to, | ||
uint256 value, | ||
bytes memory data | ||
) public override returns (bool) { | ||
require(transferFrom(from, to, value)); | ||
require( | ||
_checkOnTransferReceived(_msgSender(), from, to, value, data), | ||
"ERC1363: transfer to non ERC1363Receiver implementer" | ||
); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-approveAndCall}. | ||
*/ | ||
function approveAndCall(address spender, uint256 value) public override returns (bool) { | ||
return approveAndCall(spender, value, bytes("")); | ||
} | ||
|
||
/** | ||
* @dev See {IERC1363-approveAndCall}. | ||
*/ | ||
function approveAndCall(address spender, uint256 value, bytes memory data) public override returns (bool) { | ||
require(approve(spender, value)); | ||
require( | ||
_checkOnApprovalReceived(_msgSender(), spender, value, data), | ||
"ERC1363: transfer to non ERC1363Spender implementer" | ||
); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev Internal function to invoke {IERC1363Receiver-onTransferReceived} on a target address. | ||
* The call is not executed if the target address is not a contract. | ||
*/ | ||
function _checkOnTransferReceived( | ||
address operator, | ||
address from, | ||
address to, | ||
uint256 value, | ||
bytes memory data | ||
) private returns (bool) { | ||
try IERC1363Receiver(to).onTransferReceived(operator, from, value, data) returns (bytes4 retval) { | ||
return retval == IERC1363Receiver.onTransferReceived.selector; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we always revert we could just revert in here instead of returning boolean no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I copied the approach we use in the ERC721 checks, for consistency through the code base, but I guess putting the require here would be ok |
||
} catch (bytes memory reason) { | ||
if (reason.length == 0) { | ||
revert("ERC1363: transfer to non ERC1363Receiver implementer"); | ||
} else { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
revert(add(32, reason), mload(reason)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @dev Internal function to invoke {IERC1363Spender-onApprovalReceived} on a target address. | ||
* The call is not executed if the target address is not a contract. | ||
*/ | ||
function _checkOnApprovalReceived( | ||
address owner, | ||
frangio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
address spender, | ||
uint256 value, | ||
bytes memory data | ||
) private returns (bool) { | ||
try IERC1363Spender(spender).onApprovalReceived(owner, value, data) returns (bytes4 retval) { | ||
return retval == IERC1363Spender.onApprovalReceived.selector; | ||
} catch (bytes memory reason) { | ||
if (reason.length == 0) { | ||
revert("ERC1363: transfer to non ERC1363Spender implementer"); | ||
} else { | ||
/// @solidity memory-safe-assembly | ||
assembly { | ||
revert(add(32, reason), mload(reason)) | ||
} | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.