跳轉到主要內容

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.

本頁內容由 AI 自動翻譯,所有內容以英文版本為準。查看英文版 →
在閱讀 Raydium 代碼之前,Solana 的帳戶模型是最重要的概念。與以太坊不同的是,以太坊的狀態與合約代碼並存,而 Solana 程式完全無狀態:所有狀態都存在於程式操作的獨立「帳戶」中。每個 Raydium 流動性池、頭寸和金庫都是一個帳戶 — 理解這些帳戶的工作原理是掌握其餘文件的關鍵。

核心區分:程式與帳戶

程式

Solana 上的程式是可執行的代碼 — 一個從檔案編譯而來的二進制檔案,部署到某個 Pubkey,並可通過交易呼叫。程式沒有關聯的狀態;它們只包含邏輯。 Raydium 的程式:
  • CPMM: CPMMoo8L3F4NbTegBCKVNunggL7H1Zpdmwpwh8KMoZ0F
  • CLMM: CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK
  • AMM v4: 675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8
每一個都是固定的二進制檔案。程式在多次呼叫之間不會「記住」任何東西。

帳戶

帳戶是鏈上的一行數據。每個帳戶都有:
  • pubkey — 其地址。
  • owner — 擁有它的程式(控制寫入)。
  • data — 原始位元組。
  • lamports — SOL 餘額(1 SOL = 1,000,000,000 lamports)。
  • rent_epoch — 舊版租賃徵收欄位(自租賃豁免變為強制後已忽略)。
當你查詢 Raydium 流動性池時,你正在讀取一個或多個帳戶。當交換執行時,CPMM 程式會讀取/寫入多個帳戶 — 流動性池狀態、金庫、觀察狀態和用戶的代幣帳戶。

所有權

每個帳戶由恰好一個程式擁有。只有該程式的代碼才能修改帳戶的 data 欄位。用戶可以修改他們可以簽署的帳戶的 lamports(發送/接收 SOL),但修改 data 需要所有者程式代表他們進行。 例子:
  • 你的用戶錢包:由系統程式擁有。Lamports 存在於此;你簽署以轉移。
  • 你的 USDC 代幣帳戶:由 SPL Token 程式擁有。代幣程式的 transfer 指令更新餘額。
  • Raydium 流動性池狀態帳戶:由 CPMM 程式擁有。只有 CPMM 的指令才能修改儲備、費用等。
  • Raydium 頭寸 NFT 的 PersonalPositionState:由 CLMM 程式擁有。
「擁有者」是嚴格的:如果程式 A 寫入由程式 B 擁有的帳戶,Solana 運行時會拒絕交易。

租賃和租賃豁免

創建帳戶會消耗存儲空間。Solana 對該空間收取租賃費用,但自 2020 年以來,所有新帳戶都必須是租賃豁免的 — 意味著它們持有足夠的 lamports 來預付 2 年的租賃費用。實際上:
  • 租賃豁免帳戶永久存在。
  • 關閉帳戶會將 lamports 返回給關閉簽署者。
對於 165 位元組的帳戶(例如 SPL Token 帳戶),租賃豁免約為 0.00204 SOL。對於 1,440 位元組的 Raydium CPMM 流動性池狀態,約為 0.011 SOL。

Raydium 租賃成本

帳戶大小租賃
CPMM PoolState~1,440 B~0.011 SOL
CLMM PoolState~1,500 B~0.012 SOL
CLMM TickArray~9,000 B~0.063 SOL
CLMM PersonalPositionState~280 B~0.003 SOL
ATA165 B~0.002 SOL
金庫(代幣帳戶)165 B~0.002 SOL
流動性池創建需要一次支付多個帳戶的租賃 — 這就是為什麼 CPMM 流動性池創建成本總計約 0.15 SOL。

數據帳戶與可執行帳戶

帳戶分為兩種:

數據帳戶

儲存狀態(流動性池儲備、代幣餘額、用戶頭寸)。executable = false。這是絕大多數。

可執行帳戶

儲存程式位元組碼。executable = true。這些是程式(CPMM、CLMM 等)。程式除了位元組碼外沒有數據。

程式衍生帳戶(PDA)

PDA 是一個數據帳戶,其地址是從程式和某些種子確定性衍生的 — 此地址不存在私鑰。只有衍生程式可以通過 invoke_signed 代表 PDA 簽署。 Raydium 廣泛使用 PDA:
  • 流動性池狀態 PDA:衍生自 [poolTypeDiscriminator, mintA, mintB, ammConfig]
  • 金庫 PDA:衍生自 [pool, mint]
  • 觀察狀態 PDA:衍生自 [observationSeed, pool]
PDA 讓 Raydium 在可預測的地址建立帳戶,無需管理密鑰。任何人都可以根據已知流動性池和種子計算 PDA 地址。 參見 solana-fundamentals/pdas-and-cpis

交易和帳戶參考

每個 Solana 交易都帶有一個明確的帳戶列表,它將讀取/寫入該列表。運行時強制執行:
  • 列出的帳戶可以被讀取或寫入(根據其 is_writable 標誌)。
  • 未列出的帳戶無法被觸及。
對於 Raydium 交換,交易的帳戶列表包括:
[readonly] CPMM 程式
[writable] 流動性池狀態
[readonly] amm 設定
[readonly] 流動性池權限(PDA)
[writable] 輸入金庫
[writable] 輸出金庫
[writable] 用戶輸入 ATA
[writable] 用戶輸出 ATA
[readonly] 輸入 mint
[readonly] 輸出 mint
[readonly] 輸入代幣程式
[readonly] 輸出代幣程式
[writable] 觀察狀態
[signer,writable] 用戶
這種明確的列舉是為什麼 Solana 交易既快速又可並行化的原因 — 運行時可以提前確定非衝突的交易。

帳戶大小和數據佈局

每個 Raydium 帳戶都有固定或有界的大小。佈局在代碼中定義(帶有 #[repr(C)] 的 Rust 結構),並在 sdk-api/anchor-idl 中記錄。 Anchor 程式在創建的每個帳戶前加入 8 位元組的鑑別器,衍生自 hash("account:<StructName>")[0..8]。這讓客戶端只需讀取前 8 位元組就能識別帳戶的類型 — 對於列舉某類型所有帳戶的 getProgramAccounts 掃描至關重要。

讀取 Raydium 流動性池狀態

通過 SDK:
const pool = await raydium.cpmm.getPoolInfoFromRpc({ poolId });
console.log(pool.poolInfo);
通過原始 RPC + 佈局:
const accountInfo = await connection.getAccountInfo(poolId);
const data = accountInfo.data;
// 跳過前 8 位元組(鑑別器),然後根據結構佈局解析。
const poolState = CpmmPoolStateLayout.decode(data.slice(8));
佈局在 SDK 源中的 src/raydium/cpmm/layout.ts

實例:讀取代幣帳戶

讓我們讀取用戶的 USDC 餘額。
import { Connection, PublicKey } from "@solana/web3.js";
import { getAssociatedTokenAddressSync, AccountLayout } from "@solana/spl-token";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const user       = new PublicKey("YourUserWallet...");
const usdcMint   = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

// 1. 計算 ATA 地址(PDA 樣式的衍生)。
const ata = getAssociatedTokenAddressSync(usdcMint, user);

// 2. 讀取帳戶。
const accountInfo = await connection.getAccountInfo(ata);
if (!accountInfo) {
  console.log("ATA 尚不存在(用戶從未持有過 USDC)。");
  return;
}

// 3. 驗證所有者是 SPL Token 程式。
console.assert(accountInfo.owner.toBase58() === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");

// 4. 解碼數據。
const parsed = AccountLayout.decode(accountInfo.data);
console.log("餘額(最小單位):", parsed.amount.toString());
console.log("Mint:",                     new PublicKey(parsed.mint).toBase58());
console.log("所有者:",                    new PublicKey(parsed.owner).toBase58());
這種模式 — 衍生地址、取得帳戶、驗證所有者、解碼 — 適用於每個鏈上讀取,包括 Raydium 流動性池。

為什麼這對 Raydium 很重要

帳戶模型塑造了 Raydium 的設計:
  • 流動性池狀態是單個帳戶 — 有關流動性池的一切(mint、儲備、費用、管理員)都存在於流動性池程式擁有的一個帳戶中。
  • LP 代幣是標準 SPL 代幣帳戶 — Raydium 將代幣化委託給 SPL Token 程式。
  • Tick 陣列是分塊的 — CLMM 不能有單個可增長的 tick 陣列,因為帳戶的分配大小是固定的;相反,它使用分塊的 TickArray PDA。
  • 頭寸 NFT 是 Metaplex NFT — CLMM 頭寸是根據 Metaplex 的標準 NFT;頭寸狀態是單獨的 PDA。
理解這一點讓你能正確回答「X 存在於哪裡?」的問題:
  • 「流動性池儲備在哪裡?」→ 兩個金庫帳戶(代幣帳戶),由 SPL Token 程式擁有,權限委託給流動性池程式的 PDA。
  • 「CLMM 的 tick 數據在哪裡?」→ 一系列 TickArray PDA,每個涵蓋 60 個連續的 tick。
  • 「我的農場質押在哪裡?」→ 一個 UserLedger PDA,衍生自 [user, farmId],由農場程式擁有。

相關資源

來源: