3

I want to iterate over all token ids of a ethereum ERC-721 contract. Some contracts have counting ids (0, 1, 2, 3, ...) which is easy, but some have random ids, e.g. https://etherscan.io/token/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d#inventory

Sadly etherscan only shows the last 10000 token ids used, but I want to iterate over all 79490. Is there a way to accomplish this? For me, everything is fine. Setup my own ethereum node, using some API.

Alai
  • 123
  • 1
  • 8

3 Answers3

11

You can loop through all Transfer() events emitted by the collection contract.

You're looking for transfers from address 0x0 (minted tokens). And excluding from the list transfers to address 0x0 (destroyed tokens).

One way to achieve this is by using the Web3 Contract getPastEvents() function (docs).

const myContract = new web3.eth.Contract(abiJson, contractAddress);
myContract.getPastEvents('Transfer', {
    filter: {
        _from: '0x0000000000000000000000000000000000000000'
    },
    fromBlock: 0
}).then((events) => {
    for (let event of events) {
        console.log(event.returnValues._tokenId);
    }
});
Pang
  • 9,564
  • 146
  • 81
  • 122
Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • Thank you very much! This works for contracts with less than 10000 tokens. If there are more, I get `query returned more than 10000 results`. Is this an infura limitation and would it help to setup my own geth instance? – Alai Sep 28 '21 at 22:45
  • 2
    @Alai Exactly, this 10k limit is imposed by Infura, you can bypass it by using your own node. – Petr Hejda Sep 29 '21 at 07:42
  • 2
    Thank you very much. Do I need an archive node for this or is a full node sufficient? – Alai Oct 16 '21 at 13:00
  • 1
    @Alai was the fast sync enough you to get the transfers? – luca992 Nov 22 '21 at 21:35
  • @luca992 Sadly it did not work with a full node on my side. I did not try it with an archive node. – Alai Nov 25 '21 at 12:57
  • 2
    I got it working with infura with the above code by specifying fromBlock: x, toBlock: y. and incrementing x and y in a loop where the range of x and y is small enough to return less than 10,000 results. @Alai – luca992 Nov 26 '21 at 23:17
  • This limitation is forced by infura you can get a private node from fastlynode, they don't have any limitations – OUBADI Ahmed Mar 09 '22 at 16:14
  • how to modify this code to get last 10? https://stackoverflow.com/questions/72956286/erc-721-get-last-10-minted-nft-token-ids – mhmd Jul 12 '22 at 17:47
  • @mhmd, I do not think there is a such way. Tx are grouped in blocks and mined altogether. The order of tx to be mined depends on its nonce and its gas prices, therefore it would not be a linear order. You can track the latest block number where you have your previous 10 tx and start filter events from it. In the example above you have to put block number in the filter's ```_from``` field. – Joe Dow Dec 08 '22 at 07:38
  • On kotlin with ```web3j``` it is not fast. It was ok for testing the method, but with history growth pulling events one by one takes non-solution ready time. – Joe Dow Dec 08 '22 at 07:41
5

There's no easy way to do it with an Ethereum node in a contract-agnostic way...the ERC-721 does not specify any interface methods that allow querying for all token ID, so unless the contract you're looking at uses sequential token ids, there's no way to guess all token ids from a simple node query.

Unless you want to iterate over the whole transaction history of the contract to get the ids of every minted NFT (you'd need an archive node for that, as a full node would not have the full transaction history) you should use an API from services that index all NFT activity.

You could use this API from CovalentHQ: https://www.covalenthq.com/docs/api/#/0/Class-A/Get-NFT-Token-IDs-for-contract/lng=en

Or this one from Moralis: https://docs.moralis.io/moralis-server/web3-sdk/token#getalltokenids

Pang
  • 9,564
  • 146
  • 81
  • 122
volky.eth
  • 198
  • 2
  • 8
0

I needed the same with Ethers instead of Web3, here i the code snippet for ethers.js:

const getTransferEvents = async () => { 
    const provider = new ethers.providers.Web3Provider(window.ethereum)
    const contract = new ethers.Contract("address", "abi", provider);

    const events = await contract.queryFilter('Transfer', 0);

    console.log(events);
};
Doğukan Akkaya
  • 423
  • 3
  • 12