> ## Documentation Index
> Fetch the complete documentation index at: https://tokenterminal.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfers

> Cross-chain bridge transfers, with the sending and arriving sides matched.

One cross-chain transfer, one row. Sending a token from one chain to another leaves an event on the chain it departs from and another on the chain it arrives at, and `facts.bridge_transfers` pairs those two sides into that single row. Each side is called a leg. Where only one leg turns up, the row carries the leg it has and says why the other is missing. The table puts LayerZero v1 and v2, Circle CCTP v1 and v2, and Chainlink CCIP onto the same columns, matching each bridge's legs on identifiers of its own, with token amounts and USD values worked out once before publication.

A row is keyed on the onchain identity of whichever leg it is anchored on: the source leg for a matched pair or a departure with no arrival, the destination leg for an arrival with no departure.

## Columns

<table>
  <thead>
    <tr>
      <th width="280">Column</th>
      <th width="130">Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>block\_timestamp</code></td>
      <td><code>TIMESTAMP</code></td>
      <td>The column the table is partitioned on: the sending leg's time, or the arriving leg's where there is no sending leg.</td>
    </tr>

    <tr>
      <td><code>project\_id</code></td>
      <td><code>STRING</code></td>
      <td>Project operating the bridge.</td>
    </tr>

    <tr>
      <td><code>product\_id</code></td>
      <td><code>STRING</code></td>
      <td>Version of the bridge within that project.</td>
    </tr>

    <tr>
      <td><code>app\_id</code></td>
      <td><code>STRING</code></td>
      <td>Bridge app: <code>layerzero-v1</code>, <code>layerzero-v2</code>, <code>circle-cctp-v1</code>, <code>circle-cctp-v2</code> or <code>chainlink-ccip</code>.</td>
    </tr>

    <tr>
      <td><code>match\_key</code></td>
      <td><code>STRING</code></td>
      <td>The identifier the two legs were paired on, provided by the bridge.</td>
    </tr>

    <tr>
      <td><code>match\_method</code></td>
      <td><code>STRING</code></td>
      <td><code>exact</code> on matched rows, empty otherwise.</td>
    </tr>

    <tr>
      <td><code>latency\_seconds</code></td>
      <td><code>INT64</code></td>
      <td>How long the transfer took, in seconds. Empty on unmatched rows.</td>
    </tr>

    <tr>
      <td><code>unmatched\_reason</code></td>
      <td><code>STRING</code></td>
      <td>Why the other leg was not found. Empty on matched rows.</td>
    </tr>

    <tr>
      <td><code>intermediary\_address</code></td>
      <td><code>STRING</code></td>
      <td>Address of the relayer that carried the message. Empty on every row.</td>
    </tr>

    <tr>
      <td><code>source\_chain\_id</code></td>
      <td><code>STRING</code></td>
      <td>Chain the transfer left. Empty where no sending leg was found.</td>
    </tr>

    <tr>
      <td><code>source\_transaction\_hash</code></td>
      <td><code>STRING</code></td>
      <td>Transaction the sending leg was in.</td>
    </tr>

    <tr>
      <td><code>source\_log\_index</code></td>
      <td><code>INT64</code></td>
      <td>Position of the sending event within its block.</td>
    </tr>

    <tr>
      <td><code>source\_sub\_log\_index</code></td>
      <td><code>INT64</code></td>
      <td>Which token, within a sending event that moved more than one. Zero everywhere except CCIP sending legs.</td>
    </tr>

    <tr>
      <td><code>source\_block\_timestamp</code></td>
      <td><code>TIMESTAMP</code></td>
      <td>Time of the block the transfer left in.</td>
    </tr>

    <tr>
      <td><code>source\_block\_number</code></td>
      <td><code>INT64</code></td>
      <td>Number of that block.</td>
    </tr>

    <tr>
      <td><code>source\_sender\_address</code></td>
      <td><code>STRING</code></td>
      <td>Address that sent the transfer.</td>
    </tr>

    <tr>
      <td><code>source\_recipient\_address</code></td>
      <td><code>STRING</code></td>
      <td>Who the sending leg says should receive it.</td>
    </tr>

    <tr>
      <td><code>source\_token\_address</code></td>
      <td><code>STRING</code></td>
      <td>Token contract on the chain it left.</td>
    </tr>

    <tr>
      <td><code>source\_token\_id</code></td>
      <td><code>STRING</code></td>
      <td>That token's key in <code>dimensions.tokens</code>. Empty where we do not carry the token.</td>
    </tr>

    <tr>
      <td><code>source\_bridge\_contract\_address</code></td>
      <td><code>STRING</code></td>
      <td>Bridge contract the sending event came from.</td>
    </tr>

    <tr>
      <td><code>source\_amount\_raw</code></td>
      <td><code>BIGNUMERIC</code></td>
      <td>Amount leaving, in the token's smallest units, before decimals are applied.</td>
    </tr>

    <tr>
      <td><code>destination\_chain\_id</code></td>
      <td><code>STRING</code></td>
      <td>Chain the transfer arrived on. Empty where no arrival was found.</td>
    </tr>

    <tr>
      <td><code>destination\_transaction\_hash</code></td>
      <td><code>STRING</code></td>
      <td>Transaction the arriving leg was in.</td>
    </tr>

    <tr>
      <td><code>destination\_log\_index</code></td>
      <td><code>INT64</code></td>
      <td>Position of the arriving event within its block.</td>
    </tr>

    <tr>
      <td><code>destination\_block\_timestamp</code></td>
      <td><code>TIMESTAMP</code></td>
      <td>Time of the block the transfer arrived in.</td>
    </tr>

    <tr>
      <td><code>destination\_block\_number</code></td>
      <td><code>INT64</code></td>
      <td>Number of that block.</td>
    </tr>

    <tr>
      <td><code>destination\_sender\_address</code></td>
      <td><code>STRING</code></td>
      <td>Sender named on the arriving leg.</td>
    </tr>

    <tr>
      <td><code>destination\_recipient\_address</code></td>
      <td><code>STRING</code></td>
      <td>Address that received the transfer.</td>
    </tr>

    <tr>
      <td><code>destination\_token\_address</code></td>
      <td><code>STRING</code></td>
      <td>Token contract on the chain it arrived on.</td>
    </tr>

    <tr>
      <td><code>destination\_token\_id</code></td>
      <td><code>STRING</code></td>
      <td>That token's key in <code>dimensions.tokens</code>. Empty where we do not carry the token.</td>
    </tr>

    <tr>
      <td><code>destination\_bridge\_contract\_address</code></td>
      <td><code>STRING</code></td>
      <td>Bridge contract the arriving event came from.</td>
    </tr>

    <tr>
      <td><code>destination\_amount\_raw</code></td>
      <td><code>BIGNUMERIC</code></td>
      <td>Amount arriving, in the token's smallest units, before decimals are applied.</td>
    </tr>

    <tr>
      <td><code>source\_transfer\_amount</code></td>
      <td><code>FLOAT64</code></td>
      <td>Amount leaving, in whole token units.</td>
    </tr>

    <tr>
      <td><code>source\_transfer\_amount\_usd</code></td>
      <td><code>FLOAT64</code></td>
      <td>Amount leaving, in USD.</td>
    </tr>

    <tr>
      <td><code>destination\_transfer\_amount</code></td>
      <td><code>FLOAT64</code></td>
      <td>Amount arriving, in whole token units, with the fallback described above.</td>
    </tr>

    <tr>
      <td><code>destination\_transfer\_amount\_usd</code></td>
      <td><code>FLOAT64</code></td>
      <td>Amount arriving, in USD, with the same fallback.</td>
    </tr>

    <tr>
      <td><code>transfer\_amount</code></td>
      <td><code>FLOAT64</code></td>
      <td>The transfer counted once, in whole token units: the sending side's amount, or the arriving side's where there is no sending leg.</td>
    </tr>

    <tr>
      <td><code>transfer\_amount\_usd</code></td>
      <td><code>FLOAT64</code></td>
      <td>The transfer counted once, in USD. Total volume by summing this column, never by adding the two directional columns.</td>
    </tr>
  </tbody>
</table>

## Sample queries

<Warning>
  The table is partitioned on `block_timestamp`. Put a filter on that column in every query. Leave it out and the query reads every day we hold.
</Warning>

<Tabs>
  <Tab title="Daily volume by app">
    **Total daily bridged USD volume per app, counting each transfer once.** `transfer_amount_usd` takes the sending side's value, or the arriving side's where there is no sending leg, so every transfer contributes exactly once.

    ```sql theme={null}
    select
        timestamp_trunc(block_timestamp, day) as day,
        app_id,
        sum(transfer_amount_usd) as volume_usd,
        count(*) as transfers
    from `facts.bridge_transfers`
    where block_timestamp >= timestamp('2026-08-01')
    group by day, app_id
    order by day, volume_usd desc
    ```
  </Tab>

  <Tab title="Routes by volume">
    **Rank routes by volume over one week.** Filtering on `unmatched_reason is null` keeps matched pairs, where both chains are known.

    ```sql theme={null}
    select
        source_chain_id,
        destination_chain_id,
        sum(transfer_amount_usd) as volume_usd,
        count(*) as transfers
    from `facts.bridge_transfers`
    where block_timestamp >= timestamp('2026-08-01')
      and block_timestamp < timestamp('2026-08-08')
      and unmatched_reason is null
    group by source_chain_id, destination_chain_id
    order by volume_usd desc
    limit 20
    ```
  </Tab>

  <Tab title="Unmatched breakdown">
    **Break the unmatched rows down by what went wrong.**

    ```sql theme={null}
    select
        app_id,
        unmatched_reason,
        count(*) as legs
    from `facts.bridge_transfers`
    where block_timestamp >= timestamp('2026-08-01')
      and unmatched_reason is not null
    group by app_id, unmatched_reason
    order by legs desc
    ```
  </Tab>
</Tabs>

## Notes

Every transfer has a sending side and an arriving side, and this table values both. `source_transfer_amount_usd` is the money leaving. `destination_transfer_amount_usd` is that same money arriving. Add the two columns together and you have counted the same money twice.

To total volume, use `transfer_amount_usd`, or `transfer_amount` for token units. Each takes the sending side's value, falling back to the arriving side's where there is no sending leg, so a transfer counts exactly once however many legs were found.

Use the two directional columns only when you want to credit volume to the chain it left or the chain it reached.

`destination_transfer_amount` and `destination_transfer_amount_usd` describe the money arriving. Each carries the arriving leg's own amount where the event states one. Some bridges do not state it, CCIP's arrival event among them, so where the row is matched but the arriving event names no amount, these columns fall back to the sending side's value. Where no arriving leg was found at all, both stay empty.

The legs are paired on `match_key`, an identifier each bridge provides of its own, such as LayerZero's GUID or CCIP's message id, with the arriving leg required to fall within a window around the sending leg's time. `match_method` reads `exact` on a matched row and is empty otherwise. `latency_seconds` is how long the transfer took: the arriving block's time minus the sending block's. `block_timestamp`, the column the table is partitioned on, is the sending leg's time, or the arriving leg's where there is no sending leg.

`unmatched_reason` is empty on a matched row and says what went wrong on every other one. A row with a sending leg but no arrival reads `source_key_missing`, `destination_chain_not_indexed`, `destination_leg_not_decoded` or `destination_pending_or_failed`. A row with an arrival but no sending leg has the mirror image: `destination_key_missing`, `source_chain_not_indexed`, `source_leg_not_decoded` or `source_leg_missing`.
