diff --git a/src/ERC20/ERC20/libraries/LibERC20.sol b/src/ERC20/ERC20/libraries/LibERC20.sol index 85ff4004..78e58217 100644 --- a/src/ERC20/ERC20/libraries/LibERC20.sol +++ b/src/ERC20/ERC20/libraries/LibERC20.sol @@ -1,38 +1,69 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.30; +/// @title LibERC20 — ERC-20 Library +/// @notice Provides internal functions and storage layout for ERC-20 token logic. +/// @dev Uses ERC-8042 for storage location standardization and ERC-6093 for error conventions. library LibERC20 { - // ERC-6093: Custom errors for ERC-20 + + /// @notice Thrown when a sender attempts to transfer or burn more tokens than their balance. + /// @param _sender The address attempting the transfer or burn. + /// @param _balance The sender's current balance. + /// @param _needed The amount required to complete the operation. error ERC20InsufficientBalance(address _sender, uint256 _balance, uint256 _needed); + + /// @notice Thrown when the sender address is invalid (e.g., zero address). + /// @param _sender The invalid sender address. error ERC20InvalidSender(address _sender); + + /// @notice Thrown when the receiver address is invalid (e.g., zero address). + /// @param _receiver The invalid receiver address. error ERC20InvalidReceiver(address _receiver); + + /// @notice Thrown when a spender tries to spend more than their allowance. + /// @param _spender The address attempting to spend. + /// @param _allowance The current allowance. + /// @param _needed The required amount to complete the transfer. error ERC20InsufficientAllowance(address _spender, uint256 _allowance, uint256 _needed); + + /// @notice Emitted when tokens are transferred between addresses. + /// @param _from The address tokens are transferred from. + /// @param _to The address tokens are transferred to. + /// @param _value The amount of tokens transferred. event Transfer(address indexed _from, address indexed _to, uint256 _value); - - // Struct storage position defined by keccak256 hash - // of diamond storage identifier + + + /// @notice Storage slot identifier, defined using keccak256 hash of the library diamond storage identifier. bytes32 constant STORAGE_POSITION = keccak256("compose.erc20"); - // Storage defined using the ERC-8042 standard - // @custom:storage-location erc8042:compose.erc20 + /// @notice ERC-20 storage layout using the ERC-8042 standard. + /// @custom:storage-location erc8042:compose.erc20 struct ERC20Storage { string name; string symbol; uint8 decimals; uint256 totalSupply; mapping(address owner => uint256 balance) balanceOf; - mapping(address owner => mapping(address spender => uint256 allowance)) allowances; + mapping(address owner => mapping(address spender => uint256 allowance)) allowances; } + + /// @notice Returns a pointer to the ERC-20 storage struct. + /// @dev Uses inline assembly to bind the storage struct to the fixed storage position. + /// @return s The ERC-20 storage struct. function getStorage() internal pure returns (ERC20Storage storage s) { bytes32 position = STORAGE_POSITION; assembly { s.slot := position } - } + } + /// @notice Mints new tokens to a specified address. + /// @dev Increases both total supply and the recipient’s balance. + /// @param _account The address receiving the newly minted tokens. + /// @param _value The number of tokens to mint. function mint(address _account, uint256 _value) internal { ERC20Storage storage s = getStorage(); if (_account == address(0)) { @@ -45,6 +76,10 @@ library LibERC20 { emit Transfer(address(0), _account, _value); } + /// @notice Burns tokens from a specified address. + /// @dev Decreases both total supply and the sender’s balance. + /// @param _account The address whose tokens will be burned. + /// @param _value The number of tokens to burn. function burn(address _account, uint256 _value) internal { ERC20Storage storage s = getStorage(); if (_account == address(0)) { @@ -61,6 +96,11 @@ library LibERC20 { emit Transfer(_account, address(0), _value); } + /// @notice Transfers tokens from one address to another using an allowance. + /// @dev Deducts the spender’s allowance and updates balances. + /// @param _from The address to send tokens from. + /// @param _to The address to send tokens to. + /// @param _value The number of tokens to transfer. function transferFrom(address _from, address _to, uint256 _value) internal { ERC20Storage storage s = getStorage(); if (_from == address(0)) { @@ -84,6 +124,4 @@ library LibERC20 { } emit Transfer(_from, _to, _value); } - - -} \ No newline at end of file +} diff --git a/src/ERC721/ERC721/libraries/LibERC721.sol b/src/ERC721/ERC721/libraries/LibERC721.sol index 97ac8079..053e1292 100644 --- a/src/ERC721/ERC721/libraries/LibERC721.sol +++ b/src/ERC721/ERC721/libraries/LibERC721.sol @@ -1,32 +1,59 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.30; +/// @title ERC-721 Library for Compose +/// @notice Provides internal logic for ERC-721 token management using diamond storage. +/// @dev Implements minting, burning, and transferring of ERC-721 tokens without dependencies. +/// Uses ERC-8042-compliant storage definition and includes ERC-6093 standard custom errors. library LibERC721 { - // ERC-6093: Custom errors for ERC-721 - error ERC721NonexistentToken(uint256 _tokenId); + /// @notice Thrown when attempting to interact with a non-existent token. + /// @param _tokenId The ID of the token that does not exist. + error ERC721NonexistentToken(uint256 _tokenId); + + /// @notice Thrown when the sender is not the owner of the token. + /// @param _sender The address attempting the operation. + /// @param _tokenId The ID of the token being transferred. + /// @param _owner The actual owner of the token. error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + + /// @notice Thrown when the sender address is invalid (e.g., zero address). + /// @param _sender The invalid sender address. error ERC721InvalidSender(address _sender); + + /// @notice Thrown when the receiver address is invalid (e.g., zero address). + /// @param _receiver The invalid receiver address. error ERC721InvalidReceiver(address _receiver); + + /// @notice Thrown when an operator lacks sufficient approval to manage a token. + /// @param _operator The address attempting the unauthorized operation. + /// @param _tokenId The ID of the token involved. error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + /// @notice Emitted when ownership of a token changes, including minting and burning. + /// @param _from The address transferring the token, or zero for minting. + /// @param _to The address receiving the token, or zero for burning. + /// @param _tokenId The ID of the token being transferred. event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); - - // Struct storage position defined by keccak256 hash - // of diamond storage identifier + + /// @dev Storage position constant defined via keccak256 hash of diamond storage identifier. bytes32 constant STORAGE_POSITION = keccak256("compose.erc721"); - // Storage defined using the ERC-8042 standard - // @custom:storage-location erc8042:compose.erc721 + /// @custom:storage-location erc8042:compose.erc721 + /// @notice Storage layout for ERC-721 token management. + /// @dev Defines ownership, balances, approvals, and operator mappings per ERC-721 standard. struct ERC721Storage { string name; - string symbol; + string symbol; mapping(uint256 tokenId => address owner) ownerOf; mapping(address owner => uint256 balance) balanceOf; mapping(uint256 tokenId => address approved) approved; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; } + /// @notice Returns the ERC-721 storage struct from its predefined slot. + /// @dev Uses inline assembly to access diamond storage location. + /// @return s The storage reference for ERC-721 state variables. function getStorage() internal pure returns (ERC721Storage storage s) { bytes32 position = STORAGE_POSITION; assembly { @@ -34,9 +61,14 @@ library LibERC721 { } } + /// @notice Transfers ownership of a token ID from one address to another. + /// @dev Validates ownership, approval, and receiver address before updating state. + /// @param _from The current owner of the token. + /// @param _to The address that will receive the token. + /// @param _tokenId The ID of the token being transferred. function transferFrom(address _from, address _to, uint256 _tokenId) internal { ERC721Storage storage s = getStorage(); - if (_to == address(0)) { + if (_to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address owner = s.ownerOf[_tokenId]; @@ -45,9 +77,9 @@ library LibERC721 { } if (owner != _from) { revert ERC721IncorrectOwner(_from, _tokenId, owner); - } + } if (msg.sender != _from) { - if(!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) { + if (!s.isApprovedForAll[_from][msg.sender] && msg.sender != s.approved[_tokenId]) { revert ERC721InsufficientApproval(msg.sender, _tokenId); } } @@ -56,10 +88,14 @@ library LibERC721 { s.balanceOf[_from]--; s.balanceOf[_to]++; } - s.ownerOf[_tokenId] = _to; + s.ownerOf[_tokenId] = _to; emit Transfer(_from, _to, _tokenId); } + /// @notice Mints a new ERC-721 token to the specified address. + /// @dev Reverts if the receiver address is zero or if the token already exists. + /// @param _to The address that will own the newly minted token. + /// @param _tokenId The ID of the token to mint. function mint(address _to, uint256 _tokenId) internal { ERC721Storage storage s = getStorage(); if (_to == address(0)) { @@ -75,6 +111,9 @@ library LibERC721 { emit Transfer(address(0), _to, _tokenId); } + /// @notice Burns (destroys) a specific ERC-721 token. + /// @dev Reverts if the token does not exist. Clears ownership and approval. + /// @param _tokenId The ID of the token to burn. function burn(uint256 _tokenId) internal { ERC721Storage storage s = getStorage(); address owner = s.ownerOf[_tokenId]; @@ -88,7 +127,4 @@ library LibERC721 { } emit Transfer(owner, address(0), _tokenId); } - - - -} \ No newline at end of file +} diff --git a/src/ERC721/ERC721Enumerable/libraries/LibERC721Enumerable.sol b/src/ERC721/ERC721Enumerable/libraries/LibERC721Enumerable.sol index ac0b5e64..3481a575 100644 --- a/src/ERC721/ERC721Enumerable/libraries/LibERC721Enumerable.sol +++ b/src/ERC721/ERC721Enumerable/libraries/LibERC721Enumerable.sol @@ -1,37 +1,62 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.30; +/// @title ERC-721 Enumerable Library for Compose +/// @notice Provides internal logic for enumerable ERC-721 tokens using diamond storage. +/// @dev Implements ERC-721 operations with token enumeration support (tracking owned and global tokens). +/// Follows ERC-8042 for storage layout and ERC-6093 for standardized custom errors. library LibERC721 { - // ERC-6093: Custom errors for ERC-721 - error ERC721NonexistentToken(uint256 _tokenId); + /// @notice Thrown when attempting to interact with a non-existent token. + /// @param _tokenId The ID of the token that does not exist. + error ERC721NonexistentToken(uint256 _tokenId); + + /// @notice Thrown when the sender is not the owner of the token. + /// @param _sender The address attempting the operation. + /// @param _tokenId The ID of the token being transferred. + /// @param _owner The actual owner of the token. error ERC721IncorrectOwner(address _sender, uint256 _tokenId, address _owner); + + /// @notice Thrown when the sender address is invalid. + /// @param _sender The invalid sender address. error ERC721InvalidSender(address _sender); + + /// @notice Thrown when the receiver address is invalid. + /// @param _receiver The invalid receiver address. error ERC721InvalidReceiver(address _receiver); + + /// @notice Thrown when an operator lacks approval to manage a token. + /// @param _operator The address attempting the unauthorized operation. + /// @param _tokenId The ID of the token involved. error ERC721InsufficientApproval(address _operator, uint256 _tokenId); + /// @notice Emitted when ownership of a token changes, including minting and burning. + /// @param _from The address transferring the token, or zero for minting. + /// @param _to The address receiving the token, or zero for burning. + /// @param _tokenId The ID of the token being transferred. event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); - - // Struct storage position defined by keccak256 hash - // of diamond storage identifier + + /// @dev Storage slot defined via keccak256 hash of the diamond storage identifier. bytes32 constant STORAGE_POSITION = keccak256("compose.erc721.enumerable"); - // Storage defined using the ERC-8042 standard - // @custom:storage-location erc8042:compose.erc721.enumerable + /// @custom:storage-location erc8042:compose.erc721.enumerable + /// @notice Storage layout for ERC-721 enumerable tokens. + /// @dev Includes mappings for ownership, approvals, operator permissions, and enumeration tracking. struct ERC721EnumerableStorage { string name; - string symbol; - + string symbol; mapping(uint256 tokenId => address owner) ownerOf; mapping(address owner => uint256[] ownedTokens) ownedTokensOf; mapping(uint256 tokenId => uint256 ownedTokensIndex) ownedTokensIndexOf; uint256[] allTokens; mapping(uint256 tokenId => uint256 allTokensIndex) allTokensIndexOf; - mapping(uint256 tokenId => address approved) approved; - mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; + mapping(address owner => mapping(address operator => bool approved)) isApprovedForAll; } + /// @notice Returns the ERC-721 enumerable storage struct from its predefined slot. + /// @dev Uses inline assembly to point to the correct diamond storage position. + /// @return s The storage reference for ERC-721 enumerable state variables. function getStorage() internal pure returns (ERC721EnumerableStorage storage s) { bytes32 position = STORAGE_POSITION; assembly { @@ -39,9 +64,15 @@ library LibERC721 { } } + /// @notice Transfers a token ID from one address to another, updating enumeration data. + /// @dev Validates ownership, approval, and receiver address before state updates. + /// @param _from The current owner of the token. + /// @param _to The address receiving the token. + /// @param _tokenId The ID of the token being transferred. + /// @param _sender The initiator of the transfer (may be owner or approved operator). function transferFrom(address _from, address _to, uint256 _tokenId, address _sender) internal { ERC721EnumerableStorage storage s = getStorage(); - if (_to == address(0)) { + if (_to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address owner = s.ownerOf[_tokenId]; @@ -50,29 +81,34 @@ library LibERC721 { } if (owner != _from) { revert ERC721IncorrectOwner(_from, _tokenId, owner); - } + } if (_sender != _from) { - if(!s.isApprovedForAll[_from][_sender] && _sender != s.approved[_tokenId]) { + if (!s.isApprovedForAll[_from][_sender] && _sender != s.approved[_tokenId]) { revert ERC721InsufficientApproval(_sender, _tokenId); } } + delete s.approved[_tokenId]; - // removing token from _from's ownedTokens + uint256 tokenIndex = s.ownedTokensIndexOf[_tokenId]; uint256 lastTokenIndex = s.ownedTokensOf[_from].length - 1; - if(tokenIndex != lastTokenIndex) { + if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = s.ownedTokensOf[_from][lastTokenIndex]; - s.ownedTokensOf[_from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - s.ownedTokensIndexOf[lastTokenId] = tokenIndex; // Update the moved token's index + s.ownedTokensOf[_from][tokenIndex] = lastTokenId; + s.ownedTokensIndexOf[lastTokenId] = tokenIndex; } - s.ownedTokensOf[_from].pop(); - // adding token to _to's ownedTokens + s.ownedTokensOf[_from].pop(); + s.ownedTokensIndexOf[_tokenId] = s.ownedTokensOf[_to].length; s.ownedTokensOf[_to].push(_tokenId); - s.ownerOf[_tokenId] = _to; + s.ownerOf[_tokenId] = _to; emit Transfer(_from, _to, _tokenId); } + /// @notice Mints a new ERC-721 token to the specified address, adding it to enumeration lists. + /// @dev Reverts if the receiver address is zero or if the token already exists. + /// @param _to The address that will own the newly minted token. + /// @param _tokenId The ID of the token to mint. function mint(address _to, uint256 _tokenId) internal { ERC721EnumerableStorage storage s = getStorage(); if (_to == address(0)) { @@ -81,15 +117,18 @@ library LibERC721 { if (s.ownerOf[_tokenId] != address(0)) { revert ERC721InvalidSender(address(0)); } + s.ownedTokensIndexOf[_tokenId] = s.ownedTokensOf[_to].length; s.ownedTokensOf[_to].push(_tokenId); - s.allTokensIndexOf[_tokenId] = s.allTokens.length; - s.allTokens.push(_tokenId); - + s.allTokens.push(_tokenId); emit Transfer(address(0), _to, _tokenId); } + /// @notice Burns (destroys) an existing ERC-721 token, removing it from enumeration lists. + /// @dev Reverts if the token does not exist or if the sender is not authorized. + /// @param _tokenId The ID of the token to burn. + /// @param _sender The address initiating the burn. function burn(uint256 _tokenId, address _sender) internal { ERC721EnumerableStorage storage s = getStorage(); address owner = s.ownerOf[_tokenId]; @@ -97,36 +136,31 @@ library LibERC721 { revert ERC721NonexistentToken(_tokenId); } if (_sender != owner) { - if(!s.isApprovedForAll[owner][_sender] && _sender != s.approved[_tokenId]) { + if (!s.isApprovedForAll[owner][_sender] && _sender != s.approved[_tokenId]) { revert ERC721InsufficientApproval(_sender, _tokenId); } } + delete s.ownerOf[_tokenId]; delete s.approved[_tokenId]; - // removing token from _from's ownedTokens uint256 tokenIndex = s.ownedTokensIndexOf[_tokenId]; uint256 lastTokenIndex = s.ownedTokensOf[owner].length - 1; - if(tokenIndex != lastTokenIndex) { + if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = s.ownedTokensOf[owner][lastTokenIndex]; - s.ownedTokensOf[owner][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - s.ownedTokensIndexOf[lastTokenId] = tokenIndex; // Update the moved token's index + s.ownedTokensOf[owner][tokenIndex] = lastTokenId; + s.ownedTokensIndexOf[lastTokenId] = tokenIndex; } - s.ownedTokensOf[owner].pop(); + s.ownedTokensOf[owner].pop(); - // removing token from allTokens tokenIndex = s.allTokensIndexOf[_tokenId]; lastTokenIndex = s.allTokens.length - 1; - if(tokenIndex != lastTokenIndex) { + if (tokenIndex != lastTokenIndex) { uint256 lastTokenId = s.allTokens[lastTokenIndex]; - s.allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token - s.allTokensIndexOf[lastTokenId] = tokenIndex; // Update the moved token's index + s.allTokens[tokenIndex] = lastTokenId; + s.allTokensIndexOf[lastTokenId] = tokenIndex; } - s.allTokens.pop(); - + s.allTokens.pop(); emit Transfer(owner, address(0), _tokenId); } - - - -} \ No newline at end of file +}