Using chainstar

Using chainstar for chainstar. Raw blockchain data to clean dimensional tables in your own warehouse, from one command.

chainstar quickstart --chain ethereum --yes            # the latest 20 blocks
chainstar run --chain ethereum --blocks 20000000:20000100

run writes Parquet files to ./data/ethereum/ and the tables to ./warehouse.duckdb. Without --rpc-url or CHAINSTAR_RPC_URL both use the chain's free public endpoint; with your own:

export CHAINSTAR_RPC_URL="https://your-ethereum-rpc.example"
chainstar doctor --chain ethereum
chainstar run --chain ethereum --blocks 20000000:20000100

Bitcoin and Solana work the same way:

chainstar run --chain bitcoin --blocks 972460:972463
chainstar run --chain solana --blocks 459564290:459564310   # slots, see below

Query the tables with the DuckDB command-line tool:

duckdb warehouse.duckdb "
  select d.token_address, d.symbol, count(*) as transfers, sum(f.amount) as volume
  from chainstar.fct_transfers f
  join chainstar.dim_tokens d using (token_id)
  group by 1, 2 order by 3 desc limit 10"

Every command has --help with examples. Add --debug before the command (chainstar --debug run ...) to see the full traceback when something fails.

Commands

Command What it does
chainstar quickstart Guided first run: asks for a chain and how many of the latest blocks, reads the range from the chain head, runs everything, and prints next steps. --yes for scripts.
chainstar run Runs extract, decode, load and model for a block range, then prints row counts.
chainstar export Saves a table, or the transfers view with token symbols, as CSV, Excel or Parquet, with optional filters.
chainstar wallet All token transfers sent or received by one address in a range, as a spreadsheet.
chainstar doctor Checks that cryo is installed (Ethereum and Base only), the RPC serves the expected chain (EVM chain id, Bitcoin network main, or Solana mainnet's genesis hash), the target is reachable, and dbt is installed.
chainstar init Writes a commented chainstar.yml you can edit.

chainstar run options:

Option Default Meaning
--chain from chainstar.yml ethereum, base, bitcoin or solana
--blocks START:END from chainstar.yml Block range. END is exclusive, as in cryo: 100:200 is 100 blocks. On Solana the numbers are slots.
--rpc-url $CHAINSTAR_RPC_URL, then the chain's public endpoint JSON-RPC endpoint for the chain
--target $CHAINSTAR_TARGET, then chainstar.yml, then duckdb:warehouse.duckdb duckdb:PATH or postgres:postgresql://USER@HOST:PORT/DBNAME
--data-dir ./data Where Parquet files are kept
--requests-per-second no limit; the public endpoint's rate when using one Cap on requests sent to the RPC
--chunk-size 100; 10 on public endpoints Blocks (Solana: slots) per Parquet file. Lower it if your RPC often fails requests (see below).
--config ./chainstar.yml Config file, if you use one

Command-line flags override environment variables, which override chainstar.yml.

chainstar quickstart options: --chain (default base), --recent N (default 20, or 1 on Bitcoin), --rpc-url, --target (default duckdb:warehouse.duckdb), --data-dir (default ./data) and --yes. On Ethereum and Base the range ends 5 blocks behind the head, since chainstar does not handle reorganisations; on Bitcoin it is the latest block, and on Solana the latest finalized slots.

Re-running and adding ranges

Runs are safe to repeat. Parquet files already written are skipped, by cryo on Ethereum and Base and by chainstar on Bitcoin and Solana, and each load replaces a chain's raw rows with everything in its data directory, not only the range you just asked for. So you can:

The extract lines count the range you asked for; the load line counts everything loaded, and the line under it says which blocks the warehouse now holds. When that is more than this run's range, chainstar says so:

extract  erc20_transfers  4,640 rows                            0.4s
...
load     chainstar_raw    4 tables, 18,844 rows                 0.1s
         ethereum now in the warehouse: blocks 26,240,971 to 26,240,990 (20 blocks)
         (each run loads every chunk saved in data/ethereum, so earlier runs' blocks are included)

To keep a range on its own, give it its own --data-dir and --target. dbt rebuilds the star-schema tables in full on each run.

Public endpoints

When no RPC URL is set, chainstar uses these free shared endpoints and prints a one-line notice saying so. They are run by PublicNode, are shared by everyone, and limit request rates, so chainstar asks them gently:

Chain Endpoint Requests per second Blocks per chunk
Ethereum https://ethereum-rpc.publicnode.com 5 10
Base https://base-rpc.publicnode.com 5 10
Bitcoin https://bitcoin-rpc.publicnode.com 2 10
Solana https://solana-rpc.publicnode.com 5 10 (slots)

All four ran chainstar quickstart in October 2026 without a refused request: 20 Ethereum blocks in 4.8 minutes, 20 Base blocks in 3.4 minutes (most of both reading names and symbols of 300 to 400 tokens, three calls each), 20 Solana slots in 26 seconds, and one Bitcoin block in 6 seconds. In earlier runs the Ethereum endpoint refused a few percent of requests at 5 per second with 100-block chunks, and none with 10-block chunks. --requests-per-second and --chunk-size override these, and your own --rpc-url or CHAINSTAR_RPC_URL replaces them (with no rate cap unless you set one). Public endpoints can change their limits or go away at any time; if they refuse requests, chainstar's hint says to wait and run the same command again, which resumes from the chunks already saved.

Exporting

chainstar export [TABLE] saves a table to a file. TABLE is transfers (the default) or any star-schema table: fct_transfers, fct_fees, dim_wallets, dim_tokens, dim_contracts, dim_dates.

chainstar export                                         # transfers.csv
chainstar export transfers --output transfers.xlsx
chainstar export transfers --chain base --wallet 0xYourAddress --output mine.xlsx
chainstar export transfers --token 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 --limit 1000
chainstar export fct_fees --format parquet --target postgres:postgresql://me@localhost:5432/chain
Option Meaning
--output, -o File to write. Default <table>.<format> in the current folder.
--format csv, xlsx or parquet. Default: from --output's extension, else csv.
--chain Only this chain's rows.
--wallet ADDRESS Only rows where the address sends or receives (transfers and fees: from_address or to_address; dim_wallets: address). Ethereum and Base addresses match in any letter case.
--token ADDRESS Only this token: ERC-20 contract, SPL mint, or native for BTC and SOL.
--limit N At most N rows.
--target, --config Which warehouse, as for run.

transfers is fct_transfers joined to dim_tokens, ordered by chain, block and position: block_timestamp, chain, block_number, transaction_hash, transfer_position, token_address, token_symbol, token_name, from_address, to_address, amount, amount_raw.

In every format amount_raw stays text, so 256-bit token amounts keep every digit, and amount is a number. Excel files are written by DuckDB's excel extension, which DuckDB downloads the first time (the Docker image includes it), so chainstar needs no extra Python package. An Excel sheet holds at most 1,048,576 rows including the header; a larger export is refused before anything is written, with a suggestion to filter it or use CSV or Parquet, which have no limit. Excel files store numbers as floating point, so block_number and the like arrive as numbers with up to 15 significant digits, which covers every block number and index.

On Bitcoin, --wallet matches outputs the address received, and outputs of transactions the address funded alone (see from_address in fct_transfers); a transaction funded by several addresses has no single sender.

Wallet history

chainstar wallet ADDRESS builds a warehouse for one address and writes its transfers to a spreadsheet:

chainstar wallet 0xYourAddress --chain base --recent 1000
chainstar wallet 0xYourAddress --chain ethereum --blocks 20000000:20010000 \
  --rpc-url "$MY_RPC" --blocks-per-request 1000
chainstar wallet YourSolanaAddress --chain solana --recent 100 --output mine.csv

Give either --blocks START:END or --recent N. The output defaults to <chain>-<address>.xlsx; the data goes to data/wallet-<chain>-<address>/, with its own warehouse.duckdb, so it never mixes with your main warehouse.

What it costs, honestly:

When something goes wrong

chainstar prints one error: line saying what happened and, for common problems, a hint: line saying what to try, instead of a traceback. For example:

error: getblockchaininfo: could not reach https://no-such-host.invalid/<redacted> (URLError)
hint:  check your internet connection and the RPC URL; a free public endpoint may also be down for a while, so try again later or use your own RPC URL

Covered: cryo missing (with install and Docker instructions), RPC unreachable or rate limited (HTTP 429), the RPC serving another chain, a range past the chain head, Postgres refusing the password or not running, a DuckDB file another program has open, folders that cannot be written, a full disk, and DuckDB extensions that cannot be downloaded. Anything else is reported as a likely bug, with a pointer to --debug and the issue tracker. Ctrl-C stops cleanly; finished chunks are kept.

Your RPC URL stays private

Hosted RPC URLs usually contain an API key, so chainstar:

chainstar sends no telemetry, and turns off dbt's anonymous usage statistics.