> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moralis.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Token Bonding Status (Solana Launchpads)

> Sync every pump.fun / Raydium LaunchLab launchpad event, created, traded, migrated, into your own database, and serve a launchpad's new / bonding / graduated token lists plus any token's bonding-curve status.

### Question it answers

> "For a Solana launchpad like **pump.fun**: which tokens just launched, which are still climbing the bonding curve, and which have graduated to a DEX, and what's a given token's bonding-curve status?"

Lands one row per **launchpad event** (`created` / `traded` / `migrated`) from the decoded `launchpadEvents` feed, and derives a per-token latest-status projection. The four legacy pump.fun surfaces, new, bonding, graduated, and per-token status, are all reads over this one recipe.

### What you get

The recipe reads the master-block `launchpadEvents` array and lands two surfaces:

* **The launchpad event log**, keyed by exchange, one row per `created` / `traded` / `migrated` event.
* **A per-token status projection**, keyed by token, latest bonding progress, graduated flag, and graduation time.

| Column                                             | Description                                                                                      |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `token_address`                                    | The launchpad token (SPL mint)                                                                   |
| `exchange_address`                                 | The launchpad program (the legacy `:exchange`, e.g. pump.fun `6EF8…F6P`)                         |
| `launchpad_platform`                               | Friendly name, `pumpfun`, `raydium_launchlab`                                                    |
| `event_type`                                       | `created` · `traded` · `migrated` (**migrated = graduated**)                                     |
| `progress_percentage`                              | Bonding-curve progress 0–100 (sparse, set on trading activity)                                   |
| `pool_address`                                     | The DEX pool a token migrated into, populated on the `migrated` event (take `max(pool_address)`) |
| `token_name`, `token_symbol`, `token_uri`          | Emitted on the `created` event only                                                              |
| `trade_direction`, `token_amount`, `native_amount` | On `traded` events, native price = `native_amount / token_amount`                                |
| `block_number`, `log_index`, `event_ts`, `tx_hash` | On-chain ordering + provenance                                                                   |

### Source

The transform reads one per-block array, `launchpadEvents`, one row per event. `event_type` distinguishes `created` (a new token, carries name/symbol/uri), `traded` (a bonding-curve fill, carries `progress_percentage` and trade amounts), and `migrated` (graduation to a DEX pool, carries `pool_address`). On the Solana feed today the price field (`current_price`) is **not populated**, see [Fidelity gaps](#fidelity-gaps). The `exchange_address` (launchpad program) is shared by every token on a platform.

### Destination

| Destination                  | Tables                                                                         | Access                                                                                                        |
| ---------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **ClickHouse** (first-class) | `fact_launchpad_events` (by exchange) + `fact_token_bonding_status` (by token) | prefix scan on `(chain_id, exchange_address, block_number)` for the event log; per-token aggregate for status |
| **Postgres**                 | `launchpad_events` + `token_bonding_status` (materialized view)                | index on `(exchange_address, block_number)`; unique on `token_address`                                        |
| **MySQL**                    | `launchpad_events` + `token_bonding_status` (trigger-maintained)               | same shape                                                                                                    |

ClickHouse uses the collapsing log-table pattern so chain reorganizations self-correct. `fact_launchpad_events` is ordered for the by-exchange event log (bloom skip index on `token_address` for the by-token log); `fact_token_bonding_status` is keyed by token for the latest-state read.

### Full schema

<Accordion title="ClickHouse, fact_launchpad_events">
  ```sql theme={null}
  CREATE TABLE recipe_token_bonding_status.fact_launchpad_events
  (
      vendor_event_id       String,
      ingested_at           DateTime64(3),
      chain_id              UInt32,
      block_hash            String,
      block_number          UInt64,
      event_ts              DateTime64(3),
      token_address         String,
      launchpad_platform    LowCardinality(String),   -- pumpfun | raydium_launchlab | …
      event_type            LowCardinality(String),   -- created | traded | migrated
      exchange_address      String,
      initiated_by          String,
      token_name            String,
      token_symbol          String,
      token_uri             String,
      token_amount          String,
      native_amount         String,
      trade_direction       LowCardinality(String),   -- buy | sell | ''
      bonding_curve_address String,
      pool_address          String,                    -- DEX pool (migrated events)
      current_price         String,
      progress_percentage   Nullable(Float64),         -- 0-100, sparse
      metadata              String,
      tx_hash               String,
      log_index             Nullable(UInt32),
      transaction_index     Nullable(Int32),
      sign                  Int8,
      INDEX bf_token token_address TYPE bloom_filter(0.01) GRANULARITY 4
  )
  ENGINE = ReplicatedCollapsingMergeTree(
      '/clickhouse/tables/{database}/fact_launchpad_events', '{replica}', sign)
  PARTITION BY (chain_id, toYYYYMM(event_ts))
  ORDER BY (chain_id, exchange_address, block_number, vendor_event_id);
  ```
</Accordion>

<Accordion title="ClickHouse, fact_token_bonding_status (per-token)">
  ```sql theme={null}
  CREATE TABLE recipe_token_bonding_status.fact_token_bonding_status
  (
      vendor_event_id     String,
      ingested_at         DateTime64(3),
      chain_id            UInt32,
      block_number        UInt64,
      event_ts            DateTime64(3),
      token_address       String,
      launchpad_platform  LowCardinality(String),
      exchange_address    String,
      event_type          LowCardinality(String),
      token_name          String,
      token_symbol        String,
      progress_percentage Nullable(Float64),
      current_price       String,
      log_index           Nullable(UInt32),
      sign                Int8
  )
  ENGINE = ReplicatedCollapsingMergeTree(
      '/clickhouse/tables/{database}/fact_token_bonding_status', '{replica}', sign)
  PARTITION BY (chain_id, toYYYYMM(event_ts))
  ORDER BY (chain_id, token_address, block_number, vendor_event_id);
  ```

  Postgres / MySQL keep an append-only `launchpad_events` table plus a `token_bonding_status` current-state projection (a materialized view on Postgres, a trigger-maintained table on MySQL).
</Accordion>

### Run it

Everything runs in **your** infrastructure: the sink writes into a database you own and you query it with plain SQL, no API and no per-call limits.

**Prerequisites**

* **Docker** and Docker Compose.
* A **Data Feeds key**, a `cm_live_…` key with `continuum:read` scope (this is *not* your legacy Web3 API key).

<Card title="Sign up for a Data Feeds key" icon="key" href="https://admin.moralis.com">
  Log in to the Moralis admin panel to create your account and generate a `cm_live_…` key.
</Card>

**Start the sink**

Generate a **starter pack** for `tokenBondingStatus` in the [Moralis Admin Panel](https://admin.moralis.com), choose **Solana** as the chain (launchpad activity is densest on pump.fun and Raydium LaunchLab) and your destination, then run it with Docker. The [quickstart](/data-feeds/migration/quickstart) walks through it end to end.

<Note>
  **Backfill by use case.** The new / bonding / graduated **lists** are event feeds, run **realtime** to track launches live, no backfill needed. A per-token **status** is current state, backfill from the token's `created` event to capture its full curve. See [History & backfill](/data-feeds/concepts/history-and-backfill).
</Note>

### Example reads

**New tokens** on a launchpad, the token's `created` event, newest first:

```sql theme={null}
SELECT token_address,
       argMax(token_name,   (block_number, log_index)) AS name,
       argMax(token_symbol, (block_number, log_index)) AS symbol,
       min(event_ts)                                   AS created_at
FROM recipe_token_bonding_status.fact_launchpad_events FINAL
WHERE exchange_address = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'
  AND event_type = 'created'
GROUP BY token_address
ORDER BY created_at DESC
LIMIT 50;
```

**Bonding tokens**, progress \< 100, not yet graduated, closest to graduating first:

```sql theme={null}
SELECT token_address,
       argMaxIf(progress_percentage, block_number, progress_percentage IS NOT NULL) AS bonding_progress
FROM recipe_token_bonding_status.fact_token_bonding_status FINAL
WHERE exchange_address = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'
GROUP BY token_address
HAVING maxIf(1, event_type = 'migrated') = 0 AND bonding_progress < 100
ORDER BY bonding_progress DESC
LIMIT 50;
```

**Graduated tokens**, tokens with a `migrated` event, plus the DEX pool they migrated into (`max` grabs the non-empty `pool_address`, since a graduation can emit more than one `migrated` row):

```sql theme={null}
SELECT token_address,
       minIf(event_ts, event_type = 'migrated') AS graduated_at,
       max(pool_address)                        AS pool_address
FROM recipe_token_bonding_status.fact_launchpad_events FINAL
WHERE exchange_address = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P'
GROUP BY token_address
HAVING maxIf(1, event_type = 'migrated') = 1
ORDER BY graduated_at DESC
LIMIT 50;
```

**Bonding status** for one token:

```sql theme={null}
SELECT token_address AS mint,
       argMaxIf(progress_percentage, (block_number, log_index),
                progress_percentage IS NOT NULL)  AS bonding_progress,
       maxIf(1, event_type = 'migrated') = 1       AS graduated,
       minIf(event_ts, event_type = 'migrated')    AS graduated_at
FROM recipe_token_bonding_status.fact_token_bonding_status FINAL
WHERE token_address = {mint:String}
GROUP BY token_address;
```

### Other ways to consume this data

The sink is the way to get **these pre-shaped tables**. The *same underlying data* is also available as the **raw decoded feed**, if you'd rather shape it yourself:

* **Your existing message clients**: stream `launchpadEvents` live with **Kafka / AMQP / SQS** (great for a real-time launch feed).
* **REST / Arrow Flight**: pull the decoded Solana block stream directly.
* **Your warehouse**: query it via **Iceberg** from Snowflake, BigQuery, Spark, or DuckDB.

Those give you the raw `launchpadEvents` array to project yourself, not these tables. All read the one data lake into **your** infrastructure. See [What are Data Feeds?](/data-feeds/concepts/what-are-data-feeds).

### Modes

Shipped defaults: **ClickHouse `hybrid`** (backfill → realtime), **Postgres / MySQL `historical`**. For live launch discovery use ClickHouse, see the [overview](/data-feeds/recipes/overview#modes).

### Multichain

Launchpad activity is densest on **Solana** (pump.fun, Raydium LaunchLab) and **Base**; on Ethereum mainnet the `launchpadEvents` array is effectively empty. Point the recipe at Solana in the Admin Panel wizard for real coverage. On Solana `logIndex` isn't row-unique within an instruction, so `vendor_event_id` widens with `(tokenAddress, eventType)` to keep rows distinct.

### Fidelity gaps

* **`name` / `symbol`** ride on the `created` event; a token seen only via `traded` events carries none until you join [Token Metadata](/data-feeds/recipes/token/token-metadata) (also the source of `decimals`).
* **`progress_percentage` is sparse**: `created` events carry no curve progress (0 is coerced to null), so a freshly-created token has null progress until its first trade.
* **No price in the feed.** `current_price` lands **empty on the Solana feed today** (0 populated in live sampling). Derive the **native price** per token from `traded` events (`native_amount / token_amount`), and convert to **USD** with a SOL/USD mark (e.g. wrapped SOL from a [Token Prices](/data-feeds/recipes/token/token-prices) sync). **Liquidity** (cumulative curve `native_amount`, or post-graduation [Pair Reserves](/data-feeds/recipes/markets/pair-reserves)) and **FDV** (`token_metadata.total_supply` × price) are derive-or-omit.
* **`pool_address` arrives on the `migrated` event**: the DEX pool the token graduated into (verified live). A graduation can emit **more than one** `migrated` row, so take the non-empty value with `max(pool_address)` rather than `anyLast`.
* **`logo`** and other off-chain metadata aren't in the feed.

Core fields, `token_address`, `exchange_address`, `event_type`, `progress_percentage`, graduation (the `migrated` event, with `pool_address`), token name/symbol (on `created`), trade amounts, and timestamps, are sourced from `launchpadEvents`.

## Migrating from the REST API

<Warning>
  All four legacy Solana launchpad endpoints below are **deprecated and sunset on July 31, 2026**. This one recipe replaces all of them, each legacy response is a read over the tables above.
</Warning>

The four endpoints share most of their response shape, so the common fields map once:

| Legacy field (all four endpoints) | Data Feeds                                                                                                                    | Fidelity     |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------ |
| `tokenAddress` / `mint`           | `token_address`                                                                                                               | exact        |
| `name`, `symbol`                  | `token_name` / `token_symbol` on the `created` event, or [Token Metadata](/data-feeds/recipes/token/token-metadata)           | exact        |
| `decimals`                        | `token_metadata.decimals` (join [Token Metadata](/data-feeds/recipes/token/token-metadata))                                   | exact        |
| `priceNative`                     | derive `native_amount / token_amount` from `traded` events, `currentPrice` is **not populated** on the Solana feed today      | calculated   |
| `priceUsd`                        | `priceNative` × a SOL/USD mark (wrapped SOL via [Token Prices](/data-feeds/recipes/token/token-prices))                       | calculated   |
| `liquidity`                       | cumulative curve `native_amount` × price; post-graduation, [Pair Reserves](/data-feeds/recipes/markets/pair-reserves) × price | calculated   |
| `fullyDilutedValuation`           | `token_metadata.total_supply` × price                                                                                         | calculated   |
| `logo`                            | not in the feed, bring your own token list, or omit                                                                           | add yourself |

In every legacy URL, `:exchange` is the launchpad's onchain **program address** (pump.fun is `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P`); each event carries it as `exchange_address` plus a friendly `launchpad_platform` (`pumpfun`, `raydium_launchlab`), filter on either. None of the lists have a per-call cap anymore; page with a keyset on the sort column.

### New tokens by exchange

```
GET /token/:network/exchange/:exchange/new
```

"New" is the set of **`created`** events on a launchpad, newest first, a view over `fact_launchpad_events`:

```sql theme={null}
CREATE VIEW new_tokens_by_exchange AS
SELECT
  token_address,
  exchange_address,
  argMax(token_name,   (block_number, log_index)) AS name,
  argMax(token_symbol, (block_number, log_index)) AS symbol,
  min(event_ts)                                   AS created_at
FROM recipe_token_bonding_status.fact_launchpad_events FINAL
WHERE event_type = 'created'
GROUP BY token_address, exchange_address;
-- per launchpad: WHERE exchange_address = :exchange ORDER BY created_at DESC
```

`createdAt` maps to `min(event_ts)` over the `created` event (exact).

* Launches are an **event list**, run the recipe realtime for a live feed, no backfill needed; index back only to list tokens created before you started.
* A brand-new token has **no price and no curve progress** until its first trade.

### Bonding tokens by exchange

```
GET /token/:network/exchange/:exchange/bonding
```

"Bonding" is the set of tokens still on the curve, progress below 100, never migrated, ranked by progress:

```sql theme={null}
CREATE VIEW bonding_tokens_by_exchange AS
SELECT
  token_address,
  exchange_address,
  argMaxIf(progress_percentage, block_number, progress_percentage IS NOT NULL) AS bonding_progress
FROM recipe_token_bonding_status.fact_token_bonding_status FINAL
GROUP BY token_address, exchange_address
HAVING maxIf(1, event_type = 'migrated') = 0   -- still bonding (not graduated)
   AND bonding_progress < 100;
-- per launchpad: WHERE exchange_address = :exchange ORDER BY bonding_progress DESC
```

`bondingCurveProgress` maps to the latest non-null `progress_percentage` per token (exact).

* **Progress is carried forward**: it updates with each trade, and is null until the first trade (same as the old endpoint, which had nothing to report before curve activity).
* A token **leaves the bonding list** the moment it emits a `migrated` event; the `HAVING` clause excludes those.

### Graduated tokens by exchange

```
GET /token/:network/exchange/:exchange/graduated
```

Graduation is the **`migrated`** event, a token with one has left the curve for a DEX pool:

```sql theme={null}
CREATE VIEW graduated_tokens_by_exchange AS
SELECT
  token_address,
  exchange_address,
  minIf(event_ts, event_type = 'migrated') AS graduated_at,
  max(pool_address)                        AS pool_address   -- the migrated DEX pool
FROM recipe_token_bonding_status.fact_launchpad_events FINAL
GROUP BY token_address, exchange_address
HAVING maxIf(1, event_type = 'migrated') = 1;  -- has graduated
-- per launchpad: WHERE exchange_address = :exchange ORDER BY graduated_at DESC
```

`graduatedAt` maps to `minIf(event_ts, event_type = 'migrated')`, and `poolAddress`, which the old endpoint didn't even expose, is `max(pool_address)` over the token's `migrated` events (both exact).

<Note>
  **This one is not a 1:1 swap.** The legacy response bundled prices, liquidity, and FDV, but a graduated token trades on a normal DEX pool, so price it with [Token Prices](/data-feeds/recipes/token/token-prices) and read pool liquidity with [Pair Reserves](/data-feeds/recipes/markets/pair-reserves), keyed on the migrated `pool_address`. Run those recipes alongside this one if you serve those fields.
</Note>

* `pool_address` is **only populated on `migrated` events** (verified live), and a graduation can emit more than one `migrated` row, take the non-empty value with `max(pool_address)`, not `anyLast`.
* Graduations are an event list, run realtime to catch them as they happen.

### Token bonding status

```
GET /token/:network/:address/bonding-status
```

The most direct migration of the set, the recipe's per-token projection *is* this response. `mint`, `bondingProgress`, and `graduatedAt` map to `token_address`, `bonding_progress`, and `graduated_at` (all exact). On Postgres or MySQL it's a single-row lookup on the maintained status table:

```sql theme={null}
SELECT token_address AS mint, bonding_progress, graduated, graduated_at
FROM token_bonding_status
WHERE token_address = $1;
```

On ClickHouse, use the per-token aggregate shown in [Example reads](#example-reads).

* **Status is current state**, not an event list, backfill from the token's `created` event to capture its full curve and graduation history.
* `graduated` / `graduated_at` come from the `migrated` event; both are empty until the token graduates.

### Related

<Columns cols={2}>
  <Card title="Token Metadata" href="/data-feeds/recipes/token/token-metadata" icon="tag">
    Names, symbols, and decimals for launchpad tokens.
  </Card>

  <Card title="Token Prices" href="/data-feeds/recipes/token/token-prices" icon="dollar-sign">
    USD marks to layer onto bonding and graduated tokens.
  </Card>
</Columns>
