It depends on the contract implementation. How the developer keep track of the owner: This is one way you can implement
mapping(address=>mapping(uint=>uint)) private _ownedTokens; this mapping is like
{"0x32j23h32h423.....":{1:tokenId-1 , 2:tokenId-2}} Have another mapping for a fast lookup
mapping(uint=> uint) private _idToOwnedIndex; Then define a function to update the state of owner:
function _addTokenToOwner(address to, uint tokenId) private{ uint length=ERC721.balanceOf(to); _ownedTokens[to][length]=tokenId; _idToOwnedIndex[tokenId]=length; } call _addTokenOwner in _beforeTokenTransfer
function _beforeTokenTransfer(address from, address to,uint tokenId)internal virtual override{ // in case this is provided in super contract, the contract that you are extending from call this super._beforeTokenTransfer(from,to,tokenId); // that means we are minting the token // Becasue inside ERC721._mint we call _beforeTokenTransfer(address(0),to,tokenId) if(from==address(0)){ _addTokenToAllTokens(tokenId); } // this also satisfied when we mint the token. if (to!=from){ _addTokenToOwner(to, tokenId); } } Now write the function to get the nft's that msg.sender owns. I dont think you should implement a way to view other account's nfts
function getOwnedNfts() public view returns(NftItem[] memory){ uint ownedItemsCount=ERC721.balanceOf(msg.sender); NftItem[] memory items= new NftItem[](ownedItemsCount); for (uint i=0; i<ownedItemsCount; i++){ uint tokenId=tokenOfOwnerByIndex(msg.sender, i); NftItem storage item=_idToNftItem[tokenId]; items[i]=item; } return items; } this is the helper function that used in getOwnedNfts function:
function tokenOfOwnerByIndex(address owner,uint index) public view returns(uint){ require(index<ERC721.balanceOf(owner), "Index out of bounds"); return _ownedTokens[owner][index]; }