Skip to main content
The transfers normalizer streams decoded SPL token transfers on Solana into your warehouse. Each row carries source/destination account context (owner, balance pre/post, UI amount). See the Solana normalizers page for the full schema.

Pipeline configuration

1

Create a pipeline

In the GoldRush Platform, navigate to Manage Pipelines and click Create Pipeline. Name it solana-spl-transfers.
2

Pick Solana + Transfers

Choose Solana as the chain and Transfers as the data type.
3

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
4

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
    )
5

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.
  • High-volume mints (USDC, USDT, WSOL). Partition by date and consider materialized views per top-N mint for fast dashboarding.