Skip to main content
本頁內容由 AI 自動翻譯,所有內容以英文版本為準。查看英文版 →
PDA(程式衍生位址)與 CPI(跨程式調用)是讓 Raydium 得以運作的兩個基本原語。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],所以請檢查特定程式的結構而不是假設一種形狀。) 在後續交易中,碰撞值從池狀態讀取而不是重新計算。

CPI:呼叫其他程式

跨程式調用讓一個程式在單一交易內內聯調用另一個程式的指令。Raydium 廣泛使用 CPI:
  • 交換指令呼叫 SPL Token 程式移動代幣。
  • CLMM 呼叫 Metaplex 鑄造頭寸 NFT。
  • 池建立呼叫系統程式分配帳戶。
  • Farm v6 呼叫 SPL Token 轉移獎勵。
整合者也使用 CPI 呼叫進入 Raydium——這就是金庫策略、槓桿 LP 協議與自動複利器的運作方式。見 integration-guides/cpi-integration。

invoke 與 invoke_signed

Solana 執行時提供兩個 CPI 原語:
  • invoke:呼叫另一個程式;被呼叫的程式繼承外部交易的簽署者。
  • invoke_signed:代表 PDA 呼叫另一個程式;執行時驗證 PDA 的種子並授權簽名。
invoke_signed 是讓程式無需管理私鑰即可持有帳戶權限的魔法。

範例:Raydium 從池金庫轉移

池金庫是一個代幣帳戶,其權限是池程式的 PDA。要在交換期間轉移代幣,池程式必須以該 PDA 簽署:
執行時看到 CPMM 程式呼叫 invoke_signed,驗證 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

這正是 Raydium SDK 在你呼叫 getPoolInfoFromRpc({ poolId }) 時在幕後所做的——它衍生相關 PDA 而無需往返。

指標

來源: