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

# LaunchLab 代碼示例

> 完整 TypeScript 流程：建立 LaunchLab 啟動、在連續曲線上買賣、畢業進入 CPMM 流動性池，以及領取創作者費用。

<Info>
  **本頁內容由 AI 自動翻譯，所有內容以英文版本為準。**

  [查看英文版 →](/products/launchlab/code-demos)
</Info>

<Info>
  **版本資訊。**

  * SDK: `@raydium-io/raydium-sdk-v2@0.2.42-alpha`
  * 集群：Solana `mainnet-beta`
  * 程式 ID：參見 [`reference/program-addresses`](/zh-Hant/reference/program-addresses)
  * 最後驗證時間：2026-04

  在你的 `package.json` 中釘選 SDK 版本。連續曲線介面在次要版本間有所變化。
</Info>

## 設定

此處的示例對應 [`raydium-sdk-V2-demo/src/launchpad`](https://github.com/raydium-io/raydium-sdk-V2-demo/tree/master/src/launchpad) 中的檔案。初始化方式遵循示例倉庫的 [`config.ts.template`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/config.ts.template)：

```ts theme={null}
import { Connection, Keypair, clusterApiUrl, PublicKey } from "@solana/web3.js";
import { Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";
import fs from "node:fs";

const connection = new Connection(process.env.RPC_URL ?? clusterApiUrl("mainnet-beta"));
const owner = Keypair.fromSecretKey(
  new Uint8Array(JSON.parse(fs.readFileSync(process.env.KEYPAIR!, "utf8"))),
);
const raydium = await Raydium.load({
  owner,
  connection,
  cluster: "mainnet",
  disableFeatureCheck: true,
  blockhashCommitment: "finalized",
});
export const txVersion = TxVersion.V0;
```

## 建立啟動

來源：[`src/launchpad/createMint.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/createMint.ts)（以及 [`createBonkMintApi.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/createBonkMintApi.ts) 中的 API 驅動 Bonk 變體）

```ts theme={null}
import { NATIVE_MINT } from "@solana/spl-token";

const { execute, extInfo } = await raydium.launchpad.createLaunchpad({
  programId: /* LaunchLab program ID from reference/program-addresses */,
  // Token metadata for the new base mint:
  name:   "Example Token",
  symbol: "EXMPL",
  uri:    "https://example.com/metadata.json",
  decimals: 6,

  // Curve params:
  curveType: 0,                                      // 0 = quadratic
  supply:    new BN(1_000_000_000).mul(new BN(10).pow(new BN(6))), // 1B base (6 dec)
  graduationFractionBps: 8000,                       // 80% → graduation
  initialK:  new BN("40"),                           // curve shape parameter

  // Quote side:
  quoteMint: NATIVE_MINT,                            // WSOL
  openTime:  new BN(Math.floor(Date.now() / 1000) + 60),  // opens in 1min

  // Fee policy:
  fees: {
    buyNumerator:   new BN(100),                     // 1.00%
    buyDenominator: new BN(10_000),
    sellNumerator:  new BN(100),
    sellDenominator: new BN(10_000),
    lpShare:         new BN(60),
    creatorShare:    new BN(20),
    protocolShare:   new BN(20),
    totalShare:      new BN(100),
  },

  postGraduationLpPolicy: "burn",                    // "burn" | "lock" | "toCreator"

  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Launch:", extInfo.launchState.toBase58());
console.log("Base mint:", extInfo.baseMint.toBase58());
console.log("Create tx:", txId);
```

注意事項：

* `initialK` 是二次曲線的縮放係數。調整它以在畢業時瞄準特定的開盤 CPMM 價格。詳見 [`products/launchlab/bonding-curve`](/zh-Hant/products/launchlab/bonding-curve) 的推導。
* SDK 在單一交易中處理建立基礎鑄造、中繼資料 PDA 和兩個保管庫。如果中繼資料 URI 很長，可能超過 1232 位元組；在這種情況下，SDK 會分成兩個交易。
* `Initialize` 之後，啟動直到 `openTime` 才可交易。將 `openTime` 設定為提前一到兩分鐘，以減少搶先交易者搶奪首筆買入的機會。

## 取得啟動狀態

```ts theme={null}
const launchId = new PublicKey("<LAUNCH_STATE>");

const launch = await raydium.launchpad.getLaunchById({ launchId });
console.log("Status:", ["Active","Graduated","Cancelled"][launch.status]);
console.log("Base sold:", launch.baseSold.toString(),
            "/", launch.baseSupplyMax.toString());
console.log("Quote collected:", launch.quoteReserveReal.toString(),
            "target:", launch.quoteReserveTarget.toString());
if (launch.status === 1) {
  console.log("Post-graduation CPMM pool:", launch.cpmmPoolState.toBase58());
}
```

`getLaunchById` 傳回解碼的 `LaunchState` 以及計算出的「畢業進度」分數作為 `Decimal`。

## 買入 — 精確報價輸入

來源：[`src/launchpad/buy.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/buy.ts)

```ts theme={null}
const quoteIn        = new BN(1).mul(new BN(10).pow(new BN(9)));  // 1 SOL
const minimumBaseOut = new BN(0);  // accept any; tighten for production

// Preview the quote off-chain so your UI can show expected base_out:
const preview = raydium.launchpad.computeBuyBase({
  launchState: launch,
  quoteIn,
});
console.log("Expected base_out:", preview.baseOut.toString(),
            "price impact:", preview.priceImpact.toString());

const { execute } = await raydium.launchpad.buyExactIn({
  launchInfo: launch,
  quoteIn,
  minimumBaseOut,
  txVersion: TxVersion.V0,
});

const { txId } = await execute({ sendAndConfirm: true });
console.log("Buy tx:", txId);
```

`computeBuyBase` 鏡像了鏈上牛頓求解器（二次曲線）或閉式 CPMM 反函數（curve\_type 1）。使用它來填充「你將收到」UI 欄位。

## 買入 — 精確基礎輸出

```ts theme={null}
const baseOut         = new BN(1_000_000).mul(new BN(10).pow(new BN(6)));  // 1M base
const maximumQuoteIn  = new BN(2).mul(new BN(10).pow(new BN(9)));          // cap at 2 SOL

const { execute } = await raydium.launchpad.buyExactOut({
  launchInfo: launch,
  baseOut,
  maximumQuoteIn,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

適用於「精確買入 X 個代幣」的 UI。如果曲線移動幅度足夠使得報價要求現在超過 `maximumQuoteIn`，則以 `ExceededSlippage` 拒絕。

## 賣出

來源：[`src/launchpad/sell.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/sell.ts)

```ts theme={null}
const baseIn           = new BN(500_000).mul(new BN(10).pow(new BN(6)));  // 0.5M base
const minimumQuoteOut  = new BN(0);

const { execute } = await raydium.launchpad.sellExactIn({
  launchInfo: launch,
  baseIn,
  minimumQuoteOut,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

曲線的賣出路徑對稱於買入路徑：將 `base_sold` 減少 `baseIn` 會返回 `quote_out`，等於曲線下 `base_sold − baseIn` 和 `base_sold` 之間的積分面積，減去賣出費用。

## 閾值交叉買入時自動畢業

當 SDK 偵測到買入後狀態將超過閾值時，會在 `buy*` 交易內部鏈結 `Graduate` 指令：

```ts theme={null}
const { execute, willGraduate } = await raydium.launchpad.buyExactIn({
  launchInfo: launch,
  quoteIn: new BN(100).mul(new BN(10).pow(new BN(9))),    // large buy
  minimumBaseOut: new BN(0),
  txVersion: TxVersion.V0,
  autoGraduate: true,                                      // default
});

if (willGraduate) {
  console.log("This buy will trigger graduation.");
}

const { txId } = await execute({ sendAndConfirm: true });
```

由於 `Graduate` 沒有權限限制，任何人（包括 MEV 機器人）都可以競相在閾值超過後著陸首個 `Graduate`——通常在幾秒後，而非幾分鐘。首個著陸者只需支付 CPMM 池帳戶的租金；他們沒有其他好處。

## 手動 `Graduate`

如果 `autoGraduate` 已關閉或閾值交叉交易失敗，你可以單獨觸發畢業：

```ts theme={null}
const { execute } = await raydium.launchpad.graduate({
  launchInfo: launch,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

如果在提交時 `quote_reserve_real < quote_reserve_target`，則以 `NotAtThreshold` 還原。重試安全——成功後的第二次 `Graduate` 嘗試將以 `NotActive` 還原。

## 領取創作者費用

來源：[`src/launchpad/claimCreatorFee.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/claimCreatorFee.ts)（單一鑄造）和 [`collectAllCreatorFees.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/collectAllCreatorFees.ts)（批次）

```ts theme={null}
const { execute } = await raydium.launchpad.collectCreatorFees({
  launchInfo: launch,
  txVersion: TxVersion.V0,
});

await execute({ sendAndConfirm: true });
```

將累計的創作者費用計數器報價金額轉移到創作者在報價鑄造上的 ATA。可在畢業前後呼叫；定期使用而非等待龐大餘額累積。

## 追蹤啟動的完整生命週期

綜合起來，監控指令碼可能看起來像：

```ts theme={null}
async function watch(launchId: PublicKey) {
  while (true) {
    const launch = await raydium.launchpad.getLaunchById({ launchId });
    const progress =
      Number(launch.quoteReserveReal) /
      Number(launch.quoteReserveTarget);

    console.log(
      `status=${["Active","Graduated","Cancelled"][launch.status]}`,
      `progress=${(progress * 100).toFixed(2)}%`,
      `num_buys=${launch.stateData.numBuys}`,
    );

    if (launch.status === 1) {
      console.log("Graduated to CPMM pool:", launch.cpmmPoolState.toBase58());
      break;
    }
    await new Promise(r => setTimeout(r, 10_000));
  }
}
```

## Rust CPI

從你自己的 Anchor 程式呼叫 LaunchLab 很罕見（大多數啟動整合僅限 TS 端）。如果你這樣做，程式會提供 Anchor crate `raydium_launchlab`，包含 `cpi::accounts::Buy`、`cpi::accounts::Sell` 等——模式鏡像 CPMM / CLMM CPI 示例。此網站填充後，參見 [`sdk-api/rust-cpi`](/zh-Hant/sdk-api/rust-cpi) 的通用範本。

## 常見陷阱

* **費用分割算術舍入誤差。** 如果 `total_share` 不完全等於 `lp_share + creator_share + protocol_share`，`Initialize` 將以 `InvalidFeeShares` 還原。將 `totalShare` 設定為總和。
* **使用不允許的報價鑄造。** `launch_config.allowed_quote_mints` 是固定列表；傳遞任何其他鑄造都會還原。先用 `raydium.launchpad.getConfig()` 檢查。
* **中繼資料大小。** 長 `uri` 字串將 Metaplex CPI 推過預算。將 `uri` 保持在 \~200 字符以下——大多數 CDN 託管的 JSON 中繼資料輕鬆適應。
* **畢業競賽。** 自動機器人監控 `quote_reserve_real` 並在閾值超過後一兩個時隙內搶先 `Graduate`。這是良性的——它只會花費他們租金——但這意味著你的 UI 應該將 `status` 轉換視為快速事件。

## 後續步驟

* [`products/launchlab/bonding-curve`](/zh-Hant/products/launchlab/bonding-curve) — 曲線數學。
* [`products/cpmm/code-demos`](/zh-Hant/products/cpmm/code-demos) — 如何使用產生的池。
* [`user-flows/launch-token-launchlab`](/zh-Hant/user-flows/launch-token-launchlab) — 端對端啟動代幣指南，包括鏈外步驟。

來源：

* [Raydium SDK v2](https://github.com/raydium-io/raydium-sdk-V2)
* Raydium LaunchLab 程式源代碼
