> ## 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.64-alpha`
  * 클러스터: Solana `mainnet-beta`
  * 프로그램 ID: [`reference/program-addresses`](/ko/reference/program-addresses) 참조
  * 마지막 검증: **2026-09-09** SDK `0.2.64-alpha` 소스, [`raydium-sdk-V2-demo/src/launchpad`](https://github.com/raydium-io/raydium-sdk-V2-demo/tree/master/src/launchpad), `raydium-launch` 프로그램 소스 기준

  `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) (Bonk 변형의 경우 [`createBonkMintApi.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/createBonkMintApi.ts))

런칭의 대부분의 경제학은 인수가 **아닙니다** — `configId`를 통해 바인딩하는 `GlobalConfig`에서 나오며, SDK는 나머지를 API의 기본 매개변수에서 채웁니다. 민트의 신원, 바인딩할 설정, 그리고 (선택적으로) 첫 번째 매매를 전달합니다:

```ts theme={null}
import {
  LAUNCHPAD_PROGRAM,
  DEVNET_PROGRAM_ID,
  LaunchpadConfig,
  getPdaLaunchpadConfigId,
} from "@raydium-io/raydium-sdk-v2";
import { Keypair, PublicKey } from "@solana/web3.js";
import { NATIVE_MINT } from "@solana/spl-token";

const programId = LAUNCHPAD_PROGRAM;          // devnet: DEVNET_PROGRAM_ID.LAUNCHPAD_PROGRAM

// 기본 민트는 생성하고 서명하는 키페어입니다.
const pair  = Keypair.generate();
const mintA = pair.publicKey;

// GlobalConfig 바인딩: (quote mint, curve_type, index).
const configId = getPdaLaunchpadConfigId(programId, NATIVE_MINT, 0, 0).publicKey;
const configData = await raydium.connection.getAccountInfo(configId);
if (!configData) throw new Error("config not found");
const configInfo = LaunchpadConfig.decode(configData.data);
const mintBInfo = await raydium.token.getTokenInfo(configInfo.mintB);

const { execute, transactions, extInfo } = await raydium.launchpad.createLaunchpad({
  programId,
  mintA,
  decimals: 6,
  name:   "Example Token",
  symbol: "EXMPL",                 // 10자 이하여야 함
  uri:    "https://example.com/metadata.json",   // 비어있지 않아야 함
  migrateType: "amm",              // "amm" | "cpmm"

  configId,
  configInfo,                      // 선택사항; SDK는 생략 시 configId를 다시 읽음
  mintBDecimals: mintBInfo.decimals,
  mintBProgram:  new PublicKey(mintBInfo.programId),

  // 같은 번들의 선택적 첫 번째 매매.
  createOnly: true,                // true = 민트만 생성
  buyAmount:  new BN(1000),        // createOnly가 true일 때 무시됨
  slippage:   new BN(100),         // 100 = 1%

  extraSigners: [pair],            // 기본 민트 키페어가 서명해야 함
  txVersion: TxVersion.V0,

  // 기본값은 바인딩된 설정의 API 기본값에서 나옴; 필요한 경우에만 재정의:
  // supply:            new BN(1_000_000_000_000_000),
  // totalSellA:        new BN(793_100_000_000_000),
  // totalFundRaisingB: new BN(85_000_000_000),   // mintB가 SOL일 때 85 SOL
  // totalLockedAmount: new BN(0),
  // cliffPeriod:       new BN(0),                // 초
  // unlockPeriod:      new BN(0),                // 초
  // platformId:        new PublicKey("4Bu96XjU84XjPDSpveTVf6LYGCkfW5FK7SNkREWcEfV4"), // Raydium의 기본값
});

// createLaunchpad는 여러 트랜잭션을 반환합니다 - 순서대로 실행하세요.
const { txIds } = await execute({ sequentially: true });
console.log("Pool:", extInfo.address.poolId.toBase58());
console.log("Base mint:", mintA.toBase58());
console.log("Create txs:", txIds);
```

참고:

* `curveType`, `graduationFractionBps`, `initialK`, `quoteMint`, `openTime`, `fees` 또는 `postGraduationLpPolicy` 인수는 **없습니다**. 곡선 형태와 quote 민트는 바인딩하는 `GlobalConfig`의 속성입니다; 수수료율은 `GlobalConfig`와 `PlatformConfig`에 있습니다; 졸업 시 LP 처리는 `PlatformConfig` 설정입니다. [`products/launchlab/instructions`](/ko/products/launchlab/instructions#initialize)에서 `InitializeV2`가 정확히 무엇을 수용하는지 확인하세요.
* 10자를 초과하는 `symbol`과 빈 `uri`는 모두 RPC 전에 클라이언트 측에서 거부됩니다.
* `transferFeeExtensionParams`를 전달하면 빌더가 `InitializeWithToken2022`로 전환되고 `migrateType`을 `"cpmm"`으로 강제합니다.
* 빌더는 `transactions` 배열을 반환합니다; `createOnly: false`는 첫 번째 매매 트랜잭션을 추가합니다. 항상 `execute({ sequentially: true })`를 사용하세요 — 민트가 존재해야 매매가 진행됩니다.

## 런칭 상태 조회

소스: [`src/launchpad/poolInfo.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/launchpad/poolInfo.ts)

`getLaunchById`는 없습니다. 풀 ID는 `(programId, mintA, mintB)`의 PDA이며, `getRpcPoolInfo`는 바인딩된 `configInfo`가 첨부된 디코딩된 `PoolState`를 반환합니다:

```ts theme={null}
import { getPdaLaunchpadPoolId, Curve } from "@raydium-io/raydium-sdk-v2";

const poolId = getPdaLaunchpadPoolId(programId, mintA, NATIVE_MINT).publicKey;
const poolInfo = await raydium.launchpad.getRpcPoolInfo({ poolId });

console.log("status:", poolInfo.status);
console.log("base sold:", poolInfo.realA.toString(), "/", poolInfo.totalSellA.toString());
console.log("quote raised:", poolInfo.realB.toString(), "/", poolInfo.totalFundRaisingB.toString());

// 곡선의 현재 현물 가격.
const price = Curve.getPrice({
  poolInfo,
  curveType: poolInfo.configInfo.curveType,
  decimalA: poolInfo.mintDecimalsA,
  decimalB: poolInfo.mintDecimalsB,
});
console.log("price:", price.toString());
```

`getRpcPoolsInfo({ poolIdList })`는 배치 형식이며 풀 ID로 키가 지정된 `poolInfoMap`을 반환합니다 — 루프 대신 포트폴리오 보기에 사용하세요. 원시 계정을 직접 디코딩하려면 `LaunchpadPool.decode`와 `LaunchpadConfig.decode`가 내보내집니다.

## 매매 — 정확한 quote 입력

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

오프체인 견적 도우미는 \*\*`Curve.buyExactIn`\*\*입니다. 내보낸 `Curve` 클래스의 정적 메서드입니다 — `raydium.launchpad`의 메서드가 아닙니다. 트랜잭션 빌더는 \*\*`buyToken`\*\*입니다. 둘 다 플랫폼의 수수료율이 필요하며, 이는 별도의 `PlatformConfig` 계정에 있습니다:

```ts theme={null}
import { Curve, PlatformConfig, toTransferFeeConfig } from "@raydium-io/raydium-sdk-v2";

const inAmount = new BN(1).mul(new BN(10).pow(new BN(9)));   // 1 SOL
const slippage = new BN(100);                                 // 100 = 1%

const poolInfo = await raydium.launchpad.getRpcPoolInfo({ poolId });
const platformData = await raydium.connection.getAccountInfo(poolInfo.platformId);
const platformInfo = PlatformConfig.decode(platformData!.data);

const mintInfo  = await raydium.token.getTokenInfo(mintA);
const mintBInfo = await raydium.token.getTokenInfo(poolInfo.mintB);
const epochInfo = await raydium.connection.getEpochInfo();

// 오프체인 미리보기 — 온체인 곡선 수학을 반영합니다.
const res = Curve.buyExactIn({
  poolInfo,
  amountB: inAmount,
  protocolFeeRate: poolInfo.configInfo.tradeFeeRate,
  platformFeeRate: platformInfo.feeRate,
  creatorFeeRate:  platformInfo.creatorFeeRate,
  curveType:       poolInfo.configInfo.curveType,
  shareFeeRate:    new BN(0),
  transferFeeConfigA: mintInfo.extensions.feeConfig  ? toTransferFeeConfig(mintInfo,  epochInfo.epoch) : undefined,
  transferFeeConfigB: mintBInfo.extensions.feeConfig ? toTransferFeeConfig(mintBInfo, epochInfo.epoch) : undefined,
  slot: await raydium.connection.getSlot(),
});
console.log("expected out:", res.amountA.amount.sub(res.amountA.fee ?? new BN(0)).toString());

const { execute, extInfo } = await raydium.launchpad.buyToken({
  programId,
  mintA,
  mintAProgram: new PublicKey(mintInfo.programId),
  mintB:        poolInfo.mintB,
  mintBProgram: new PublicKey(mintBInfo.programId),
  transferFeeConfigB: toTransferFeeConfig(mintBInfo, epochInfo.epoch),
  poolInfo,
  configInfo:      poolInfo.configInfo,
  platformFeeRate: platformInfo.feeRate,
  buyAmount:       inAmount,
  slippage,                       // BN in bps; SDK는 이로부터 minMintAAmount를 도출합니다
  txVersion: TxVersion.V0,
});

console.log("expected receive amount:", extInfo.decimalOutAmount.toString());
const { txId } = await execute({ sendAndConfirm: true });
console.log("Buy tx:", txId);
```

`slippage` 대신 `minMintAAmount`를 명시적으로 전달할 수 있습니다; 둘 다 생략하면 SDK는 라이브 RPC 데이터에서 바운드를 계산합니다. `shareFeeRate`는 `poolInfo.configInfo.maxShareFeeRate`를 초과할 수 없으며, `shareFeeReceiver`도 전달할 때만 적용됩니다.

## 매매 — 정확한 기본 출력

빌더는 \*\*`buyTokenExactOut`\*\*이며, 금액 인수는 `outAmount` (원하는 기본)과 `maxBuyAmount` (최대 지출할 quote)입니다. `Curve.buyExactOut({ poolInfo, amountA, ... })`는 일치하는 오프체인 미리보기입니다.

```ts theme={null}
const { execute, extInfo } = await raydium.launchpad.buyTokenExactOut({
  programId,
  mintA,
  mintAProgram: new PublicKey(mintInfo.programId),
  mintB:        poolInfo.mintB,
  mintBProgram: new PublicKey(mintBInfo.programId),
  poolInfo,
  configInfo:      poolInfo.configInfo,
  platformFeeRate: platformInfo.feeRate,

  outAmount:    new BN(1_000_000).mul(new BN(10).pow(new BN(6))),  // 1M 기본
  maxBuyAmount: new BN(2).mul(new BN(10).pow(new BN(9))),          // 2 SOL로 제한
  slippage:     new BN(100),

  txVersion: TxVersion.V0,
});

console.log("out:", extInfo.outAmount.toString(), "max spent:", extInfo.maxSpentAmount.toString());
await execute({ sendAndConfirm: true });
```

"정확히 X 토큰 매매" UI에 유용합니다. 빌더는 0의 `outAmount`를 클라이언트 측에서 거부하며, 곡선이 충분히 이동하여 quote 요구사항이 `maxBuyAmount`를 초과하면 프로그램이 되돌립니다.

## 판매

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

빌더는 \*\*`sellToken`\*\*입니다 (정확한 quote 출력 변형의 경우 `sellTokenExactOut`), `sellAmount`와 선택적 `minAmountB`가 있습니다. `Curve.sellExactIn({ poolInfo, amountA, ... })`는 이를 미리봅니다.

```ts theme={null}
const inAmount = new BN(500_000).mul(new BN(10).pow(new BN(6)));  // 0.5M 기본

const res = Curve.sellExactIn({
  poolInfo,
  amountA: inAmount,
  protocolFeeRate: poolInfo.configInfo.tradeFeeRate,
  platformFeeRate: platformInfo.feeRate,
  creatorFeeRate:  platformInfo.creatorFeeRate,
  curveType:       poolInfo.configInfo.curveType,
  shareFeeRate:    new BN(0),
  transferFeeConfigA: mintInfo.extensions.feeConfig  ? toTransferFeeConfig(mintInfo,  epochInfo.epoch) : undefined,
  transferFeeConfigB: mintBInfo.extensions.feeConfig ? toTransferFeeConfig(mintBInfo, epochInfo.epoch) : undefined,
  slot: await raydium.connection.getSlot(),
});
console.log("expected quote out:", res.amountB.toString());

const { execute } = await raydium.launchpad.sellToken({
  programId,
  mintA,
  mintAProgram: new PublicKey(mintInfo.programId),
  mintB:        poolInfo.mintB,
  mintBProgram: new PublicKey(mintBInfo.programId),
  poolInfo,
  configInfo:      poolInfo.configInfo,
  platformFeeRate: platformInfo.feeRate,
  sellAmount:      inAmount,
  slippage:        new BN(100),
  txVersion: TxVersion.V0,
});

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

곡선의 판매 경로는 매매 경로와 대칭입니다: `real_a`를 `sellAmount`로 줄이면 `quote_out`은 `real_a − sellAmount`와 `real_a` 사이의 곡선 아래 적분 영역에서 판매 수수료를 뺀 값과 같습니다. `sellAmount`는 0보다 커야 합니다 — 빌더는 RPC 전에 0을 거부합니다.

## 졸업

<Warning>
  **졸업은 통합자가 트리거할 수 있는 것이 아닙니다.** `raydium.launchpad.graduate`가 없고, 매매 빌더에 `autoGraduate` 플래그가 없으며, 반환 값에 `willGraduate` 필드가 없습니다. 온체인에서 졸업은 `MigrateToCpswap` (또는 레거시 `MigrateToAmm`)이며, 둘 다 서명자를 바인딩 `GlobalConfig`에 기록된 지갑으로 제한합니다 — `migrate_to_cpswap_wallet`과 `migrate_to_amm_wallet` 각각. 다른 서명자의 호출은 `InvalidOwner`로 실패합니다. **허가 없는 것이 아니며**, MEV 봇이 이를 경쟁할 수 없습니다.
</Warning>

이러한 지갑은 Raydium의 졸업 크랭크에 의해 운영되므로, 실제로는 누가 최종 매매를 했든 임계값을 넘은 후 몇 초 내에 마이그레이션이 진행됩니다. 통합자 측에서 할 수 있는 유일한 것은 상태 전환을 빠른 이벤트로 취급하는 것입니다: `getRpcPoolInfo`를 폴링하고 (또는 아래 gRPC를 통해 구독), `status`가 변경될 때 UI를 곡선에서 결과 CPMM 풀로 전환합니다.

계정 목록, 마이그레이션 수수료, 졸업 시 LP 분할 방법은 [`products/launchlab/instructions`](/ko/products/launchlab/instructions#migratetoamm-/-migratetocpswap)을 참조하세요.

## 크리에이터 수수료 수집

소스: [`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) (배치)

메서드는 \*\*`claimCreatorFee`\*\*입니다 (배치 형식의 경우 `claimMultipleCreatorFee`). **quote 민트**로 키가 지정되며, 런칭이 아닙니다 — 한 번의 호출로 서명자의 해당 민트에서 런칭 전체에 걸쳐 누적된 크리에이터 수수료를 쓸어냅니다:

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

const { execute } = await raydium.launchpad.claimCreatorFee({
  programId,
  mintB: NATIVE_MINT,          // 청구할 quote 민트
  // mintBProgram: TOKEN_PROGRAM_ID,   // 또는 TOKEN_2022_PROGRAM_ID
  txVersion: TxVersion.V0,
});

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

누적된 크리에이터 수수료 카운터를 크리에이터의 quote 민트의 ATA로 전송합니다. 졸업 전후로 호출 가능합니다; 거대한 잔액이 축적될 때까지 기다리지 말고 주기적으로 사용하세요. 플랫폼 운영자는 병렬 `claimPlatformFee` / `claimAllPlatformFee` / `claimVaultPlatformFee` 빌더를 가지고 있습니다.

## 런칭을 생명주기를 통해 추적

모두 합치면, 모니터링 스크립트는 다음과 같을 수 있습니다:

```ts theme={null}
async function watch(poolId: PublicKey) {
  const startStatus = (await raydium.launchpad.getRpcPoolInfo({ poolId })).status;

  while (true) {
    const p = await raydium.launchpad.getRpcPoolInfo({ poolId });
    const progress = Number(p.realB.toString()) / Number(p.totalFundRaisingB.toString());

    console.log(`status=${p.status}`, `progress=${(progress * 100).toFixed(2)}%`);

    if (p.status !== startStatus) {
      console.log("status changed - the launch has migrated; switch to the pool page");
      break;
    }
    await new Promise((r) => setTimeout(r, 10_000));
  }
}
```

10초 타이머로 `getRpcPoolInfo`를 폴링하는 것은 단일 런칭에 적합합니다. 인덱서의 경우 대신 아래 gRPC 구독을 사용하세요 — `getRpcPoolsInfo({ poolIdList })`는 배치하지만, 수백 개의 풀을 폴링하는 것은 여전히 이점 없이 RPC 크레딧을 소모합니다.

## gRPC를 통해 설정 및 풀 감시

소스: [`src/grpc/launchpadPoolInfo.ts`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/grpc/launchpadPoolInfo.ts)

Yellowstone gRPC를 통해 LaunchLab 프로그램을 구독하고 각 민트의 토큰 프로그램과 함께 모든 `GlobalConfig` 및 `PoolState` 업데이트를 기록합니다. 한 `accounts` 구독에 두 개의 `datasize` 필터가 두 계정 유형을 분리하므로, 단일 스트림이 둘 다를 포함합니다.

데모는 런칭의 민트를 분류하기 위한 가장 짧은 올바른 참조이며, [Token-2022 quote-mint 릴리스](/ko/reference/changelog/2026-08-24-launchlab-token2022-quote-mint)는 이를 필요하게 합니다:

* **풀**은 `PoolState.token_program_flag`에서 직접 분류되며, 민트 조회 없이 기본 민트에 대해 bit0을, quote 민트에 대해 bit1을 디코딩합니다. 전체 바이트를 `0`에 대해 테스트하는 것 — quote 민트가 레거시 전용이었을 때 충분했던 — quote 민트가 Token-2022일 때마다 레거시 기본 민트를 Token-2022로 잘못 읽습니다.
* **설정**은 그런 식으로 분류될 수 없습니다: `GlobalConfig`는 quote 민트의 주소를 저장하고 프로그램 플래그는 없으므로, 데모는 민트 계정의 소유자를 한 번 읽고 민트당 캐시합니다.

또한 둘을 서로 교차 검사하며, 이는 자신의 인덱서에 유지할 유용한 어설션입니다. `create` 대 `update`는 데모의 자체 캐시에서 추론되며, 시작 시 `getProgramAccounts`에서 시드됩니다; 시드가 비활성화되면 "create"를 "이 스트림에서 처음 본" 것으로 읽으세요.

gRPC 액세스는 공개 RPC 표면의 일부가 아닙니다 — 데모 저장소의 [`src/grpc/README.md`](https://github.com/raydium-io/raydium-sdk-V2-demo/blob/master/src/grpc/README.md)를 참조하세요.

## Rust CPI

자신의 Anchor 프로그램에서 LaunchLab을 호출하는 것은 드뭅니다 (대부분의 런칭 통합은 TS 측만입니다). 그렇다면:

* 크레이트는 \*\*`raydium-launchpad`\*\*입니다 (lib 이름 `raydium_launchpad`), 버전 `0.2.0`, `cpi` 기능을 노출합니다. **`raydium_launchlab`은 없습니다**.
* **crates.io에 게시되지 않습니다** — 다른 Raydium 프로그램 크레이트와 동일한 방식으로 git 리비전으로 의존합니다.
* CPI 진입점은 SDK의 TypeScript 메서드가 아닌 온체인 명령어 이름을 따릅니다: `buy_exact_in`, `buy_exact_out`, `sell_exact_in`, `sell_exact_out`, `claim_creator_fee`, `create_vesting_account`, `claim_vested_token`. `migrate_to_amm` / `migrate_to_cpswap`은 `GlobalConfig` 마이그레이션 지갑에 서명자 게이트되며 통합자 프로그램에서 호출할 수 없습니다.

크레이트 고정 규칙은 [`sdk-api/rust-cpi`](/ko/sdk-api/rust-cpi)를 참조하세요 — 특히 Anchor 버전 분할은 한 크레이트가 LaunchLab과 CLMM 모두에 CPI할 수 있는지 결정합니다.

## 함정

* **온체인 명령어 이름과 SDK 메서드 이름을 혼동합니다.** 프로그램은 `buy_exact_in` / `sell_exact_in`을 가지고 있습니다; SDK는 `buyToken` / `sellToken`을 가지고 있습니다. `buyExactIn`과 `sellExactIn`은 **`Curve`의 정적 메서드**로만 존재하며, 견적을 계산합니다 — 트랜잭션을 빌드하지 않습니다.
* **`extraSigners` 잊기.** `createLaunchpad`는 생성하는 키페어에서 기본 민트를 생성하므로, 해당 키페어가 서명해야 합니다. `extraSigners: [pair]`를 생략하면 트랜잭션이 서명 검증에 실패합니다.
* **quote 민트를 선택하지 않습니다.** 하나를 전달하지 않습니다. quote 민트는 `configId`를 통해 바인딩하는 `GlobalConfig`의 속성입니다 — `getPdaLaunchpadConfigId(programId, quoteMint, curveType, index)`. 다른 quote 민트에 대해 런칭하려면 다른 설정을 바인딩하세요; `raydium.api.fetchLaunchConfigs()`는 기본 매개변수와 함께 사용 가능한 것들을 나열합니다.
* **메타데이터 크기.** 긴 `uri` 문자열은 Metaplex CPI를 예산 초과로 밀어냅니다. `uri`를 \~200자 이하로 유지하세요 — 대부분의 CDN 호스팅 JSON 메타데이터는 쉽게 맞습니다. `symbol`은 10자로 하드 캡됩니다.
* **졸업을 경쟁할 수 있는 경쟁으로 취급합니다.** 그렇지 않습니다 — 위의 경고를 참조하세요. UI는 여전히 `status` 전환을 빠른 이벤트로 취급해야 합니다. Raydium의 크랭크는 임계값을 넘은 후 몇 초 내에 마이그레이션을 진행하기 때문입니다.

## 다음 단계

* [`products/launchlab/bonding-curve`](/ko/products/launchlab/bonding-curve) — 곡선 수학.
* [`products/cpmm/code-demos`](/ko/products/cpmm/code-demos) — 결과 풀로 할 일.
* [`user-flows/launch-token-launchlab`](/ko/user-flows/launch-token-launchlab) — 오프체인 단계를 포함한 엔드-투-엔드 토큰 런칭 가이드.

소스:

* [Raydium SDK v2](https://github.com/raydium-io/raydium-sdk-V2)
* Raydium LaunchLab 프로그램 소스


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.