Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.chainstream.io/llms.txt

Use this file to discover all available pages before exploring further.

What Are Metrics?

Metrics are aggregation functions available as fields on Cube Record types. They let you compute statistics directly in your GraphQL query without post-processing. When you select a metric field alongside dimension fields, the query groups by the selected dimensions and computes the metric for each group. Supported metrics:
MetricSQL EquivalentDescription
countCOUNT(*) or COUNT(column)Count rows or distinct values
sumSUM(column)Sum of numeric values
avgAVG(column)Average of numeric values
minMIN(column)Minimum value
maxMAX(column)Maximum value
uniqCOUNT(DISTINCT column)Count of unique values

Metric Fields on Record Types

Metrics appear as top-level fields on each Cube’s Record type. Not all Cubes support all metrics — it depends on the Cube definition.
type DEXTradesRecord {
  # Dimension fields...
  Block { ... }
  Trade { ... }

  # Metric fields
  count: Int
  sum(of: DEXTradesSumOf!): Float
}
To use a metric, simply include it in your field selection:
query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      Trade { Dex { ProtocolName } }
    }
  }
}
This query groups DEX trades by protocol name and returns the count for each group.

The of Parameter

Metrics like sum, avg, min, max, and uniq require an of parameter that specifies which dimension to aggregate. The of value is an enum generated per Cube, following the dimension path naming convention.
sum(of: Trade_Buy_Amount)
avg(of: Trade_Buy_PriceInUSD)
min(of: Block_Time)
max(of: Trade_Sell_Amount)
uniq(of: Trade_Buy_Account_Owner)

Example: Total Buy Volume by DEX

query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      Trade { Dex { ProtocolName } }
    }
  }
}
Response:
{
  "data": {
    "Solana": {
      "DEXTrades": [
        {
          "count": 1842,
          "sum": 2847291.45,
          "Trade": { "Dex": { "ProtocolName": "Raydium" } }
        },
        {
          "count": 923,
          "sum": 1293847.12,
          "Trade": { "Dex": { "ProtocolName": "Orca" } }
        }
      ]
    }
  }
}

count — Counting Rows

count without an of parameter counts the total rows in each group (equivalent to COUNT(*)):
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
    }
  }
}
When used with dimension fields, it returns the count per group:
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      count
      Trade { Dex { ProtocolName } }
    }
  }
}

uniq — Count Distinct

uniq maps to COUNT(DISTINCT column) in SQL. Use it to count unique values of a dimension:
query {
  Solana {
    DEXTrades(
      tokenAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      where: { Block: { Time: { after: "2025-03-27T00:00:00Z" } } }
    ) {
      uniq(of: Trade_Buy_Account_Owner)
    }
  }
}
This returns the number of unique buyer wallets that traded USDC today on Solana.

selectWhere — HAVING-Style Filtering

selectWhere lets you filter on aggregation results, similar to SQL’s HAVING clause. It is applied after grouping and aggregation, allowing you to filter groups based on their metric values.
query {
  Solana {
    DEXTrades(
      where: { Block: { Time: { after: "2026-04-01T00:00:00Z" } } }
    ) {
      count(selectWhere: { gt: "100" })
      Trade { Dex { ProtocolName } }
    }
  }
}
This returns only DEX protocols that had more than 100 trades — protocols with fewer trades are excluded from the result.
selectWhere values must be passed as strings (e.g., "100" not 100). They are parsed as numeric values internally.
selectWhere supports these comparison operators:
OperatorDescription
gtGreater than
geGreater than or equal
ltLess than
leLess than or equal
eqEqual to
Known limitation: When using selectWhere, the orderBy field must reference a dimension that is part of the implicit GROUP BY (i.e., a field you are selecting) or an aggregation result. Ordering by a field not in the GROUP BY (e.g., Block_Time) will produce a database error.

Practical Example: Top Traders

Find the top 10 wallets by trade count for a token today, showing their total buy volume and unique trade count:
query TopTraders {
  Solana {
    DEXTrades(
      tokenAddress: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"
      where: {
        Block: { Time: { after: "2025-03-27T00:00:00Z" } }
      }
      orderBy: {descending: Block_Time}
      limit: { count: 10 }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      Trade {
        Buy {
          Account { Owner }
        }
      }
    }
  }
}
Response:
{
  "data": {
    "Solana": {
      "DEXTrades": [
        {
          "count": 47,
          "sum": 892341023.5,
          "Trade": {
            "Buy": {
              "Account": { "Owner": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU" }
            }
          }
        },
        {
          "count": 31,
          "sum": 451203847.2,
          "Trade": {
            "Buy": {
              "Account": { "Owner": "3kMq5RezM9XBbBGRNxP9vXkJHAfG8S7gn5WfBsHFQr7T" }
            }
          }
        }
      ]
    }
  }
}

Combining Multiple Metrics

You can select multiple metric fields in a single query:
query {
  Solana {
    DEXTrades(
      tokenAddress: "So11111111111111111111111111111111111111112"
      where: {
        Block: { Time: { after: "2025-03-27T00:00:00Z" } }
      }
    ) {
      count
      sum(of: Trade_Buy_Amount)
      min(of: Trade_Buy_PriceInUSD)
      max(of: Trade_Buy_PriceInUSD)
      uniq(of: Trade_Buy_Account_Owner)
      Trade { Dex { ProtocolName } }
    }
  }
}
This returns per-DEX statistics: trade count, total volume, price range, and unique traders — all in one query.

Metrics vs Pre-Aggregated Cubes

A common question: should I use metrics on DWD Cubes or query DWM/DWS Cubes directly?
ApproachWhen to UsePerformance
Metrics on DWD (e.g., DEXTrades.count)Custom aggregations, ad-hoc groupings, flexible time windowsSlower — aggregates raw events at query time
DWM Cubes (e.g., Pairs, Tokens)Standard time-series analysis, OHLC charts, volume over timeFast — reads pre-computed minute-level rollups
DWS Cubes (e.g., TokenHolders, WalletTokenPnL)Current snapshots, cumulative totals, leaderboardsFastest — reads pre-aggregated summary data
Rule of thumb: Use DWM/DWS Cubes when they cover your use case — they’re pre-aggregated and significantly faster. Fall back to DWD Cubes with metrics when you need custom groupings or aggregation logic that pre-built Cubes don’t support.

Decision Guide

Use Pairs (DWM). It already has pre-computed open/high/low/close/volume per minute. No need to aggregate DEXTrades yourself.
Use Tokens (DWM). It pre-aggregates trade counts, volumes, and unique traders per minute.
Use TokenHolders (DWS). Pre-computed latest balances per holder — much faster than aggregating BalanceUpdates.
Use DEXTrades with count + sum(of: Trade_Buy_Amount) grouped by Trade.Dex.ProtocolName. This isn’t available as a pre-built Cube, so DWD metrics are the right choice.
Use WalletTokenPnL (DWS). Pre-computed buy/sell volumes and trade counts per wallet-token pair.

Next Steps

Data Cubes

Explore all 25 Cubes and their field structures.

Query Examples

See real-world query examples with metrics and aggregation.