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

# 程式與 Anchor

> Solana 程式如何部署與升級、Anchor 框架在原生 Solana 之上提供了什麼、以及如何閱讀 Raydium 的 IDL 來產生你自己的客戶端。

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

  [查看英文版 →](/solana-fundamentals/programs-and-anchor)
</Info>

<Info>
  Raydium 的較新程式（CPMM、CLMM、Farm v6、LaunchLab）是用 **Anchor** 編寫的——一個建立在 Solana 原生程式模型之上的 Rust 框架，提供帳戶驗證、錯誤處理和 IDL（介面描述）。AMM v4 和較舊的農場程式早於 Anchor。理解兩種範式有助於你閱讀程式碼、從 IDL 產生客戶端，以及除錯意外的錯誤。
</Info>

## 程式部署模型

每個 Solana 程式都位於一個 `Pubkey`。程式的位元組碼儲存在由 **BPF 可升級載入器**（`BPFLoaderUpgradeab1e11111111111111111111111`）擁有的**可執行**帳戶中。

一個程式部署包含三個帳戶：

1. **程式帳戶**：程式 ID 處的小型元資料帳戶。擁有者：BPF 可升級載入器。
2. **ProgramData 帳戶**：保存實際的位元組碼。衍生為 `[program_id, "programdata"]`。
3. **Buffer 帳戶**（暫時性）：在升級期間保存新位元組碼。升級後被丟棄。

ProgramData 帳戶有一個**升級授權**——一個可以用新版本替換位元組碼的金鑰。Raydium 的升級授權是一個 24 小時時間鎖後面的多簽；見 [`security/admin-and-multisig`](/zh-Hant/security/admin-and-multisig)。

### 驗證已部署的程式

要確認鏈上的內容與經過審計批准的原始碼相符：

```bash theme={null}
# 從主網轉儲程式
solana program dump CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C cpmm-onchain.so

# 從已知原始碼構建
cargo build-sbf --manifest-path raydium-cp-swap/programs/cp-swap/Cargo.toml
cp target/deploy/raydium_cp_swap.so cpmm-source.so

# 比較
sha256sum cpmm-onchain.so cpmm-source.so
```

匹配的雜湊值證明你正在與你認為的原始碼互動。Raydium 在發行說明中發佈經過驗證的構建說明。

## Anchor：Solana 之上的框架

原生 Solana 程式是具有以下簽名的 Rust 函數：

```rust theme={null}
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    // 手動解析 instruction_data
    // 手動驗證帳戶
    // 手動反序列化帳戶資料
    // 手動檢查簽署者/可寫標誌
    // ... 然後執行實際工作
    Ok(())
}
```

Anchor 包裝了所有樣板程式碼，讓你可以寫：

```rust theme={null}
#[program]
pub mod cpmm {
    use super::*;

    pub fn swap_base_input(
        ctx: Context<Swap>,       // Anchor 1.0: one lifetime.
                                  // Under 0.3x this was Context<'_, '_, '_, '_, Swap>.
        amount_in: u64,
        min_out: u64,
    ) -> Result<()> {
        // 只是業務邏輯——ctx 已預先驗證
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Swap<'info> {
    pub payer: Signer<'info>,
    pub amm_config: Account<'info, AmmConfig>,
    #[account(mut, seeds = [b"pool", amm_config.key().as_ref()], bump)]
    pub pool_state: AccountLoader<'info, PoolState>,
    #[account(mut)]
    pub input_vault: Box<Account<'info, TokenAccount>>,
    // ... 更多帳戶
}
```

Anchor：

* 為每個指令和每個帳戶類型自動產生一個確定性的 8 位元組**判別器**。
* 在你的程式碼執行前驗證帳戶約束（擁有者、種子、可寫、簽署者、鑄幣匹配、代幣程式匹配）。
* 產生一個 **IDL**——客戶端用來呼叫程式的介面描述檔案。
* 附帶 Rust、TypeScript 和 Python 客戶端庫。

### 8 位元組判別器

每個 Anchor 帳戶和每個 Anchor 指令都以 8 位元組判別器開始——固定字串的 SHA-256 的前 8 位元組：

```
帳戶判別器：     sha256("account:PoolState")[0..8]
指令判別器：     sha256("global:swap_base_input")[0..8]
```

當你呼叫 Anchor 指令時，指令資料的前 8 位元組是這個判別器；Anchor 通過查詢它們來分派到正確的處理程式。

當你讀取 Anchor 帳戶時，前 8 位元組告訴你它的類型——對於列舉某個類型的所有帳戶的 `getProgramAccounts` 等工具至關重要。

### 錯誤

Anchor 程式通過 `#[error_code]` 定義錯誤：

```rust theme={null}
#[error_code]
pub enum ErrorCode {
    #[msg("Slippage tolerance exceeded")]
    SlippageExceeded,
    #[msg("Pool is disabled")]
    PoolDisabled,
    // ...
}
```

Anchor 自動從 6000（0x1770）開始分配這些數字代碼。Raydium 的完整錯誤代碼表在 [`reference/error-codes`](/zh-Hant/reference/error-codes)。

## IDL

Anchor **IDL**（介面描述語言）檔案是程式的 JSON 描述：其指令、帳戶、類型、錯誤和事件。它相當於以太坊 ABI。

Raydium 為所有 Anchor 程式發佈 IDL。從鏈上即時獲取：

```bash theme={null}
anchor idl fetch CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C -o cpmm.idl.json
```

或從 SDK 原始碼：`src/raydium/*/idl/*.json`。

### IDL 結構

```json theme={null}
{
  "version":      "0.1.0",
  "name":         "raydium_cp_swap",
  "instructions": [ { "name": "swap_base_input", "accounts": [ ... ], "args": [ ... ] }, ... ],
  "accounts":     [ { "name": "PoolState", "type": { ... } }, ... ],
  "types":        [ { "name": "AmmConfig", "type": { ... } }, ... ],
  "errors":       [ { "code": 6000, "name": "SlippageExceeded", "msg": "..." }, ... ],
  "events":       [ { "name": "SwapEvent", "fields": [ ... ] }, ... ]
}
```

### 從 IDL 產生客戶端

Anchor 的 `anchor` CLI 產生 TypeScript 和 Rust 類型：

```bash theme={null}
anchor idl build -o target/idl/cpmm.json
# TypeScript 類型由 Anchor 的 ts-client 自動產生
# Raydium SDK 已包含這些
```

第三方工具如 [Codama](https://github.com/codama-idl/codama) 可以從 IDL 產生 Rust、Go、Python 或 JavaScript 客戶端。（Codama 是 Metaplex 的 Kinobi 的後繼者，而 Kinobi 只提供過 JavaScript 渲染器。）

### 當 IDL 是你的朋友時

如果你想構建一個不經過 Raydium SDK 的自訂整合：

1. 獲取 IDL（從鏈上即時或從 SDK 原始碼）。
2. 查詢你想要的指令（例如 `swap_base_input`）。
3. 構造指令資料：8 位元組判別器 + 編碼的引數。
4. 按 IDL 指定的順序傳遞帳戶。

見 [`sdk-api/anchor-idl`](/zh-Hant/sdk-api/anchor-idl) 以獲得實際例子。

## 前 Anchor 程式：AMM v4 和 Farm v3/v5

這些程式早於 Anchor。它們使用：

* **手動指令分派**：`instruction_data` 中的 `u8` 標籤，帶有 `match` 陳述式。
* **手動帳戶驗證**：`if accounts[0].owner != &expected_program { ... }`。
* **Borsh 序列化的指令引數**：沒有判別器，只是 `instruction_data[1..]`。
* **通過 `#[repr(C, packed)]` 的佈局**：C 結構二進位佈局。

Raydium SDK v2 為非 Anchor AMM v4 指令提供 TypeScript 佈局，以便客戶端可以在沒有 Anchor 的情況下編碼/解碼：

```ts theme={null}
import { liquidityStateV4Layout, struct, u8, u64 }
  from "@raydium-io/raydium-sdk-v2";
import BN from "bn.js";

// `liquidityStateV4Layout` is exported for decoding AmmInfo. The instruction-data
// layout is internal to the SDK, so declare it yourself with the exported
// marshmallow helpers — note the first field is `instructionId`, not `instruction`.
const swapLayout = struct([u8("instructionId"), u64("amountIn"), u64("minAmountOut")]);

const data = Buffer.alloc(swapLayout.span);
swapLayout.encode(
  { instructionId: 9, amountIn: new BN(1_000_000), minAmountOut: new BN(950_000) },
  data,
);
```

整合模式是相同的——你只是沒有得到 Anchor 的 IDL 驅動的自動產生。

## 程式升級機制

只有 ProgramData 的 `upgrade_authority` 可以升級。步驟：

1. 編譯新位元組碼。
2. 將其寫入緩衝區帳戶（`solana program write-buffer`）。
3. 提交升級指令：`BpfLoaderUpgradeable::Upgrade { buffer, program, authority }`。
4. 執行時原子性地用緩衝區的內容替換程式的位元組碼。

Raydium 通過在 Squads 多簽設定中實現的 **24 小時時間鎖**來限制這一點。升級交易必須在多簽批准後等待 24 小時才能執行。這可以防止倉促/被迫的升級。

見 [`security/admin-and-multisig`](/zh-Hant/security/admin-and-multisig)。

### 使程式不可變

升級授權可以設定為 `None`，此時程式變成永久不可變。Raydium 還沒有為任何產品這樣做——團隊保留推送安全修復的能力。權衡：使用者必須信任多簽 + 時間鎖流程。

## 程式與租金

部署程式消耗免租 lamport：

* 一個 50 KB 程式：約 0.35 SOL 租金。
* 一個 200 KB 程式：約 1.4 SOL 租金。

關閉程式（通過 `solana program close`）返回 lamport。Raydium 程式保持活躍，未計劃關閉。

## 除錯 Anchor 程式

### 日誌輸出

Anchor 的 `msg!` 巨集寫入交易的日誌。模擬交易以查看日誌：

```ts theme={null}
const sim = await connection.simulateTransaction(tx);
console.log(sim.value.logs);
```

日誌包括：

* 程式呼叫（`Program CPMMoo8... invoke [1]`）。
* 程式碼中的 `msg!` 呼叫。
* 計算單位消耗（`consumed 137842 of 400000 compute units`）。
* 程式成功或錯誤。

### 錯誤代碼

如果 Anchor 程式拋出錯誤，日誌顯示：

```
Program CPMMoo8... failed: custom program error: 0x1770
```

0x1770 = 6000 十進位 = 第一個 Anchor 錯誤（例如 `SlippageExceeded`）。與 IDL 的 `errors` 陣列交叉參考。

見 [`reference/error-codes`](/zh-Hant/reference/error-codes) 以獲得 Raydium 的完整錯誤表。

### 帳戶佈局不匹配

如果你在錯誤的位置傳遞了錯誤的帳戶，Anchor 的帳戶驗證巨集返回如下錯誤：

```
AnchorError: AccountNotInitialized. Error Number: 3012
```

6000 以下的錯誤號是 Anchor 的內建錯誤（見 Anchor 的 `ErrorCode` 列舉）；≥6000 的錯誤是程式的自訂代碼。

## 指標

* [`solana-fundamentals/account-model`](/zh-Hant/solana-fundamentals/account-model) — 程式如何擁有帳戶。
* [`solana-fundamentals/pdas-and-cpis`](/zh-Hant/solana-fundamentals/pdas-and-cpis) — Anchor 聲明的 PDA。
* [`sdk-api/anchor-idl`](/zh-Hant/sdk-api/anchor-idl) — 獲取和使用 Raydium 的 IDL。
* [`reference/program-addresses`](/zh-Hant/reference/program-addresses) — 程式 ID。
* [`reference/error-codes`](/zh-Hant/reference/error-codes) — 錯誤代碼參考。
* [`security/admin-and-multisig`](/zh-Hant/security/admin-and-multisig) — 升級授權控制。

來源：

* [Anchor 文件](https://www.anchor-lang.com/docs)。
* [Solana 程式部署](https://solana.com/docs/programs/deploying)。
* Raydium IDL（在 SDK `src/raydium/*/idl/*.json` 中發佈）。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.