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.
Reason | Description |
---|---|
ATTRIBUTED_BY_UNSPECIFIED | Undefined (should not be present in real data). |
ATTRIBUTED_BY_ZERO_QUANTITY_TX | Price was not attributed, as there is a zero-quantity ERC-1155 transfer involved. |
ATTRIBUTED_BY_SWAP | Price 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_BUNDLE | Price was not attributed, as this is a bundle sale of tokens from different contracts. |
ATTRIBUTED_BY_NO_FUNGIBLE_META | Price was not attributed, as the fungible token contracts didn't provide decimals meta information, exchange rates are unavailable. |
ATTRIBUTED_BY_INTERMEDIATE | Price 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_PAYMENTS | Price is attributed as zero. This occurs when no native or fungible tokens are transferred. |
ATTRIBUTED_BY_SINGLE_TRANSFER | Price is attributed as a sum of all native and fungible tokens that were transferred. |
ATTRIBUTED_BY_SAME_TOKEN | Price 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_PAYMENT | Price 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_TRANSFER | Price 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_PROPORTION | Price 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_AVERAGE | Price 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:
Symbol | Token address |
---|---|
WETH | 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 |
LINK | 0x514910771af9ca656af840dff83e8264ecf986ca |
USDC | 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 |
SAND | 0x3845badade8e6dff049820680d1f14bd3903a5d0 |
SUSHI | 0x6b3595068778dd592e39a122f4f5a5cf09c90fe2 |
DAI | 0x6b175474e89094c44da98b954eedeac495271d0f |
USDT | 0xdac17f958d2ee523a2206206994597c13d831ec7 |
ENJ | 0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c |
COVAL | 0x3d658390460295fb963f54dc0899cfb1c30776df |
DEGO | 0x88ef27e69108b2633f8e1c184cc37940a075cc02 |
SHIB | 0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce |
WBTC | 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 |
CRO | 0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b |
APE | 0x4d224452801aced8b2f0aebe155379bb5d594381 |
GALA | 0x15d4c048f83bd7e37d49ea4c83a07267ec4203da |
The following ERC-20 tokens are currently converted automatically for Polygon:
Symbol | Token address |
---|---|
MATIC | 0x0000000000000000000000000000000000001010 |
WETH | 0x7ceb23fd6bc0add59e62ac25578270cff1b9f619 |
WMATIC | 0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270 |
USDC | 0x2791bca1f2de4661ed88a30c99a7a9449aa84174 |
USDT | 0xc2132d05d31c914a87c6611c10748aeb04b58e8f |
DAI | 0x8f3cf7ad23cd3cadbd9735aff958023239c6a063 |
QUICK | 0x831753dd7087cac61ab5644b308642cc1c33dc13 |
CHI | 0x0000000000004946c0e9f43f4dee607b0ef1fa1c |
Updated over 1 year ago