No transfer 'value' in EthNFTTransfers or Web3 API endpoint

I’m using a query to get all transfer history for an NFT, which includes sales.

I’m wondering why EthNFTTransfers does not contain a field ‘value’ like found in EthTransactions.
I query EthNFTTransfers but I need to the sell price, which means I have to do a $lookup from EthTransactions based on tx hash. That’s fine, but add a bit of query load.

So why did you choose not to include the tx ‘value’ in EthNFTTransfers?

This is the same when using Web3 API endpoint /nft/{address}/{token_id}/transfers, so using this API instead of a db query is not an option for me.

1 Like

Hi @matiyin

You have a specific task. Few people need this information. I will move your question to the “suggestions” category :raised_hands:

@Yomoo I respectfully disagree and see value as part of a transaction and especially for NFTs. Most ‘NFT Transfers’ will have a value attached to it actually. Just wondering why it’s left out, makes no sense to me.
:woman_mage:

2 Likes

This is just my opinion. I do not decide what to include in the database and what not :sweat_smile:

1 Like

Hey @matiyin

The dev team studied the issue in more detail and came to this conclusion:

  • 1 transactions could contain 20 different logs operations. This one transaction generated 11 logs, 2 approvals 1 batched NFT 2 single nft and 1 erc20 transfer:

  • Looking at value of EthTransaction is not always correct because there is no standard way to specify the price of NFTs, one EthTransaction can do many things

  • In your case using lookup like he does now is at the moment the best solution. As you can already see, sometimes one NFT transcation ≠ 1 eth transcation

  • We will check in the future how to reliably bring price data to NFTTransfers(изменено)

2 Likes

wow thanks for checking team!
I understand the issue. The transaction value on chain can be 1eth but that doesn’t in all cases mean this was the value of the NFT, since the contract could have other things at the same time.

I ended up using this if anyone is looking to list the transaction history of an NFT:

// get transaction history
Moralis.Cloud.define('getTransactionHistory', async (request) => {
  const query = new Moralis.Query(request.params.network+'NFTTransfers');
  query.equalTo('token_id', request.params.token_id);
  query.equalTo('token_address', request.params.token_address);
  query.equalTo('confirmed', true)
  query.descending('createdAt')
  const pipeline = [
    {
      lookup: {
        from: request.params.network+'Transactions',
        let: { transaction_hash: '$transaction_hash' },
        pipeline: [
          { $match: { $expr: { $eq: [ '$$transaction_hash',  '$hash' ] } }},
          { $project: { value: 1 }}
        ],
        as: 'value'
      }
    },
    {
      project: {
        value: { $first: '$value.value' },
        to_address: 1,
        from_address: 1,
        createdAt: 1,
        transaction_hash: 1,
      }
    },
  ]
  const results = await query.aggregate(pipeline);
  let items = []
  if (results) {
    results.reverse() // for some reason query order is reversed by pipeline
    // add user data
    for (let i = 0; i < results.length; i++) {
      const receiverQuery = new Moralis.Query(Moralis.User)
      receiverQuery.equalTo('accounts', results[i].to_address)
      const receiver = await receiverQuery.first({useMasterKey:true})
      const senderQuery = new Moralis.Query(Moralis.User)
      senderQuery.equalTo('accounts', results[i].from_address)
      const sender = await senderQuery.first({useMasterKey:true})
      if (sender) results[i].sender = { username: sender.attributes.username, avatar: sender.attributes.avatar ? sender.attributes.avatar.url() : null }
      if (receiver) results[i].receiver = { username: receiver.attributes.username, avatar: receiver.attributes.avatar ? receiver.attributes.avatar.url() : null }
      items.push(results[i])
    }
  }
  return items
},{
  fields : ['network','token_id','token_address'],
  requireUser: true 
})

Note that I still add the User data after the pipeline, sadly getting the right User data doesn’t work fully in such a pipeline. But it works, just wondering if it matters at all in performance. For my use case totally fine.

See here:

By looking at the value, to_address and from_address you can see if a transaction was a sale, normal transfer, mint (from 0x000…) or burn (to 0x000…).

1 Like

Great job @matiyin

I’ll figure it out today with the pipelines. :man_mechanic:

1 Like