Skip to main content

GoldRush Streaming on Solana

Critical Rules

  1. GraphQL WebSocket URL: wss://streaming.goldrushdata.com/graphql.
  2. Chain enum: SOLANA_MAINNET (SCREAMING_SNAKE_CASE) - not solana-mainnet (that is the Foundational REST chain name).
  3. newPairs on SOLANA_MAINNET covers Raydium AMM/CLMM, Orca Whirlpool, Meteora DLMM/Dynamic, Jupiter, and PumpFun on one subscription. Filter client-side on the exchange field.
  4. PumpFun launches surface as exchange: pumpfun. Raydium graduations surface seconds later as exchange: raydium-amm with the same base_token.address.
  5. ohlcvCandlesForPair takes pool addresses (base58); ohlcvCandlesForToken takes SPL mints and aggregates across every pool carrying that token.
  6. walletTxs accepts thousands of wallet addresses per subscription on one WebSocket.
  7. No rate limits on Streaming for Solana; no per-IP subscription cap.

Common Recipes

GoalSubscriptionFilter / Args
New-pool firehose across every Solana DEXnewPairschain_name: SOLANA_MAINNET
PumpFun launches onlynewPairs → filter exchange === "pumpfun"client-side
Graduation detectionnewPairs → track (base_token.address, exchange) pairsclient-side
Live OHLCV for a specific poolohlcvCandlesForPairpair_addresses: ["<pool-pubkey>"]
Aggregated price for an SPL mintohlcvCandlesForTokentoken_addresses: ["<mint>"]
Wallet activity firehosewalletTxschain_name: SOLANA_MAINNET, wallet_addresses: [...]

Discovering new Solana DEX pools used to mean polling per-protocol indexers or scanning every block for InitializePool instructions. GoldRush’s newDexPairs subscription delivers every new pool the moment liquidity is added - across Raydium, Orca, Meteora, Jupiter, and PumpFun - on one WebSocket.

What you get

  • Cross-protocol coverage in one stream - no per-DEX fan-out.
  • Pre-decoded pool metadata - exchange, pair address, base/quote mints, decimals, initial liquidity in USD.
  • Sub-second latency from validator-peered Solana ingestion.
  • Zero rate limits - subscribe permanently, no polling.

Subscribe

GoldRush SDK
import { GoldRushClient, StreamingChain } from "@covalenthq/client-sdk";

const client = new GoldRushClient(process.env.GOLDRUSH_API_KEY);

client.StreamingService.subscribeToNewPairs(
  { chain_name: StreamingChain.SOLANA_MAINNET },
  {
    next: (data) => {
      const pair = data.newPairs;
      console.log(
        `[${pair.exchange}] ${pair.base_token.symbol}/${pair.quote_token.symbol}`,
        `pool=${pair.pair_address}`,
        `liquidity=$${pair.initial_liquidity_usd}`
      );
    },
    error: (err) => console.error(err),
    complete: () => console.log("done"),
  }
);
GraphQL Subscription
subscription {
  newPairs(chain_name: SOLANA_MAINNET) {
    block_signed_at
    exchange
    pair_address
    base_token {
      address
      symbol
      decimals
    }
    quote_token {
      address
      symbol
      decimals
    }
    initial_liquidity_usd
  }
}
Python
import asyncio
import os
from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport

WS_URL = "wss://streaming.goldrushdata.com/graphql"

SUBSCRIPTION = gql("""
subscription {
  newPairs(chain_name: SOLANA_MAINNET) {
    block_signed_at
    exchange
    pair_address
    base_token { address symbol decimals }
    quote_token { address symbol decimals }
    initial_liquidity_usd
  }
}
""")

async def main():
    transport = WebsocketsTransport(
        url=WS_URL,
        init_payload={"GOLDRUSH_API_KEY": os.environ["GOLDRUSH_API_KEY"]},
    )
    async with Client(transport=transport, fetch_schema_from_transport=False) as session:
        async for result in session.subscribe(SUBSCRIPTION):
            print(result["newPairs"])

asyncio.run(main())
Connect via WebSocket at wss://streaming.goldrushdata.com/graphql with your API key in the connection parameters.

Protocols covered

The exchange field on each new pair takes one of:
ProtocolCommon pool shape
raydium-ammConstant-product AMM (CPAMM) pools.
raydium-clmmConcentrated-liquidity Raydium pools.
orca-whirlpoolOrca’s concentrated-liquidity pools.
meteora-dlmmMeteora dynamic-liquidity (binned) pools.
meteora-dynamicMeteora dynamic-fee constant-product pools.
jupiterJupiter aggregator-routed pools surfaced as venues.
pumpfunPumpFun bonding-curve pools and post-graduation Raydium pools.
Use the PumpFun launchpad recipe for the narrower PumpFun-only stream when you only care about the launchpad.

Patterns

”New pools” tab

Stream the firehose, dedupe by pair_address, drop pools with initial_liquidity_usd < $X to filter spam, and display chronologically. Subscribe to ohlcvCandlesForPair on the pools you want to chart.

First-mover sniping

The moment a pool appears in newDexPairs, kick off:
  1. A get-pool-spot-prices call to confirm the price.
  2. A get-token-balances-for-address for your trading wallet to confirm size.
  3. A trade against the new pool with a recommended priority fee from p90.

Honeypot screening

Combine the firehose with walletTxs pinned to the new pool’s creator wallet. If the creator removes liquidity within minutes, flag the pool as a likely rug.

Cross-DEX arbitrage discovery

The same base/quote pair can launch on Raydium and Orca within minutes. Group new pools by (base_token.address, quote_token.address) to discover venues for arb routing.

Production considerations

The happy path is enough to prototype. Three things to add before shipping:
  • Reconnect on close or error. Wrap the subscribe call in a function and re-invoke on error / complete with a 1-2s backoff.
  • Dedupe by pair_address. During brief reconnects you may see a recently-emitted pool twice; deduplicate on pair_address.
  • No backfill on reconnect. The subscription delivers events from the moment it opens. For continuity across a gap, the warehouse swaps entity has every pool genesis recorded.
  • PumpFun launchpad - the PumpFun-only narrower stream.
  • OHLCV markets - real-time candles for pools you discover.
  • Pricing on Solana - spot prices + price resolution path.
  • New DEX Pairs Stream reference - full subscription schema.

PumpFun launches thousands of tokens per day on Solana. Each token starts on a bonding curve, accumulates swaps until graduation, then migrates to Raydium. GoldRush streams every step in real time via the DEX firehose, with decoded swap and graduation events delivered alongside Raydium / Orca / Meteora pools on the same subscription. For warehouse-delivered PumpFun analytics (graduations, top swappers, virtual-reserve history), see the Pipeline guide.

What you get

  • Token creates - every new bonding-curve token the moment it’s launched, with mint, creator, and initial bonding-curve parameters.
  • Swaps - buy and sell on the bonding curve with virtual SOL / virtual Token reserves so you can reconstruct the curve.
  • Graduations - the moment a token’s bonding curve completes and it migrates to Raydium.
  • Post-graduation Raydium pool - the new Raydium pool surfaces in the same firehose with exchange: raydium-amm and the same base mint.

Real-time stream

PumpFun pools surface in the same DEX firehose subscription with exchange: pumpfun. Filter client-side on exchange === "pumpfun" to isolate PumpFun launches.
GoldRush SDK
import { GoldRushClient, StreamingChain } from "@covalenthq/client-sdk";

const client = new GoldRushClient(process.env.GOLDRUSH_API_KEY);

client.StreamingService.subscribeToNewPairs(
  { chain_name: StreamingChain.SOLANA_MAINNET },
  {
    next: (data) => {
      const pair = data.newPairs;
      if (pair.exchange !== "pumpfun") return;
      console.log(
        `pump.fun mint=${pair.base_token.address}`,
        `liquidity=$${pair.initial_liquidity_usd}`,
        `at=${pair.block_signed_at}`
      );
    },
    error: (err) => console.error(err),
    complete: () => console.log("done"),
  }
);
GraphQL Subscription
subscription {
  newPairs(chain_name: SOLANA_MAINNET) {
    block_signed_at
    exchange
    pair_address
    base_token { address symbol decimals }
    quote_token { address symbol decimals }
    initial_liquidity_usd
  }
}
For tick-by-tick swaps on a specific PumpFun token, subscribe to ohlcvCandlesForPair with the bonding-curve pair address you got from newDexPairs.

Graduation watch

Tokens that complete the bonding curve migrate to Raydium. The migration emits two events in the firehose within seconds of each other:
  1. A PumpFun completion (visible in the warehouse sol_pf_complete table - not currently surfaced as a typed streaming event).
  2. A new Raydium pool with exchange: raydium-amm and the same base mint as the PumpFun token.
To detect graduations in the streaming layer, watch for exchange: raydium-amm events whose base_token.address you previously saw in a exchange: pumpfun event.
const pumpFunMints = new Set();

client.StreamingService.subscribeToNewPairs(
  { chain_name: StreamingChain.SOLANA_MAINNET },
  {
    next: (data) => {
      const pair = data.newPairs;
      if (pair.exchange === "pumpfun") {
        pumpFunMints.add(pair.base_token.address);
      } else if (
        pair.exchange === "raydium-amm" &&
        pumpFunMints.has(pair.base_token.address)
      ) {
        console.log(`🎓 GRADUATED: ${pair.base_token.address}${pair.pair_address}`);
      }
    },
    error: (err) => console.error(err),
  }
);

Patterns

Live PumpFun screener

Filter the firehose to exchange: pumpfun, sort by block_signed_at, and display a chronological feed. For each row, subscribe to ohlcvCandlesForPair on the bonding curve to render a live sparkline.

Sniper bot

The moment a PumpFun pool appears in newDexPairs:
  1. Apply your honeypot filter (deployer reputation, social signal, prior rugs).
  2. Look up get-pool-spot-prices for the bonding-curve pair address.
  3. Submit a buy with a p90 priority fee.

Backtesting & analytics

Real-time is for execution; backtests need history. The Pipeline normalizer lands every PumpFun create, swap, complete, and withdraw into your warehouse (sol_pf_create, sol_pf_swap, sol_pf_complete, sol_pf_withdraw). See Track PumpFun Swaps on Solana for the SQL recipes.

Reading the bonding curve

PumpFun’s price curve is a function of virtual SOL and virtual Token reserves. The Pipeline sol_pf_swap table exposes both per-swap so you can reconstruct price and slippage exactly:
SELECT
  block_time,
  direction,
  sol_amount,
  token_amount,
  virtual_sol_reserves,
  virtual_token_reserves,
  virtual_sol_reserves / virtual_token_reserves AS implied_price_sol_per_token
FROM sol_pf_swap
WHERE mint = ''
ORDER BY block_time;
The streaming layer doesn’t expose the virtual reserves directly today; for that depth, pair the live stream with the warehouse table.

Production considerations

  • Reconnect on close or error. Wrap subscribe in a function and re-invoke with 1-2s backoff.
  • Spam pools. PumpFun emits high volume; if you only care about pools that get past their first 30 seconds, hold each new pool in a buffer and only act once it accumulates N swaps.
  • Mint reuse on graduation. The same SPL mint appears in both the PumpFun pool and the Raydium pool post-graduation - track (mint, exchange) as your key, not mint alone.
  • DEX firehose - parent stream covering all Solana DEX protocols.
  • Track PumpFun Swaps (Pipeline) - warehouse-delivered PumpFun lifecycle events.
  • OHLCV markets - tick-level candles for any PumpFun bonding curve.
  • Pricing on Solana - spot prices for pools after they exist.

The walletTxs subscription delivers every signed transaction for the wallets you subscribe to, in real time, with decoded details for common transaction types (SPL transfers, DEX swaps, native SOL movements). One WebSocket can carry thousands of wallets.

What you get

  • Many wallets, one connection. Pass an array of base58 wallet addresses; updates for any of them arrive on the same stream.
  • Pre-decoded events. SPL transfers (Token + Token-2022) and DEX swaps come through as typed GraphQL union members - no parsing.
  • Sub-second latency. Tokyo-colocated, validator-peered Solana ingestion.
  • No rate limits.

Subscribe

GoldRush SDK
import { GoldRushClient, StreamingChain } from "@covalenthq/client-sdk";

const client = new GoldRushClient(process.env.GOLDRUSH_API_KEY);

const SUBSCRIPTION_QUERY = `
  subscription {
    walletTxs(
      wallet_addresses: [
        "4ZJhPQAgUseCsWhKvJLTmmRRUV74fdoTpQLNfKoekbPY",
        "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
      ]
      chain_name: SOLANA_MAINNET
    ) {
      tx_hash
      block_signed_at
      from_address
      successful
      decoded_details {
        ... on SwapTransaction {
          protocol_name
          pool_address
          base_token { address symbol }
          quote_token { address symbol }
          base_amount
          quote_amount
          price_usd
        }
        ... on TransferTransaction {
          token_address
          from_address
          to_address
          amount
          token_symbol
        }
      }
    }
  }
`;

client.StreamingService.rawQuery(
  SUBSCRIPTION_QUERY,
  {},
  {
    next: (data) => console.log(JSON.stringify(data, null, 2)),
    error: (err) => console.error(err),
    complete: () => console.log("done"),
  }
);
Python
import asyncio
import os
from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport

WS_URL = "wss://streaming.goldrushdata.com/graphql"

SUBSCRIPTION = gql("""
subscription {
  walletTxs(
    wallet_addresses: [
      "4ZJhPQAgUseCsWhKvJLTmmRRUV74fdoTpQLNfKoekbPY"
    ]
    chain_name: SOLANA_MAINNET
  ) {
    tx_hash
    block_signed_at
    successful
    decoded_details {
      ... on SwapTransaction {
        protocol_name pool_address
        base_token { address symbol }
        quote_token { address symbol }
        base_amount quote_amount price_usd
      }
      ... on TransferTransaction {
        token_address from_address to_address amount token_symbol
      }
    }
  }
}
""")

async def main():
    transport = WebsocketsTransport(
        url=WS_URL,
        init_payload={"GOLDRUSH_API_KEY": os.environ["GOLDRUSH_API_KEY"]},
    )
    async with Client(transport=transport, fetch_schema_from_transport=False) as session:
        async for result in session.subscribe(SUBSCRIPTION):
            print(result)

asyncio.run(main())
The chain enum is SOLANA_MAINNET (SCREAMING_SNAKE_CASE), not solana-mainnet. The Streaming API uses the enum; the Foundational REST API uses the kebab-case form.

Patterns

Copy-trading

Subscribe to a curated list of high-performing wallets. Filter the stream on SwapTransaction, then mirror (pool_address, base_amount, quote_amount) to your own execution layer with a p90 priority fee.

Whale watch

Subscribe to top SPL holders (from get-token-holders-as-of-any-block-height-v2) and surface every fill and large transfer in a live tape.

Compliance / AML alerts

Subscribe to flagged addresses. Filter on TransferTransaction events and alert when amount exceeds a threshold.

Push vs pull

Use get-paginated-transactions-for-address-v3 for historical backfill and walletTxs here for the tip. The two share enough field shape that you can normalize into one internal type after fetching.

Production considerations

  • Reconnect on close or error. Wrap subscribe in a function; re-invoke with 1-2s backoff on error / complete.
  • Validate addresses up front. Malformed base58 can tear down the whole subscription. Pre-filter with a base58 validator before passing in.
  • No gap-fill on reconnect. Events that occurred during a disconnect are not replayed. For continuity across the gap, query get-paginated-transactions-for-address-v3 over the disconnect window.

Scaling

ScaleApproach
Up to ~1,000 walletsOne subscription with the full address list.
1,000 - 10,000+ walletsShard across multiple subscriptions on the same connection. The SDK reuses one WebSocket.
Live cohort changesUnsubscribe and resubscribe with the new address list - no need to tear down the connection.
  • Wallet endpoints - REST companion for historical balances and transfers.
  • Transactions on Solana - REST companion for paginated history.
  • Wallet Activity Stream reference - full subscription schema.

GoldRush exposes two OHLCV streams on SOLANA_MAINNET:
  • ohlcvCandlesForPair - candles for a specific pool address (Raydium AMM, Orca Whirlpool, Meteora DLMM / Dynamic).
  • ohlcvCandlesForToken - candles aggregated across every pool carrying that SPL mint.
Both deliver candles in real time over WebSocket as new swaps land in the pool.

Stream candles for a specific pool

Best for “chart this exact pool” UIs (the one your bot trades on, the deepest USDC pool, etc.).
GoldRush SDK
import {
  GoldRushClient,
  StreamingChain,
  StreamingInterval,
  StreamingTimeframe,
} from "@covalenthq/client-sdk";

const client = new GoldRushClient(process.env.GOLDRUSH_API_KEY);

client.StreamingService.subscribeToOHLCVPairs(
  {
    chain_name: StreamingChain.SOLANA_MAINNET,
    pair_addresses: ["58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2"],
    interval: StreamingInterval.ONE_MINUTE,
    timeframe: StreamingTimeframe.ONE_HOUR,
  },
  {
    next: (data) => console.log("OHLCV:", data),
    error: (err) => console.error(err),
    complete: () => console.log("done"),
  }
);
GraphQL Subscription
subscription {
  ohlcvCandlesForPair(
    chain_name: SOLANA_MAINNET
    pair_addresses: ["58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2"]
    interval: ONE_MINUTE
    timeframe: ONE_HOUR
  ) {
    pair_address
    timestamp
    open
    high
    low
    close
    volume_usd
  }
}
pair_addresses is an array of base58 pool addresses. You can subscribe to multiple pools on one subscription - useful for arbitrage monitoring across the same pair on Raydium and Orca.

Stream candles for a token (across all pools)

Best for “what’s the canonical USD price of TOKEN” - GoldRush aggregates across every pool carrying that mint, weighted by liquidity.
client.StreamingService.subscribeToOHLCVTokens(
  {
    chain_name: StreamingChain.SOLANA_MAINNET,
    token_addresses: ["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"], // USDC
    interval: StreamingInterval.ONE_MINUTE,
    timeframe: StreamingTimeframe.ONE_HOUR,
  },
  {
    next: (data) => console.log("Token OHLCV:", data),
    error: (err) => console.error(err),
  }
);
The aggregation includes Raydium AMM/CLMM, Orca Whirlpool, Meteora DLMM/Dynamic, and PumpFun bonding curves where applicable.

When to use pairs vs tokens

Use caseStream
Chart a single Raydium poolohlcvCandlesForPair with the pool address
Track arbitrage between Raydium and Orca for the same pairohlcvCandlesForPair with both pool addresses
Get “the” USD price of a tokenohlcvCandlesForToken
Aggregate across all PumpFun + Raydium pools for a graduated tokenohlcvCandlesForToken

Patterns

TradingView chart

Subscribe to ohlcvCandlesForPair at the user’s selected interval; push candles into TradingView’s UDF datafeed adapter. The timestamp / open / high / low / close / volume_usd fields map 1:1 to UDF.

Discovery → chart

Pair this stream with the DEX firehose: when a new pool appears in newDexPairs, automatically subscribe to its OHLCV. Limit to pools above an initial_liquidity_usd threshold to avoid spam.

Pricing service

Use ohlcvCandlesForToken on 1d candles for the canonical USD price; pair with the REST get-historical-token-prices for backfill.
  • Pricing on Solana - REST companion for historical and spot prices.
  • DEX firehose - discover new pools to subscribe to.
  • OHLCV Pairs Stream reference - full subscription schema.
  • OHLCV Tokens Stream reference - full subscription schema.

Note: Coming soon. A dedicated NFT-mint subscription for Solana is on the roadmap. Until it ships, the patterns below cover what you can build today with the existing wallet and warehouse surfaces.

What it will deliver

When the subscription ships, it will surface every Metaplex Token Metadata mint and every Bubblegum cNFT mint in real time, with:
  • The new NFT’s mint pubkey (Metaplex) or Merkle leaf index (cNFT).
  • The collection mint or tree address.
  • The initial owner (wallet that received the mint).
  • The metadata URI and parsed JSON (name, image, attributes).
  • A compressed flag to disambiguate.

What you can do today

Track mints via the wallet activity stream

Subscribe to walletTxs on the collection creator’s wallet or on the candy machine / Bubblegum tree authority. Filter on TransferTransaction events where the source is the mint authority and the destination is a new wallet.
client.StreamingService.rawQuery(
  `subscription {
    walletTxs(
      wallet_addresses: [""]
      chain_name: SOLANA_MAINNET
    ) {
      tx_hash
      block_signed_at
      decoded_details {
        ... on TransferTransaction {
          token_address from_address to_address amount
        }
      }
    }
  }`,
  {},
  { next: console.log, error: console.error }
);
See Wallet activity for the full pattern.

Backfill mints via the warehouse

The Pipeline transfers normalizer captures every SPL transfer including initial mints. Filter on source_owner = '' and is_raw_amount = true, amount = 1 to isolate NFT mints from a specific candy machine or tree.
SELECT block_time, mint, destination_owner, source_owner
FROM transfers
WHERE source_owner = ''
  AND amount = 1
  AND is_raw_amount = true
ORDER BY block_time DESC
LIMIT 100;

Ownership lookup

Once you’ve spotted a mint, use check-ownership-in-nft-collection or check-ownership-in-nft-collection-token for gating logic.
  • NFTs on Solana (Foundational) - REST endpoints for Metaplex + cNFT lookups today.
  • Wallet activity - live transfer firehose, including mint transfers.
  • Roadmap - tracking the dedicated NFT-mint stream.

Warehouse Recipes

The swaps normalizer streams every decoded Solana DEX trade into your warehouse. One table covers Raydium AMM/CLMM, Orca Whirlpool, Meteora DLMM/Dynamic, Jupiter, and PumpFun bonding-curve trades - with unified fields across protocols so you don’t write per-DEX adapters.

Why warehouse delivery

Streaming and REST are great for live execution and lookups, but every retention cohort, churn model, token analytics dashboard, and tax export lives in your data warehouse - not in HTTP responses. The Pipeline API streams Solana data continuously into customer-managed destinations with no ETL on your side.

Pipeline configuration

Create a pipeline In the GoldRush Platform, navigate to Manage Pipelines and click Create Pipeline. Name it solana-swaps. Pick Solana + Swaps Choose Solana as the chain and Swaps as the data type. Configure your destination Connect ClickHouse, BigQuery, Postgres, Kafka, S3/GCS/R2, SQS, or a Webhook. ClickHouse is recommended for high-volume analytical queries over swaps.
destination:
  type: "clickhouse"
  url: "https://your-cluster.clickhouse.cloud:8443"
  user: "${CH_USER}"
  password: "${CH_PASSWORD}"
  database: "solana_swaps"
  batch_size: 5000
Optional: SQL transform Filter or reshape rows before they land. Example: keep only swaps over $100 of volume.
transforms:
  swaps: >
    SELECT *
    FROM swaps
    WHERE volume_usd >= 100
Deploy Decoded swaps begin flowing within seconds.

Schema

The swaps table is documented in full on the Solana Normalizers page. Key columns:
  • block_slot, block_time, tx_id (signature), signer.
  • pool_address, base_mint, quote_mint, base_vault, quote_vault.
  • base_amount, quote_amount, price, price_usd, volume_usd.
  • protocol_name - raydium-amm, raydium-clmm, orca-whirlpool, meteora-dlmm, meteora-dynamic, jupiter, pumpfun.
  • outer_program, inner_program, instruction_type for CPI attribution.

Sample analytical queries

Top tokens by 24h volume

SELECT
  base_mint,
  SUM(volume_usd) AS volume_24h
FROM swaps
WHERE block_time >= now() - INTERVAL 1 DAY
GROUP BY base_mint
ORDER BY volume_24h DESC
LIMIT 50;

Per-protocol market share by token

SELECT
  protocol_name,
  SUM(volume_usd) AS volume_24h,
  COUNT(*) AS swap_count
FROM swaps
WHERE base_mint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
  AND block_time >= now() - INTERVAL 1 DAY
GROUP BY protocol_name
ORDER BY volume_24h DESC;

Whale activity

SELECT
  signer,
  SUM(volume_usd) AS volume_24h,
  COUNT(*) AS swap_count
FROM swaps
WHERE block_time >= now() - INTERVAL 1 DAY
GROUP BY signer
HAVING volume_24h > 1000000
ORDER BY volume_24h DESC;

Jupiter routing through Raydium and Orca

SELECT
  tx_id,
  protocol_name,
  outer_program,
  inner_program,
  base_mint,
  quote_mint,
  volume_usd
FROM swaps
WHERE outer_program = 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4' -- Jupiter v6
  AND block_time >= now() - INTERVAL 1 HOUR
ORDER BY tx_id, instruction_index;

Production tips

  • ClickHouse for high-volume analytics. Solana swap volume is significant; analytical queries over millions of rows are much faster on ClickHouse than Postgres.
  • Partition by block_date. Most analytical queries are time-bounded.
  • Materialized views for per-token metrics. Build hourly / daily roll-ups in ClickHouse materialized views to keep dashboards snappy.
  • Solana Normalizers (full schema) - column-level reference for swaps and other Solana entities.
  • SPL Transfers warehouse recipe - companion table for transfer-side analytics.
  • DEX firehose (streaming) - real-time push for the same data shape.
  • Track PumpFun Swaps - PumpFun-specific tables on top of this same pipeline.

The transfers normalizer streams every decoded SPL token transfer on Solana into your warehouse - both classic Token Program and Token-2022. Each row carries full source/destination account context (owner, balance pre/post, UI amount) so you can build wallet-centric, token-centric, or compliance-oriented analytics without re-deriving from raw txs.

Pipeline configuration

Create a pipeline In the GoldRush Platform, navigate to Manage Pipelines and click Create Pipeline. Name it solana-spl-transfers. Pick Solana + Transfers Choose Solana as the chain and Transfers as the data type. Configure your destination Connect ClickHouse, BigQuery, Postgres, Kafka, S3/GCS/R2, SQS, or a Webhook.
destination:
  type: "postgres"
  url: "postgresql://your-host:5432/solana_data"
  user: "${PG_USER}"
  password: "${PG_PASSWORD}"
  batch_size: 1000
Optional: SQL transform Filter rows before they land. Example: keep only stablecoin transfers.
transforms:
  transfers: >
    SELECT *
    FROM transfers
    WHERE mint IN (
      'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',  -- USDC
      'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'   -- USDT
    )
Deploy Decoded transfers begin flowing within seconds.

Schema

The transfers table is documented in full on the Solana Normalizers page. Key columns:
  • block_slot, slot, block_time, tx_hash (signature).
  • mint, amount (raw uint64), token_decimals, is_raw_amount.
  • source_address (token account), source_owner (wallet pubkey).
  • destination_address, destination_owner.
  • Pre/post balances on both sides (*_pre_balance, *_post_balance) plus UI-amount variants.
  • transfer_index for ordering within a transaction.

Sample analytical queries

Daily stablecoin volume by mint

SELECT
  date_trunc('day', to_timestamp(block_time)) AS day,
  mint,
  SUM(amount::numeric / power(10, token_decimals)) AS volume_tokens
FROM transfers
WHERE mint IN (
  'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
  'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB'
)
  AND block_time >= extract(epoch from now() - interval '30 days')
GROUP BY day, mint
ORDER BY day DESC, volume_tokens DESC;

Net flow per wallet for a token

WITH inflow AS (
  SELECT destination_owner AS wallet, mint, SUM(amount::numeric) AS in_amount
  FROM transfers
  WHERE mint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
  GROUP BY destination_owner, mint
),
outflow AS (
  SELECT source_owner AS wallet, mint, SUM(amount::numeric) AS out_amount
  FROM transfers
  WHERE mint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
  GROUP BY source_owner, mint
)
SELECT
  COALESCE(i.wallet, o.wallet) AS wallet,
  COALESCE(i.in_amount, 0) - COALESCE(o.out_amount, 0) AS net
FROM inflow i
FULL OUTER JOIN outflow o ON i.wallet = o.wallet AND i.mint = o.mint
ORDER BY net DESC
LIMIT 100;

Exchange deposit attribution

Maintain a table of known exchange wallets; join against destination_owner to attribute deposits.
SELECT
  ex.exchange_name,
  t.mint,
  date_trunc('day', to_timestamp(t.block_time)) AS day,
  SUM(t.amount::numeric / power(10, t.token_decimals)) AS deposits
FROM transfers t
JOIN known_exchanges ex ON t.destination_owner = ex.wallet
WHERE t.block_time >= extract(epoch from now() - interval '7 days')
GROUP BY ex.exchange_name, t.mint, day
ORDER BY day DESC, deposits DESC;

Production tips

  • source_owner and destination_owner are the wallet pubkeys, not the SPL token-account pubkeys. Index on these for wallet-centric queries.
  • amount is raw uint64. Always divide by power(10, token_decimals) (or use the *_ui balance columns) for human-readable amounts.
  • Token-2022 mints arrive in the same table as classic SPL mints. If you only want classic SPL, filter on a known-Token-2022 mint allow/deny list.
  • High-volume mints (USDC, USDT, WSOL). Partition by block_date and consider materialized views per top-N mint for fast dashboarding.
  • Solana Normalizers (full schema) - column-level reference for transfers and other Solana entities.
  • DEX Swaps warehouse recipe - companion table for DEX-trade analytics.
  • Stablecoin Transfers (cross-chain) - multi-chain pattern; works for Solana too.
  • Wallet endpoints (REST) - REST lookups for the same transfer history.