-
Notifications
You must be signed in to change notification settings - Fork 1
/
ERC721.sol
261 lines (218 loc) · 6.76 KB
/
ERC721.sol
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;
interface IERC165 {
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
interface IERC721 is IERC165 {
function balanceOf(address owner) external view returns (uint balance);
function ownerOf(uint tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint tokenId
) external;
function safeTransferFrom(
address from,
address to,
uint tokenId,
bytes calldata data
) external;
function transferFrom(
address from,
address to,
uint tokenId
) external;
function approve(address to, uint tokenId) external;
function getApproved(uint tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator)
external
view
returns (bool);
}
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint tokenId,
bytes calldata data
) external returns (bytes4);
}
contract ERC721 is IERC721 {
using Address for address;
event Transfer(address indexed from, address indexed to, uint indexed tokenId);
event Approval(
address indexed owner,
address indexed approved,
uint indexed tokenId
);
event ApprovalForAll(
address indexed owner,
address indexed operator,
bool approved
);
// Mapping from token ID to owner address
mapping(uint => address) private _owners;
// Mapping owner address to token count
mapping(address => uint) private _balances;
// Mapping from token ID to approved address
mapping(uint => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
function supportsInterface(bytes4 interfaceId)
external
pure
override
returns (bool)
{
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC165).interfaceId;
}
function balanceOf(address owner) external view override returns (uint) {
require(owner != address(0), "owner = zero address");
return _balances[owner];
}
function ownerOf(uint tokenId) public view override returns (address owner) {
owner = _owners[tokenId];
require(owner != address(0), "token doesn't exist");
}
function isApprovedForAll(address owner, address operator)
external
view
override
returns (bool)
{
return _operatorApprovals[owner][operator];
}
function setApprovalForAll(address operator, bool approved) external override {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function getApproved(uint tokenId) external view override returns (address) {
require(_owners[tokenId] != address(0), "token doesn't exist");
return _tokenApprovals[tokenId];
}
function _approve(
address owner,
address to,
uint tokenId
) private {
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function approve(address to, uint tokenId) external override {
address owner = _owners[tokenId];
require(
msg.sender == owner || _operatorApprovals[owner][msg.sender],
"not owner nor approved for all"
);
_approve(owner, to, tokenId);
}
function _isApprovedOrOwner(
address owner,
address spender,
uint tokenId
) private view returns (bool) {
return (spender == owner ||
_tokenApprovals[tokenId] == spender ||
_operatorApprovals[owner][spender]);
}
function _transfer(
address owner,
address from,
address to,
uint tokenId
) private {
require(from == owner, "not owner");
require(to != address(0), "transfer to the zero address");
_approve(owner, address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function transferFrom(
address from,
address to,
uint tokenId
) external override {
address owner = ownerOf(tokenId);
require(
_isApprovedOrOwner(owner, msg.sender, tokenId),
"not owner nor approved"
);
_transfer(owner, from, to, tokenId);
}
function _checkOnERC721Received(
address from,
address to,
uint tokenId,
bytes memory _data
) private returns (bool) {
if (to.isContract()) {
return
IERC721Receiver(to).onERC721Received(
msg.sender,
from,
tokenId,
_data
) == IERC721Receiver.onERC721Received.selector;
} else {
return true;
}
}
function _safeTransfer(
address owner,
address from,
address to,
uint tokenId,
bytes memory _data
) private {
_transfer(owner, from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver");
}
function safeTransferFrom(
address from,
address to,
uint tokenId,
bytes memory _data
) public override {
address owner = ownerOf(tokenId);
require(
_isApprovedOrOwner(owner, msg.sender, tokenId),
"not owner nor approved"
);
_safeTransfer(owner, from, to, tokenId, _data);
}
function safeTransferFrom(
address from,
address to,
uint tokenId
) external override {
safeTransferFrom(from, to, tokenId, "");
}
function mint(address to, uint tokenId) external {
require(to != address(0), "mint to zero address");
require(_owners[tokenId] == address(0), "token already minted");
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
}
function burn(uint tokenId) external {
address owner = ownerOf(tokenId);
_approve(owner, address(0), tokenId);
_balances[owner] -= 1;
delete _owners[tokenId];
emit Transfer(owner, address(0), tokenId);
}
}
library Address {
function isContract(address account) internal view returns (bool) {
uint size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
}