From 0f88c36f9d63c765d22d0aa7b92e3cc6a8f90900 Mon Sep 17 00:00:00 2001 From: Amirhossein Banavi Date: Wed, 27 Apr 2022 03:38:23 +0430 Subject: [PATCH] Feat: Add Interface (#243) * Add interfaces * Use interfaces in main contracts * change structure similar to OZ * move errors in interfaces class level * run prettier --- contracts/ERC721A.sol | 45 +------------- contracts/IERC721A.sol | 56 ++++++++++++++++++ contracts/extensions/ERC721ABurnable.sol | 3 +- contracts/extensions/ERC721AQueryable.sol | 5 +- contracts/extensions/IERC721ABurnable.sol | 20 +++++++ contracts/extensions/IERC721AQueryable.sol | 69 ++++++++++++++++++++++ contracts/interfaces/IERC721A.sol | 6 ++ contracts/interfaces/IERC721ABurnable.sol | 6 ++ contracts/interfaces/IERC721AQueryable.sol | 6 ++ contracts/mocks/ERC721AMock.sol | 6 +- 10 files changed, 175 insertions(+), 47 deletions(-) create mode 100644 contracts/IERC721A.sol create mode 100644 contracts/extensions/IERC721ABurnable.sol create mode 100644 contracts/extensions/IERC721AQueryable.sol create mode 100644 contracts/interfaces/IERC721A.sol create mode 100644 contracts/interfaces/IERC721ABurnable.sol create mode 100644 contracts/interfaces/IERC721AQueryable.sol diff --git a/contracts/ERC721A.sol b/contracts/ERC721A.sol index 43bbf1808..321a7878b 100644 --- a/contracts/ERC721A.sol +++ b/contracts/ERC721A.sol @@ -3,28 +3,13 @@ pragma solidity ^0.8.4; -import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; +import './IERC721A.sol'; import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol'; import '@openzeppelin/contracts/utils/Address.sol'; import '@openzeppelin/contracts/utils/Context.sol'; import '@openzeppelin/contracts/utils/Strings.sol'; import '@openzeppelin/contracts/utils/introspection/ERC165.sol'; -error ApprovalCallerNotOwnerNorApproved(); -error ApprovalQueryForNonexistentToken(); -error ApproveToCaller(); -error ApprovalToCurrentOwner(); -error BalanceQueryForZeroAddress(); -error MintToZeroAddress(); -error MintZeroQuantity(); -error OwnerQueryForNonexistentToken(); -error TransferCallerNotOwnerNorApproved(); -error TransferFromIncorrectOwner(); -error TransferToNonERC721ReceiverImplementer(); -error TransferToZeroAddress(); -error URIQueryForNonexistentToken(); - /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension. Built to optimize for lower gas during batch mints. @@ -35,34 +20,10 @@ error URIQueryForNonexistentToken(); * * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256). */ -contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { +contract ERC721A is Context, ERC165, IERC721A { using Address for address; using Strings for uint256; - // Compiler will pack this into a single 256bit word. - struct TokenOwnership { - // The address of the owner. - address addr; - // Keeps track of the start time of ownership with minimal overhead for tokenomics. - uint64 startTimestamp; - // Whether the token has been burned. - bool burned; - } - - // Compiler will pack this into a single 256bit word. - struct AddressData { - // Realistically, 2**64-1 is more than enough. - uint64 balance; - // Keeps track of mint count with minimal overhead for tokenomics. - uint64 numberMinted; - // Keeps track of burn count with minimal overhead for tokenomics. - uint64 numberBurned; - // For miscellaneous variable(s) pertaining to the address - // (e.g. number of whitelist mint slots used). - // If there are multiple variables, please pack them into a uint64. - uint64 aux; - } - // The tokenId of the next token to be minted. uint256 internal _currentIndex; @@ -341,7 +302,7 @@ contract ERC721A is Context, ERC165, IERC721, IERC721Metadata { * * Requirements: * - * - If `to` refers to a smart contract, it must implement + * - If `to` refers to a smart contract, it must implement * {IERC721Receiver-onERC721Received}, which is called for each safe transfer. * - `quantity` must be greater than 0. * diff --git a/contracts/IERC721A.sol b/contracts/IERC721A.sol new file mode 100644 index 000000000..583433b6b --- /dev/null +++ b/contracts/IERC721A.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; +import '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol'; + +/** + * @dev Interface of an ERC721A compliant contract. + */ +interface IERC721A is IERC721, IERC721Metadata { + error ApprovalCallerNotOwnerNorApproved(); + error ApprovalQueryForNonexistentToken(); + error ApproveToCaller(); + error ApprovalToCurrentOwner(); + error BalanceQueryForZeroAddress(); + error MintToZeroAddress(); + error MintZeroQuantity(); + error OwnerQueryForNonexistentToken(); + error TransferCallerNotOwnerNorApproved(); + error TransferFromIncorrectOwner(); + error TransferToNonERC721ReceiverImplementer(); + error TransferToZeroAddress(); + error URIQueryForNonexistentToken(); + + // Compiler will pack this into a single 256bit word. + struct TokenOwnership { + // The address of the owner. + address addr; + // Keeps track of the start time of ownership with minimal overhead for tokenomics. + uint64 startTimestamp; + // Whether the token has been burned. + bool burned; + } + + // Compiler will pack this into a single 256bit word. + struct AddressData { + // Realistically, 2**64-1 is more than enough. + uint64 balance; + // Keeps track of mint count with minimal overhead for tokenomics. + uint64 numberMinted; + // Keeps track of burn count with minimal overhead for tokenomics. + uint64 numberBurned; + // For miscellaneous variable(s) pertaining to the address + // (e.g. number of whitelist mint slots used). + // If there are multiple variables, please pack them into a uint64. + uint64 aux; + } + + /** + * @dev Returns the total amount of tokens stored by the contract. + * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens. + */ + function totalSupply() external view returns (uint256); +} diff --git a/contracts/extensions/ERC721ABurnable.sol b/contracts/extensions/ERC721ABurnable.sol index 490ce0e8e..f337da1c4 100644 --- a/contracts/extensions/ERC721ABurnable.sol +++ b/contracts/extensions/ERC721ABurnable.sol @@ -3,13 +3,14 @@ pragma solidity ^0.8.4; +import './IERC721ABurnable.sol'; import '../ERC721A.sol'; /** * @title ERC721A Burnable Token * @dev ERC721A Token that can be irreversibly burned (destroyed). */ -abstract contract ERC721ABurnable is ERC721A { +abstract contract ERC721ABurnable is ERC721A, IERC721ABurnable { /** * @dev Burns `tokenId`. See {ERC721A-_burn}. * diff --git a/contracts/extensions/ERC721AQueryable.sol b/contracts/extensions/ERC721AQueryable.sol index 0f9aed86c..63aced942 100644 --- a/contracts/extensions/ERC721AQueryable.sol +++ b/contracts/extensions/ERC721AQueryable.sol @@ -3,15 +3,14 @@ pragma solidity ^0.8.4; +import './IERC721AQueryable.sol'; import '../ERC721A.sol'; -error InvalidQueryRange(); - /** * @title ERC721A Queryable * @dev ERC721A subclass with convenience query functions. */ -abstract contract ERC721AQueryable is ERC721A { +abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable { /** * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. * diff --git a/contracts/extensions/IERC721ABurnable.sol b/contracts/extensions/IERC721ABurnable.sol new file mode 100644 index 000000000..78f39f13e --- /dev/null +++ b/contracts/extensions/IERC721ABurnable.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '../IERC721A.sol'; + +/** + * @dev Interface of an ERC721ABurnable compliant contract. + */ +interface IERC721ABurnable is IERC721A { + /** + * @dev Burns `tokenId`. See {ERC721A-_burn}. + * + * Requirements: + * + * - The caller must own `tokenId` or be an approved operator. + */ + function burn(uint256 tokenId) external; +} diff --git a/contracts/extensions/IERC721AQueryable.sol b/contracts/extensions/IERC721AQueryable.sol new file mode 100644 index 000000000..2f4d40c0c --- /dev/null +++ b/contracts/extensions/IERC721AQueryable.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '../IERC721A.sol'; + +/** + * @dev Interface of an ERC721AQueryable compliant contract. + */ +interface IERC721AQueryable is IERC721A { + error InvalidQueryRange(); + + /** + * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. + * + * If the `tokenId` is out of bounds: + * - `addr` = `address(0)` + * - `startTimestamp` = `0` + * - `burned` = `false` + * + * If the `tokenId` is burned: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `true` + * + * Otherwise: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `false` + */ + function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory); + + /** + * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. + * See {ERC721AQueryable-explicitOwnershipOf} + */ + function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory); + + /** + * @dev Returns an array of token IDs owned by `owner`, + * in the range [`start`, `stop`) + * (i.e. `start <= tokenId < stop`). + * + * This function allows for tokens to be queried if the collection + * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. + * + * Requirements: + * + * - `start` < `stop` + */ + function tokensOfOwnerIn( + address owner, + uint256 start, + uint256 stop + ) external view returns (uint256[] memory); + + /** + * @dev Returns an array of token IDs owned by `owner`. + * + * This function scans the ownership mapping and is O(totalSupply) in complexity. + * It is meant to be called off-chain. + * + * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into + * multiple smaller scans if the collection is large enough to cause + * an out-of-gas error (10K pfp collections should be fine). + */ + function tokensOfOwner(address owner) external view returns (uint256[] memory); +} diff --git a/contracts/interfaces/IERC721A.sol b/contracts/interfaces/IERC721A.sol new file mode 100644 index 000000000..2d7e3c820 --- /dev/null +++ b/contracts/interfaces/IERC721A.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '../IERC721A.sol'; diff --git a/contracts/interfaces/IERC721ABurnable.sol b/contracts/interfaces/IERC721ABurnable.sol new file mode 100644 index 000000000..0ca320c5b --- /dev/null +++ b/contracts/interfaces/IERC721ABurnable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '../extensions/IERC721ABurnable.sol'; diff --git a/contracts/interfaces/IERC721AQueryable.sol b/contracts/interfaces/IERC721AQueryable.sol new file mode 100644 index 000000000..9c9c49f2f --- /dev/null +++ b/contracts/interfaces/IERC721AQueryable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import '../extensions/IERC721AQueryable.sol'; diff --git a/contracts/mocks/ERC721AMock.sol b/contracts/mocks/ERC721AMock.sol index 8395cb0cf..70cd8ac72 100644 --- a/contracts/mocks/ERC721AMock.sol +++ b/contracts/mocks/ERC721AMock.sol @@ -36,7 +36,11 @@ contract ERC721AMock is ERC721A { _safeMint(to, quantity); } - function safeMint(address to, uint256 quantity, bytes memory _data) public { + function safeMint( + address to, + uint256 quantity, + bytes memory _data + ) public { _safeMint(to, quantity, _data); }