> ## 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.

> ## Agent Instructions
> To query the Token Terminal data catalog, read https://tokenterminal.com/docs/catalog/agents-manual.md first. It is the whole catalog as one page: table naming grammar, key columns, partition and cluster rules, units, additivity, and the tables that are documented but not served yet.
> Never query a catalog table on a time bound alone. Also filter its cluster key, which you read from INFORMATION_SCHEMA.COLUMNS; an empty result means the object is a view, whose pruning contract is on its page. Compute is billed to the caller's own Google Cloud project.

# Tokens

> Solana token transfers, holder balances and daily prices.

Four surfaces cover token activity on Solana.

* [`facts_tokens.transfers`](/docs/catalog/tokens/transfers): one row per token transfer.
* [`functions.calculate_latest_token_balances`](/docs/catalog/tokens/balances) and its historical sibling: holder balances at any date, computed at query time.
* [`metrics_tokens.price_daily`](/docs/catalog/tokens/metrics): one USD price per token per day.

Solana's token standard is SPL, so `token_type = 'spl'` and `token_address` is the mint address in base58, matched exactly as the chain writes it. `from_address` and `to_address` are token accounts rather than wallets, since one wallet can own several accounts in the same mint; `to_owner` is the wallet behind the credited one.

The token list is everything we see onchain, which reaches far past the tokens anyone would want: a ranking of the busiest tokens returns mints whose symbols imitate well-known ones. Nothing has gone wrong when those appear. Joining `dimensions.asset_tokens` keeps the tokens tied to a named asset with a named issuer; [Assets](/docs/catalog/chain-verticals/solana/assets) covers that layer.

Every column and both function signatures are documented at [Tokens](/docs/catalog/tokens/index).

## Sample queries

<Warning>
  `facts_tokens.transfers` is large and split by month. Bound `partition_key` and filter `chain_id` on every query, or you read the whole table and the whole table is billed to you.
</Warning>

<Tabs>
  <Tab title="Daily activity">
    **Count the transfers and the accounts behind them on one day.** A distinct-account count holds for the window it was asked for, and summing several days counts anyone active on more than one of them twice; [Assets ▸ Senders](/docs/catalog/assets/senders) covers the longer windows.

    ```sql theme={null}
    select
        count(*) as transfers,
        count(distinct from_address) as senders,
        count(distinct to_address) as recipients
    from `facts_tokens.transfers`
    where partition_key >= timestamp('2026-08-01')
      and partition_key < timestamp('2026-09-01')
      and block_timestamp >= timestamp('2026-08-20')
      and block_timestamp < timestamp('2026-08-21')
      and chain_id = 'solana'
      and token_type = 'spl'
    ```
  </Tab>

  <Tab title="Busiest tokens">
    **Rank the busiest Solana mints on one day.** Joining `dimensions.tokens` on `token_id` adds the symbol and name of each mint.

    ```sql theme={null}
    select
        tokens.symbol,
        transfers.token_address,
        count(*) as transfer_count,
        count(distinct transfers.from_address) as senders
    from `facts_tokens.transfers` as transfers
    join `dimensions.tokens` as tokens
        using (token_id)
    where transfers.partition_key >= timestamp('2026-08-01')
      and transfers.partition_key < timestamp('2026-09-01')
      and transfers.block_timestamp >= timestamp('2026-08-20')
      and transfers.block_timestamp < timestamp('2026-08-21')
      and transfers.chain_id = 'solana'
    group by tokens.symbol, transfers.token_address
    order by transfer_count desc
    limit 20
    ```
  </Tab>

  <Tab title="Net balance moves">
    **Find the wallets that gained and lost the most of one mint on one day.** Each movement is unnested into its two legs -- the sender debited, the receiver credited -- and summing them per wallet nets the day; dividing by `10^decimals` converts to token amounts. The credit leg reads `to_owner` so the answer lands on wallets rather than on their token accounts.

    ```sql theme={null}
    select
        leg.account_address,
        sum(leg.balance_change_raw) / pow(10, tokens.decimals) as net_change
    from `facts_tokens.transfers` as movements
    cross join unnest([
        struct(movements.from_address as account_address,
               -cast(movements.amount_raw as bignumeric) as balance_change_raw),
        struct(coalesce(movements.to_owner, movements.to_address) as account_address,
                cast(movements.amount_raw as bignumeric) as balance_change_raw)
    ]) as leg
    join `dimensions.tokens` as tokens
        on tokens.token_id = movements.token_id
    where movements.partition_key >= timestamp('2026-08-01')
      and movements.partition_key < timestamp('2026-09-01')
      and movements.block_timestamp >= timestamp('2026-08-20')
      and movements.block_timestamp < timestamp('2026-08-21')
      and movements.chain_id = 'solana'
      and movements.token_address = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
      and leg.account_address is not null
    group by leg.account_address, tokens.decimals
    order by net_change desc
    limit 20
    ```
  </Tab>

  <Tab title="Current holders">
    **List the largest current holders of one Solana mint.** An account here is an SPL token account rather than the wallet that owns it, so a wallet spread across several accounts appears once per account; the returned `balance` is already converted out of raw units. Each call adds up the token's whole movement history, so a one-off holder set costs a full scan; [Assets ▸ Metrics](/docs/catalog/assets/metrics) holds a daily supply and holder-count series for curated assets.

    ```sql theme={null}
    select
        account_address,
        balance
    from `functions.calculate_latest_token_balances`(
        'solana',
        'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
        'spl'
    )
    order by balance desc
    limit 20
    ```
  </Tab>

  <Tab title="Balance history">
    **Trace the largest holding account's balance over time.** Taking the account from the latest-balances function keeps the query runnable unchanged; the historical function returns a row only on the days the balance moved, so the balance on any other date is the latest row at or before it. The function replays the same history, so it costs a full scan of the token as well.

    ```sql theme={null}
    with largest as (
        select account_address
        from `functions.calculate_latest_token_balances`(
            'solana',
            'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            'spl'
        )
        order by balance desc
        limit 1
    )

    select
        history.balance_date,
        history.balance
    from `functions.calculate_historical_eod_token_balances`(
        'solana',
        'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
        'spl'
    ) as history
    join largest
        using (account_address)
    order by history.balance_date desc
    limit 20
    ```
  </Tab>

  <Tab title="Daily price">
    **Read one Solana mint's daily price.** Each row is the midnight UTC price in USD, and `token_id` is the mint address and the chain joined with a dash.

    ```sql theme={null}
    select
        prices.timestamp,
        tokens.symbol,
        prices.price
    from `metrics_tokens.price_daily` as prices
    join `dimensions.tokens` as tokens
        using (token_id)
    where prices.timestamp >= timestamp('2026-08-16')
      and prices.timestamp < timestamp('2026-08-23')
      and prices.token_id = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v-solana'
    order by prices.timestamp
    ```
  </Tab>
</Tabs>
