# Track Pump.fun launches and trades (Carbon 2.0)

> Decode Pump.fun token launches, bonding-curve buys and sells, and completed curves from the program's events with the Pump.fun decoder.

Pump.fun records every token launch, every trade on a bonding curve and every curve that fills in an Anchor event, which it emits through a self-CPI. The [Pump.fun decoder](https://carbon.sevenlabs.org/decoders/pumpfun.md) decodes those events as `PumpfunInstruction::CpiEvent`, so your processor reads what actually executed. This guide prints launches, trades and completed curves as they happen on mainnet.

**You need:** Rust 1.96.1 or newer. The example uses Solana's public RPC, so no account is needed.

## Read events, not instruction arguments

| You want           | Read                                                                              | Why                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| New tokens         | [`CreateEvent`](https://carbon.sevenlabs.org/decoders/pumpfun.md#createevent)     | Name, symbol, metadata URI, mint, bonding curve and creator in one place.                                     |
| Buys and sells     | [`TradeEvent`](https://carbon.sevenlabs.org/decoders/pumpfun.md#tradeevent)       | The SOL and token amounts that executed, `is_buy`, the trader, fees and the curve's reserves after the trade. |
| Curves that filled | [`CompleteEvent`](https://carbon.sevenlabs.org/decoders/pumpfun.md#completeevent) | The mint and bonding curve; trading moves to an AMM after this.                                               |

The trade instructions carry limits, not results: `Buy` has `amount` and `max_sol_cost`, `Sell` has `amount` and `min_sol_output`, and newer variants such as `BuyExactSolIn` take `spendable_sol_in` and `min_tokens_out`. Each emits the same `TradeEvent`, whose `ix_name` field names the instruction that ran, so one match arm covers all of them.

### Step 1: Create a project

```sh
cargo new pumpfun-tracker
cd pumpfun-tracker
cargo add carbon-core@2.0.0 carbon-rpc-transaction-crawler-datasource@2.0.0 carbon-pumpfun-decoder@2.0.0 carbon-log-metrics@2.0.0
cargo add tokio --features full
cargo add env_logger
```

### Step 2: Write the tracker

The [RPC transaction crawler](https://carbon.sevenlabs.org/datasources/rpc-transaction-crawler.md) fetches every transaction that calls Pump.fun, newest first, then keeps polling for new ones. The processor ignores every decoded instruction except the events, and prints the three it cares about.

`src/main.rs`:

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

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

    let rpc_url = std::env::var("RPC_URL")
        .unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string());

    // Every transaction that calls Pump.fun, newest first, then new ones as they land.
    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(PumpfunDecoder, LaunchTracker)
        .build()?
        .run()
        .await
}

/// SOL has 9 decimals; Pump.fun tokens have 6.
const LAMPORTS_PER_SOL: f64 = 1e9;
const TOKEN_UNITS: f64 = 1e6;

struct LaunchTracker;

impl Processor<InstructionProcessorInputType<'_, PumpfunInstruction>> for LaunchTracker {
    async fn process(
        &mut self,
        input: &InstructionProcessorInputType<'_, PumpfunInstruction>,
    ) -> CarbonResult<()> {
        // Pump.fun reports what happened in events, emitted as a self-CPI after each action.
        let PumpfunInstruction::CpiEvent { data, .. } = input.decoded_instruction else {
            return Ok(());
        };
        match data {
            CpiEvent::CreateEvent(e) => {
                println!(
                    "launch   {} ({}) mint={} creator={}",
                    e.name, e.symbol, e.mint, e.creator
                )
            }
            CpiEvent::TradeEvent(e) => println!(
                "{} {:>12.4} SOL {:>16.2} tokens mint={}",
                if e.is_buy { "buy     " } else { "sell    " },
                e.sol_amount as f64 / LAMPORTS_PER_SOL,
                e.token_amount as f64 / TOKEN_UNITS,
                e.mint,
            ),
            CpiEvent::CompleteEvent(e) => println!("complete mint={} bonding curve filled", e.mint),
            _ => {}
        }
        Ok(())
    }
}
```

### Step 3: Run it

```sh
cargo run
```

The first build takes one to three minutes. Then trades start printing, newest first. On the public endpoint the first line can take a minute or more, because it rate-limits `getTransaction`.

Output:

```text
buy            0.0059 SOL        201272.73 tokens mint=7TUpdfVds47vFqwiDubJjAq51y9nMhU5mmCwEmkUpump
sell           0.2325 SOL       1385083.79 tokens mint=EaCgYCrKyGDfquYhUapfw33Xh65U4ZQshPgQnKt1pump
sell           0.0063 SOL      26635028.55 tokens mint=Fqptwy5FV7y89yawECyK9xiCNKmcLHWhLGAoJxL4pump
sell           0.0259 SOL      39136997.32 tokens mint=3sTmKFwVV6PRB5GD8M88VapRa524Mozu3LAwdB4Ypump
sell           0.0028 SOL         28738.62 tokens mint=GdMjsTtrn3AKFHD8x3u15Hg6QLCWfXbdvPsofd83pump
sell           0.0067 SOL      12869465.83 tokens mint=3DvhrxjakLL5s8aJJcHUuCtaUMVrCHAj68KBYiLfpump
…
```

A `launch` line prints when a token is created, and a `complete` line when its bonding curve fills. Both are much rarer than trades. Press `Ctrl+C` to stop.

## Amounts and decimals

- `sol_amount` is in lamports: divide by 10⁹ for SOL.
- `token_amount` is in the token's base units. Pump.fun mints have 6 decimals, so divide by 10⁶.
- `fee` and `creator_fee` are in lamports too, with their rates in `fee_basis_points` and `creator_fee_basis_points`.
- The event also has `quote_mint` and `quote_amount` fields. Check `quote_mint` before you treat `sol_amount` as the price if you index curves quoted in another token.

Every field is listed on the decoder page under [`TradeEvent`](https://carbon.sevenlabs.org/decoders/pumpfun.md#tradeevent).

## After a curve completes

Once `CompleteEvent` fires, the token leaves the bonding curve and trades on an AMM. Pump.fun moves completed tokens to PumpSwap, and `CompletePumpAmmMigrationEvent` records the move. To keep following a token after it graduates, add the [PumpSwap decoder](https://carbon.sevenlabs.org/decoders/pump-swap.md) and read its `BuyEvent` and `SellEvent`: [index swaps across DEXs](https://carbon.sevenlabs.org/guides/index-dex-swaps.md) shows how. Other launchpads work the same way; see [launchpad decoders](https://carbon.sevenlabs.org/decoders/launchpads.md).

## Stream launches in real time

The crawler polls every few seconds and the public endpoint is slow. For launches within a second of landing, swap the datasource for a Geyser stream that follows Pump.fun's program ID: [stream with Yellowstone gRPC](https://carbon.sevenlabs.org/guides/stream-with-yellowstone.md) or [stream with Helius LaserStream](https://carbon.sevenlabs.org/guides/stream-with-laserstream.md). The processor doesn't change.

## Common problems

### Nothing prints for a minute or more

The public endpoint rate-limits `getTransaction` per IP, and Pump.fun has more transactions than it lets through. Run with `RUST_LOG=info` to see the crawler's counters and 429 retries, and set `RPC_URL` to your own endpoint for anything beyond a test.

### Trades print out of order

The crawler fetches transactions concurrently, so they complete in any order. Sort by `input.metadata.transaction_metadata.slot`, or set `max_concurrent_requests: 1` in [`ConnectionConfig`](https://carbon.sevenlabs.org/datasources/rpc-transaction-crawler.md#connectionconfig) for newest-first order.

## Next steps

- [Index swaps across DEXs](https://carbon.sevenlabs.org/guides/index-dex-swaps.md): Follow graduated tokens on PumpSwap, Raydium and Meteora in one record.
- [Store decoded data in Postgres](https://carbon.sevenlabs.org/guides/postgres.md): Write launches and trades to tables instead of printing them.
- [Pump.fun decoder](https://carbon.sevenlabs.org/decoders/pumpfun.md): Every instruction, account and event, with all fields.

---

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