11// SPDX-License-Identifier: MIT
22pragma solidity >= 0.8.30 ;
33
4+ /// @title ERC-721 Library for Compose
5+ /// @notice Provides internal logic for ERC-721 token management using diamond storage.
6+ /// @dev Implements minting, burning, and transferring of ERC-721 tokens without dependencies.
7+ /// Uses ERC-8042-compliant storage definition and includes ERC-6093 standard custom errors.
48library LibERC721 {
59
6- // ERC-6093: Custom errors for ERC-721
7- error ERC721NonexistentToken (uint256 _tokenId );
10+ /// @notice Thrown when attempting to interact with a non-existent token.
11+ /// @param _tokenId The ID of the token that does not exist.
12+ error ERC721NonexistentToken (uint256 _tokenId );
13+
14+ /// @notice Thrown when the sender is not the owner of the token.
15+ /// @param _sender The address attempting the operation.
16+ /// @param _tokenId The ID of the token being transferred.
17+ /// @param _owner The actual owner of the token.
818 error ERC721IncorrectOwner (address _sender , uint256 _tokenId , address _owner );
19+
20+ /// @notice Thrown when the sender address is invalid (e.g., zero address).
21+ /// @param _sender The invalid sender address.
922 error ERC721InvalidSender (address _sender );
23+
24+ /// @notice Thrown when the receiver address is invalid (e.g., zero address).
25+ /// @param _receiver The invalid receiver address.
1026 error ERC721InvalidReceiver (address _receiver );
27+
28+ /// @notice Thrown when an operator lacks sufficient approval to manage a token.
29+ /// @param _operator The address attempting the unauthorized operation.
30+ /// @param _tokenId The ID of the token involved.
1131 error ERC721InsufficientApproval (address _operator , uint256 _tokenId );
1232
33+ /// @notice Emitted when ownership of a token changes, including minting and burning.
34+ /// @param _from The address transferring the token, or zero for minting.
35+ /// @param _to The address receiving the token, or zero for burning.
36+ /// @param _tokenId The ID of the token being transferred.
1337 event Transfer (address indexed _from , address indexed _to , uint256 indexed _tokenId );
14-
15- // Struct storage position defined by keccak256 hash
16- // of diamond storage identifier
38+
39+ /// @dev Storage position constant defined via keccak256 hash of diamond storage identifier.
1740 bytes32 constant STORAGE_POSITION = keccak256 ("compose.erc721 " );
1841
19- // Storage defined using the ERC-8042 standard
20- // @custom:storage-location erc8042:compose.erc721
42+ /// @custom:storage-location erc8042:compose.erc721
43+ /// @notice Storage layout for ERC-721 token management.
44+ /// @dev Defines ownership, balances, approvals, and operator mappings per ERC-721 standard.
2145 struct ERC721Storage {
2246 string name;
23- string symbol;
47+ string symbol;
2448 mapping (uint256 tokenId = > address owner ) ownerOf;
2549 mapping (address owner = > uint256 balance ) balanceOf;
2650 mapping (uint256 tokenId = > address approved ) approved;
27- mapping (address owner = > mapping (address operator = > bool approved )) isApprovedForAll;
51+ mapping (address owner = > mapping (address operator = > bool approved )) isApprovedForAll;
2852 }
2953
54+ /// @notice Returns the ERC-721 storage struct from its predefined slot.
55+ /// @dev Uses inline assembly to access diamond storage location.
56+ /// @return s The storage reference for ERC-721 state variables.
3057 function getStorage () internal pure returns (ERC721Storage storage s ) {
3158 bytes32 position = STORAGE_POSITION;
3259 assembly {
3360 s.slot := position
3461 }
3562 }
3663
64+ /// @notice Transfers ownership of a token ID from one address to another.
65+ /// @dev Validates ownership, approval, and receiver address before updating state.
66+ /// @param _from The current owner of the token.
67+ /// @param _to The address that will receive the token.
68+ /// @param _tokenId The ID of the token being transferred.
3769 function transferFrom (address _from , address _to , uint256 _tokenId ) internal {
3870 ERC721Storage storage s = getStorage ();
39- if (_to == address (0 )) {
71+ if (_to == address (0 )) {
4072 revert ERC721InvalidReceiver (address (0 ));
4173 }
4274 address owner = s.ownerOf[_tokenId];
@@ -45,9 +77,9 @@ library LibERC721 {
4577 }
4678 if (owner != _from) {
4779 revert ERC721IncorrectOwner (_from, _tokenId, owner);
48- }
80+ }
4981 if (msg .sender != _from) {
50- if (! s.isApprovedForAll[_from][msg .sender ] && msg .sender != s.approved[_tokenId]) {
82+ if (! s.isApprovedForAll[_from][msg .sender ] && msg .sender != s.approved[_tokenId]) {
5183 revert ERC721InsufficientApproval (msg .sender , _tokenId);
5284 }
5385 }
@@ -56,10 +88,14 @@ library LibERC721 {
5688 s.balanceOf[_from]-- ;
5789 s.balanceOf[_to]++ ;
5890 }
59- s.ownerOf[_tokenId] = _to;
91+ s.ownerOf[_tokenId] = _to;
6092 emit Transfer (_from, _to, _tokenId);
6193 }
6294
95+ /// @notice Mints a new ERC-721 token to the specified address.
96+ /// @dev Reverts if the receiver address is zero or if the token already exists.
97+ /// @param _to The address that will own the newly minted token.
98+ /// @param _tokenId The ID of the token to mint.
6399 function mint (address _to , uint256 _tokenId ) internal {
64100 ERC721Storage storage s = getStorage ();
65101 if (_to == address (0 )) {
@@ -75,6 +111,9 @@ library LibERC721 {
75111 emit Transfer (address (0 ), _to, _tokenId);
76112 }
77113
114+ /// @notice Burns (destroys) a specific ERC-721 token.
115+ /// @dev Reverts if the token does not exist. Clears ownership and approval.
116+ /// @param _tokenId The ID of the token to burn.
78117 function burn (uint256 _tokenId ) internal {
79118 ERC721Storage storage s = getStorage ();
80119 address owner = s.ownerOf[_tokenId];
@@ -88,7 +127,4 @@ library LibERC721 {
88127 }
89128 emit Transfer (owner, address (0 ), _tokenId);
90129 }
91-
92-
93-
94- }
130+ }
0 commit comments