6

I have created a ERC-721 contract deployed on ropston network. Using contract I'm creating NFT's and its totally working fine.

Now for the transfer part I need to get tokenID of any NFT and transfer to to other address but I'm not able get the tokenID whenever I fetch transaction details from etherscan or using web3.

I want to store the tokenID in DB so it can be utilized while transferring to other address.

enter image description here

I have encircled the exact tokenID required in above image.

I'm using following code:

window.ethereum
    .request({
        method: 'eth_sendTransaction',
        params: [
            {
                from: fromAddress,
                to: contractAddress,
                gas: '50000',
                data: nftContract.methods.transferFrom(fromAddress, toAddress, tokenNumber).encodeABI()
            },
        ],
    })

I just want to get tokenID when NFT was created and store into DB for reference and perform business logic.

function mintNFT(address recipient, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(recipient, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }

Above is the solidity function responsible for creating the NFT.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Omar
  • 145
  • 1
  • 1
  • 9
  • now i need to fetch newItemID and store into my DB as part of storing reference to my NFT to transfer any point later to other address. – Omar Jun 02 '21 at 11:00

4 Answers4

18

Your mintNFT() function doesn't emit any event containing the newItemId.

solidity is using the standard definition of transfer

There's no "standard definition", the ERC-721 standard only defines an interface and few other rules - and the actual implementation (of the interface) is on each developer. However I'm assuming that by the "standard definition" you mean the OpenZeppelin implementation, which is a widely used implementation of the ERC-721 standard and is used by many people who start coding in Solidity.

You can see in the linked implementation, that the OZ _mint() function emits the Transfer() event, where the 3rd argument is the minted token ID.

So executing your mintNFT() function effectively emits the Transfer() event that contains the newly minted token ID as a value of the 3rd parameter.


After you've executed the mintNFT() contract function from your JS code, it returns a PromiEvent object, that you can use to catch its receipt event.

The receipt contains the emited logs, where you can find the Transfer() log as well.

const tx = nftContract.methods.mintNFT(...).send({from: ...});

tx.on('receipt', function(receipt){
    console.log(receipt.logs[0].topics[3]); // this prints the hex value of the tokenId
    // you can use `web3.utils.hexToNumber()` to convert it to decimal
});

If you want to get the token ID from an already existing transaction (using the tx hash), you can use this snippet:

web3.eth.getTransactionReceipt('0x258a6d35445814d091ae67ec01cf60f87a4a58fa5ac1de25d0746edf8472f189').then(function(data){
    let transaction = data;
    let logs = data.logs;
    console.log(logs);
    console.log(web3.utils.hexToNumber(logs[0].topics[3]));
});

You can find more details in the web3 docs for the send() method and the receipt.


Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • Thankyou for this detailed information , please let me know the way forward or resource that particularly help to solve this issue. i got the idea whatever you described above but emitting event and getting there value on application side is kind of grey area for me without any documentation reference. – Omar Jun 02 '21 at 11:21
  • @Omar I've updated my answer with an example of getting the event log data from a JS code (using `web3`). – Petr Hejda Jun 02 '21 at 11:37
  • Thank you , I tried the solution but converting always gives 0 as a result which is of course not the tokenID im looking for : code that i tried : web3.eth.getTransactionReceipt('0x3216a1abc2a955c4323180a0d76a333631e823f39a1ebe82746aed8f9e8f9f73').then(function(data){ console.log(web3.utils.hexToNumber(data.logs[0].data)); }) – Omar Jun 02 '21 at 12:33
  • Can you inspect the `data.logs` variable? Some versions of `web3` use different structure of the logs than the documentation covers... It's also possible that the tx emits more log events than just this one (e.g. from your `_setTokenURI()` and the index 0 is a different event log). Also note that the `data` field of the event log only contains unindexed arguments (without the `indexed` keyword) so it might contain more than just one value. – Petr Hejda Jun 02 '21 at 12:35
  • It gives this detail information against the log : [ { "blockHash": ".....2", "address": "....", "logIndex": 2, "data": "0x", "removed": false, "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x000......0", "0x000...000000a6c18c5e4914ed8819aedac08d803d25982425b", "0x...." ], "blockNumber": 10303949, "transactionIndex": 3, "transactionHash": "0x..............", "id": "log_0b37afa6" } ] – Omar Jun 02 '21 at 12:50
  • sorry i had to trim it considering the text length. – Omar Jun 02 '21 at 12:51
  • `topics` contain the indexed values. Now I see that OpenZeppelin defines the 3rd argument as `indexed` as well. So I'll correct my answer to use the `topics[3]` instead of `data` field. – Petr Hejda Jun 02 '21 at 12:56
  • yes its inside "logs[0].topics[3]" i tried with two transactions and its returning right tokenID this way , please add this code in answer so I can mark it right answer. web3.eth.getTransactionReceipt('0x258a6d35445814d091ae67ec01cf60f87a4a58fa5ac1de25d0746edf8472f189').then(function(data){ let transaction = data ; let logs = data.logs ; console.log(logs); console.log(web3.utils.hexToNumber(logs[0].topics[3])); }) – Omar Jun 02 '21 at 12:58
  • @Omar I've added your snippet to the answer. – Petr Hejda Jun 02 '21 at 13:37
  • Thanks a lot , please refer me to the OZ reference that you mentioned in comment about 3rd argument as indexed – Omar Jun 02 '21 at 14:21
  • @Omar [ERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L18) extends [IERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/IERC721.sol#L14), which defines the `Transfer()` event (on line 14) with the 3rd argument as `indexed`. – Petr Hejda Jun 02 '21 at 15:01
  • @PetrHejda why are you using `topics[3]` here? how this 3 came here? – Volatil3 Aug 20 '21 at 18:54
  • 2
    @Volatil3 The ERC-721 standard defines the `Transfer()` event with 3 indexed arguments. `topics[1]` is the `address` sender, `topics[2]` is the `address` recipient, and `topics[3]` is the `uint256` token ID. (And `topics[0]` is the event signature)... Mind that this is the ERC-721 for NFTs. If you want a "regular" ERC-20 token, there's only 2 indexed topics. – Petr Hejda Aug 20 '21 at 19:07
  • 1
    @TamásSengel Yes, thank you for pointing that out. I expected the code in the answer to be self-explanatory, but the actual code should be `receipt.logs[0].topics[3]`. Just updated the answer. – Petr Hejda Sep 04 '22 at 19:49
6

You can try:

const receipt = await web3.eth.getTransactionReceipt(hash)
const tokenId = Web3.utils.hexToNumber(receipt.logs[0].topics[3])

I check hash from ropsten testnet: https://ropsten.etherscan.io/tx/0x59928012c3e0605b9346215c24654e84be29f2bf47949a2284aecf9991996a28

and output is 11

P. Huy
  • 81
  • 1
  • 2
2

For some reason my tokenId was under receipt.logs[1].topics[3] not receipt.logs[0].topics[3] (something to do with the indexed arguments?). It was easy to find though, as if your id's are auto generated (and they usually are), it will show up in the logs as for example tokenId 3:

0x0000000000000000000000000000000000000000000000000000000000000003

So as long as your id is under 10 they can be spotted as a decimal, 0x00...03 = 3:

You can see it in the log on you network Scanner.

Then it's just a case of converting it from Hex to Decimal. Which you can do in plain JS, passing in the radix 16 for hex:

const tokenId = parseInt(receipt.logs[1].topics[3], 16);
Id10T-ERROR
  • 447
  • 3
  • 18
0

Try this if receipt.logs[0].topics[3] doesn't work:

receipt.events.Transfer.returnValues.tokenId
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223