# Quickstart (Carbon 2.0)

> Build an indexer that decodes live Meteora DLMM swaps from Solana mainnet in about five minutes. No account or API key needed.

You'll build a small Rust program that reads a Solana program's transactions, decodes each instruction into a typed Rust struct, and prints swaps as they happen. It uses Solana's public RPC endpoint, so you don't need an account or an API key.

**You need:** Rust 1.96.1 or newer, an internet connection, and about 3 GB of free disk space for the first build.

### Step 1: Check your Rust version

```sh
rustc --version
```

Output:

```text
rustc 1.99.0 (b940084d7 2026-09-28)
```

Any version from 1.96.1 up works. If yours is older, run `rustup update stable`. If you don't have Rust, install it from [rustup.rs](https://rustup.rs).

### Step 2: Create a project

```sh
cargo new carbon-quickstart
cd carbon-quickstart
```

### Step 3: Add Carbon

Add the core crate, a datasource, a decoder for the program you want to read, and a metrics exporter. All Carbon crates in one project must have the same version.

```sh
cargo add carbon-core@2.0.0 carbon-rpc-transaction-crawler-datasource@2.0.0 carbon-meteora-dlmm-decoder@2.0.0 carbon-log-metrics@2.0.0
cargo add tokio --features full
cargo add env_logger log
```

Your `[dependencies]` now look like this (patch versions of `tokio`, `env_logger` and `log` may be newer):

`Cargo.toml`:

```toml
[dependencies]
carbon-core = "2.0.0"
carbon-log-metrics = "2.0.0"
carbon-meteora-dlmm-decoder = "2.0.0"
carbon-rpc-transaction-crawler-datasource = "2.0.0"
env_logger = "0.11.11"
log = "0.4.35"
tokio = { version = "1.54.1", features = ["full"] }
```

### Step 4: Write the pipeline

Replace `src/main.rs` with this program. It connects three parts: a **datasource** that fetches the program's transactions, a **decoder** that turns Meteora DLMM instructions into Rust types, and a **processor**, `SwapLogger`, that prints them.

`src/main.rs`:

```rust
use {
    carbon_core::{
        error::CarbonResult, instruction::InstructionProcessorInputType, pipeline::Pipeline,
        processor::Processor,
    },
    carbon_log_metrics::LogMetrics,
    carbon_meteora_dlmm_decoder::{
        instructions::MeteoraDlmmInstruction, MeteoraDlmmDecoder, PROGRAM_ID,
    },
    carbon_rpc_transaction_crawler_datasource::{ConnectionConfig, Filters, RpcTransactionCrawler},
    std::sync::Arc,
};

#[tokio::main]
async fn main() -> CarbonResult<()> {
    env_logger::init();

    // Solana's public endpoint works for this example. Set RPC_URL to use your own.
    let rpc_url = std::env::var("RPC_URL")
        .unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string());

    // Fetches the program's recent transactions, newest first, then keeps polling for new ones.
    let datasource = RpcTransactionCrawler::new(
        rpc_url,
        PROGRAM_ID,
        ConnectionConfig::default(),
        Filters::new(None, None, None),
        None,
    );

    Pipeline::builder()
        .datasource(datasource)
        .metrics(Arc::new(LogMetrics::new()))
        .instruction(MeteoraDlmmDecoder, SwapLogger)
        .build()?
        .run()
        .await
}

struct SwapLogger;

impl Processor<InstructionProcessorInputType<'_, MeteoraDlmmInstruction>> for SwapLogger {
    async fn process(
        &mut self,
        input: &InstructionProcessorInputType<'_, MeteoraDlmmInstruction>,
    ) -> CarbonResult<()> {
        let signature = input.metadata.transaction_metadata.signature;
        match input.decoded_instruction {
            MeteoraDlmmInstruction::Swap { data, accounts, .. } => println!(
                "swap  pool={} amount_in={} min_amount_out={}",
                accounts.lb_pair, data.amount_in, data.min_amount_out,
            ),
            MeteoraDlmmInstruction::Swap2 { data, accounts, .. } => println!(
                "swap2 pool={} amount_in={} min_amount_out={}",
                accounts.lb_pair, data.amount_in, data.min_amount_out,
            ),
            // Every other instruction is decoded too; print its name.
            other => println!("{} in {signature}", variant_name(other)),
        }
        Ok(())
    }
}

/// The variant name of a decoded instruction, e.g. "AddLiquidityByStrategy2".
fn variant_name(instruction: &MeteoraDlmmInstruction) -> String {
    format!("{instruction:?}").split([' ', '{', '(']).next().unwrap_or_default().to_string()
}
```

### Step 5: Run it

```sh
cargo run
```

The first build compiles the Solana dependencies and takes one to three minutes. Then decoded instructions start printing, newest transactions first. On Solana's public endpoint the first line can take a minute or more, because it rate-limits the crawler; see [Nothing prints for a minute or more](#nothing-prints-for-a-minute-or-more).

Output:

```text
swap2 pool=6e7V9eegCHw997T72MxgwwJipZ6GJyZF8NvjkzT1rvpN amount_in=84225603 min_amount_out=0
CpiEvent in 5oc7rbKmGEti7jEfvCt77cWWTG8gUvQn5breYsMwpA396gn3FJAozKjUvKE6Nqma3SHZbKg4kXSi8G9pzSfTL8cA
CpiEvent in 5oc7rbKmGEti7jEfvCt77cWWTG8gUvQn5breYsMwpA396gn3FJAozKjUvKE6Nqma3SHZbKg4kXSi8G9pzSfTL8cA
swap  pool=GMgh4NtWrGaUf1RR2kcXD7LY1jou1qFAuSsQeKp5ow4a amount_in=14968688 min_amount_out=0
swap2 pool=4ju8phDxUU4WSJP1wWPLZ8R2KRCqwP3VZFXAqhT9cfVy amount_in=56765035 min_amount_out=1
…
```

`CpiEvent` lines are the events Meteora emits through self-CPI after each swap; Carbon decodes them as instructions too. Press `Ctrl+C` to stop.

## What you built

- **The datasource**, [`RpcTransactionCrawler`](https://carbon.sevenlabs.org/datasources/rpc-transaction-crawler.md), asks the RPC node for the program's signatures (`getSignaturesForAddress`), fetches each transaction (`getTransaction`), skips failed ones, and sends the rest into the pipeline. It keeps polling every five seconds for new transactions.
- **The decoder**, `MeteoraDlmmDecoder` from [`carbon-meteora-dlmm-decoder`](https://carbon.sevenlabs.org/decoders/meteora-dlmm.md), matches each instruction's discriminator and returns a `MeteoraDlmmInstruction` with typed `data` and named `accounts`. It decodes instructions nested inside other programs' calls too, such as a Jupiter route that swaps through a DLMM pool.
- **The processor**, `SwapLogger`, implements [`Processor`](https://carbon.sevenlabs.org/concepts/processors.md) for the decoder's output. Carbon calls `process` once per decoded instruction, with the instruction's [metadata](https://carbon.sevenlabs.org/reference/processor-inputs.md) (signature, slot, position in the transaction).
- **The pipeline** wires them together and runs until you stop it. See [how a pipeline runs](https://carbon.sevenlabs.org/concepts/architecture.md).

## See the pipeline's metrics

`LogMetrics` writes throughput and queue depth to the log every five seconds. Turn the log on with `RUST_LOG`:

```sh
RUST_LOG=info cargo run
```

Every five seconds you get a summary line followed by each metric's value. An excerpt:

Output:

```text
[2026-11-09T00:02:47Z INFO  carbon_log_metrics] 00:00:50 (+5.006587213s) | 7 processed (100%), 7 successful, 0 failed (0%), 0 in queue
[2026-11-09T00:02:47Z INFO  carbon_log_metrics]   carbon_updates_received_total: 7
[2026-11-09T00:02:47Z INFO  carbon_log_metrics]   transaction_crawler_signatures_fetched_total: 1000
[2026-11-09T00:02:47Z INFO  carbon_log_metrics]   transaction_crawler_transactions_fetched_total: 7
…
```

The [metrics reference](https://carbon.sevenlabs.org/reference/metrics.md) lists every metric.

## Use your own RPC endpoint

The public endpoint is rate limited, so the example fetches only a few transactions per second. Any Solana RPC provider's URL works in its place:

```sh
RPC_URL="https://your-rpc-provider.example/?api-key=…" cargo run
```

For production, stream with Geyser gRPC instead of polling: see [Stream with Yellowstone gRPC](https://carbon.sevenlabs.org/guides/stream-with-yellowstone.md). The processor doesn't change.

## Troubleshooting

### `rustc 1.95.0 is not supported by the following packages`

Output:

```text
error: rustc 1.95.0 is not supported by the following packages:
  carbon-core@2.0.0 requires rustc 1.96.1
```

Your Rust toolchain is older than Carbon 2.0 needs. Run `rustup update stable`, then `cargo run` again.

### Nothing prints for a minute or more

The public RPC endpoint limits how many requests you can make, and the crawler waits and retries when it hits the limit. It also skips failed transactions, so a burst of failed bot transactions prints nothing. To see whether it's working, run with metrics:

```sh
RUST_LOG=info cargo run
```

If `transaction_crawler_signatures_fetched_total` grows while `transaction_crawler_transactions_fetched_total` stays low, the crawler has found transactions and is being rate limited while it fetches them. Output follows; use your own endpoint with `RPC_URL` for steady throughput.

### `Failed to fetch transaction … after 3 retries` in the log

The RPC node kept rejecting requests, usually with HTTP 429 (Too Many Requests). The crawler logs the error, skips that transaction and continues. Lower the request rate with a custom [`ConnectionConfig`](https://carbon.sevenlabs.org/datasources/rpc-transaction-crawler.md#configuration), or use your own endpoint.

### `the trait InstructionDecoder is not implemented` for a decoder

A Carbon 1 crate is mixed with Carbon 2, so two versions of `carbon-core` are compiled; the compiler adds the note `there are multiple different versions of crate carbon_core`. Make every `carbon-*` dependency `2.0.0`, then run `cargo update`. See [Installation](https://carbon.sevenlabs.org/installation.md).

More problems and fixes are in [Troubleshooting](https://carbon.sevenlabs.org/troubleshooting.md).

## Next steps

- [Generate a decoder](https://carbon.sevenlabs.org/guides/generate-a-decoder.md): Decode your own program from its Anchor or Codama IDL.
- [Choose a datasource](https://carbon.sevenlabs.org/datasources.md): Real-time streams, backfills, snapshots and current state.
- [Persist to Postgres](https://carbon.sevenlabs.org/guides/postgres.md): Store decoded instructions and accounts in typed tables.

---

Carbon 2.0.0 documentation · Web: https://carbon.sevenlabs.org/quickstart · Last updated 2026-11-09 · Index of all pages: https://carbon.sevenlabs.org/llms.txt
