
As you may know, Standard ERC721 lacksbuilt-in functionalities for efficiently listing all tokens owned by a specificaddress or enumerating all tokens in circulation. This poses a significantobstacle for applications like marketplaces, games, or collectible platformsthat necessitate such features.
Let's say we wanted to find all NFTs heldby a specific wallet address within an ERC721 contract. We will need to callthe BalanceOf() function on the address to first get the number of NFTs ownedby the address. Afterward, loop over the tokenIDs in the contract and call theownerOf() function on each of the tokenIDs. However, executing this methodunder a significant volume of NFTs is deemed computationally expensive.
In Solidity, arrays incur additional gascosts due to implicit length checks whenever indexed, meaning for each index i,it verifies if i < array.length. This check contributes to increased gasconsumption when working with arrays. Conversely, when using a mapping toemulate an array, this check is bypassed, resulting in gas savings. But, theyare also limited because they don’t have the “length property” to track totalNFTs in contracts. This is where the ERC721 Enumerable extension comes in.
The ERC721Enumerable extension resolvesthese limitations by introducing supplementary data structures and functions,facilitating the listing of tokens while adhering to the ERC721 standard. Itbasically tracks all the tokenIDs using the data structures _allTokens and_allTokensIndex and also all tokenIDs owned by an address using the datastructures _ownedTokens and _ownedTokensIndex.
The Data Structures of ERC721Enumerable:

_allTokens array: This private array storesthe IDs of all tokens currently in circulation. For example, _allTokens mightcontain values such as [1, 2, 3, 4, 5, 1337].
_allTokensIndex mapping: Similar to_ownedTokensIndex, this mapping is employed to set and retrieve the position ofa token ID within the _allTokens array. So, instead of looping through the_allTokens array to locate the index corresponding to a tokenID, it uses thetokenID directly as a key to retrieve its index within the _allTokens array.
_ownedTokens mapping: It has a nestedmapping (i.e., owner -> index -> tokenID) that sets and retrieves theindex of a token ID for a given address. For instance,_ownedTokens[Bob][index0] might correspond to Bob owning token ID 25.
_ownedTokensIndex mapping:_ownedTokensIndex is a mapping that correlates tokenIDs to their respectiveindices within the _ownedTokens mapping for a specific user.
As seen in the snippet above, the datastructures mentioned above are private but can be interfaced with the followingpublic functions: totalSupply(), tokenByIndex(index), andtokenOfOwnerByIndex(owner, index).
totalSupply(): The function returns thelength of the _allTokens array. That translates to the total number of NFTs ina contract. Also, each token has a valid owner address that is not address(0).

tokenByIndex(): The function returns thetokenID of an NFT that is stored at the provided index.

tokenOfOwnerByIndex(): The function returnsthe tokenID of an NFT stored at a particular index in the owner's list oftokens.
