Skip to main content
このページは AI による自動翻訳です。すべての内容は英語版を正とします。英語版を表示 →
PDA(プログラム派生アドレス)と CPI(クロスプログラム呼び出し)は、Raydium を可能にする 2 つのプリミティブです。PDA により、プログラムは秘密鍵なしで決定論的なアドレスを「所有」できます。これがプール権限とボールトの仕組みです。CPI により、あるプログラムが別のプログラムを呼び出せます。これが Raydium が SPL Token プログラム経由でトークンをスワップする方法であり、統合者が Raydium を独自のフローに合成する方法です。Raydium のソースコードを読む前に、両方を理解する価値があります。

PDA:鍵のないアドレス

プログラム派生アドレスは以下の特性を持つ公開鍵です:
  • ed25519 曲線上にない(秘密鍵が存在しない)。
  • プログラム ID とシードのセットから決定論的に導出される。
  • invoke_signed を通じて、導出プログラムのみで署名できる。
すべての Raydium プール権限、すべてのプール状態アカウント、すべてのボールト、すべてのファーム状態 — これらはすべて PDA です。

導出

PDA は、プログラム ID とシードをハッシュして計算され、その後、結果を曲線外に強制する「バンプ」バイトが見つかります。オフカーブアドレスを生成する最初のバンプ(通常は 255 から始まり減少)が勝ちます。これが正規バンプです。
シードは何でもかまいません — 文字列、他の公開鍵、リトルエンディアンバイトとしての u64 値。Raydium の慣例は、人間が読める接頭辞の後に一意の識別子が続きます。

Raydium の PDA パターン

Raydium のプログラムの一般的な PDA: ユーザーと統合者は、何も取得することなくこれらを計算できます。公開入力(プール ID、ファーム ID、ユーザーキー)が与えられれば、PDA は決定論的です。

正規バンプ

原則として複数のバンプがオフカーブアドレスを生成する可能性がありますが、Raydium のプログラムは常に正規バンプを使用します(255 から減少させて見つかったもの)。これは PDA のアカウントデータに保存されるため、後続のトランザクションはそれを渡して(高コストな)導出ループをスキップできます:
(CLMM の PoolState は代わりに bump: [u8; 1] を保存するため、1 つの形状を想定するのではなく、特定のプログラムの構造体を確認してください。) 後続のトランザクションでは、バンプは再計算されるのではなく、プール状態から読み取られます。

CPI:他のプログラムの呼び出し

クロスプログラム呼び出しにより、プログラムは別のプログラムの命令を単一のトランザクション内でインラインで呼び出せます。Raydium は CPI を広範に使用します:
  • スワップ命令は SPL Token プログラムを呼び出してトークンを移動します。
  • CLMM は Metaplex を呼び出してポジション NFT をミントします。
  • プール作成はシステムプログラムを呼び出してアカウントを割り当てます。
  • Farm v6 は SPL Token を呼び出して報酬を転送します。
統合者も CPI を使用して Raydium に呼び出します。これがボールト戦略、レバレッジ LP プロトコル、自動複利プロトコルの仕組みです。integration-guides/cpi-integration を参照してください。

invoke と invoke_signed

Solana ランタイムは 2 つの CPI プリミティブを提供します:
  • invoke:別のプログラムを呼び出します。呼び出されたプログラムは外側のトランザクションの署名者を継承します。
  • invoke_signed:PDA の代わりに別のプログラムを呼び出します。ランタイムは PDA のシードを検証し、署名を認可します。
invoke_signed は、プログラムが秘密鍵を管理することなくアカウントに対する権限を保持できるようにする魔法です。

例:Raydium がプールボールトから転送する

プールボールトは、権限がプールプログラムの PDA であるトークンアカウントです。スワップ中にトークンを転送するには、プールプログラムはその PDA として署名する必要があります:
ランタイムは invoke_signed が CPMM プログラムによって呼び出されていることを確認し、vault_and_lp_mint_auth_seed + バンプ が CPMM プログラム ID でハッシュされたときに pool_authority のアドレスに導出されることを検証し、トークン転送の権限署名を許可します。秘密鍵は関係ありません。

例:統合者が Raydium CPMM を呼び出す

統合者プログラム(例:エスクロー)は CPI 経由で Raydium の swap_base_input を呼び出せます:
これは正規の統合パターンです。完全なエスクロー例については integration-guides/cpi-integration を参照してください。

CPI 深度制限

Solana は CPI 深度を 4 レベルに制限します。トランザクションのトップレベル命令は深度 0 としてカウントされます。各 CPI 呼び出しは深度を増加させます。 実用的な影響:Raydium 自体のスワップは既に 1~2 レベルの CPI を使用します(Raydium → SPL Token)。統合者が Raydium を呼び出す場合は 2 です。その統合者が別の統合者によって呼び出される場合は 3 です。4 番目のレベルが制限です。 ほとんどの合成はこれを簡単に下回りますが、深いネスト(アグリゲーター → ルーター → Raydium → フック)はそれに達する可能性があります。深いのではなく平らに設計してください。

残りのアカウント

Raydium 命令が可変数のアカウントを必要とする場合(例:CLMM スワップが不明な数のティック配列を横切る場合)、追加のアカウントは残りのアカウントとして渡されます — 固定アカウントリストに追加され、位置で解釈されます。 CPMM の SwapV2 は、転送フックプログラムの追加の必須アカウント用に残りのアカウントを使用します。クライアントは必要なアカウントを取得して追加します:
CPI レベルでは、統合者は独自の命令を通じて残りのアカウントを転送する必要があります:

PDA の落とし穴

間違ったシード → 間違ったアドレス

シードが間違った順序、間違ったエンコーディング、または余分なバイトを含める/除外するバグは、静かに異なる PDA を生成します。トランザクションは曖昧に失敗します(プログラムは存在しないアカウントを読み取ろうとします)。常にシード導出を既知のゴールデン値に対してユニットテストしてください。

バンプを保存しない

すべてのトランザクションでバンプを再導出する場合、導出ループの計算を支払います。正規バンプを PDA のデータに保存し、そこから読み取ります。

正規バンプと非正規バンプを混同する

非正規バンプ(オフカーブを生成するものが見つかった場合)は invoke_signed で許可されますが、Raydium のプログラムは assert_eq!(bump, canonical_bump) を通じて拒否します。誰かが非正規バンプで PDA を要求しようとする場合、トランザクションは失敗します。

所有プログラムでない場合に PDA を署名者として渡す

PDA の導出に ID が含まれるプログラムのみが、そのシードで invoke_signed できます。試みた場合、ランタイムは拒否します。

CPI の落とし穴

remaining_accounts を転送し忘れる

外側の命令が remaining_accounts に転送フックアカウントを渡しますが、Raydium への CPI がそれらを転送しない場合、Raydium はフックアカウントを見つけられないため失敗します。常に必要な CPI に with_remaining_accounts を含めてください。

書き込み可能フラグの不一致

外側の命令が書き込み可能としてマークするアカウントは、呼び出されたプログラムがそれを書き込む場合、CPI 呼び出しでも書き込み可能である必要があります。不一致 → ランタイム拒否。

家賃を考慮しない

アカウントを作成するプログラムへの CPI(例:ATA 作成)では、支払者が家賃用に十分な SOL を持つ必要があります。失敗した家賃チェックは不可解なエラーとして表示されます。

実例:Raydium CPMM PDA の計算

これは、getPoolInfoFromRpc({ poolId }) を呼び出すときに Raydium SDK が内部で行うことと全く同じです — ラウンドトリップなしで関連する PDA を導出します。

ポインタ

ソース: