ローンチタイプ

Launch Pool

Last updated October 12, 2026

Launch Pool は Solana 上でのフェアトークンローンチのための自然な価格発見メカニズムを提供します。ユーザーはウィンドウ期間中に SOL を入金し、総入金額に対するシェアに比例して SPL トークンを受け取ります。スナイピングなし、フロントランニングなし、全員にとって公平な配布です。

学べること

このガイドでは以下を説明します:

  • Launch Pool の価格設定と配布の仕組み
  • 入金ウィンドウと請求ウィンドウの設定方法
  • 資金回収のための End Behavior の設定
  • ユーザー操作:入金、引き出し、請求

概要

Launch Pool は、定義されたウィンドウ期間中に入金を受け付け、トークンを比例配分で配布するクラウドセール型のトークンローンチメカニズムです。最終的なトークン価格は、総入金額をトークン割り当て量で割って決定され、トークン生成イベント(TGE)のための透明なオンチェーン価格発見を実現します。

  • ユーザーは入金ウィンドウ期間中に SOL を入金します(0% の手数料が適用)
  • 入金期間中は引き出しが可能です(0% の手数料)
  • トークン配布は入金シェアに比例します
  • 任意のソフトキャップにより、ローンチが保持する額に上限を設け、超過分を比例配分で返金できます
  • End Behavior が集められた SOL をトレジャリー bucket にルーティングします

Launch Pool は入金に基づいて価格を発見します。事前に設定された固定価格には Presale を、入札ベースのクリアリングには Uniform Price Auction を使用してください。流動性プールの作成は、Launch Pool bucket 自体ではなく Raydium グラデュエーション bucket が担当します。

クイックスタート

仕組み

  1. 特定量のトークンが Launch Pool bucket に割り当てられます
  2. ユーザーは入金ウィンドウ期間中に SOL を入金します(手数料付きで引き出し可能)
  3. ウィンドウが閉じると、入金シェアに基づいてトークンが比例配分されます

価格発見

トークン価格は総入金額から決まります:

tokenPrice = totalDeposits / tokenAllocation
userTokens = (userDeposit / totalDeposits) * tokenAllocation

例: 1,000,000 トークンが割り当てられ、総入金額が 100 SOL の場合 = 1トークンあたり 0.0001 SOL

価格発見はデフォルトでは上限がなく、申込が増えるほど暗黙の価格は上昇します。ソフトキャップを設定すると、その上限を設けられます。

ライフサイクル

  1. 入金期間 - ユーザーは定義されたウィンドウ期間中に SOL を入金します
  2. triggerBehaviorsV2 - End Behavior が実行されます(例:集められた SOL を別の bucket に送信)
  3. 請求期間 - ユーザーは入金の重みに比例してトークンを請求します
  4. 返金期間(条件付き) - ソフトキャップを超過した場合、または最小 Quote Token しきい値に届かなかった場合、入金者は refundLaunchPoolV2 を呼び出します

Launch Pool のソフトキャップ

ソフトキャップは、Launch Pool が保持する Quote Token の上限であり、addLaunchPoolBucketV2 の softCap 拡張で設定します。上限を超える入金も入金ウィンドウ期間中は受け付けられ、ローンチは成功し、超過分の Quote Token はウィンドウ終了後に比例配分で返金されます。

ソフトキャップがない場合、価格発見には上限がありません。すべての入金が保持され、申込が増えるほどトークンの暗黙の価格が上昇します。ソフトキャップはローンチの最大調達額を固定するため、入金がどれだけ上限を超えても、実効価格は softCap / baseTokenAllocation になります。

プロパティソフトキャップ設定時の動作
上限を超える入金入金ウィンドウ期間中は受け付けられ、上限で入金が拒否されることはありません
ローンチの結果成功します。ソフトキャップは上限であり、失敗条件ではありません
Base Token の配布baseTokenAllocation 全体がすべての入金に比例して配布されます
超過分の Quote Token入金ウィンドウ終了後、refundLaunchPoolV2 により比例配分で返金されます
End Behavior から見える調達額ソフトキャップに制限されるため、SendQuoteTokenPercentage が転送するのは最大でも softCap です
Raydium グラデュエーションの開始価格生の入金総額ではなく、上限適用後の調達額から算出されます

ソフトキャップは調達額の上限であり、下限ではありません。下限は別の拡張である minimumQuoteTokenThreshold です。詳しくはソフトキャップと最小 Quote Token しきい値の併用を参照してください。

Launch Pool Bucket にソフトキャップを設定する

bucket を追加する際に、addLaunchPoolBucketV2 に softCap の値を渡します。金額は Quote Token の最小単位(wSOL の場合は lamports)で指定します。

100 SOL のソフトキャップを持つ Launch Pool bucket を追加する
import { sol } from '@metaplex-foundation/umi';
await addLaunchPoolBucketV2(umi, {
genesisAccount,
baseMint: baseMint.publicKey,
baseTokenAllocation: TOTAL_SUPPLY,
// ...time conditions and end behaviors...
// Floor: the launch fails below this and everyone can take a full refund.
minimumQuoteTokenThreshold: { amount: sol(10).basisPoints },
// Ceiling: the launch keeps at most this much; the rest is refunded pro-rata.
softCap: { amount: sol(100).basisPoints },
}).sendAndConfirm(umi);

softCap は @metaplex-foundation/genesis 0.42.0 の addLaunchPoolBucketV2 における必須引数です。他の Launch Pool 拡張とは異なりデフォルト値がないため、上限を設けない場合は明示的に softCap: null を渡してください。

ソフトキャップは、拡張を設定する際と finalizeV2 の際に検証されます:

ルール違反時のエラー
softCap.amount は 0 より大きい必要がありますInvalidSoftCap (221)
softCap.amount は minimumQuoteTokenThreshold.amount 以上である必要がありますSoftCapBelowThreshold (222)
拡張の追加・削除は finalizeV2 の前にのみ可能ですアカウントはファイナライズ済みとして拒否されます

既存の bucket に対しても、LaunchPoolV2ExtensionType の SoftCap メンバーを使って addLaunchPoolBucketV2Extensions と removeLaunchPoolBucketV2Extensions でソフトキャップを設定または解除できます。ただし、Genesis Account がファイナライズされる前に限ります。

申込超過と比例配分返金の計算

quoteTokenDepositTotal が softCap.amount を厳密に上回る場合、Launch Pool は**申込超過(オーバーサブスクライブ)**の状態です。このとき各入金は、上限にカウントされる*充当分(filled)と、返金対象の超過分(excess)*に分割されます:

申込超過の Launch Pool における入金者ごとの分割
filled_i = ceil(deposit_i * softCap / totalDeposits)
excess_i = deposit_i - filled_i
tokens_i = (weighted_i / weightedQuoteTokenTotal) * baseTokenAllocation

filled は切り上げで計算されるため、すべての充当分の合計は常にソフトキャップ以上になります。これにより、返金とグラデュエーションのどちらを先に実行しても、上限適用後のグラデュエーション転送に対して bucket の残高が不足することはありません。各入金者の端数の切り上げは 1 最小単位未満であるため、bucket に残る端数(ダスト)は合計で最大 depositCount - 1 最小単位です。

トークン割り当ては上限の影響を受けません。超過分を返金しても入金者の重み付き貢献分は削除されないため、全員が入金全額に比例してトークンを受け取ります。

具体例 — 1,000,000 トークンが割り当てられ、ソフトキャップが 100 SOL、入金額が 150 SOL の場合:

入金者入金額充当分(保持)返金額受け取るトークン
Alice50 SOL約 33.33 SOL約 16.67 SOL333,333(1/3)
Bob100 SOL約 66.67 SOL約 33.33 SOL666,667(2/3)
合計150 SOL100 SOL50 SOL1,000,000

実効価格は 1トークンあたり 0.0001 SOL(100 SOL / 1,000,000)であり、上限なしの入金総額から導かれる 1トークンあたり 0.00015 SOL ではありません。オンチェーンの値は filled を切り上げた lamports 単位で計算されるため、実際の数値は上記の丸めた SOL 額と数 lamports 異なります。

refundLaunchPoolV2 で超過入金を返金する

refundLaunchPoolV2 は、申込超過の入金ウィンドウ終了後に、入金者の超過分の Quote Token を返金します。金額の引数はなく、プログラムが入金額、ソフトキャップ、bucket の入金総額から返金額を計算します。

refundLaunchPool.ts
1import {
2 genesis,
3 refundLaunchPoolV2,
4} from '@metaplex-foundation/genesis'
5import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7
8const umi = createUmi('https://api.mainnet-beta.solana.com')
9 .use(mplToolbox())
10 .use(genesis())
11
12// umi.use(keypairIdentity(yourKeypair));
13
14// Assumes genesisAccount, launchPoolBucket, and baseMint from previous steps.
15// Only valid after the deposit window closed AND either the
16// minimumQuoteTokenThreshold was missed or the softCap was exceeded.
17
18// The program computes the refundable amount, so there is no amount argument.
19// A missed threshold refunds the full deposit; an exceeded soft cap refunds
20// only the excess, leaving the depositor's token allocation intact.
21await refundLaunchPoolV2(umi, {
22 genesisAccount,
23 bucket: launchPoolBucket,
24 baseMint: baseMint.publicKey,
25 recipient: umi.identity.publicKey,
26}).sendAndConfirm(umi)

返金パスの主な特性:

  • クランクは誰でも実行できます。 署名が必要なのは payer のみです。入金者も署名した場合、入金者の空の Base Token アカウントがクローズされます。
  • 返金には手数料もペナルティも適用されません。入金・引き出しのペナルティスケジュールは返金に影響しません。
  • 請求の順序は問いません。 超過分の返金は claimLaunchPoolV2 の前でも後でも可能で、どちらの順序でも同じ最終状態になります。
  • 1 つの入金につき返金は 1 回です。 2 回目の呼び出しは DepositAlreadyRefunded を返します。
  • 返金は入金ウィンドウの終了後に限られます。 それより前に呼び出すと LaunchPoolNotEnded を返します。
  • 返金には、下限未達または上限超過が必要です。 どちらにも該当しない場合、呼び出しは LaunchPoolThresholdMet を返します。

ソフトキャップと最小 Quote Token しきい値の併用

softCap と minimumQuoteTokenThreshold は、Launch Pool を反対方向から制限する独立した拡張であり、refundLaunchPoolV2 は両方に対応します。下限に達しなかった場合はそちらが優先され、返金は全額返金になります。

構成入金が下限未満入金が下限と上限の間入金が上限超過
いずれも未設定ローンチ成功、返金なしローンチ成功、返金なしローンチ成功、返金なし
下限のみローンチ失敗、全額返金ローンチ成功、返金なしローンチ成功、返金なし
上限のみローンチ成功、返金なしローンチ成功、返金なしローンチ成功、超過分を比例配分で返金
下限と上限ローンチ失敗、全額返金ローンチ成功、返金なしローンチ成功、超過分を比例配分で返金

全額返金は入金者の重み付き貢献分を bucket から削除するため、請求の後に行うことはできません。下限未達の場合は請求自体が不可能です。超過分の返金は重み付き貢献分をそのまま残すため、比例配分の請求計算式は正しいままです。

手数料

インストラクションSolana
User deposit fee0%
User withdraw fee0%
Creator withdraw fee5%*

* This fee only applies when creators withdraw liquidity

各入金では、0% のユーザー入金手数料を差し引いた後の SOL が入金アカウントの残高に反映されます。

セットアップガイド

前提条件

npm install @metaplex-foundation/genesis @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-toolbox

1. Genesis Account の初期化

Genesis Account はトークンを作成し、すべての配布 bucket を調整します。

initializeV2.ts
1import {
2 findGenesisAccountV2Pda,
3 genesis,
4 initializeV2,
5} from '@metaplex-foundation/genesis'
6import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
7import { generateSigner, keypairIdentity } from '@metaplex-foundation/umi'
8import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
9
10const umi = createUmi('https://api.mainnet-beta.solana.com')
11 .use(mplToolbox())
12 .use(genesis())
13
14// umi.use(keypairIdentity(yourKeypair));
15
16const baseMint = generateSigner(umi)
17const TOTAL_SUPPLY = 1_000_000_000_000_000n // 1 million tokens (9 decimals)
18
19// Store this account address for later or recreate it when needed.
20const [genesisAccount] = findGenesisAccountV2Pda(umi, {
21 baseMint: baseMint.publicKey,
22 genesisIndex: 0,
23})
24
25await initializeV2(umi, {
26 baseMint,
27 fundingMode: 0,
28 totalSupplyBaseToken: TOTAL_SUPPLY,
29 name: 'My Token',
30 symbol: 'MTK',
31 uri: 'https://example.com/metadata.json',
32}).sendAndConfirm(umi)

totalSupplyBaseToken は、すべての bucket 割り当ての合計と等しくなるようにしてください。

2. Launch Pool Bucket の追加

Launch Pool bucket は入金を収集し、トークンを比例配分で配布します。ここでタイミングを設定します。

addLaunchPoolBucket.ts
1import {
2 addLaunchPoolBucketV2,
3 findLaunchPoolBucketV2Pda,
4 findUnlockedBucketV2Pda,
5 genesis,
6} from '@metaplex-foundation/genesis'
7import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
8import { publicKey } from '@metaplex-foundation/umi'
9import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
10
11const umi = createUmi('https://api.mainnet-beta.solana.com')
12 .use(mplToolbox())
13 .use(genesis())
14
15// umi.use(keypairIdentity(yourKeypair));
16
17// Assumes genesisAccount, baseMint, and TOTAL_SUPPLY from the Initialize step.
18
19const [launchPoolBucket] = findLaunchPoolBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 })
20const [unlockedBucket] = findUnlockedBucketV2Pda(umi, { genesisAccount, bucketIndex: 0 })
21
22const now = BigInt(Math.floor(Date.now() / 1000))
23const depositStart = now
24const depositEnd = now + 86400n // 24 hours
25const claimStart = depositEnd + 1n
26const claimEnd = claimStart + 604800n // 1 week
27
28await addLaunchPoolBucketV2(umi, {
29 genesisAccount,
30 baseMint: baseMint.publicKey,
31 baseTokenAllocation: TOTAL_SUPPLY,
32
33 // Timing
34 depositStartCondition: {
35 __kind: 'TimeAbsolute',
36 padding: Array(47).fill(0),
37 time: depositStart,
38 triggeredTimestamp: null,
39 },
40 depositEndCondition: {
41 __kind: 'TimeAbsolute',
42 padding: Array(47).fill(0),
43 time: depositEnd,
44 triggeredTimestamp: null,
45 },
46 claimStartCondition: {
47 __kind: 'TimeAbsolute',
48 padding: Array(47).fill(0),
49 time: claimStart,
50 triggeredTimestamp: null,
51 },
52 claimEndCondition: {
53 __kind: 'TimeAbsolute',
54 padding: Array(47).fill(0),
55 time: claimEnd,
56 triggeredTimestamp: null,
57 },
58
59 // Optional: Minimum deposit
60 minimumDepositAmount: null, // or { amount: sol(0.1).basisPoints }
61
62 // Required since @metaplex-foundation/genesis 0.42.0: pass null for no soft cap,
63 // or { amount: sol(100).basisPoints } to cap the quote tokens kept
64 softCap: null,
65
66 // Where collected SOL goes after transition
67 endBehaviors: [
68 {
69 __kind: 'SendQuoteTokenPercentage',
70 padding: Array(4).fill(0),
71 destinationBucket: publicKey(unlockedBucket),
72 percentageBps: 10000, // 100%
73 processed: false,
74 },
75 ],
76}).sendAndConfirm(umi)

3. Unlocked Bucket の追加

Unlocked bucket は triggerBehaviorsV2 実行後に Launch Pool から SOL を受け取ります。

addUnlockedBucket.ts
1import {
2 addUnlockedBucketV2,
3 genesis,
4} from '@metaplex-foundation/genesis'
5import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
6import { keypairIdentity } from '@metaplex-foundation/umi'
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(mplToolbox())
11 .use(genesis())
12
13// umi.use(keypairIdentity(yourKeypair));
14
15// Assumes genesisAccount, baseMint, claimStart, and claimEnd from previous steps.
16
17await addUnlockedBucketV2(umi, {
18 genesisAccount,
19 baseMint: baseMint.publicKey,
20 baseTokenAllocation: 0n,
21 recipient: umi.identity.publicKey,
22 claimStartCondition: {
23 __kind: 'TimeAbsolute',
24 padding: Array(47).fill(0),
25 time: claimStart,
26 triggeredTimestamp: null,
27 },
28 claimEndCondition: {
29 __kind: 'TimeAbsolute',
30 padding: Array(47).fill(0),
31 time: claimEnd,
32 triggeredTimestamp: null,
33 },
34 backendSigner: null,
35}).sendAndConfirm(umi)

4. ファイナライズ

すべての bucket が設定されたら、ファイナライズしてローンチを有効化します。この操作は取り消せません。

finalize.ts
1import {
2 genesis,
3 finalizeV2,
4} from '@metaplex-foundation/genesis'
5import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
6import { keypairIdentity } from '@metaplex-foundation/umi'
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(mplToolbox())
11 .use(genesis())
12
13// umi.use(keypairIdentity(yourKeypair));
14
15// Assumes genesisAccount and baseMint from the Initialize step.
16
17await finalizeV2(umi, {
18 baseMint: baseMint.publicKey,
19 genesisAccount,
20}).sendAndConfirm(umi)

ユーザー操作

SOL のラッピング

ユーザーは入金前に SOL を wSOL にラップする必要があります。

wrapSol.ts
1import {
2 findAssociatedTokenPda,
3 createTokenIfMissing,
4 transferSol,
5 syncNative,
6 mplToolbox,
7} from '@metaplex-foundation/mpl-toolbox'
8import { WRAPPED_SOL_MINT, genesis } from '@metaplex-foundation/genesis'
9import { keypairIdentity, publicKey, sol } from '@metaplex-foundation/umi'
10import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
11
12const umi = createUmi('https://api.mainnet-beta.solana.com')
13 .use(mplToolbox())
14 .use(genesis())
15
16// umi.use(keypairIdentity(yourKeypair));
17
18const userWsolAccount = findAssociatedTokenPda(umi, {
19 owner: umi.identity.publicKey,
20 mint: WRAPPED_SOL_MINT,
21})
22
23await createTokenIfMissing(umi, {
24 mint: WRAPPED_SOL_MINT,
25 owner: umi.identity.publicKey,
26 token: userWsolAccount,
27})
28 .add(
29 transferSol(umi, {
30 destination: publicKey(userWsolAccount),
31 amount: sol(10),
32 })
33 )
34 .add(syncNative(umi, { account: userWsolAccount }))
35 .sendAndConfirm(umi)

入金

depositLaunchPool.ts
1import {
2 genesis,
3 depositLaunchPoolV2,
4 findLaunchPoolDepositV2Pda,
5 fetchLaunchPoolDepositV2,
6} from '@metaplex-foundation/genesis'
7import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
8import { keypairIdentity, sol } from '@metaplex-foundation/umi'
9import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
10
11const umi = createUmi('https://api.mainnet-beta.solana.com')
12 .use(mplToolbox())
13 .use(genesis())
14
15// umi.use(keypairIdentity(yourKeypair));
16
17// Assumes genesisAccount, launchPoolBucket, and baseMint from previous steps.
18
19await depositLaunchPoolV2(umi, {
20 genesisAccount,
21 bucket: launchPoolBucket,
22 baseMint: baseMint.publicKey,
23 amountQuoteToken: sol(10).basisPoints,
24}).sendAndConfirm(umi)
25
26// Verify
27const [depositPda] = findLaunchPoolDepositV2Pda(umi, {
28 bucket: launchPoolBucket,
29 recipient: umi.identity.publicKey,
30})
31const deposit = await fetchLaunchPoolDepositV2(umi, depositPda)
32
33console.log('Deposited (after fee):', deposit.amountQuoteToken)

同じユーザーからの複数の入金は、単一の入金アカウントに蓄積されます。

引き出し

ユーザーは入金期間中に引き出すことができます。0% の手数料が適用されます。

withdrawLaunchPool.ts
1import {
2 genesis,
3 withdrawLaunchPoolV2,
4} from '@metaplex-foundation/genesis'
5import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
6import { keypairIdentity, sol } from '@metaplex-foundation/umi'
7import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
8
9const umi = createUmi('https://api.mainnet-beta.solana.com')
10 .use(mplToolbox())
11 .use(genesis())
12
13// umi.use(keypairIdentity(yourKeypair));
14
15// Assumes genesisAccount, launchPoolBucket, and baseMint from previous steps.
16
17await withdrawLaunchPoolV2(umi, {
18 genesisAccount,
19 bucket: launchPoolBucket,
20 baseMint: baseMint.publicKey,
21 amountQuoteToken: sol(3).basisPoints,
22}).sendAndConfirm(umi)

ユーザーが残高全額を引き出すと、入金 PDA はクローズされます。

トークンの請求

入金期間が終了し、請求が開始された後:

claimLaunchPool.ts
1import {
2 claimLaunchPoolV2,
3 genesis,
4} from '@metaplex-foundation/genesis'
5import { mplToolbox } from '@metaplex-foundation/mpl-toolbox'
6import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
7
8const umi = createUmi('https://api.mainnet-beta.solana.com')
9 .use(mplToolbox())
10 .use(genesis())
11
12// umi.use(keypairIdentity(yourKeypair));
13
14// Assumes genesisAccount, launchPoolBucket, and baseMint from previous steps.
15
16await claimLaunchPoolV2(umi, {
17 genesisAccount,
18 bucket: launchPoolBucket,
19 baseMint: baseMint.publicKey,
20 recipient: umi.identity.publicKey,
21}).sendAndConfirm(umi)

トークン割り当て:userTokens = (userDeposit / totalDeposits) * bucketTokenAllocation

入金の返金

返金が可能なのは 2 つの場合です。ローンチが minimumQuoteTokenThreshold に届かなかった場合(全額返金)、または softCap を超過した場合(超過分のみ返金)です。どちらも同じインストラクションを使用します。詳しくは refundLaunchPoolV2 で超過入金を返金するを参照してください。

管理者操作

triggerBehaviorsV2 の実行

入金が終了した後、triggerBehaviorsV2 を実行して収集した SOL を Unlocked bucket に移動します。

triggerBehaviors.ts
1import {
2 genesis,
3 triggerBehaviorsV2,
4 WRAPPED_SOL_MINT,
5} from '@metaplex-foundation/genesis'
6import {
7 findAssociatedTokenPda,
8 mplToolbox,
9} from '@metaplex-foundation/mpl-toolbox'
10import { publicKey } from '@metaplex-foundation/umi'
11import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'
12
13const umi = createUmi('https://api.mainnet-beta.solana.com')
14 .use(mplToolbox())
15 .use(genesis())
16
17// umi.use(keypairIdentity(yourKeypair));
18
19// Assumes genesisAccount, launchPoolBucket, unlockedBucket, and baseMint from previous steps.
20
21const unlockedBucketQuoteTokenAccount = findAssociatedTokenPda(umi, {
22 owner: unlockedBucket,
23 mint: WRAPPED_SOL_MINT,
24})
25
26await triggerBehaviorsV2(umi, {
27 genesisAccount,
28 primaryBucket: launchPoolBucket,
29 baseMint,
30})
31 .addRemainingAccounts([
32 { pubkey: publicKey(unlockedBucket), isSigner: false, isWritable: true },
33 { pubkey: publicKey(unlockedBucketQuoteTokenAccount), isSigner: false, isWritable: true },
34 ])
35 .sendAndConfirm(umi)

これが重要な理由: triggerBehaviorsV2 を実行しないと、集められた SOL は Launch Pool bucket にロックされたままになります。ユーザーはトークンを請求できますが、チームは調達した資金にアクセスできません。

リファレンス

Time Condition

4つの条件が Launch Pool のタイミングを制御します:

条件目的
depositStartCondition入金の開始タイミング
depositEndCondition入金の終了タイミング
claimStartCondition請求の開始タイミング
claimEndCondition請求の終了タイミング

TimeAbsolute をUnixタイムスタンプと共に使用します:

const condition = {
__kind: 'TimeAbsolute',
padding: Array(47).fill(0),
time: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour from now
triggeredTimestamp: null,
};

End Behavior

入金期間後に集められた SOL の処理方法を定義します:

endBehaviors: [
{
__kind: 'SendQuoteTokenPercentage',
padding: Array(4).fill(0),
destinationBucket: publicKey(unlockedBucket),
percentageBps: 10000, // 100% = 10000 basis points
processed: false,
},
]

資金を複数の bucket に分割することもできます:

endBehaviors: [
{
__kind: 'SendQuoteTokenPercentage',
padding: Array(4).fill(0),
destinationBucket: publicKey(treasuryBucket),
percentageBps: 2000, // 20%
processed: false,
},
{
__kind: 'SendQuoteTokenPercentage',
padding: Array(4).fill(0),
destinationBucket: publicKey(liquidityBucket),
percentageBps: 8000, // 80%
processed: false,
},
]

Launch Pool 拡張

拡張は、Launch Pool bucket に設定する任意のガードです。すべて addLaunchPoolBucketV2 で設定するか、finalizeV2 の前に addLaunchPoolBucketV2Extensions と removeLaunchPoolBucketV2Extensions で個別に追加・削除できます。

拡張型目的
softCap{ amount: bigint }保持する Quote Token の上限。超過分は比例配分で返金
minimumQuoteTokenThreshold{ amount: bigint }これを下回るとローンチが失敗し、全額返金が可能になる下限
minimumDepositAmount{ amount: bigint }1 回の入金あたりの最小 Quote Token 量
depositLimit{ limit: bigint }アカウントあたりの最大 Quote Token 量
allowlistAllowlist入金を許可リストのウォレットに限定
claimScheduleClaimSchedule請求した Base Token を時間をかけてベスティング
bonusScheduleLinearBpsScheduleV2時間に応じた入金ボーナス
depositPenaltyLinearBpsScheduleV2時間に応じた入金ペナルティ
withdrawPenaltyLinearBpsScheduleV2時間に応じた引き出しペナルティ
backendSignerBackendSignerユーザー操作にバックエンドの共同署名を要求

よくあるエラー

以下のエラーは、無効なソフトキャップ設定と、refundLaunchPoolV2 が返金リクエストを拒否する Launch Pool の状態を示します。

エラーコード原因
InvalidSoftCap221softCap.amount が 0 です。{ amount: 0n } ではなく softCap: null を渡してください
SoftCapBelowThreshold222softCap.amount が minimumQuoteTokenThreshold.amount を下回っています
LaunchPoolNotEnded—入金ウィンドウが閉じる前に refundLaunchPoolV2 が呼び出されました
LaunchPoolThresholdMet173下限に達し、上限も超過していない状態で返金がリクエストされました
DepositAlreadyRefunded—同じ入金に対して refundLaunchPoolV2 が 2 回呼び出されました
DepositAlreadyClaimed—入金者がすでにトークンを請求した後に全額返金がリクエストされました

状態の取得

Bucket の状態:

import { fetchLaunchPoolBucketV2 } from '@metaplex-foundation/genesis';
const bucket = await fetchLaunchPoolBucketV2(umi, launchPoolBucket);
console.log('Total deposits:', bucket.quoteTokenDepositTotal);
console.log('Deposit count:', bucket.depositCount);
console.log('Claim count:', bucket.claimCount);
console.log('Token allocation:', bucket.bucket.baseTokenAllocation);
// Soft cap state (Option<SoftCap>)
console.log('Soft cap:', bucket.extensions.softCap);
console.log('Floor:', bucket.extensions.minimumQuoteTokenThreshold);

入金の状態:

import { fetchLaunchPoolDepositV2, safeFetchLaunchPoolDepositV2 } from '@metaplex-foundation/genesis';
const deposit = await fetchLaunchPoolDepositV2(umi, depositPda); // throws if not found
const maybeDeposit = await safeFetchLaunchPoolDepositV2(umi, depositPda); // returns null
if (deposit) {
console.log('Amount deposited:', deposit.amountQuoteToken);
console.log('Claimed:', deposit.claimed);
console.log('Refunded:', deposit.refunded);
}

注意事項

  • Launch Pool のユーザー入金・出金手数料は上記の 手数料 を参照してください。
  • 同じユーザーからの複数の入金は1つの入金アカウントに蓄積されます
  • ユーザーが残高全額を引き出すと、入金 PDA はクローズされます
  • End Behavior を処理するには、入金終了後に triggerBehaviorsV2 を実行する必要があります
  • ユーザーは入金するために wSOL(ラップされた SOL)を保持している必要があります
  • softCap は @metaplex-foundation/genesis 0.42.0 の addLaunchPoolBucketV2 における必須引数です。上限が不要な場合は softCap: null を渡してください
  • softCap を含む Launch Pool 拡張は、finalizeV2 の前にのみ追加・削除できます
  • ソフトキャップは Genesis プログラムと JavaScript SDK でサポートされています。mplx CLI にはまだソフトキャップのフラグがありません
  • 申込超過の Launch Pool では、各入金者の充当分が切り上げられるため、bucket に最大 depositCount - 1 最小単位の端数(ダスト)が残ります
  • quoteTokenDepositTotal と depositCount は返金後も履歴として保持され、処理済みの返金数は refundCount で追跡されます

FAQ

Launch Pool でトークン価格はどのように決まりますか?

価格は総入金額に基づいて自然に発見されます。最終価格は、入金された SOL の総額を割り当てられたトークン数で割った値になります。入金が多いほど、トークンあたりの暗黙の価格が高くなります。

ユーザーは入金を引き出せますか?

はい、入金期間中に引き出すことができます。システムの悪用を防ぐため、0% の引き出し手数料が適用されます。

複数回入金するとどうなりますか?

同じウォレットからの複数の入金は、単一の入金アカウントに蓄積されます。あなたの合計シェアは、合算された入金額に基づきます。

ユーザーはいつトークンを請求できますか?

入金期間が終了し、請求ウィンドウが開いた後(claimStartCondition で定義)に請求できます。End Behavior を処理するために、先に triggerBehaviorsV2 を実行する必要があります。

Launch Pool と Presale の違いは何ですか?

Launch Pool は入金に基づいて自然に価格を発見し、比例配分で配布します。Presale は事前に固定価格が設定され、先着順で上限まで割り当てられます。

Launch Pool のソフトキャップとは何ですか?

ソフトキャップは、Launch Pool が保持する Quote Token の上限であり、softCap 拡張で設定します。上限を超える入金も受け付けられ、ローンチは成功し、超過分は入金ウィンドウ終了後に比例配分で返金されます。

ソフトキャップと最小 Quote Token しきい値の違いは何ですか?

ソフトキャップは調達額の上限であり、ローンチを失敗させることはありません。minimumQuoteTokenThreshold は下限で、総入金額がこれを下回るとローンチは失敗し、すべての入金者が全額返金を受けられます。これらは別々の拡張であり、併用できます。

Launch Pool が申込超過(オーバーサブスクライブ)になると、入金者の受け取るトークンは減りますか?

いいえ。Base Token の割り当て全体は、引き続きすべての入金に比例して配布されます。申込超過の場合はトークン割り当てを減らす代わりに超過分の Quote Token が返金されるため、実効価格は softCap / baseTokenAllocation で上限が決まります。

ソフトキャップは Raydium グラデュエーションの開始価格に影響しますか?

はい。Launch Pool が申込超過の場合、グラデュエーションの開始価格は生の入金総額ではなく上限適用後の調達額から算出されるため、SendQuoteTokenPercentage によって実際に転送される額と一致します。

用語集

用語定義
Launch Pool入金ベースの配布方式で、終了時に価格が発見される
入金ウィンドウユーザーが SOL を入金・引き出しできる期間
請求ウィンドウユーザーが比例配分されたトークンを請求できる期間
End Behavior入金期間終了後に実行される自動アクション
triggerBehaviorsV2End Behavior を処理し資金をルーティングするインストラクション
比例配分総入金額に対するユーザーのシェアに基づくトークン割り当て
Quote Tokenユーザーが入金するトークン(通常は wSOL)
Base Token配布されるトークン
ソフトキャップLaunch Pool が保持する Quote Token の上限。超過分は比例配分で返金される
最小 Quote Token しきい値これを下回ると Launch Pool が失敗し、全額返金が可能になる下限
申込超過(オーバーサブスクリプション)総入金額が設定されたソフトキャップを超えている状態
充当分(Filled Portion)入金のうちソフトキャップにカウントされ、ローンチが保持する部分
超過分の返金(Excess Refund)入金のうちソフトキャップを超える部分の返却。トークン割り当ては維持される

次のステップ