If the NFT contract is a simple ERC721
(i.e., not extending the ERC721Enumerable
), then one way to get the token ids owned by an address is to fetch and filter the logs of the corresponding NFT contract.
Here's a minimal JS code (created by following this hardhat implementation) to achieve the same:
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider("JSON_RPC_URL");
const ERC721 = require('@openzeppelin/contracts/build/contracts/ERC721.json');
async function listTokensOfOwner(tokenAddress, account) {
const token = new ethers.Contract(
tokenAddress,
ERC721.abi,
provider,
);
const sentLogs = await token.queryFilter(
token.filters.Transfer(account, null),
);
const receivedLogs = await token.queryFilter(
token.filters.Transfer(null, account),
);
const logs = sentLogs.concat(receivedLogs)
.sort(
(a, b) =>
a.blockNumber - b.blockNumber ||
a.transactionIndex - b.transactionIndex,
);
const owned = new Set();
for (const { args: { from, to, tokenId } } of logs) {
if (addressEqual(to, account)) {
owned.add(tokenId.toString());
} else if (addressEqual(from, account)) {
owned.delete(tokenId.toString());
}
}
return owned;
};
function addressEqual(a, b) {
return a.toLowerCase() === b.toLowerCase();
}
async function getTokenName(tokenAddress) {
const token = new ethers.Contract(
tokenAddress,
ERC721.abi,
provider,
);
return token.name();
}
async function main(token, account) {
console.log(await getTokenName(token), 'tokens owned by', account);
const owned = await listTokensOfOwner(token, account);
console.log([...owned].join('\n'));
};
main("NFT_CONTRACT_ADDRESS", "WALLET_ADDRESS")