Advanced Transfers

To get the full story behind an NFT transfer, you'll need to dig deeper than the data you see on the chain. Read on to learn about some of the complexities associated with NFT transfer data, and how Mnemonic's API can help you solve for them.

Transfers represent a value exchange or a transfer of ownership between two accounts on the Ethereum blockchain. Mnemonic's API provides access to historical transfers of NFTs and fungible tokens, enabling you to retreive, analyze, and embed this information into your application without scanning the blockchain, which is a challenging and time-consuming task.

Furthermore, obtaining just a single transfer event from the blockchain often doesn't provide enough context to help understand what actually happened in the transaction beyond just the transfer of ownership. The most valuable information is often hidden in internal calls that happen between various contracts, in the relation to other transfers within the same transaction, such as ERC20 payments, swaps, and other properties.

Our Transfer Insights endpoints retreive rich data on transfers, making it easy to:

  • Get the accurate price of an NFT
  • Differentiate between mints, burns, sales, and airdrops
  • Calculate wash trades, bundles, and swaps
  • Calculate various fees paid by the buyer and the seller
  • Calculate fees received by a third-party

Read further to better understand the taxonomy of Mnemonic's Advanced Transfer endpoints.

General structure

ERC721 and ERC1155 transfers represent transfers of non-fungible and semi-fungible tokens between accounts.

Below is the top level NftTransfer object returned in the transfer responses, followed by a breakdown of each property.

Copy
Copied
// NftTransfer holds details of the NFT transfer events (transfer, minting, burning),
// including pricing details attributed to each specific transfer.
message NftTransfer {
    // Blockchain event details.
    BlockchainEvent blockchain_event = 1;

    // Contract address that tracks the NFT.
    string contract_address = 2;

    // Token ID of the NFT.
    string token_id = 3;

    // Type of the token (ERC-721, ERC-1155, etc), detected by the event type
    // on the blockchain.
    mnemonic.ethereum.tokens.v1beta1.TokenType token_type = 4;

    // Type of the transfer, detected by the from/to addresses.
    TransferType transfer_type = 5;

    // Quantity of the NFTs in the transfer (given as string to provide simple
    // representation of uint256).
    //
    // All pricing details for the transfer event are provided according to this
    // quantity. For ERC-1155 quantity can be `0`, in this case pricing details
    // won't be provided.
    string quantity = 6;

    // Sender details.
    Address sender = 7;

    // Sale price details (how much sender received for the NFT).
    //
    // The value may equal to `null` when:
    // - sender's type is "intermediate";
    // - tokens quantity in transfer is `0` (for ERC-1155 transfers);
    // - ambiguous taxonomy of the transaction(e.g. complicated swap,
    //   multi-contract bundle sale, erc).
    Price sender_received = 8;

    // Recipient details.
    Address recipient = 9;

    // Purchase price details (how much recipient paid for the NFT).
    //
    // The value may equal to `null` when:
    // - recipient's type is "intermediate";
    // - tokens quantity in transfer is `0` (for ERC-1155 transfers);
    // - ambiguous taxonomy of the transaction(e.g. complicated swap,
    //   multi-contract bundle sale, erc).
    Price recipient_paid = 10;


    // BlockchainEvent holds blockchain event location details.
    message BlockchainEvent {
        // Blockchain transaction hash.
        string tx_hash = 1;

        // Index of the log event within a block where transfer event occured.
        uint64 log_index = 2;

        // Sequence index of the token transfer within a batch transfer, zero
        // otherwise.
        uint64 seq_index = 3;

        // Block number.
        uint64 block_number = 4;

        // Block mint timestamp.
        google.protobuf.Timestamp block_timestamp = 5;
    }

    // Address holds details about transfer sender/recipient.
    message Address {
        // Sender/recipient address.
        string address = 1;

        // Type of the entity within the transaction.
        Type type = 2;

        // Type represents type of the from/to address within the transaction,
        // to which the transfer belongs.
        enum Type {
            // Undefined (should not be present in real data).
            TYPE_UNSPECIFIED = 0;

            // Address entity is typed as owner, when it was either an initial owner
            // of the tokens or the final recipient.
            TYPE_OWNER = 1;

            // Address entity is typed as intermediate, when by the end of the
            // transaction its NFT balance didn't change. Such addresses serve as
            // intermediaries within complex transfers and are neither initial
            // senders nor final recipients.
            TYPE_INTERMEDIATE = 2;
        }
    }

    // Price holds detailed information about the ETH/ERC20 transfers sent
    // or received for the NFT being transferred.
    //
    // All values provided are attributed to the specific NFT transfer within the
    // transaction and its quantity. Attribution is especially important when
    // multiple transfers occur within a single transaction (batch-mint, bundle
    // sale, etc).
    message Price {
        // Total value converted to ETH (sum of `eth_transfers_total` and
        // `erc20_transfers_total`). The value may be `null`, if any of underlaying
        // values is `null`.
        google.protobuf.StringValue total_eth = 1;

        // Total value converted to USD (according to the rate effective at the time
        // of ). Equals to `null` if `total_eth` is `null`.
        google.protobuf.StringValue total_usd = 2;

        // Total sum of all attributed ETH transfers.
        google.protobuf.StringValue eth_transfers_total = 3;

        // Total sum of all attributed ERC20 transfers given in ETH. The value may
        // be `null` if:
        // - any of used ERC20 contracts didn't provide decimals meta information;
        // - there was no exchange rate from specific ERC20 contract to ETH.
        google.protobuf.StringValue erc20_transfers_total = 4;
    }
}

TransferType enum in the above message allows easy filtering of minted, burnt, transferred or sold NFTs.

Copy
Copied
// TransferType represents type of the transfer event.
enum TransferType {
    // Undefined (should not be present in real data).
    TRANSFER_TYPE_UNSPECIFIED = 0;

    // Mint.
    //
    // Transfer is considered to be a mint, when sender address is
    // `0x0000000000000000000000000000000000000000`.
    TRANSFER_TYPE_MINT = 1;

    // Burn.
    //
    // Transfer is considered to be burn, when recepient is
    // `0x0000000000000000000000000000000000000000` or `0x000000000000000000000000000000000000dead`.
    TRANSFER_TYPE_BURN = 2;

    // Regular transfer.
    //
    // Transfer is considered to be regular, when it is neither burn nor mint.
    // Another special case is when it is mint and burn at the same time.
    TRANSFER_TYPE_REGULAR = 3;
}

ERC1155 Transfers

ERC1155 transfers represent transfers of semi-fungible tokens.

These transfers are defined using the same message type from above, however it is worth noting a couple of special cases.

Single Transfers

Single transfers are treated similarly to ERC721.

Batch Transfers

Batch transfers are provided unwrapped. If multiple tokens were transferred within a single transaction there will be multiple transfers provided in the response (one for each token in a batch).

In order to be able to differentiate between a single transfer and a batch transfer, an additonal seq_index field is provided within the BlockchainEvent message.

Copy
Copied
message BlockchainEvent {
    ...

    // Sequence index of the token transfer within a batch transfer, zero
    // otherwise.
    uint64 seq_index = 3;
}

ETH Transfers

While a single transfer event indicates a transfer of ownership of an NFT, there is usually more detail hidden in the transaction itself and in the special types of calls made between the contracts in the same transaction.

This data provides critical information needed to accurately compute an NFT price and the various fees paid by the parties involved.

Mnemonic provides a fully unwrapped list of such events in association with each NFT transfer.

Copy
Copied
// EthTransfer represents ETH transfer.
message EthTransfer {
    // Sender address of the ETH transfer.
    string from_address = 1;

    // Recipient address of the ETH transfer.
    string to_address = 2;

    // Transfer value in wei.
    string value_raw = 3;

    // Transfer value in ETH.
    string value_normalized = 4;
}

Fungible (ERC20) Transfers

ERC20 transfers represent transfers of fungible tokens between addresses. In the context of NFTs, fungible transfers often used as payments for an NFT, and sometimes there can be multiple ERC20 tokens involved.

Mnemonic provides a full list of all ERC20 transfers with normalized values into ETH according to the exchange rate at the time of the transaction. This makes it possible to easily calculate the price of an NFT paid by the buyer with ERC20, or to get a list of all tokens that have been involved in an exchange.

Copy
Copied
// Erc20Transfer represents single ERC20 transfer.
message Erc20Transfer {
    // Sender of the ERC20 transfer.
    string from_address = 1;

    // Recipient of the ERC20 transfer.
    string to_address = 2;

    // Contract that tracks respective ERC20 token.
    string contract_address = 3;

    // ERC20 transfer value as stored on chain (as uint256).
    string value_raw = 4;

    // Value provided as decimal according to the decimal places of the
    // contract that tracks respective ERC20 token. The value may be `null`
    // if the contract does not provide decimals information.
    google.protobuf.StringValue value_normalized = 5;

    // Value of the ERC20 token converted to ETH by the exchange rate effective
    // on the related block minting timestamp. The value may be `null` if
    // either `value_normalized` is `null` or there is no exchange rate
    // available for the current ERC20 token.
    google.protobuf.StringValue value_eth = 6;
}

EIP-2309 (ERC721) Consecutive Transfers

Consecutive transfers are not provided by Mnemonic at this time. However, we are collecting this data as well. If your use case requires access to these rarely used types of transfers please reach out to us, we would love to learn more about your use case.