forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathERC1363.sol
More file actions
146 lines (130 loc) · 5.03 KB
/
ERC1363.sol
File metadata and controls
146 lines (130 loc) · 5.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: MIT
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";
/**
* @title ERC1363
* @dev Extension of {ERC20} tokens that adds support for code execution after transfers and approvals
* on recipient contracts. Calls after transfers are enabled through the {ERC1363-transferAndCall} and
* {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);
/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1363).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @inheritdoc IERC1363
*/
function transferAndCall(address to, uint256 value) public virtual returns (bool) {
return transferAndCall(to, value, "");
}
/**
* @inheritdoc IERC1363
*/
function transferAndCall(address to, uint256 value, bytes memory data) public virtual returns (bool) {
transfer(to, value);
_checkOnTransferReceived(_msgSender(), to, value, data);
return true;
}
/**
* @inheritdoc IERC1363
*/
function transferFromAndCall(address from, address to, uint256 value) public virtual returns (bool) {
return transferFromAndCall(from, to, value, "");
}
/**
* @inheritdoc IERC1363
*/
function transferFromAndCall(
address from,
address to,
uint256 value,
bytes memory data
) public virtual returns (bool) {
transferFrom(from, to, value);
_checkOnTransferReceived(from, to, value, data);
return true;
}
/**
* @inheritdoc IERC1363
*/
function approveAndCall(address spender, uint256 value) public virtual returns (bool) {
return approveAndCall(spender, value, "");
}
/**
* @inheritdoc IERC1363
*/
function approveAndCall(address spender, uint256 value, bytes memory data) public virtual returns (bool) {
approve(spender, value);
_checkOnApprovalReceived(spender, value, data);
return true;
}
/**
* @dev Performs a call to {IERC1363-onTransferReceived} on a target address.
* This will revert if the target doesn't implement the {IERC1363Receiver} interface or
* if the target doesn't accept the token transfer or
* if the target address is not a contract.
*
*/
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 {IERC1363-onApprovalReceived} on a target address.
* This will revert if the target doesn't implement the {IERC1363Spender} interface or
* if the target doesn't accept the token approval or
* if the target address is not a contract.
*
*/
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))
}
}
}
}
}