Skip to content

Commit c83c7a3

Browse files
committed
ERC721 dev
1 parent cfe398c commit c83c7a3

File tree

5 files changed

+361
-56
lines changed

5 files changed

+361
-56
lines changed

src/ERC20/ERC20/ERC20Facet.sol

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-License-Identifier: UNLICENSED
1+
// SPDX-License-Identifier: MIT
22
pragma solidity >=0.8.30;
33

44

@@ -36,7 +36,6 @@ contract ERC20Facet {
3636
s.slot := position
3737
}
3838
}
39-
4039

4140
function name() external view returns (string memory) {
4241
return getStorage().name;
@@ -140,5 +139,4 @@ contract ERC20Facet {
140139
emit Transfer(msg.sender, address(0), _value);
141140
}
142141

143-
144142
}

src/ERC721/ERC721/ERC721Facet.sol

Lines changed: 12 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ contract ERC721Facet {
3636
// @custom:storage-location erc8042:compose.erc721
3737
struct ERC721Storage {
3838
string name;
39-
string symbol;
39+
string symbol;
40+
mapping(uint256 tokenId => string tokenURI) tokenURIOf;
4041
mapping(uint256 tokenId => address owner) ownerOf;
4142
mapping(address owner => uint256 balance) balanceOf;
4243
mapping(uint256 tokenId => address approved) approved;
@@ -56,7 +57,7 @@ contract ERC721Facet {
5657

5758
function symbol() external view returns (string memory) {
5859
return getStorage().symbol;
59-
}
60+
}
6061

6162
function balanceOf(address _owner) external view returns (uint256) {
6263
if (_owner == address(0)) {
@@ -104,9 +105,9 @@ contract ERC721Facet {
104105
}
105106
getStorage().isApprovedForAll[msg.sender][_operator] = _approved;
106107
emit ApprovalForAll(msg.sender, _operator, _approved);
107-
}
108-
109-
function transferFrom(address _from, address _to, uint256 _tokenId) external {
108+
}
109+
110+
function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal {
110111
ERC721Storage storage s = getStorage();
111112
if (_to == address(0)) {
112113
revert ERC721InvalidReceiver(address(0));
@@ -131,31 +132,13 @@ contract ERC721Facet {
131132
s.ownerOf[_tokenId] = _to;
132133
emit Transfer(_from, _to, _tokenId);
133134
}
135+
136+
function transferFrom(address _from, address _to, uint256 _tokenId) external {
137+
internalTransferFrom(_from, _to, _tokenId);
138+
}
134139

135140
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
136-
ERC721Storage storage s = getStorage();
137-
if (_to == address(0)) {
138-
revert ERC721InvalidReceiver(address(0));
139-
}
140-
address owner = s.ownerOf[_tokenId];
141-
if (owner == address(0)) {
142-
revert ERC721NonexistentToken(_tokenId);
143-
}
144-
if (owner != _from) {
145-
revert ERC721IncorrectOwner(_from, _tokenId, owner);
146-
}
147-
if (msg.sender != _from) {
148-
if(!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) {
149-
revert ERC721InsufficientApproval(msg.sender, _tokenId);
150-
}
151-
}
152-
delete s.approved[_tokenId];
153-
unchecked {
154-
s.balanceOf[_from]--;
155-
s.balanceOf[_to]++;
156-
}
157-
s.ownerOf[_tokenId] = _to;
158-
emit Transfer(_from, _to, _tokenId);
141+
internalTransferFrom(_from, _to, _tokenId);
159142

160143
// If _to is a contract, check for IERC721Receiver implementation
161144
if (_to.code.length > 0) {
@@ -179,30 +162,7 @@ contract ERC721Facet {
179162
}
180163

181164
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
182-
ERC721Storage storage s = getStorage();
183-
if (_to == address(0)) {
184-
revert ERC721InvalidReceiver(address(0));
185-
}
186-
address owner = s.ownerOf[_tokenId];
187-
if (owner == address(0)) {
188-
revert ERC721NonexistentToken(_tokenId);
189-
}
190-
if (owner != _from) {
191-
revert ERC721IncorrectOwner(_from, _tokenId, owner);
192-
}
193-
if (msg.sender != _from) {
194-
if(!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) {
195-
revert ERC721InsufficientApproval(msg.sender, _tokenId);
196-
}
197-
}
198-
delete s.approved[_tokenId];
199-
unchecked {
200-
s.balanceOf[_from]--;
201-
s.balanceOf[_to]++;
202-
}
203-
s.ownerOf[_tokenId] = _to;
204-
emit Transfer(_from, _to, _tokenId);
205-
165+
internalTransferFrom(_from, _to, _tokenId);
206166
// If _to is a contract, check for IERC721Receiver implementation
207167
if (_to.code.length > 0) {
208168
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 retval) {

src/ERC721/ERC721/libraries/LibERC721.sol

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ library LibERC721 {
1010
error ERC721InvalidReceiver(address _receiver);
1111
error ERC721InsufficientApproval(address _operator, uint256 _tokenId);
1212

13-
1413
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
1514

1615
// Struct storage position defined by keccak256 hash
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.30;
3+
4+
5+
/// @notice Interface for contracts that want to support safeTransfers.
6+
interface IERC721Receiver {
7+
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data) external returns (bytes4);
8+
}
9+
10+
11+
/// @title ERC-721 token (zero-dependency implementation)
12+
/// @notice A complete, dependency-free ERC-721 implementation using the project's storage pattern.
13+
/// @dev This contract provides metadata, ownership, approvals, safe transfers (with local IERC721Receiver check),
14+
/// minting, burning, and helpers. It intentionally avoids external imports.
15+
contract ERC721EnumerableFacet {
16+
17+
// ERC-6093: Custom errors for ERC-721
18+
error ERC721InvalidOwner(address _owner);
19+
error ERC721NonexistentToken(uint256 _tokenId);
20+
error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner);
21+
error ERC721InvalidSender(address _sender);
22+
error ERC721InvalidReceiver(address _receiver);
23+
error ERC721InsufficientApproval(address _operator, uint256 _tokenId);
24+
error ERC721InvalidApprover(address _approver);
25+
error ERC721InvalidOperator(address _operator);
26+
error ERC721OutOfBoundsIndex(address _owner, uint256 _index);
27+
28+
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
29+
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
30+
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
31+
32+
// Struct storage position defined by keccak256 hash
33+
// of diamond storage identifier
34+
bytes32 constant STORAGE_POSITION = keccak256("compose.erc721.enumerable");
35+
36+
// Storage defined using the ERC-8042 standard
37+
// @custom:storage-location erc8042:compose.erc721.enumerable
38+
struct ERC721EnumerableStorage {
39+
string name;
40+
string symbol;
41+
mapping(uint256 tokenId => string tokenURI) tokenURIOf;
42+
43+
mapping(uint256 tokenId => address owner) ownerOf;
44+
mapping(address owner => uint256[] ownedTokens) ownedTokensOf;
45+
mapping(uint256 tokenId => uint256 ownedTokensIndex) ownedTokensIndexOf;
46+
uint256[] allTokens;
47+
mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndexOf;
48+
49+
mapping(uint256 tokenId => address approved) approved;
50+
mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll;
51+
}
52+
53+
function getStorage() internal pure returns (ERC721EnumerableStorage storage s) {
54+
bytes32 position = STORAGE_POSITION;
55+
assembly {
56+
s.slot := position
57+
}
58+
}
59+
60+
function name() external view returns (string memory) {
61+
return getStorage().name;
62+
}
63+
64+
function symbol() external view returns (string memory) {
65+
return getStorage().symbol;
66+
}
67+
68+
function totalSupply() external view returns (uint256) {
69+
return getStorage().allTokens.length;
70+
}
71+
72+
function balanceOf(address _owner) external view returns (uint256) {
73+
if (_owner == address(0)) {
74+
revert ERC721InvalidOwner(_owner);
75+
}
76+
return getStorage().ownedTokensOf[_owner].length;
77+
}
78+
79+
function ownerOf(uint256 _tokenId) public view returns (address) {
80+
address owner = getStorage().ownerOf[_tokenId];
81+
if (owner == address(0)) {
82+
revert ERC721NonexistentToken(_tokenId);
83+
}
84+
return owner;
85+
}
86+
87+
// IERC721Enumerable
88+
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256) {
89+
ERC721EnumerableStorage storage s = getStorage();
90+
if (_index >= s.ownedTokensOf[_owner].length) {
91+
revert ERC721OutOfBoundsIndex(_owner, _index);
92+
}
93+
return s.ownedTokensOf[_owner][_index];
94+
}
95+
96+
function getApproved(uint256 _tokenId) external view returns (address) {
97+
address owner = getStorage().ownerOf[_tokenId];
98+
if (owner == address(0)) {
99+
revert ERC721NonexistentToken(_tokenId);
100+
}
101+
return getStorage().approved[_tokenId];
102+
}
103+
104+
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
105+
return getStorage().isApprovedForAll[_owner][_operator];
106+
}
107+
108+
function approve(address _approved, uint256 _tokenId) external {
109+
ERC721EnumerableStorage storage s = getStorage();
110+
address owner = s.ownerOf[_tokenId];
111+
if (owner == address(0)) {
112+
revert ERC721NonexistentToken(_tokenId);
113+
}
114+
if (msg.sender != owner && !s.isApprovedForAll[owner][msg.sender]) {
115+
revert ERC721InvalidApprover(_approved);
116+
}
117+
s.approved[_tokenId] = _approved;
118+
emit Approval(owner, _approved, _tokenId);
119+
}
120+
121+
function setApprovalForAll(address _operator, bool _approved) external {
122+
if (_operator == address(0)) {
123+
revert ERC721InvalidOperator(_operator);
124+
}
125+
getStorage().isApprovedForAll[msg.sender][_operator] = _approved;
126+
emit ApprovalForAll(msg.sender, _operator, _approved);
127+
}
128+
129+
function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal {
130+
ERC721EnumerableStorage storage s = getStorage();
131+
if (_to == address(0)) {
132+
revert ERC721InvalidReceiver(address(0));
133+
}
134+
address owner = s.ownerOf[_tokenId];
135+
if (owner == address(0)) {
136+
revert ERC721NonexistentToken(_tokenId);
137+
}
138+
if (owner != _from) {
139+
revert ERC721IncorrectOwner(_from, _tokenId, owner);
140+
}
141+
if (msg.sender != _from) {
142+
if(!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) {
143+
revert ERC721InsufficientApproval(msg.sender, _tokenId);
144+
}
145+
}
146+
delete s.approved[_tokenId];
147+
// removing token from _from's ownedTokens
148+
uint256 tokenIndex = s.ownedTokensIndexOf[_tokenId];
149+
uint256 lastTokenIndex = s.ownedTokensOf[_from].length - 1;
150+
if(tokenIndex != lastTokenIndex) {
151+
uint256 lastTokenId = s.ownedTokensOf[_from][lastTokenIndex];
152+
s.ownedTokensOf[_from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
153+
s.ownedTokensIndexOf[lastTokenId] = tokenIndex; // Update the moved token's index
154+
}
155+
s.ownedTokensOf[_from].pop();
156+
// adding token to _to's ownedTokens
157+
s.ownedTokensIndexOf[_tokenId] = s.ownedTokensOf[_to].length;
158+
s.ownedTokensOf[_to].push(_tokenId);
159+
s.ownerOf[_tokenId] = _to;
160+
emit Transfer(_from, _to, _tokenId);
161+
}
162+
163+
function transferFrom(address _from, address _to, uint256 _tokenId) external {
164+
internalTransferFrom(_from, _to, _tokenId);
165+
}
166+
167+
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
168+
internalTransferFrom(_from, _to, _tokenId);
169+
170+
// If _to is a contract, check for IERC721Receiver implementation
171+
if (_to.code.length > 0) {
172+
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") returns (bytes4 retval) {
173+
if (retval != IERC721Receiver.onERC721Received.selector) {
174+
revert ERC721InvalidReceiver(_to);
175+
}
176+
} catch (bytes memory reason) {
177+
if (reason.length == 0) {
178+
// non-IERC721Receiver implementer
179+
revert ERC721InvalidReceiver(_to);
180+
} else {
181+
// Return the original revert reason
182+
// "memory-safe" means we used memory safely so Solidity does not disable optimizations
183+
assembly ("memory-safe") {
184+
revert(add(reason, 0x20), mload(reason))
185+
}
186+
}
187+
}
188+
}
189+
}
190+
191+
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
192+
internalTransferFrom(_from, _to, _tokenId);
193+
194+
// If _to is a contract, check for IERC721Receiver implementation
195+
if (_to.code.length > 0) {
196+
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 retval) {
197+
if (retval != IERC721Receiver.onERC721Received.selector) {
198+
revert ERC721InvalidReceiver(_to);
199+
}
200+
} catch (bytes memory reason) {
201+
if (reason.length == 0) {
202+
// non-IERC721Receiver implementer
203+
revert ERC721InvalidReceiver(_to);
204+
} else {
205+
// Return the original revert reason
206+
// "memory-safe" means we used memory safely so Solidity does not disable optimizations
207+
assembly ("memory-safe") {
208+
revert(add(reason, 0x20), mload(reason))
209+
}
210+
}
211+
}
212+
}
213+
}
214+
215+
216+
}

0 commit comments

Comments
 (0)