Examples
Every runnable example in the Carbon 2.0.0 repository, what it shows, what it needs, the command to run it and the output to expect.
On this page
The Carbon repository ships nine example pipelines in examples/. Each one is a small, complete indexer for one pattern. Use this page to pick one, check what it needs, and run it from a checkout of the v2.0.0 tag.
| Example | Shows | Datasource | Decoder | Needs |
|---|---|---|---|---|
versioned-decoders | Two decoder versions routed by slot | Mock, in the example | Two generated simple-dex decoders | Nothing |
gpa-rpc | One-shot load of a program's accounts | GpaDatasource | carbon-marginfi-v2-decoder | Public RPC works |
transaction-crawler-rpc | Backfill a program's transaction history | RpcTransactionCrawler | carbon-meteora-dlmm-decoder | Public RPC works, slowly |
custom-datasource | Writing your own Datasource | HttpPollDatasource, in the example | carbon-jupiter-swap-decoder | Public RPC works, slowly |
block-subscribe-rpc | Real-time blocks over WebSocket | RpcBlockSubscribe | carbon-raydium-clmm-decoder | An RPC provider with blockSubscribe |
yellowstone-grpc | Real-time transactions over Geyser gRPC | YellowstoneGrpcGeyserClient or LaserStreamGeyserClient | carbon-jupiter-swap-decoder | A Yellowstone or LaserStream endpoint |
postgres-graphql | Persist accounts to Postgres, serve them with GraphQL | YellowstoneGrpcGeyserClient | carbon-token-program-decoder | Postgres and a Yellowstone endpoint |
snapshot-validator | Load accounts from a validator snapshot | SnapshotDatasource | A pass-through decoder in the example | libclang, a snapshot, lots of disk |
jetstreamer | Archive backfill over Old Faithful | JetstreamerDatasource (Carbon 1) | carbon-token-2022-decoder | A Carbon 1 project; doesn't build in the 2.0 workspace |
Run an example#
Every example except jetstreamer is a member of the Carbon workspace, so you build it from a checkout of the repository. You need Rust 1.96.1 (the checkout's rust-toolchain. pins it, and rustup installs it on first use) and a few GB of disk for the first build.
git clone --branch v2.0.0 https://github.com/sevenlabs-hq/carbon.git
cd carbon/examples/gpa-rpc
RPC_URL=https://api.mainnet-beta.solana.com RUST_LOG=info cargo run --release -p gpa-rpc-carbon-exampleGit prints a "detached HEAD" note after the clone; that's expected for a tag checkout. The first build compiles the Solana dependencies and takes a few minutes; later runs start in seconds.
Two things apply to every example:
- Logging. Examples print through
logandenv_logger, so nothing appears unlessRUST_LOGis set.RUST_shows decoded items and theLOG=info LogMetricssummary every five seconds;RUST_also shows the variants the processors log at debug level.LOG=debug .envfiles. Each example callsdotenv::, which reads adotenv() .envfile from the current directory or one of its parents. Put the file in the example's directory and runcargo runfrom there. Running from the repository root doesn't pick upexamples/. Setting the variables inline, as in the commands on this page, works from anywhere.< name>/ . env
versioned-decoders#
Indexes a program that was upgraded with a breaking instruction layout change. A fictional DEX adds a fee_ field to Swap at slot 500, transaction index 10. The example registers two generated decoders, carbon-simple-dex-decoder-v1 and -v2 (in decoder-v1/ and decoder-v2/, generated from the IDLs in idls/), each with instruction_ and a SlotRangeFilter so every instruction reaches only the version that was live at its slot. One processor implements Processor for both instruction types and normalizes them to a shared struct.
The datasource is a mock inside src/main.rs that sends four synthetic transactions and returns, so the pipeline exits on its own. The pattern is explained in Versioned decoders.
Needs: nothing. No environment variables, no network.
cd carbon/examples/versioned-decoders
RUST_LOG=info cargo run --release -p versioned-decoders-carbon-example[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] === Versioned Decoders Example ===
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] Program upgrade at slot=500, tx_index=10
[2026-11-08T23:48:10Z INFO carbon_core::pipeline] starting pipeline. num_datasources: 1, num_exporters: 0, num_account_pipes: 0, num_account_deletion_pipes: 0, num_instruction_pipes: 2, num_transaction_pipes: 0
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] [V1] Swap | slot=499 tx_idx=Some(0) | amount_in=1000000 min_amount_out=990000
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] [V1] Swap | slot=500 tx_idx=Some(9) | amount_in=2000000 min_amount_out=1980000
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] [V2] Swap | slot=500 tx_idx=Some(10) | amount_in=3000000 min_amount_out=2970000 fee_tier=5
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] [V2] Swap | slot=501 tx_idx=Some(0) | amount_in=4000000 min_amount_out=3960000 fee_tier=10
[2026-11-08T23:48:10Z INFO carbon_core::pipeline] update_receiver closed, shutting down.
[2026-11-08T23:48:10Z INFO carbon_core::pipeline] pipeline shutdown complete.
[2026-11-08T23:48:10Z INFO versioned_decoders_carbon_example] Done.The filters do the routing. The decoders alone can't tell the versions apart reliably: generated decoders ignore trailing bytes, so the V1 decoder also accepts V2 data and silently drops fee_tier.
gpa-rpc#
Loads every account owned by the Marginfi v2 program once, with a single getProgramAccounts call, decodes each into MarginfiV2Account, and exits when the datasource has sent everything. It logs banks and groups at info and user accounts at debug. src/ also has a Helius gPA v2 variant (paginated, with changed_ for incremental syncs); switch by editing the let datasource = … line in main.rs. See Load account state.
Needs:
| Variable | Required | Used by |
|---|---|---|
RPC_URL | Yes, for the default RPC variant | GpaDatasource. The public endpoint works for Marginfi's size. |
HELIUS_ | Only for the Helius variant | HeliusGpaV2Datasource. A Helius RPC URL with the API key in it. |
cd carbon/examples/gpa-rpc
RPC_URL=https://api.mainnet-beta.solana.com RUST_LOG=info cargo run --release -p gpa-rpc-carbon-exampleAgainst the public endpoint the run took about 12 seconds and processed 171,340 accounts. The end of the output:
[2026-11-08T23:48:31Z INFO gpa_rpc_carbon_example] bank pubkey=J5mxC3hLXqmsJB4Nauf9YrtbNMSTHGz7FWfAmy1v6Fzr mint=So11111111111111111111111111111111111111112 group=4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8
[2026-11-08T23:48:31Z INFO gpa_rpc_carbon_example] group pubkey=J9VZnaMGTELGCPsqMxk8aoyEGYcVzhorj48HvtDdEtc8 admin=J525txDGqSuPisS3NQwySwDEDExVhjJNUfx94PGQUNJR
[2026-11-08T23:48:31Z INFO carbon_core::pipeline] update_receiver closed, shutting down.
[2026-11-08T23:48:31Z INFO carbon_log_metrics] 00:00:11 (+1.82520643s) | 171340 processed (100%), 171340 successful, 0 failed (0%), 0 in queue
[2026-11-08T23:48:31Z INFO carbon_log_metrics] carbon_account_updates_processed_total: 171340
[2026-11-08T23:48:31Z INFO carbon_log_metrics] rpc_gpa_accounts_processed_total: 171340
[2026-11-08T23:48:31Z INFO carbon_log_metrics] rpc_gpa_fetch_duration_milliseconds: count=1
[2026-11-08T23:48:31Z INFO carbon_core::pipeline] pipeline shutdown complete.
[2026-11-08T23:48:31Z INFO gpa_rpc_carbon_example] snapshot completetransaction-crawler-rpc#
Backfills Meteora DLMM activity by walking the program's signatures backwards from the newest (getSignaturesForAddress), fetching each transaction (getTransaction), and decoding it. It logs Swap, AddLiquidity and RemoveLiquidity at info and every other instruction at debug. After it reaches the oldest signature it can see, it keeps polling for new ones every five seconds. src/ also has a Helius GTFA variant that pages through a fixed slot window and then exits. See Backfill transactions.
Needs:
| Variable | Required | Used by |
|---|---|---|
RPC_URL | Yes, for the default RPC variant | RpcTransactionCrawler. The public endpoint works but is heavily rate limited. |
UNTIL_ | No | Stops the backward walk at this signature. The crawler still keeps polling for new transactions afterwards, so the pipeline doesn't exit. |
HELIUS_ | Only for the GTFA variant | HeliusGtfaDatasource. |
cd carbon/examples/transaction-crawler-rpc
RPC_URL=https://api.mainnet-beta.solana.com RUST_LOG=info cargo run --release -p transaction-crawler-rpc-carbon-exampleOn the public endpoint, expect a handful of swaps per minute, with pauses. This excerpt is from a 60-second run that processed 17 transactions:
[2026-11-08T23:48:38Z ERROR carbon_rpc_transaction_crawler_datasource] Failed to fetch signatures after 0 retries: Error { request: Some(GetSignaturesForAddress), kind: Reqwest(reqwest::Error { kind: Status(413, Some(ReasonPhrase(b"Request Entity Too Large"))), url: "https://api.mainnet-beta.solana.com/" }) }
[2026-11-08T23:48:42Z INFO transaction_crawler_rpc_carbon_example] swap slot=467105207 sig=5FVRVLgohHkAMTsqb5M5vCKJ8xpWGuQddfMBtdaifGFeDtnHQ3TFDSBdZAsUFezqimgh2i2RB94c3qtchJScyvWz data=Swap { amount_in: 367843, min_amount_out: 0 }
[2026-11-08T23:48:42Z INFO transaction_crawler_rpc_carbon_example] swap slot=467105207 sig=2cFZ9cJkYpQGtnyLi9TMDTFErAkxktDRdwnjiHwz2BAEYLTFLYGzGx86mmPpMbSuarJxZeeFR4BXKVUWpPiMZKfC data=Swap { amount_in: 91821913, min_amount_out: 7958851 }
[2026-11-08T23:48:46Z INFO carbon_log_metrics] 00:00:10 (+5.00022008s) | 8 processed (100%), 8 successful, 0 failed (0%), 0 in queue
…
[2026-11-08T23:49:36Z INFO carbon_log_metrics] transaction_crawler_signatures_fetched_total: 1000
[2026-11-08T23:49:36Z INFO carbon_log_metrics] transaction_crawler_transactions_fetched_total: 26The example sets RetryConfig::, so a rejected getSignaturesForAddress call is logged once and retried on the next poll, as in the first line above. The crawler fetches a thousand signatures quickly and then waits on getTransaction, which the public endpoint limits hard: it answers HTTP 429 with retry-after: 10, and the Solana RPC client sleeps without logging anything at info. See Crawler prints nothing for seconds at a time.
custom-datasource#
A complete hand-written Datasource, HttpPollDatasource, that polls getSignaturesForAddress for the Jupiter v6 program every five seconds, fetches each transaction with getTransaction in Base64, converts the metadata with transaction_, and sends Update::s. It shows the parts a real source needs: cancellation checks at every sleep, a watermark (until) so it doesn't re-send old transactions, backoff on failed calls, skipping failed transactions, and two custom Counter metrics registered in the global registry. The processor logs the discriminant of each decoded JupiterSwapInstruction.
Needs:
| Variable | Required | Used by |
|---|---|---|
RPC_URL | Yes | HttpPollDatasource. The public endpoint works, slowly. |
cd carbon/examples/custom-datasource
RPC_URL=https://api.mainnet-beta.solana.com RUST_LOG=info cargo run --release -p custom-datasource-carbon-example[2026-11-08T23:50:51Z INFO custom_datasource_carbon_example] ix slot=467105803 sig=2r4uo4VNLHHy9KZQi4KjCFqQM4ugBJgc5tQgMS1RGjKrMzsfYoAFG9AkiGdcRL24YpY9zUziRMXMhmMZuDmhXXcx variant=Discriminant(11)
[2026-11-08T23:50:51Z INFO custom_datasource_carbon_example] ix slot=467105803 sig=2r4uo4VNLHHy9KZQi4KjCFqQM4ugBJgc5tQgMS1RGjKrMzsfYoAFG9AkiGdcRL24YpY9zUziRMXMhmMZuDmhXXcx variant=Discriminant(9)
[2026-11-08T23:50:51Z INFO custom_datasource_carbon_example] ix slot=467105803 sig=2r4uo4VNLHHy9KZQi4KjCFqQM4ugBJgc5tQgMS1RGjKrMzsfYoAFG9AkiGdcRL24YpY9zUziRMXMhmMZuDmhXXcx variant=Discriminant(17)
[2026-11-08T23:50:55Z INFO carbon_log_metrics] 00:00:05 (+4.999159689s) | 2 processed (100%), 2 successful, 0 failed (0%), 0 in queue
…
[2026-11-08T23:51:49Z INFO carbon_log_metrics] custom_http_poll_transactions_fetched_total: 12
[2026-11-08T23:51:49Z INFO carbon_log_metrics] custom_http_poll_fetch_failures_total: 0One transaction produces several lines because the processor runs for every Jupiter instruction in it, including inner instructions and the events Jupiter emits through self-CPI (nested instructions). It runs until you press Ctrl-C; the example uses ShutdownStrategy::.
block-subscribe-rpc#
Subscribes to confirmed blocks that mention the Raydium CLMM program over the RPC WebSocket method blockSubscribe, decodes Raydium CLMM instructions (Swap, SwapV2 and OpenPosition at info), and logs every block's header with a block_ processor. The RpcBlockSubscribeConfig it passes asks for Base64 encoding, full transaction details and max_, which Carbon 2 requires. See Stream with RPC.
Needs: an RPC provider that enables blockSubscribe. Solana's public endpoint doesn't.
| Variable | Required | Used by |
|---|---|---|
RPC_WS_URL | In practice, yes | RpcBlockSubscribe. A wss:// URL. Falls back to wss:/, which rejects the method. |
cd carbon/examples/block-subscribe-rpc
RPC_WS_URL="wss://your-provider.example/?api-key=…" RUST_LOG=info cargo run --release -p block-subscribe-rpc-carbon-exampleWith a provider that supports the method, block slot=… hash=… parent=… time=… lines interleave with decoded swap, swap_v2 and open_ lines. Without RPC_WS_URL, the run against the public endpoint looks like this and the process exits after about 40 seconds:
[2026-11-08T23:51:57Z ERROR carbon_rpc_block_subscribe_datasource] Failed to subscribe to block updates: SubscribeFailed { reason: "Method not found (-32601)", message: "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}" }
…
[2026-11-08T23:52:37Z ERROR carbon_core::pipeline] error consuming datasource: Custom("Failed to subscribe after 10 attempts: subscribe failed: Method not found (-32601)")
[2026-11-08T23:52:37Z INFO carbon_core::pipeline] update_receiver closed, shutting down.
[2026-11-08T23:52:37Z INFO carbon_core::pipeline] pipeline shutdown complete.yellowstone-grpc#
The standard real-time setup: a Yellowstone Geyser gRPC subscription to confirmed, successful transactions that include the Jupiter v6 program (account_, vote: false, failed: false), decoded with JupiterSwapDecoder. It logs Route and SharedAccountsRoute at info. src/ has a Helius LaserStream variant built with LaserStreamClientConfig and replay enabled. main installs the aws_lc_rs rustls crypto provider before connecting. See Stream with Yellowstone gRPC and Stream with Helius LaserStream.
Needs: a Yellowstone gRPC endpoint (Triton, Helius, QuickNode, a self-hosted Geyser plugin) or a Helius LaserStream plan.
| Variable | Required | Used by |
|---|---|---|
GEYSER_URL | Yes, for the default variant | YellowstoneGrpcGeyserClient. Include the scheme and port, e.g. https:/. |
X_TOKEN | If your provider requires it | Sent as the x-token header. |
LASERSTREAM_ | Only for the LaserStream variant | LaserStreamGeyserClient. |
API_KEY | Only for the LaserStream variant | Your Helius API key. |
cd carbon/examples/yellowstone-grpc
GEYSER_URL=https://your-yellowstone-endpoint:443 X_TOKEN=your-x-token RUST_LOG=info cargo run --release -p yellowstone-grpc-carbon-exampleThe example compiles at v2.0.0; it wasn't run for this page because it needs a paid endpoint. With a working endpoint, route slot=… sig=… data=Route { … } and shared_ lines stream continuously between LogMetrics summaries until you press Ctrl-C. If GEYSER_URL is missing the example panics with GEYSER_.
postgres-graphql#
Streams SPL Token account updates from Yellowstone, stores each decoded account as JSON in Postgres with PostgresJsonAccountProcessor< (Carbon's postgres feature), and serves a small GraphQL API over the same table with Carbon's graphql feature: a mints(limit) query and a mint(pubkey) lookup. The migration in migrations/ creates the accounts table (_, _, data JSONB) and runs at startup. The GraphQL server starts before the pipeline, on /graphql, with a GraphiQL playground on /graphiql. See Postgres and GraphQL.
Needs: Postgres and a Yellowstone endpoint. The subscription covers every account owned by the Token program, which is a high-volume stream.
| Variable | Required | Used by |
|---|---|---|
DATABASE_ | Yes | sqlx connection pool. |
GEYSER_URL | Yes | YellowstoneGrpcGeyserClient. |
X_TOKEN | If your provider requires it | Yellowstone x-token. |
BIND_ADDR | No | GraphQL server address. Default 0.. |
The bundled compose. starts Postgres 16 with user, password and database carbon / carbon / carbon_, published on host port 5433. The example's README uses port 5432 in DATABASE_; with the bundled database, use 5433:
cd carbon/examples/postgres-graphql
docker compose up -d
DATABASE_URL=postgres://carbon:carbon@localhost:5433/carbon_example \
GEYSER_URL=https://your-yellowstone-endpoint:443 X_TOKEN=your-x-token \
RUST_LOG=info cargo run --release -p postgres-graphql-carbon-exampleThe first log line is graphiql at http:/. Open http:/ and run:
{
mints(limit: 5) {
pubkey
slot
decimals
supply
}
}The example compiles at v2.0.0; it wasn't run end to end for this page (no Postgres or Geyser endpoint was available). Without a reachable database it fails at startup with:
Error: Custom("failed to connect to Postgres: pool timed out while waiting for an open connection")snapshot-validator#
Loads accounts from a Solana validator snapshot, either downloaded or already extracted on disk, filtered by owner program or account address. A pass-through AccountDecoder hands each raw Account to the processor, which logs pubkey, slot, lamports, data size and owner; an account_ processor logs deletions. It uses ShutdownStrategy:: and exits when the snapshot has been read. See Load a snapshot.
Needs:
libclangat build time:carbon-validator-snapshot-datasourcedepends onsolana-ledger, which builds RocksDB withbindgen. Installlibclang-devon Debian and Ubuntu orllvmwith Homebrew. It's the only example that needs it.- Disk and memory for a snapshot. The current mainnet full snapshot was about 126 GB on 2026-11-09. The remote source downloads
genesis.and the snapshot into atar. bz2 ledger/directory under the current directory.
| Variable | Required | Used by |
|---|---|---|
VALIDATOR_ | One of these two | Base URL of an RPC node that serves snapshots, e.g. https:/. Carbon appends / and /. |
SNAPSHOT_ | One of these two | An extracted ledger directory containing genesis. and snapshots/. |
PROGRAM_ | At least one of these two | Comma-separated owner program IDs. |
ACCOUNT_IDS | At least one of these two | Comma-separated account addresses. |
cd carbon/examples/snapshot-validator
VALIDATOR_URL=https://api.mainnet-beta.solana.com \
PROGRAM_OWNERS=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA \
RUST_LOG=info cargo run --release -p snapshot-validator-carbon-exampleThe example compiles at v2.0.0 with libclang installed; a full mainnet load wasn't run for this page. Loaded accounts print as account pubkey=… slot=… lamports=… data_, followed by snapshot bootstrap complete. That last line also prints when loading failed, because the pipeline returns Ok after a datasource error; check the log for error consuming datasource (see Pipeline exits with status 0 after a datasource error).
jetstreamer#
Backfills Token-2022 activity over a slot range from Old Faithful through the Jetstreamer datasource, with server-side transaction filters and parallel workers, then exits.
This is a Carbon 1 / Solana 3 example. Jetstreamer doesn't support the Solana 4 crates yet, so carbon-jetstreamer-datasource was not published on the 2.0 line (crates.io has 1.0.0 only), and the workspace Cargo.toml excludes examples/. It doesn't build from a v2.0.0 checkout: from the repository root, cargo run -p jetstreamer-carbon-example fails with package(s) `jetstreamer-carbon-example` not found in workspace, and from its own directory with error inheriting `edition` from workspace root manifest's `workspace.. To use it, copy the pattern into a project that depends on Carbon 1.0.0 crates throughout. Don't combine it with Carbon 2 crates (why).
It reads START_SLOT (default 415500000), END_SLOT (415931999), WORKER_ (4) and PAGE_SIZE (100), all optional.