Price Attribution

When NFTs first appeared, it was relatively easy to properly attribute NFT prices because most transactions involved the transfer of just a single NFT. As a result, we could be confident all payments that occured within a given transaction were related to the same NFT transfer.

However as time goes by, NFT transactions are becoming increasingly complicated with the emergence of things like bundle mints and sales, swaps, circular trades and so on. In addition to multiple tokens being transfered back and forth in single transactions, we also are seeing more parties participating in a single transaction aside from the initial owner and the final recipient. All of these practices make price attribution for individual NFTs an increasingly complicated task.

For example, in June 2022, there were 13.38 million NFT transfers on Ethereum. Only 29.6% of those were single NFT transfers. The rest of these transfers were "bundles" where multiple NFTs were transfered in a single transaction. For comparison, in June 2019 68% of NFT transfers were single token transfers.

How Mnemonic approaches price attribution

Mnemonic indexes various transfers on the blockchain, detects payments, exchanges, and other types of transactions between accounts involving fungible and non-fungible tokens. As a next step Mnemonic applies pricing attribution algorithms to that data, enabling us to provide our end users with accurate and easy-to-use pricing data for individual NFT tokens.

Our algorithm accounts for the following unique transaction edge cases to name just a few:

  • Batch mints of NFTs;
  • Bundle sales (both single- and multi-contract);
  • Transfers via intermediate addresses/contracts;
  • ERC-1155 tokens with multiple quantities;
  • Bi-directional payments;
  • Refunds;
  • Burn NFT(s) to mint NFT(s);
  • Burn NFT(s) to redeem ERC20(s);
  • and many others...

In the vast majority of cases, we are able to provide accurate pricing attribution for NFTs. We also provide a dedicated endpoint that returns a full, detailed breakdown of all NFTs and payments involved in any given transaction.

Attribution types

There are various types of transactions and a combination of edge cases which make pricing attribution a very complex problem. This results into various ways of how the price per each sale can be attributed.

In order to provide clarity and a much more human readable way to understand and interpret how the price is attributed on a case by case basis Mnemonic provides a set of descriptive labels each of which provides a reasoning for each price attribution decision by the Mnemonic's algorithm.

ReasonDescription
ATTRIBUTED_BY_UNSPECIFIEDUndefined (should not be present in real data).
ATTRIBUTED_BY_ZERO_QUANTITY_TXPrice was not attributed, as there is a zero-quantity ERC-1155 transfer involved.
ATTRIBUTED_BY_SWAPPrice was not attributed, as the transaction is a swap. The destination wallet has both incoming and outgoing transfers of different NFTs. Note: in the case of a mint, where all tokens are minted by the same contract inside a transaction before sending, the attribution type will be ATTRIBUTED_BY_MINT_AND_TRANSFER, not ATTRIBUTED_BY_SWAP.
ATTRIBUTED_BY_MULTI_CONTRACTS_BUNDLEPrice was not attributed, as this is a bundle sale of tokens from different contracts.
ATTRIBUTED_BY_NO_FUNGIBLE_METAPrice was not attributed, as the fungible token contracts didn't provide decimals meta information, exchange rates are unavailable.
ATTRIBUTED_BY_INTERMEDIATEPrice was not attributed, as a target wallet typed as intermediate. This applies when the destination wallet has no NFT ownership changes at the end of the transaction. This happens when the sending wallet sends exactly the same amount of tokens as it received (can't divide to zero to estimate token value).
ATTRIBUTED_BY_NO_PAYMENTSPrice is attributed as zero. This occurs when no native or fungible tokens are transferred.
ATTRIBUTED_BY_SINGLE_TRANSFERPrice is attributed as a sum of all native and fungible tokens that were transferred.
ATTRIBUTED_BY_SAME_TOKENPrice is attributed in a proportional manner. In this case, there were multiple transfers of the same token. We sum up all payments, and split them proportionally.
ATTRIBUTED_BY_RECIPIENT_NO_PAYMENTPrice is attributed as a transaction value. Typically this happens when a 3rd party wallet pays for the transaction that is not the receiving wallet.
ATTRIBUTED_BY_MINT_AND_TRANSFERPrice is attributed in a proportional manner. This is applied when multiple tokens from the same contract were minted and transferred inside the same transaction
ATTRIBUTED_BY_PAYMENT_PROPORTIONPrice is attributed proportionally. This is applied when we were able to estimate the price for all tokens by a simple proportion. For example, in the case:
A -> 1 x NFT.X -> B A -> 2 x NFT.Y -> B C -> 2 x NFT.X -> B
we can estimate NFT.Y is worth half of what Wallet C paid and then mathematically infer NFT.X.
ATTRIBUTED_BY_SAME_CONTRACT_AVERAGEPrice is attributed as an average. This is applied when all tokens are related to the same contract, and there isn't a more exact proportional attribution approach available. All tokens are evaluated as equal, and all transfers are split proportionally.

Getting NFT prices

Our Transfer Insights provide the ability to get a list of NFT sales filtered by various fields. For the GetNftTransfers endpoint, the response aside from the regular information about transfers will contain details related to pricing attribution. At the same time GetAllTransfersByTxHash endpoint will allow you to get a full breakdown of each transaction.

Pricing details

For each NFT transfer returned, the response contains the senderReceived and recipientPaid sub-objects with the following structure:

  • totalEth - total amount attributed to this transfer, converted to ETH (if the payment or part of the payment has been made by non-eth fungilbe tokens);
  • totalUsd - total amount converted to USD according to the exchange rate at the transfer time;
  • ethTransfersTotal - total for the portion of payments made natively in ETH;
  • erc20TransfersTotal - total for the portion of payments made native in ERC20 and converted to ETH.

Basically the totalEth field is a sum of ethTransfersTotal and erc20TransfersTotal, the latter is provided solely for your convenience.

It is worth noting that if the pricing attribution was impossible for either the sender or recipient of a transfer, the whole object will be equal to null. Such cases include, for example, a zero-quantity ERC-1155 transfer or transfers where multiple tokens are being burned at the same time.

Another possible case is if the erc20TransfersTotal field (and as a result also totalEth and totalUsd) begins equal to null, which means that the transfer included fungible tokens, that either do not fully conform to the ERC20 standard or do not have an exchange rate to ETH.

Example with a mixed payment

Let's take a look at a sample transfer which illustrates the fields mentioned above. In this case, you can see that the seller has been paid in solely in ERC-20 tokens, while the purchaser paid partially in ERC-20 and partially in native ETH.

{
  "blockchainEvent": {
    "txHash": "0x0ca31a30b9a828d5e8022f925db13ce220d067acc9e882e189c1fca628ee6620",
    "logIndex": 427,
    "seqIndex": 0,
    "blockNumber": 15129373,
    "blockTimestamp": "2022-07-12 18:05:44+00"
  },
  "contractAddress": "0x2ee6af0dff3a1ce3f7e3414c52c48fd50d73691e",
  "tokenId": "4802",
  "tokenType": "TYPE_ERC721",
  "transferType": "TRANSFER_TYPE_REGULAR",
  "quantity": "1",
  "sender": {
    "address": "0x910baeba92c30a1bf3d9a23d9eb0e645a88135fd",
    "type": "TYPE_OWNER"
  },
  "senderReceived": {
    "totalEth": "0.13636656005335413952",
    "totalUsd": "146.80918416606025558869750243225024",
    "ethTransfersTotal": "0",
    "erc20TransfersTotal": "0.13636656005335413952"
  },
  "recipient": {
    "address": "0x512a966f39775225850f447b87e36699a12dd516",
    "type": "TYPE_OWNER"
  },
  "recipientPaid": {
    "totalEth": "0.1549743640150963025024",
    "totalUsd": "166.8419291269694475347252231256271488",
    "ethTransfersTotal": "0.050432",
    "erc20TransfersTotal": "0.1045423640150963025024"
  }
}

Sender/Recipient Types

Another helpful field returned in the response is type within the sender and recipient objects. This field helps to detect transfers via intermediate contracts. It can take one of the following values:

  • TYPE_OWNER - the address is either the initial owner or the final recipient of the NFT;
  • TYPE_INTERMEDIATE - the address is an intermediary and neither the initial owner nor the final recipient.

In cases where either the sender or recipient is marked as intermediate, respective pricing details object will be equal to null because the above mentioned entity does not buy or sell NFTs in the transaction.

Example

Let's take a look at another example where an NFT transfer occurs via intermediate party (GemSwap).

[
  {
    "blockchainEvent": {
      "txHash": "0xa9ac922d5816b4575ba91f833429ca952a70b9df824c55ba784cf70dbdf01d74",
      "logIndex": 136,
      "seqIndex": 0,
      "blockNumber": 15129929,
      "blockTimestamp": "2022-07-12 20:11:01+00"
    },
    "contractAddress": "0xeccae88ff31e9f823f25beb404cbf2110e81f1fa",
    "tokenId": "7254",
    "tokenType": "TYPE_ERC721",
    "transferType": "TRANSFER_TYPE_TRANSFER",
    "quantity": "1",
    "sender": {
      "address": "0x3b267a2664256a0102ad2d4d182e80ff797d991c",
      "type": "TYPE_OWNER"
    },
    "senderReceived": {
      "totalEth": "0.028855",
      "totalUsd": "30.043725466467958947",
      "ethTransfersTotal": "0.028855",
      "erc20TransfersTotal": "0"
    },
    "recipient": {
      "address": "0x83c8f28c26bf6aaca652df1dbbe0e1b56f8baba2",
      "type": "TYPE_INTERMEDIATE"
    },
    "recipientPaid": null
  },
  {
    "blockchainEvent": {
      "txHash": "0xa9ac922d5816b4575ba91f833429ca952a70b9df824c55ba784cf70dbdf01d74",
      "logIndex": 140,
      "seqIndex": 0,
      "blockNumber": 15129929,
      "blockTimestamp": "2022-07-12 20:11:01+00"
    },
    "contractAddress": "0xeccae88ff31e9f823f25beb404cbf2110e81f1fa",
    "tokenId": "7254",
    "tokenType": "TYPE_ERC721",
    "transferType": "TRANSFER_TYPE_TRANSFER",
    "quantity": "1",
    "sender": {
      "address": "0x83c8f28c26bf6aaca652df1dbbe0e1b56f8baba2",
      "type": "TYPE_INTERMEDIATE"
    },
    "senderReceived": null,
    "recipient": {
      "address": "0x0e6bb4218c44668051fda95c94fcbf11f3fca08f",
      "type": "TYPE_OWNER"
    },
    "recipientPaid": {
      "totalEth": "0.029",
      "totalUsd": "30.1946989612743306",
      "ethTransfersTotal": "0.029",
      "erc20TransfersTotal": "0"
    }
  }
]

All transfers & sales by transaction

In addition to returning attributed prices, our Transfer Insights endpoints allow you to get a full breakdown of all transfers (NFT, ERC20, ETH, and/or others depending on the blockchain) within a transaction — via GetAllTransfersByTxHash endpoint.

This endpoint returns txHash as input and returns a set of lists for each type of transfer within to the requested transaction.

Example

Let's take a look at the following example:

{
  "nftTransfers": [
    {
      "blockchainEvent": {
        "txHash": "0x4441fe5daceeb0a10d7682a445abdd5b6c4806d43fc6def186575a882f9f599c",
        "logIndex": 271,
        "seqIndex": 0,
        "blockNumber": 15120950,
        "blockTimestamp": "2022-07-11 10:51:17+00"
      },
      "contractAddress": "0xd46c8648f2ac4ce1a1aace620460fbd24f640853",
      "tokenId": "5779",
      "tokenType": "TYPE_ERC721",
      "transferType": "TRANSFER_TYPE_BURN",
      "quantity": "1",
      "sender": {
        "address": "0x962871224822525c963d2d397b728a2aead83a1b",
        "type": "TYPE_OWNER"
      },
      "senderReceived": null,
      "recipient": {
        "address": "0x0000000000000000000000000000000000000000",
        "type": "TYPE_OWNER"
      },
      "recipientPaid": null
    }
    # ...more transfers...
  ],
  "ethTransfers": [
    {
      "fromAddress": "0x962871224822525c963d2d397b728a2aead83a1b",
      "toAddress": "0x3b968d2d299b895a5fcf3bba7a64ad0f566e6f88",
      "valueRaw": "2941445554065432573",
      "valueNormalized": "2.941445554065432573"
    }
    # ...more transfers...
  ],
  "erc20Transfers": [
    {
      "fromAddress": "0x0000000000000000000000000000000000000000",
      "toAddress": "0x43078abfb76bd24885fd64effb22049f92a8c495",
      "contractAddress": "0xed1840223484483c0cb050e6fc344d1ebf0778a9",
      "valueRaw": "340841025061864773",
      "valueNormalized": "0.3408410250618648",
      "valueEth": null
    }
    # ...more transfers...
  ]
}

Collection pricing data

Mnemonic is the only NFT data solution on the market that provides accurate pricing information for all existing collections on the blockchain as a time-series. The data is updated every 15 minutes for every collection on the blockchain (ERC721 and ERC1155) and is aggregated into 1h time intervals.

The provided data covers all transactions from across the entire blockchain regardless of which marketplace the trade was executed on.

There is no limit as to how far back the historical data can be retrieved.

All price values are converted into native token according to the exchange rate at the time of the transaction if payments were made in ERC20 tokens.

Below is an example of pricing time-series data returned for the BAYC contract for the last 7 days aggregated into 1h time periods (trimmed for the purpose of this example).

The data is zero-padded to make charting the graph easier.

{
    "dataPoints": [
        {
            "timestamp": "2022-05-25T05:00:00Z",
            "min": "95",
            "max": "105",
            "avg": "98.6666666666666667"
        },
        {
            "timestamp": "2022-05-25T06:00:00Z",
            "min": "",
            "max": "",
            "avg": ""
        },
        {
            "timestamp": "2022-05-25T07:00:00Z",
            "min": "109",
            "max": "109",
            "avg": "109"
        },
        {
            "timestamp": "2022-05-25T08:00:00Z",
            "min": "",
            "max": "",
            "avg": ""
        },
        {
            "timestamp": "2022-05-25T09:00:00Z",
            "min": "",
            "max": "",
            "avg": ""
        },
        {
            "timestamp": "2022-05-25T10:00:00Z",
            "min": "",
            "max": "",
            "avg": ""
        },
        {
            "timestamp": "2022-05-25T11:00:00Z",
            "min": "86.0901233378872304",
            "max": "86.0901233378872304",
            "avg": "86.0901233378872304"
        }
        ...
        <trimmed>
    ]
}

Try using Mnemonic's Collection Analytics API to get the accurate time-series data for you project.

ERC20 conversion

Mnemonic automatically converts ERC-20 token values into native ETH value according to the exchange rate at the time of the transaction.

The following ERC-20 tokens are currently converted automatically for Ethereum:

SymbolToken address
WETH0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
LINK0x514910771af9ca656af840dff83e8264ecf986ca
USDC0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
SAND0x3845badade8e6dff049820680d1f14bd3903a5d0
SUSHI0x6b3595068778dd592e39a122f4f5a5cf09c90fe2
DAI0x6b175474e89094c44da98b954eedeac495271d0f
USDT0xdac17f958d2ee523a2206206994597c13d831ec7
ENJ0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c
COVAL0x3d658390460295fb963f54dc0899cfb1c30776df
DEGO0x88ef27e69108b2633f8e1c184cc37940a075cc02
SHIB0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce
WBTC0x2260fac5e5542a773aa44fbcfedf7c193bc2c599
CRO0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b
APE0x4d224452801aced8b2f0aebe155379bb5d594381
GALA0x15d4c048f83bd7e37d49ea4c83a07267ec4203da

The following ERC-20 tokens are currently converted automatically for Polygon:

SymbolToken address
MATIC0x0000000000000000000000000000000000001010
WETH0x7ceb23fd6bc0add59e62ac25578270cff1b9f619
WMATIC0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270
USDC0x2791bca1f2de4661ed88a30c99a7a9449aa84174
USDT0xc2132d05d31c914a87c6611c10748aeb04b58e8f
DAI0x8f3cf7ad23cd3cadbd9735aff958023239c6a063
QUICK0x831753dd7087cac61ab5644b308642cc1c33dc13
CHI0x0000000000004946c0e9f43f4dee607b0ef1fa1c