> ## 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.

# 帳戶模型

> Solana 的帳戶中心架構 — 帳戶儲存狀態，程式是操作帳戶的無狀態代碼。租賃、所有權、程式帳戶與數據帳戶，以及閱讀 Raydium 流動性池狀態時的含義。

<Info>
  **本頁內容由 AI 自動翻譯，所有內容以英文版本為準。**

  [查看英文版 →](/solana-fundamentals/account-model)
</Info>

在閱讀 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 |
| ATA                        | 165 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`](/zh-Hant/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`](/zh-Hant/sdk-api/anchor-idl) 中記錄。

Anchor 程式在創建的每個帳戶前加入 8 位元組的**鑑別器**，衍生自 `hash("account:<StructName>")[0..8]`。這讓客戶端只需讀取前 8 位元組就能識別帳戶的類型 — 對於列舉某類型所有帳戶的 `getProgramAccounts` 掃描至關重要。

### 讀取 Raydium 流動性池狀態

通過 SDK：

```ts theme={null}
const pool = await raydium.cpmm.getPoolInfoFromRpc({ poolId });
console.log(pool.poolInfo);
```

通過原始 RPC + 佈局：

```ts theme={null}
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 餘額。

```ts theme={null}
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]`，由農場程式擁有。

## 相關資源

* [`solana-fundamentals/programs-and-anchor`](/zh-Hant/solana-fundamentals/programs-and-anchor) — 程式如何處理帳戶。
* [`solana-fundamentals/pdas-and-cpis`](/zh-Hant/solana-fundamentals/pdas-and-cpis) — PDA 衍生和 CPI。
* [`solana-fundamentals/transactions-and-fees`](/zh-Hant/solana-fundamentals/transactions-and-fees) — 帳戶如何在交易中被參考。
* [`sdk-api/anchor-idl`](/zh-Hant/sdk-api/anchor-idl) — Raydium 程式的帳戶佈局。

來源：

* [Solana Account docs](https://docs.solana.com/developing/programming-model/accounts)。
* [SPL Token program](https://spl.solana.com/token) — 代幣帳戶所在的地方。
