Skip to content

Commit 131a89c

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/erc20-natspec
# Conflicts: # src/ERC20/ERC20/ERC20Facet.sol
2 parents 1ee7e85 + c83c7a3 commit 131a89c

File tree

10 files changed

+697
-90
lines changed

10 files changed

+697
-90
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ If this feature ban breaks your mind, just realize that this smart contract libr
3030

3131
4. ### No public or private or internal variables
3232

33-
No contract or library may have variables declared private or public or internal. For example: `uint256 public counter;`. These visibility labels are not needed because the library uses ERC-8042 Diamond Storage through out.
33+
No contract or library may have variables declared private or public or internal. For example: `uint256 public counter;`. These visibility labels are not needed because the library uses ERC-8042 Diamond Storage throughout.
3434

3535
5. ### No private functions
3636

script/ERC20.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.13;
33

44
import {Script} from "forge-std/Script.sol";
5-
import {ERC20} from "../src/ERC20/ERC20.sol";
5+
import {ERC20} from "../src/ERC20/ERC20/ERC20.sol";
66

77
contract CounterScript is Script {
88
ERC20 public erc20;
Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
// SPDX-License-Identifier: UNLICENSED
1+
// SPDX-License-Identifier: MIT
22
pragma solidity >=0.8.30;
33

4-
/**
5-
* @title ERC20
6-
* @notice A full ERC-20 token implementation using ERC-8042 diamond storage.
7-
* @dev This implementation appears minimal but fully complies with the ERC-20 standard.
8-
* It avoids Solidity inheritance, constructors, and modifiers as per Compose conventions,
9-
* and uses ERC-6093 custom errors for efficient error handling.
10-
*/
11-
contract ERC20 {
4+
5+
contract ERC20Facet {
126

137
/// @notice Thrown when an account has insufficient balance for a transfer or burn.
148
/// @param _sender Address attempting the transfer.

src/ERC20/ERC20/libraries/LibERC20.sol

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity >=0.8.30;
33

4-
library LibERC20 {
4+
library LibERC20 {
5+
6+
// ERC-6093: Custom errors for ERC-20
7+
error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed);
8+
error ERC20InvalidSender(address _sender);
9+
error ERC20InvalidReceiver(address _receiver);
10+
error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed);
11+
12+
event Transfer(address indexed _from, address indexed _to, uint256 _value);
513

614
// Struct storage position defined by keccak256 hash
715
// of diamond storage identifier
@@ -23,5 +31,59 @@ library LibERC20 {
2331
assembly {
2432
s.slot := position
2533
}
34+
}
35+
36+
function mint(address _account, uint256 _value) internal {
37+
ERC20Storage storage s = getStorage();
38+
if (_account == address(0)) {
39+
revert ERC20InvalidReceiver(address(0));
40+
}
41+
unchecked {
42+
s.totalSupply += _value;
43+
s.balanceOf[_account] += _value;
44+
}
45+
emit Transfer(address(0), _account, _value);
46+
}
47+
48+
function burn(address _account, uint256 _value) internal {
49+
ERC20Storage storage s = getStorage();
50+
if (_account == address(0)) {
51+
revert ERC20InvalidSender(address(0));
52+
}
53+
uint256 accountBalance = s.balanceOf[_account];
54+
if (accountBalance < _value) {
55+
revert ERC20InsufficientBalance(_account, accountBalance, _value);
56+
}
57+
unchecked {
58+
s.balanceOf[_account] = accountBalance - _value;
59+
s.totalSupply -= _value;
60+
}
61+
emit Transfer(_account, address(0), _value);
2662
}
63+
64+
function transferFrom(address _from, address _to, uint256 _value) internal {
65+
ERC20Storage storage s = getStorage();
66+
if (_from == address(0)) {
67+
revert ERC20InvalidSender(address(0));
68+
}
69+
if (_to == address(0)) {
70+
revert ERC20InvalidReceiver(address(0));
71+
}
72+
uint256 currentAllowance = s.allowances[_from][msg.sender];
73+
if (currentAllowance < _value) {
74+
revert ERC20InsufficientAllowance(msg.sender, currentAllowance, _value);
75+
}
76+
uint256 fromBalance = s.balanceOf[_from];
77+
if (fromBalance < _value) {
78+
revert ERC20InsufficientBalance(msg.sender, fromBalance, _value);
79+
}
80+
unchecked {
81+
s.allowances[_from][msg.sender] = currentAllowance - _value;
82+
s.balanceOf[_from] = fromBalance - _value;
83+
s.balanceOf[_to] += _value;
84+
}
85+
emit Transfer(_from, _to, _value);
86+
}
87+
88+
2789
}

src/ERC20/ERC20/libraries/LibERC20Transfer.sol

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/ERC721/ERC721/ERC721Facet.sol

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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 ERC721Facet {
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+
27+
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
28+
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
29+
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
30+
31+
// Struct storage position defined by keccak256 hash
32+
// of diamond storage identifier
33+
bytes32 constant STORAGE_POSITION = keccak256("compose.erc721");
34+
35+
// Storage defined using the ERC-8042 standard
36+
// @custom:storage-location erc8042:compose.erc721
37+
struct ERC721Storage {
38+
string name;
39+
string symbol;
40+
mapping(uint256 tokenId => string tokenURI) tokenURIOf;
41+
mapping(uint256 tokenId => address owner) ownerOf;
42+
mapping(address owner => uint256 balance) balanceOf;
43+
mapping(uint256 tokenId => address approved) approved;
44+
mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll;
45+
}
46+
47+
function getStorage() internal pure returns (ERC721Storage storage s) {
48+
bytes32 position = STORAGE_POSITION;
49+
assembly {
50+
s.slot := position
51+
}
52+
}
53+
54+
function name() external view returns (string memory) {
55+
return getStorage().name;
56+
}
57+
58+
function symbol() external view returns (string memory) {
59+
return getStorage().symbol;
60+
}
61+
62+
function balanceOf(address _owner) external view returns (uint256) {
63+
if (_owner == address(0)) {
64+
revert ERC721InvalidOwner(_owner);
65+
}
66+
return getStorage().balanceOf[_owner];
67+
}
68+
69+
function ownerOf(uint256 _tokenId) public view returns (address) {
70+
address owner = getStorage().ownerOf[_tokenId];
71+
if (owner == address(0)) {
72+
revert ERC721NonexistentToken(_tokenId);
73+
}
74+
return owner;
75+
}
76+
77+
function getApproved(uint256 _tokenId) external view returns (address) {
78+
address owner = getStorage().ownerOf[_tokenId];
79+
if (owner == address(0)) {
80+
revert ERC721NonexistentToken(_tokenId);
81+
}
82+
return getStorage().approved[_tokenId];
83+
}
84+
85+
function isApprovedForAll(address _owner, address _operator) external view returns (bool) {
86+
return getStorage().isApprovedForAll[_owner][_operator];
87+
}
88+
89+
function approve(address _approved, uint256 _tokenId) external {
90+
ERC721Storage storage s = getStorage();
91+
address owner = s.ownerOf[_tokenId];
92+
if (owner == address(0)) {
93+
revert ERC721NonexistentToken(_tokenId);
94+
}
95+
if (msg.sender != owner && !s.isApprovedForAll[owner][msg.sender]) {
96+
revert ERC721InvalidApprover(_approved);
97+
}
98+
s.approved[_tokenId] = _approved;
99+
emit Approval(owner, _approved, _tokenId);
100+
}
101+
102+
function setApprovalForAll(address _operator, bool _approved) external {
103+
if (_operator == address(0)) {
104+
revert ERC721InvalidOperator(_operator);
105+
}
106+
getStorage().isApprovedForAll[msg.sender][_operator] = _approved;
107+
emit ApprovalForAll(msg.sender, _operator, _approved);
108+
}
109+
110+
function internalTransferFrom(address _from, address _to, uint256 _tokenId) internal {
111+
ERC721Storage storage s = getStorage();
112+
if (_to == address(0)) {
113+
revert ERC721InvalidReceiver(address(0));
114+
}
115+
address owner = s.ownerOf[_tokenId];
116+
if (owner == address(0)) {
117+
revert ERC721NonexistentToken(_tokenId);
118+
}
119+
if (owner != _from) {
120+
revert ERC721IncorrectOwner(_from, _tokenId, owner);
121+
}
122+
if (msg.sender != _from) {
123+
if(!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) {
124+
revert ERC721InsufficientApproval(msg.sender, _tokenId);
125+
}
126+
}
127+
delete s.approved[_tokenId];
128+
unchecked {
129+
s.balanceOf[_from]--;
130+
s.balanceOf[_to]++;
131+
}
132+
s.ownerOf[_tokenId] = _to;
133+
emit Transfer(_from, _to, _tokenId);
134+
}
135+
136+
function transferFrom(address _from, address _to, uint256 _tokenId) external {
137+
internalTransferFrom(_from, _to, _tokenId);
138+
}
139+
140+
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external {
141+
internalTransferFrom(_from, _to, _tokenId);
142+
143+
// If _to is a contract, check for IERC721Receiver implementation
144+
if (_to.code.length > 0) {
145+
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, "") returns (bytes4 retval) {
146+
if (retval != IERC721Receiver.onERC721Received.selector) {
147+
revert ERC721InvalidReceiver(_to);
148+
}
149+
} catch (bytes memory reason) {
150+
if (reason.length == 0) {
151+
// non-IERC721Receiver implementer
152+
revert ERC721InvalidReceiver(_to);
153+
} else {
154+
// Return the revert reason
155+
// "memory-safe" means we used memory safely so Solidity does not disable optimizations
156+
assembly ("memory-safe") {
157+
revert(add(reason, 0x20), mload(reason))
158+
}
159+
}
160+
}
161+
}
162+
}
163+
164+
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata _data) external {
165+
internalTransferFrom(_from, _to, _tokenId);
166+
// If _to is a contract, check for IERC721Receiver implementation
167+
if (_to.code.length > 0) {
168+
try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4 retval) {
169+
if (retval != IERC721Receiver.onERC721Received.selector) {
170+
revert ERC721InvalidReceiver(_to);
171+
}
172+
} catch (bytes memory reason) {
173+
if (reason.length == 0) {
174+
// non-IERC721Receiver implementer
175+
revert ERC721InvalidReceiver(_to);
176+
} else {
177+
// Return the revert reason
178+
// "memory-safe" means we used memory safely so Solidity does not disable optimizations
179+
assembly ("memory-safe") {
180+
revert(add(reason, 0x20), mload(reason))
181+
}
182+
}
183+
}
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)