このページは AI による自動翻訳です。すべての内容は英語版を正とします。英語版を表示 →
すべての Solana トランザクションは、暗黙的または明示的に 2 つのパラメータを設定します:コンピュートユニット制限(CU の最大消費量。デフォルトは instruction 数 × 200,000、ただしトランザクション単位で上限あり)と 優先度フィー(マイクロラムポート/CU 単位)。どちらかを過小設定するとトランザクションが失敗します — CU 制限が低すぎると ProgramFailedToComplete が発生し、優先度フィーが低すぎると確認されないまま期限切れになります。
2 つの設定
import { ComputeBudgetProgram } from "@solana/web3.js";
const tx = new Transaction()
.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }))
.add(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50_000 }))
.add(yourRaydiumSwapIx);
setComputeUnitLimit(units) — コンピュートを制限します。トランザクションは最大 units CU 分の料金を支払います。
setComputeUnitPrice(microLamports) — 優先度フィーのビッド額(マイクロラムポート/CU 単位)。総優先度フィー = units × microLamports × 1e-6 ラムポート。
コスト計算:250k CU 制限で 50k マイクロラムポート/CU の場合、優先度フィーは 250_000 × 50_000 / 1e6 = 12,500 ラムポート ≈ 0.0000125 SOL ≈ SOL が $200 の場合 $0.003 です。この規模の優先度フィーはほとんどのユーザースワップでは無視できる金額ですが、1 日 1000 トランザクション実行するボットにとっては実質的な費用になります。
インストラクション別 CU ベンチマーク
メインネット実行ログから取得したベンチマーク。最近の実行結果を平均化しています。数値は概算値(±15%)です。特定のフローに対して再計測してください。
| インストラクション | SPL Token | Token-2022(シンプル) | Token-2022(転送フィー) |
|---|
| CPMM initialize_pool | 180,000 | 200,000 | — |
| CPMM swap_base_input | 140,000 | 180,000 | 200,000 |
| CPMM swap_base_output | 150,000 | 185,000 | 205,000 |
| CPMM deposit | 130,000 | 160,000 | 180,000 |
| CPMM withdraw | 120,000 | 150,000 | 170,000 |
| CLMM create_pool | 70,000 | 85,000 | — |
| CLMM open_position_v2 | 120,000 | 140,000 | 160,000 |
| CLMM increase_liquidity_v2 | 150,000 | 175,000 | 195,000 |
| CLMM decrease_liquidity_v2 | 140,000 | 165,000 | 185,000 |
| CLMM swap_v2(ティック交差 0) | 170,000 | 205,000 | 225,000 |
| CLMM swap_v2(ティック交差 1) | 220,000 | 255,000 | 275,000 |
| CLMM swap_v2(ティック交差 3) | 320,000 | 355,000 | 375,000 |
| CLMM collect_fee | 80,000 | 95,000 | 105,000 |
| AMM v4 swap_base_in | 140,000 | — | — |
| AMM v4 deposit | 120,000 | — | — |
| AMM v4 withdraw | 110,000 | — | — |
| Farm v6 create_farm | 70,000 | 85,000 | — |
| Farm v6 deposit(1 リワード スロット) | 130,000 | 155,000 | 175,000 |
| Farm v6 deposit(3 リワード スロット) | 220,000 | 255,000 | 275,000 |
| Farm v6 withdraw | deposit と同じ | | |
| Farm v6 harvest | deposit と同じ | | |
| Farm v3/v5 deposit | 100,000 | — | — |
| LaunchLab initialize | 100,000 | — | — |
| LaunchLab buy_exact_in | 140,000 | — | — |
| LaunchLab graduate | 250,000 | — | — |
CLMM の「ティック交差」行が最大の CU 変数です。スワップが何個のティックを交差するか不明な場合は、最悪ケースで予算を組んでください — 8 交差が上限です(プログラムは最大 8 個のティックアレイを読み込みます)。
構成されたトランザクション
個別の予算を合計し、以下を追加します:
- CPI フレームあたり +1,500 CU — 各クロスプログラムコール用のランタイム固定オーバーヘッド。
- ATA 作成あたり +20,000 CU —
create_associated_token_account は無料ではありません。
setComputeUnitLimit / setComputeUnitPrice あたり +5,000 CU。
例:出力 ATA を作成してネイティブ SOL をラップするユーザースワップ:
wrap_sol(create_ata + system transfer + sync_native) ≈ 30,000
CPMM swap_base_input(SPL) ≈ 140,000
close_account(unwrap) ≈ 5,000
ComputeBudget インストラクション ≈ 10,000
────────────────────────────────────────────────────────
合計 ≈ 185,000 → 250,000 に設定
パディング:CU 制限を予想使用量の約 25% 上回るレベルに設定します。過小見積もりはトランザクション全体を失敗させ、過剰見積もりは優先度フィーコストを比例的に増加させるだけです(優先度フィーは units × microLamports なので、25% の超過予算は優先度フィーで 25% 多く費用がかかります)。
優先度フィー推定
Solana のローカルフィーマーケットでは、優先度フィーはライタブルアカウントごとです。ホットなアカウント(人気のあるプール状態)に書き込むトランザクションは、コールドなアカウントに書き込むトランザクションより多く支払います。グローバルなフィーレベルは Raydium スワップの適切な指標ではありません。接触しているプール上の手数料がほしいところです。
戦略 1:RPC プロバイダー推定器
主要な RPC プロバイダーはそれぞれ、特定のアカウントの最近のフィーをクエリする優先度フィー推定器を公開しています:
// Helius
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${apiKey}`, {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: "fee-estimate",
method: "getPriorityFeeEstimate",
params: [{
accountKeys: [poolStatePubkey.toBase58()],
options: { priorityLevel: "High" },
}],
}),
});
const { result } = await response.json();
const microLamports = result.priorityFeeEstimate;
ほとんどのプロバイダー全体の優先度レベル:Min / Low / Medium / High / VeryHigh / UnsafeMax。パーセンタイルにマッピングします:
| レベル | パーセンタイル | ユースケース |
|---|
| Min | 25 パーセンタイル | バックグラウンド、緊急性のないボット トラフィック |
| Low | 50 パーセンタイル | 通常のユーザースワップ |
| Medium | 60 パーセンタイル | ウォレット UI のデフォルト |
| High | 75 パーセンタイル | 時間に敏感なアービトラージ |
| VeryHigh | 95 パーセンタイル | 清算、最後のチャンスの出口 |
プロバイダー:Helius(getPriorityFeeEstimate)、Triton(アカウントリスト付き getRecentPrioritizationFees)、QuickNode(類似)。
戦略 2:直接 RPC クエリ
標準の getRecentPrioritizationFees RPC を使用します:
const fees = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: [poolStatePubkey],
});
// fees: Array<{ slot, prioritizationFee }>
// 最近の N スロット。デフォルト約 150 スロット。
const median = percentile(fees.map(f => f.prioritizationFee), 0.5);
これは標準的な Solana RPC メソッドです。すべてのプロバイダーで機能します。欠点:サンプルが小さい(150 スロット ≈ 60 秒)でノイズが多い。より滑らかな推定には、プロバイダーの集約を使用します。
戦略 3:履歴自己調整
定常的なフローを実行するボットの場合、着地率と失効率を追跡します:
プールごとのターゲット:<30 秒での 80% 着地率
現在の着地率 < 80%:priorityFee += 10%
現在の着地率 > 95%:priorityFee -= 5%
これは公開推定器よりも速く自己補正し、公開推定器が常に見落とすプールごとの構造をキャプチャします。
CU 枯渇失敗の処理
症状:トランザクションが exceeded maximum number of instructions allowed (200000) または ProgramFailedToComplete で失敗します。
診断:
solana confirm <tx-sig> -v
# 「consumed N of M compute units」を探し、どのインストラクションが枯渇したかを確認します。
修正:
- CU 制限を引き上げます。 トランザクションが 200k の予算のうち 195k を使用していた場合は、300k に引き上げます。
- トランザクションを分割します。 1.4M のトランザクション当たり上限に達している場合は、2 つのトランザクションに分割します。リワードが多いときのファーム
harvest then stake は分割する典型的なケースです。
- アカウントを削減します。 ライタブルアカウント 1 つ追加あたり約 2,000 CU が追加されます。未使用のアカウントを削除すると、限界的なケースで役立ちます。
- ルックアップテーブルを使用します。 LUT ルックアップはアドレスごと約 50 CU で、完全なアカウント参照の 5,000 CU をエントリごと節約します。
スタックしたトランザクションの処理
症状:トランザクションが送信され、確認されず、最終的に BlockhashNotFound で期限切れになります。
診断:
getSignatureStatuses([sig]) が null を返す → リーダーが見たことがない。
{ confirmationStatus: null } を返す → リーダーが見たがインクルードしていない。
修正:
- 優先度フィーを引き上げます。 現在のフィーの 2 倍で再送信します。
- 新しいブロックハッシュで再構築します。 ブロックハッシューライフタイムは約 60 秒です。それを超えるとフィーに関係なくトランザクションは無効です。
- マルチ RPC ブロードキャスト。 RPC によってはリーダーコネクティビティが優れているものがあります。並行して 3~5 に送信します。
- Jito バンドルに切り替えます。
/ja/integration-guides/routing-and-mev を参照してください。バンドルはパブリック パケット キューをバイパスします。
再試行ロジックスケルトン:
async function submitWithRetry(buildTx, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const tx = await buildTx({
priorityFee: basePriorityFee * Math.pow(1.5, attempt),
blockhash: (await connection.getLatestBlockhash()).blockhash,
});
try {
const sig = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: attempt > 0, // 最初の試行後はスキップしてレイテンシを節約
});
const result = await connection.confirmTransaction(sig, "confirmed");
if (result.value.err) {
// ロジックエラー。再試行しません。
throw result.value.err;
}
return sig;
} catch (e) {
if (isExpiredError(e)) continue; // 再試行
if (isRevertError(e)) throw e; // 再試行しない。決定論的な失敗
throw e;
}
}
throw new Error("submit: exhausted retries");
}
混雑時対応
ネットワークが混雑している場合(Jupiter / Jito バンドル ダッシュボードがバックログを表示、RPC レイテンシが急上昇、トランザクション期限切れ率が上昇)、以下を調整します:
| パラメータ | 通常の状態 | 混雑時 |
|---|
| CU 制限 | 推定値 + 25% | 推定値 + 25%(変更なし) |
| 優先度フィー パーセンタイル | 50 パーセンタイル | 75~95 パーセンタイル |
| 再試行回数 | 3 | 5~7 |
| 再試行バックオフ | 500ms | 1000ms |
| Jito バンドル使用 | オプション | 強く推奨 |
| 再試行時のブロックハッシュ更新 | はい | はい、必須 |
混雑シグナルの監視:
- 優先度フィー 75 パーセンタイル > 500k マイクロラムポート:混雑。
- Jito 50 パーセンタイル チップ > 0.001 SOL:混雑。
- RPC レスポンス p99 > 2s:RPC 固有の問題または混雑。
ボット向けフィー予算
1 日約 1000 トランザクション実行するトレーディング ボットには優先度フィー予算が必要です。概算:
トランザクションあたりの平均 CU: 約 250,000
50 パーセンタイル フィー: 約 20,000 マイクロラムポート/CU
トランザクションあたりのコスト: 250_000 × 20_000 × 1e-6 = 5_000 ラムポート = 5e-6 SOL
1 日のコスト(1000 トランザクション): 5e-3 SOL ≈ $200 SOL 時 $1
月額コスト: 約 $30
これは最小値です。混雑時には 5~10 倍にしてください。定常フロー ボットの場合、月に約 $150~300 の優先度フィーを計画します。
特定のスロットでランド化する必要があるボット(清算、アービトラージ)は継続的に 95 パーセンタイルを支払い、約 10 倍多く費用がかかります。Jito バンドル チップがその規模で支配的 — 多くの場合 $1000+/月 — ですが、代替案(フロントランまたは期限切れ)はより悪いです。
よくある落とし穴
1. CU 制限の忘却
デフォルトはトランザクション内のインストラクション数 × 200k CU です。単一インストラクション スワップはデフォルト 200k ですが、これは SPL Token の CPMM には十分ですが、ティック交差のある CLMM または Token-2022 には十分ではありません。常に明示的に設定してください。
2. 間違ったアカウントの優先度フィー
トークン ミント に対して優先度フィーを推定したが、ホット アカウントがプール状態の場合、推定値は低すぎます。プール状態は Raydium のターゲット ライタブル アカウントです。
3. フィーが CU 制限でスケール
total_priority_fee = units × microLamports。units を 200k から 1M に引き上げ、50k マイクロラムポート/CU で優先度フィーが 5 倍になります。CU を念のため過剰予算にしないでください。計測してください。
4. デフォルト トランザクション バージョン
レガシー トランザクションのアカウント制限は低い。V0 トランザクションとアドレス ルックアップ テーブルはより大きなルートのロックを解除します。SDK は txVersion: TxVersion.V0 でデフォルトで V0 を使用します。ウォレット互換性が必要でない限り、レガシーにドロップしないでください。
5. skipPreflight が CU エラーを隠す
skipPreflight: true はローカル シミュレーションなしでトランザクションを送信します。約 100ms 節約しますが、CU 枯渇の早期フィードバックを失います。最初の試行ではなく再試行時にのみ使用してください。
ポインタ
ソース: