本文由 AI 生成,請獨立核實重要資訊。

Solana Anchor Framework 指南

Crypto Wiki|Oct 8, 2026|★★★★★★4.5 (500 人評分)
AI 摘要

Learn to build Solana programs with Anchor Framework. Complete guide covering setup, core concepts, security, testing and deployment to devnet.

Solana Anchor Framework 協助開發者建構具有結構化帳戶驗證、指令定義、生成介面和可重複使用安全性檢查的程式。

版本注意事項: Anchor、Rust、Solana CLI 和 JavaScript 客戶端版本獨立變更。在安裝或部署之前,請在官方 Anchor 文件中驗證當前相容性。

本文僅供教育目的,不構成財務或投資建議。加密貨幣投資具有重大風險。在做出投資決策之前,請務必進行自己的研究。

📌 注意: 本指南使用 Anchor v0.29.x 和 Solana CLI v1.18.x+,截至 2024 年。Anchor Framework 發展迅速。在遵循這些說明之前,請運行 anchor --version 來驗證你安裝的版本。


目錄

  1. 什麼是 Solana Anchor Framework?
  2. Solana 的帳戶模型
  3. Solana Anchor Framework 的核心元件
  4. 先決條件
  5. 如何安裝 Anchor Framework
  6. 建構並部署你的第一個 Anchor 程式
  7. 測試 Anchor 程式
  8. Anchor 與替代方案
  9. Anchor Framework 安全性
  10. 疑難排解常見 Anchor 錯誤
  11. Anchor Framework 與 Solana 開發者生態系
  12. 常見問題
  13. 結論

什麼是 Solana Anchor Framework?

Solana Anchor Framework 是一個開源開發框架,它消除了用原始 Rust 編寫 Solana 程式(鏈上智能合約的對應物)的樣板程式碼和帳戶驗證複雜性。

Solana 是一個高效能的 Layer 1 區塊鏈,在最佳條件下(截至 2024 年)每秒可處理約 65,000 筆交易,具有次秒級別的最終性,費用通常低於 0.001 美元。Solana 之所以能達到如此高的吞吐量,部分歸功於一種獨特的共識機制,稱為歷史證明 (Proof of History, PoH),它創建了一個加密時鐘,無需傳統共識協定中的通信開銷即可排序交易。

在沒有框架的情況下在 Solana 上進行開發,意味著需要為每個程式編寫冗長的低階 Rust 程式碼。開發人員必須手動驗證帳戶所有權、序列化資料、處理錯誤以及為每一筆交易管理帳戶關係。對於來自 Web2 後端或以太坊開發的工程師來說,這個學習門檻非常高,足以阻止大多數專案在開始之前就中止。

Anchor 改變了這一點。你可以將 Anchor 視為 Solana 開發的 Ruby on Rails:一個有主見的框架,負責處理底層架構,讓你能夠專注於程式邏輯,而不是其底層基礎設施。Anchor 由 Armani Ferrante 創建,由 Coral XYZ 維護。你可以在 Anchor 的 GitHub 儲存庫 (coral-xyz/anchor).) 找到原始碼和版本。目前穩定版本為 v0.29.x。

Anchor 使開發者能夠建構去中心化應用程式 (dApps)。dApp 是一種後端邏輯運行在區塊鏈上的應用程式(透過 Solana 程式而非中心化伺服器)。程式是 dApp 的後端;Anchor 生成的 TypeScript 客戶端是用戶端調用它的方式。使用 Anchor 建構的 dApp 類別包括去中心化金融 (DeFi) 協定、非同質化代幣 (NFT) 平台、去中心化自治組織 (DAOs) 和 Web3 遊戲。

在本指南結束時,你將了解 Anchor 的核心架構,擁有一個可運作的開發環境,已將一個程式部署到 Solana 的 devnet(一個具有免費、無價值 SOL 的測試網路),並獲得足夠的背景知識來評估 Anchor 是否適合你的專案。若要直接了解 Anchor 與以太坊工具和原生 Rust 開發的比較,請參閱 Anchor 與以太坊的 Hardhat 和原生 Rust 開發比較。

你將學到:

  • 什麼是 Solana 以及為什麼其架構需要 Anchor
  • IDL、巨集和 PDA 各自的作用,以及程式如何相互調用
  • 如何安裝完整的 Anchor 開發環境
  • 如何編寫和部署你的第一個 Anchor 程式
  • 在上線到主網之前需要避免的安全漏洞
  • 如何解決最常見的 Anchor 錯誤

Solana 的帳戶模型:你需要先理解的基礎

Solana 的帳戶模型是使 Anchor يصبح必要的架構基礎。它與你在以太坊或 Web2 開發中遇到的任何事物都截然不同。在 Anchor 的抽象概念變得有意義之前,你需要了解它們所解決的問題。

什麼是 Solana

Solana 是一個 Layer 1 區塊鏈網路,專為高吞吐量和低交易成本而設計。與其主網每秒大約處理 15 到 30 筆交易的以太坊不同,Solana 在最佳條件下(截至 2024 年)每秒約處理 65,000 筆交易。交易費用通常低於 0.001 美元。這種速度和成本的結合使得 Solana 非常適合需要大量小型、頻繁交易的應用程式:DeFi 交易系統、NFT 鑄造平台和鏈上遊戲。

SOL 是 Solana 的原生代幣。它用於支付交易費用和讓鏈上帳戶必須維護才能在網路中保持活躍的租金。你部署的每個程式以及你的程式創建的每個帳戶都需要 SOL 才能在鏈上存在。在 devnet(Solana 的免費測試網路)上,你可以免費獲得 SOL 空投。

速度優勢的存在是因為一些架構決策,這些決策使得 Solana 的開發在底層比以太坊開發更困難。Anchor 正是解決了這些困難的部分。

Solana 如何儲存程式和資料

在 Solana 上,「帳戶」並非日常意義上的用戶帳戶或錢包帳戶。帳戶是區塊鏈上的儲存單位:獨立的容器,可以儲存資料、SOL 餘額或可執行程式碼。

核心架構區別:在 Solana 上,程式(可執行程式碼)和資料(狀態)位於獨立的帳戶中。程式是無狀態的。程式帳戶僅包含已編譯的位元組碼。資料位於程式讀寫的獨立資料帳戶中。

在以太坊中,智能合約就像一個獨立的公寓:程式碼和家具居住在同一個空間。在 Solana 中,程式更像一個設有獨立部門的公司:可執行邏輯位於一個帳戶中,而它讀寫的資料則完全位於其他帳戶中。

屬性Ethereum 智能合約Solana 程式 + 帳戶
程式碼儲存在合約內獨立的程式帳戶
狀態儲存在合約內獨立的資料帳戶
開發者管理帳戶?否是 (Anchor 使其自動化)
語言SolidityRust (透過 Anchor)
有狀態?是程式是無狀態的;狀態存在於帳戶中

每個 Solana 帳戶都有三個屬性:擁有者(控制它的程式)、公鑰地址(它的鏈上識別碼)以及 lamport 餘額(SOL 的最小單位,1 SOL 等於 10 億 lamports)。資料帳戶也持有序列化的程式資料。

實際的影響是:當你呼叫 Solana 程式時,必須顯式傳遞程式將讀取或寫入的每一個帳戶。你必須證明所有權。你必須宣告哪些帳戶是可變的。你必須驗證哪些帳戶是簽署者。在原生 Rust 中,這種帳戶管理對於每個程式都需要數百行驗證程式碼。

這種顯式的帳戶管理正是 Anchor 約束系統設計用來解決的問題。


Solana Anchor 框架的核心組件:IDL、巨集、PDA 和 CPI

Anchor 提供四種相互關聯的機制,共同消除了原生 Solana 開發中的大部分複雜性。依序理解每一個機制是撰寫第一個程式的先決條件。

IDL (介面定義語言):Anchor 的溝通合約

每當你執行 anchor build 時,Anchor 都會自動生成一個名為介面定義語言 (IDL) 的 JSON 檔案。可以把它看作是你鏈上程式的 OpenAPI 規範:一個機器可讀的描述,包含你的程式接受的每個指令、這些指令需要的每個帳戶以及涉及的每個資料類型。

IDL 對於 Anchor 的作用,就像 ABI (應用程式二進位介面) 對於 Ethereum 上的 Solidity 合約一樣。如果你在 Ethereum 上開發過,你已經知道 ABI 如何讓前端程式碼以類型安全的方式呼叫合約函數。Anchor 的 IDL 為 Solana 程式執行相同的操作,而且它是從你的 Rust 程式碼自動生成的。你永遠不需要手動撰寫它。

在 anchor build 之後,IDL 檔案會產出在 target/idl/your_program_name.json。與此同時,Anchor 會生成 target/types/your_program_name.ts,這是一個 TypeScript 類型檔案,你的前端或測試腳本可以匯入它,以獲得對程式指令的完整類型存取。

簡化的 IDL 看起來像這樣:

{
  "version": "0.1.0",
  "name": "balance_tracker",
  "instructions": [
    {
      "name": "initialize",
      "accounts": [
        { "name": "balanceAccount", "isMut": true, "isSigner": false },
        { "name": "user",           "isMut": true, "isSigner": true  },
        { "name": "systemProgram",  "isMut": false, "isSigner": false }
      ],
      "args": []
    },
    {
      "name": "deposit",
      "accounts": [
        { "name": "balanceAccount", "isMut": true, "isSigner": false },
        { "name": "user",           "isMut": false, "isSigner": true }
      ],
      "args": [{ "name": "amount", "type": "u64" }]
    }
  ]
}

TypeScript 用戶端讀取此 IDL 並為你提供完整類型的法呼叫 (program.methods.initialize() 和 program.methods.deposit(amount)),無需任何手動介面編寫。你的 TypeScript 技能可以直接應用在這裡。

Anchor 巨集:Anchor 如何消除樣板程式碼

Anchor 提供了一組 Rust 巨集,這些是程式碼生成器,可以為你編寫重複的驗證邏輯。Rust 中的巨集是一種註釋,在編譯時會擴展為程式碼。Anchor 的巨集生成了帳戶驗證、序列化和錯誤處理程式碼,否則你就必須手動編寫這些程式碼。

三個巨集構成了每個 Anchor 程式的核心:

#[program] 標記包含指令處理函數的模組。Anchor 將此模組中的每個公共函數視為可呼叫指令。

#[derive(Accounts)] 從 Rust 結構定義生成帳戶驗證程式碼。你宣告指令需要哪些帳戶,Anchor 就會自動生成驗證所有權和可變性以及每個帳戶類型的程式碼。

#[account] 將結構標記為 Anchor 管理的帳戶資料類型,並添加 8 位元組判別器 (discriminator),讓 Anchor 能夠將你的帳戶類型與其他帳戶區分開來。

以下是這些巨集在上下文中的外觀:

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,           // Anchor 建立此帳戶 (驗證它尚不存在)
        payer = user,   // 'user' 帳戶支付免租金儲值金
        space = 8 + 8   // 8 位元組判別器 + 8 位元組給 u64 total 欄位
    )]
    pub balance_account: Account<'info, BalanceAccount>,

    #[account(mut)]          // 宣告此帳戶的 lamport 餘額將會改變
    pub user: Signer<'info>, // Signer 類型:Anchor 驗證此帳戶已簽署交易

    pub system_program: Program<'info, System>,
}

帳戶約束:Anchor 如何自動驗證帳戶

#[account(...)] 內部的每個屬性都是一個 Anchor 約束。約束是在你的指令邏輯執行之前自動運行的驗證規則。它們是 Anchor 程式比原生 Rust 程式需要更少手動驗證程式碼的主要原因。

最常見的約束是:

#[derive(Accounts)]
pub struct ExampleAccounts<'info> {
    #[account(init, payer = user, space = 8 + 8)]
    // init: 建立帳戶;如果帳戶已存在則回滾
    pub new_account: Account<'info, MyData>,

    #[account(mut)]
    // mut: 宣告此帳戶的資料或 lamport 餘額將會改變
    pub existing_account: Account<'info, MyData>,

    #[account(seeds = [b"vault", user.key().as_ref()], bump)]
    // seeds + bump: 衍生出一個 PDA 地址並驗證規範 bump
    pub pda_account: Account<'info, VaultData>,

    #[account(constraint = user_account.authority == user.key())]
    // constraint: 評估一個布林表達式;若為 false 則失敗
    pub user_account: Account<'info, UserData>,

    pub user: Signer<'info>,
    // Signer: 驗證該帳戶已簽署交易
}

如果沒有約束,你必須在指令主體中手動編寫每一個檢查。有了約束,Anchor 會在你的指令邏輯運行前強制執行它們。這既是生產力的提升,也是一項安全功能,因為約束比埋藏在指令程式碼中的手動檢查更不容易被遺忘。

程式衍生地址 (PDA):確定的鏈上儲存

程式衍生地址 (PDA) 是 Solana 開發中較為棘手的概念之一。建議將本節閱讀兩次。PDA 解鎖了 Solana 最強大的模式之一,而 Anchor 使它們比原生 Rust 更容易管理。

PDA 是從一組種子 (seeds) 和一個程式 ID 確定性地衍生出來的帳戶地址。與一般的錢包地址不同,PDA 沒有對應的私鑰。沒有任何單獨的使用者可以為 PDA 簽名。只有擁有該 PDA 的程式才能授權涉及它的交易。

把 PDA 想像成一個只有銀行(程式)才能打開的保險箱。沒有客戶擁有鑰匙;銀行以程式化方式控制存取。這使得 PDA 非常適合用於程式控制的保險庫、託管帳戶以及特定於使用者的儲存,在這些情況下,應該由程式而不是使用者擁有權限。

在原生 Rust 中,衍生 PDA 需要呼叫 Pubkey::find_program_address(&seeds, &program_id) 並手動驗證結果。Anchor 透過 seeds 約束來處理這個問題:

#[derive(Accounts)]
pub struct CreateVault<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + 32,
        seeds = [b"vault", authority.key().as_ref()], // 種子決定 PDA 地址
        bump                 // Anchor 尋找並儲存規範 bump 種子
    )]
    pub vault: Account<'info, Vault>,   // PDA 帳戶,無私鑰

    #[account(mut)]
    pub authority: Signer<'info>,

    pub system_program: Program<'info, System>,
}

seeds 約束告訴 Anchor 如何派生 PDA 地址。bump 欄位儲存標準 Bump 種子,這是一個小整數,可確保派生的地址脫離橢圓曲線且沒有私鑰。在 Anchor v0.29.x 中,標準 Bump 會自動派生和驗證。

PDA 是確定性的:給定相同的種子和程式 ID,它們總是產生相同的地址。此特性讓客戶端能夠在鏈下計算 PDA 地址以獲取數據,而程式則控制寫入權限。

跨程式呼叫 (CPI):程式如何相互溝通

跨程式呼叫 (CPI) 是 Solana 中一個程式呼叫另一個程式指令的機制。它是呼叫共享函式庫函式的鏈上等效功能。CPI 實現了組合性:您的 DeFi 協議可以呼叫 SPL Token 程式來轉移代幣,您的治理合約可以呼叫質押程式,依此類推。

Solana Program Library (SPL) 是 Solana 中程式的標準函式庫等效物。SPL Token 對於 Solana 而言,如同 ERC-20 對於以太坊,但它是作為一個共享的鏈上程式實現,而不是每個專案獨立部署的合約範本。當您的 Anchor 程式需要鑄造或轉移代幣時,它會透過 CPI 呼叫 SPL Token。

Anchor 透過 anchor-spl crate 和 CpiContext 簡化了 CPI 呼叫:

use anchor_spl::token::{self, Transfer, Token};

pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
    let cpi_accounts = Transfer {
        from: ctx.accounts.vault_token_account.to_account_info(), // Source token account
        to: ctx.accounts.user_token_account.to_account_info(),   // Destination token account
        authority: ctx.accounts.vault.to_account_info(),         // PDA signs for the vault
    };
    let cpi_ctx = CpiContext::new(ctx.accounts.token_program.to_account_info(), cpi_accounts);
    token::transfer(cpi_ctx, amount)?;  // Calls SPL Token's transfer instruction
    Ok(())
}

沒有 Anchor,組合帳戶資訊、建構指令並呼叫 invoke_signed 大約需要 30 行程式碼。Anchor 的 CPI 輔助工具將其簡化為一個類型化、可讀的函式呼叫。

您將在 逐步程式教學 中實踐這些概念。


安裝 Anchor Framework 前的先決條件:您需要什麼

在 Solana 上建構 Anchor Framework 之前,需要四個工具。

所需工具:

  1. Rust 和 Cargo (透過 rustup),建議使用 1.70.0 或更高版本
  2. Solana CLI (v1.18.x 或更高版本),用於與 Solana 網路互動的官方命令列工具;負責錢包管理、空投請求和網路配置
  3. Node.js (v18 或更高版本) 及 Yarn 或 npm,適用於 TypeScript 客戶端和 Anchor 測試
  4. Anchor Version Manager (avm),用於安裝和管理 Anchor CLI 版本;始終透過 avm 安裝 Anchor,而不是直接透過 Cargo

關於 Rust: Anchor 程式使用 Rust 編寫。Rust 是必需的。無法繞過這一點。也就是說,Anchor 的巨集抽象了最令人頭痛的 Rust 模式:帳戶驗證和序列化,以及錯誤傳播。您不需要精通 Rust 即可建構您的第一個程式。

開始時您需要:結構體、函式、基本所有權和淺層的 trait bound。您可以稍後學習:生命週期、非同步 Rust 和 borrow checker 的細節。您的 TypeScript 或 JavaScript 技能直接適用於 Anchor 的客戶端程式碼。測試、前端互動和 IDL 生成的客戶端呼叫都使用 TypeScript。您不必成為 Rust 專家也能使用 Anchor 建構,就像不必成為 Ruby 專家也能使用 Ruby on Rails 一樣。但您會接觸到 Rust,基本熟悉會有所幫助。

如果 Anchor 對 Rust 模式的抽象令您感興趣,核心元件部分詳細解釋了 Anchor 抽象了 Rust 的哪些內容。

關於 Solana CLI 與 Anchor CLI: 這是兩個具有不同職責的獨立工具。solana CLI 負責網路互動:檢查餘額、請求空投以及在 devnet 和 mainnet 之間切換。anchor CLI 負責您程式的生命週期:初始化專案、建構程式、執行測試和部署。兩者都是必需的。


建構和部署您的第一個 Anchor 程式:完整逐步指南

📌 注意: 以下所有命令均在 2024 年針對 Anchor v0.29.x 和 Solana CLI v1.18.x 進行驗證。安裝後執行 anchor --version 以確認您的版本。如果在此出版後閱讀,請在 Anchor 的 GitHub 儲存庫 (coral-xyz/anchor).) 中驗證當前版本。

步驟 1:透過 rustup 安裝 Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 依照螢幕上的提示並選擇預設安裝
source "$HOME/.cargo/env"
rustc --version
# 應輸出:rustc 1.70.0 或更高版本

步驟 2:安裝 Solana CLI

sh -c "$(curl -sSfL https://release.solana.com/stable/install)"
# 依照安裝程式輸出的 PATH 指示操作
solana --version
# 應輸出:solana-cli 1.18.x 或更高版本

當您將 Anchor 程式部署到 Solana 時,它會儲存在鏈上,並在交易呼叫時由網路的驗證節點執行。Solana CLI 是您從終端機與這些驗證節點互動的方式。

步驟 3:將 Solana CLI 設定為 devnet 並建立錢包

solana config set --url devnet
solana-keygen new
# 建立一個本地金鑰對檔案 (您的開發錢包)
# 儲存輸出路徑。Anchor 使用此金鑰對簽署部署

💡 提示: 儲存您的金鑰對檔案路徑。Anchor 會從您的 Anchor.toml 配置中讀取此金鑰對,以簽署 devnet 上的部署交易。

步驟 4:請求 devnet SOL 空投

solana airdrop 2
solana balance
# 確認餘額顯示為 2 SOL

步驟 5:安裝 avm (Anchor Version Manager) 和 Anchor

cargo install --git https://github.com/coral-xyz/avm avm --locked --force
avm install latest
avm use latest
anchor --version
# 應輸出:anchor-cli 0.29.x

📌 注意: 始終透過 avm 安裝 Anchor,而不是直接透過 cargo install anchor-cli。avm 可讓您在不重新安裝的情況下切換 Anchor 版本。這在專案固定為特定版本或您正在升級現有專案時很重要。

步驟 6:初始化您的第一個專案

anchor init my-first-program
cd my-first-program

執行 anchor init 後的專案結構:

my-first-program/
├── Anchor.toml
├── programs/
│   └── my-first-program/
│       └── src/
│           └── lib.rs
├── tests/
│   └── my-first-program.ts
└── app/

💡 提示: anchor init 命令會在 lib.rs 中建立一個可運作的範例程式骨架。請在編寫您自己的程式碼之前,立即執行 anchor test 以確認您的環境運作正常。


建構和部署您的第一個 Anchor 程式:完整逐步指南

本節將引導您從一個空專案到部署在 Solana devnet 上的程式。範例是一個餘額追蹤器:一個將儲存的餘額初始化為零,然後接受儲值指令來增加餘額的程式。這展示了完整的帳戶生命週期,並且在功能上與計數器程式相同,但使用了原始變數名稱。

編寫餘額追蹤器程式

您的 programs/my-first-program/src/lib.rs 檔案已經包含 anchor init 的骨架。請將其內容替換為以下內容:

// Anchor v0.29.x Balance Tracker Program
use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkgHH5WfZCnQ");

#[program]
pub mod balance_tracker {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        ctx.accounts.balance_account.total = 0;  // 初始餘額為零
        Ok(())
    }

    pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
        ctx.accounts.balance_account.total += amount;  // 增加儲值餘額
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = user,
        space = 8 + 8   // 8 位元組 Anchor 識別符 + 8 位元組用於 u64
    )]
    pub balance_account: Account<'info, BalanceAccount>,

    #[account(mut)]
    pub user: Signer<'info>,

    pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct Deposit<'info> {
    #[account(mut)]
    pub balance_account: Account<'info, BalanceAccount>,
    pub user: Signer<'info>,
}

#[account]
pub struct BalanceAccount {
    pub total: u64,
}

在這個程式碼中,有三個模式在你繼續之前很重要。第一,「#[derive(Accounts)]」完全生成了帳戶驗證邏輯。Anchor 會讀取你的結構定義,並編寫所有權檢查、可變性檢查以及簽署者驗證。你聲明意圖;Anchor 執行它。第二,「space = 8 + 8」分配了帳戶所需的精確位元組數:8 位元組用於 Anchor 的識別符(一個唯一的標籤,用於識別帳戶類型並防止帳戶混淆攻擊),外加 8 位元組用於 u64 的總計。第三,「Signer<'info>」是 Anchor 的內建類型,用於驗證帳戶是否簽署了交易。沒有它,任何帳戶都可以呼叫你的儲值指令。

使用 Mocha 和 TypeScript 測試你的程式碼

Mocha 是一個 JavaScript 測試運行器;Chai 提供了斷言語法。當你運行 anchor init 時,兩者都已預先安裝。打開 tests/my-first-program.ts 並替換其內容:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { MyFirstProgram } from "../target/types/my_first_program";
import { assert } from "chai";

describe("balance-tracker", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);

  const program = anchor.workspace.MyFirstProgram as Program<MyFirstProgram>;
  const balanceAccount = anchor.web3.Keypair.generate();

  it("Initializes a balance account with total = 0", async () => {
    await program.methods
      .initialize()
      .accounts({
        balanceAccount: balanceAccount.publicKey,
        user: provider.wallet.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId,
      })
      .signers([balanceAccount])
      .rpc();

    const account = await program.account.balanceAccount.fetch(
      balanceAccount.publicKey
    );
    assert.equal(account.total.toNumber(), 0, "初始餘額應為 0");
  });

  it("Deposits 500 and updates the stored balance", async () => {
    await program.methods
      .deposit(new anchor.BN(500))
      .accounts({
        balanceAccount: balanceAccount.publicKey,
        user: provider.wallet.publicKey,
      })
      .rpc();

    const account = await program.account.balanceAccount.fetch(
      balanceAccount.publicKey
    );
    assert.equal(account.total.toNumber(), 500, "儲值後餘額應為 500");
  });
});

「@coral-xyz/anchor」npm 套件是 Anchor 的 TypeScript 用戶端函式庫。當 anchor build 運行時,「Program<MyFirstProgram>」類型會自動從 IDL 生成。這就是 IDL 的實際價值所在:你的 TypeScript 程式碼可以獲得針對你的 Rust 程式的介面的完整類型安全,而無需手動編寫任何介面。

這個程式碼也是一個 dApp 的後端。上面所示的 TypeScript 用戶端展示了前端應用程式將如何呼叫它。在這個方法呼叫之上添加一個 React 或 Next.js UI,你就可以擁有一個運行在 Solana 上的完整去中心化應用程式。

建置並部署到 Devnet

請按順序在你的專案根目錄中運行這三個命令:

anchor build
# 輸出:target/deploy/my_first_program.so, target/idl/my_first_program.json, target/types/my_first_program.ts
anchor test
# 啟動本地驗證器,部署,運行 Mocha 測試,關閉
anchor deploy --provider.cluster devnet
# 輸出包含你的程式 ID:ABC123...xyz789

💡 提示: 從部署輸出中複製你的程式 ID。在 lib.rs 中用它更新「declare_id!("...")」值,然後再次運行 anchor build 以將正確的位址嵌入到你編譯的程式碼中。同時更新 Anchor.toml 中的「[programs.devnet]」區段。

📌 注意: 你正在部署到 Devnet,Solana 的測試網路。這不會花費真實的 SOL。你在設定部分獲得的 Devnet 空投將支付部署費用。

你現在已經編寫並測試了一個 Solana 程式,然後使用 Anchor Framework 將其部署。該程式將在 Devnet 上透過你的程式 ID 運行,並在每次交易呼叫它時執行。


測試 Anchor 程式:anchor test 工作流程

「anchor test」是一個單一命令,它會啟動一個本地 Solana 驗證器,將你的程式碼部署到其中,運行你的完整 Mocha 測試套件,並在完成後關閉驗證器。你無需手動管理驗證器。

測試工作流程順序:

  1. anchor test 讀取你的 Anchor.toml 以獲取配置
  2. 它會在背景啟動一個本地驗證器程序
  3. 它會建置並將你的程式碼部署到該驗證器
  4. 它使用 Mocha 運行你 tests/ 目錄中的所有 TypeScript 測試檔案
  5. 它會列印通過/失敗結果並關閉驗證器

測試檔案結構: 每個測試檔案都會從 @coral-xyz/anchor 導入,設置一個 provider(它連接到本地驗證器),並透過 IDL 生成的工作區物件來引用你的程式碼。describe 和 it 區塊是標準的 Mocha 語法。

獲取帳戶資料: 呼叫指令後,獲取鏈上帳戶以斷言其狀態:

const account = await program.account.balanceAccount.fetch(
  balanceAccount.publicKey
);
assert.equal(account.total.toNumber(), 0);

測試錯誤情況: 測試無效的輸入是否會正確失敗。一個經過良好測試的程式碼應包含負面測試:

it("Rejects deposit from an unsigned account", async () => {
  const badActor = anchor.web3.Keypair.generate();
  try {
    await program.methods
      .deposit(new anchor.BN(100))
      .accounts({
        balanceAccount: balanceAccount.publicKey,
        user: badActor.publicKey,
      })
      .rpc();
    assert.fail("Transaction should have been rejected");
  } catch (err) {
    assert.ok(err, "Anchor rejected the unsigned transaction");
  }
});

Localnet 與 Devnet: 在所有活躍開發期間使用 anchor test(Localnet)。Localnet 速度即時且免費,並且在運行之間會重置。僅在你想要共享持久 URL 或測試實際網路條件時才部署到 Devnet。

Mocha 是測試運行器(describe 和 it 區塊結構)。Chai 提供斷言語法(assert.equal、assert.ok、expect)。所有這些在每個 anchor init 項目中都已預先配置,無需單獨安裝。


Anchor 與替代方案:選擇你的 Solana 開發路徑

有三種開發路徑用於在 Solana 上構建:Anchor Framework、原生 Rust(無框架)和 Seahorse。Solana 本身是值得為你的專案評估的幾個區塊鏈之一。下表呈現了關鍵差異,不帶任何偏見。

表 1:Solana 開發方法

Anchor原生 Rust (無框架)Seahorse
語言Rust + TypeScript 客戶端僅限 RustPython (編譯為 Rust)
所需樣板程式碼低 (宏處理驗證)高 (手動驗證程式碼)低
生產就緒度生產就緒生產就緒實驗性
社群規模大 (事實上的標準)中小
學習曲線中等 (需要 Rust 基礎)陡峭 (需要完整 Rust)低 (Python 語法)
IDL/客戶端生成自動手動透過 Anchor (內部實現)
最適合大部分 Solana 應用程式開發需要完全控制的系統級程式Python 開發者進行原型設計

表格 2:Solana vs. Ethereum 工具

Anchor (Solana)Hardhat (Ethereum)Foundry (Ethereum)
區塊鏈SolanaEthereumEthereum
主要語言RustSolidity + JavaScriptSolidity
測試語言TypeScript (Mocha)JavaScript/TypeScriptSolidity (原生)
程式模型無狀態程式 + 獨立帳戶有狀態合約有狀態合約
吞吐量~65,000 TPS (最佳情況,截至 2024 年)~15–30 TPS (主網,截至 2024 年)~15–30 TPS (主網,截至 2024 年)
交易費用通常低於 $0.001可變 (每次交易常為 $1–50+)可變 (每次交易常為 $1–50+)
生態系成熟度持續成長;較新最大規模的智能合約生態系最大規模的智能合約生態系
安全性審計池較小,正在成長較大,已確立較大,已確立
最適合高頻、低費用應用程式最廣泛的生態系;最高的安全記錄偏好 Solidity 原生測試的 Ethereum 高階用戶

何時選擇 Anchor 而非原生 Rust: 對於許多 Solana 應用程式開發用例,包括 DeFi 協議、NFT 平台、代幣門控系統、DAO 和鏈上遊戲,Anchor 是一個常見的起點。樣板程式碼的減少非常顯著:一個需要 300 行原生 Rust 的程式,使用 Anchor 可能只需要 80 行。原生 Rust 開發的意義在於程式需要對計算單元消耗進行精確控制,Anchor 的抽象與異常架構衝突,或者團隊擁有深厚的 Rust 專業知識並需要最大的靈活性時。

Seahorse 框架: Seahorse 允許開發者使用 Python 編寫 Solana 程式,Seahorse 然後在內部將其編譯為與 Anchor 相容的 Rust。這降低了希望在不學習 Rust 的情況下嘗試 Solana 的 Python 開發者的語言門檻。代價是成熟度:Seahorse 是一個擁有比 Anchor 小得多用戶群的社群項目,截至 2024 年不建議用於生產應用程式。對於原型設計來說,它是一個合理的選擇;而對於生產環境,Anchor 仍然是事實上的標準。

Ethereum vs. Solana: Ethereum 提供了最大規模的智能合約開發者生態系、最深的安全性審計歷史和最廣泛的工具支援。Solana 提供了顯著更高的吞吐量和更低的費用,使其更適合交易頻率和成本是主要限制因素的應用程式。這些平台適合不同的應用程式配置。開發高頻交易基礎設施或微交易遊戲的開發者可能會偏愛 Solana;開發需要最大生態系兼容性的治理系統或代幣標準的開發者可能會偏愛 Ethereum。對於大多數生產環境下的 Solana 應用程式,Anchor 是一個常見的起點,主要協議(包括 Raydium、Marinade Finance 和 Metaplex)選擇 Anchor 作為其基礎,證實了這一點。


Anchor 框架安全性:每位 Solana 開發者必須知道的五個漏洞

⚠️ 警告: Solana 程式中的安全漏洞可能導致用戶資金的永久性、不可逆轉的損失。在將任何程式部署到主網之前,請使用 Soteria 等靜態分析工具進行測試,並考慮進行專業安全性審計。Solana 生態系已經遭受了由於以下描述的漏洞造成的重大攻擊。對於生產環境的程式,安全性審查不是可選的。如需更深入的閱讀,請參閱 Anchor 官方文檔.

Anchor 的約束系統,即帳戶結構中的 #[account(...)] 屬性,是你的第一道安全防線。它會自動強制執行所有權檢查、可變性權限和類型驗證,這些是你原本需要在原生 Rust 中手動編寫的。但約束僅保護你聲明的內容。以下漏洞源於開發者忘記聲明的內容。

缺少簽署者檢查

如果呼叫者未被要求簽署交易,任何帳戶都可以調用你的指令。這允許任何錢包調用本應受限制的指令。

// 易受攻擊:AccountInfo 未驗證呼叫者是否簽署了交易
#[derive(Accounts)]
pub struct Deposit<'info> {
    #[account(mut)]
    pub balance_account: Account<'info, BalanceAccount>,
    pub user: AccountInfo<'info>,  // 未進行簽名驗證
}
// 安全:Signer<'info> 在帳戶未簽署時失敗交易
#[derive(Accounts)]
pub struct Deposit<'info> {
    #[account(mut)]
    pub balance_account: Account<'info, BalanceAccount>,
    pub user: Signer<'info>,  // 用戶未簽署時交易會回滾
}

對於應授權指令的任何帳戶,請使用 Signer<'info>。

缺少所有權檢查 (帳戶替換)

攻擊者可以替換一個惡意構造的帳戶,該帳戶具有與你預期的帳戶相同的資料佈局,但由不同的程式擁有。

// 易受攻擊:AccountInfo 未驗證帳戶的程式所有者
pub balance_account: AccountInfo<'info>,  // 接受任何帳戶
// 安全:Account<'info, T> 驗證帳戶歸你的程式所有
pub balance_account: Account<'info, BalanceAccount>,  // 所有權檢查是自動的

對於包含程式控制數據的帳戶,始終使用 Anchor 的類型化 Account<'info, T> 包裝器,而不是原始的 AccountInfo<'info>。

重新初始化攻擊

如果 initialize 指令可以在同一帳戶上運行多次,攻擊者就可以重置應為永久狀態的狀態。

// 易受攻擊:init_if_needed 會默默接受已初始化的帳戶
#[account(init_if_needed, payer = user, space = 8 + 8)]
pub balance_account: Account<'info, BalanceAccount>,
// 安全:init 會在帳戶已存在時回滾
#[account(init, payer = user, space = 8 + 8)]
pub balance_account: Account<'info, BalanceAccount>,

對於應只創建一次的帳戶,請使用 init(而不是 init_if_needed)。如果 init_if_needed 確實有必要,請添加一個明確的初始化標誌並在你的指令邏輯中進行檢查。

整數溢出和下溢

Rust 的算術運算在調試構建中會因溢出而 panic,但在發佈構建中會靜默換行,這可能損壞財務餘額而不會引發錯誤。

// 易受攻擊:直接算術運算在發佈模式下會靜默換行
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
    ctx.accounts.balance_account.total += amount;
    Ok(())
}
// 安全:checked_add 在溢出時返回 Err 而不是換行
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
    ctx.accounts.balance_account.total = ctx.accounts.balance_account.total
        .checked_add(amount)
        .ok_or(error!(ErrorCode::Overflow))?;
    Ok(())
}

對於財務值的任何算術運算,請使用 checked_add、checked_sub 和 checked_mul。定義一個帶有 Overflow 變體的自定義 ErrorCode 枚舉,以便提供清晰的錯誤訊息。

PDA 凸值規範化

使用非規範化的凸值種子(bump seed)可能會產生一個有效但意外的 PDA 位址,從而啟用位址仿冒攻擊。

// 易受攻擊:手動 bump 不保證是標準值 #[account(seeds = [b"vault", user.key().as_ref()], bump = ctx.accounts.vault.bump)] pub vault: Account<'info, Vault>,

// 安全:省略明確的bump值。Anchor v0.29.x 會自動推導並驗證標準bump值 #[account( seeds = [b"vault", user.key().as_ref()], bump, )] pub vault: Account<'info, Vault>,

在 Anchor v0.29.x 中,請省略明確的bump值,讓 Anchor 自動推導標準bump值。如果您將bump值儲存在帳戶資料中,請在 init 期間使用 ctx.bumps.vault 來填入。

若在實施這些安全模式時遇到錯誤訊息,請參閱 疑難排解常見 Anchor 錯誤區塊。


疑難排解常見 Anchor 錯誤

以下錯誤代表開發者在使用 Anchor Framework 時最常遇到的問題。每個條目都包含確切的錯誤訊息、原因和修復方法。

📌 注意: 版本不符是導致大多數 anchor build 失敗的主要原因。在調查其他原因之前,請先檢查此表格。

Anchor 版本所需 Solana CLI所需 Rust
v0.29.xv1.18.x 或更高版本1.70.0 或更高版本
v0.28.xv1.16.x 或更高版本1.69.0 或更高版本

錯誤 1:AccountNotInitialized

AnchorError caused by account: balance_account.
Error Code: AccountNotInitialized.
Error Number: 3012.

原因: 您的程式嘗試讀取一個尚未在鏈上存在的資料帳戶。尚未呼叫 initialize 指令。

修復: 在任何讀取該帳戶的指令之前,先呼叫您的 initialize 指令。在測試中,請確保初始化 it 區塊在 deposit 測試區塊之前執行。


錯誤 2:anchor build 時的類型不符

error[E0308]: mismatched types
expected struct `anchor_lang::prelude::AccountInfo`
found struct `anchor_lang::accounts::signer::Signer`

原因: 您的程式碼使用了 Anchor v0.26.x 或更早版本的帳戶約束語法,該語法在 v0.29.x 中已變更。

修復: 查看 Anchor 官方文件) 以了解 v0.29.x 的約束語法。執行 avm use 0.29.0 以確認您的 CLI 版本與 Cargo.toml 中的依賴項相符。


錯誤 3:Anchor/Solana CLI 版本不相容

Error: RPC response error -32002: Transaction simulation failed

原因: 您的 Anchor CLI 版本與 Solana CLI 版本不相容。

修復:

avm use 0.29.0
solana --version     # 確認 Solana CLI 是 1.18.x 或更高版本

錯誤 4:重新部署後 declare_id! 不符

Error: Program Id mismatch

原因: 您重新部署到了一個新地址,但 lib.rs 中的 declare_id!( 仍然是舊的程式 ID。

修復:

anchor keys sync
anchor build

錯誤 5:Devnet 資金不足

Error: Transaction simulation failed: insufficient funds

原因: 您的 devnet 錢包沒有足夠的 SOL 來支付部署或交易費用。

修復:

solana airdrop 2
solana balance

錯誤 6:程式二進位檔超過大小限制

Error: account data too small for instruction

原因: 您的編譯後的程式二進位檔超過了 Solana 對可升級程式的大小限制。

修復: 在您的 Cargo.toml 中新增發行優化:

[profile.release]
opt-level = "z"
lto = true

從您的 Cargo.toml 和 Anchor.toml 中移除未使用的依賴項和功能標誌。

如果您遇到版本錯誤,請返回 安裝指南 並確認您的工具版本與上方相容性表格相符。


Solana 的開發者生態系統與 Anchor Framework

Anchor Framework 為 Solana 主網上部署的大多數生產環境程式提供支援,包括去中心化金融 (DeFi)、NFT 基礎設施和鏈上治理中的協定。

使用 Anchor 建構的生產環境協定:

  • Raydium:自動化做市商 (AMM) 和流動性協定
  • Marinade Finance:SOL 的質押流動化協定
  • Metaplex:NFT 標準、代幣元資料和工具套件
  • Drift Protocol:去中心化永續期貨交易所
  • Kamino Finance:流動性與借貸協定
  • Jito:支援 MEV 的質押和驗證者基礎設施

DeFi 協定代表了 Anchor 部署程式中的主要類別。去中心化交易所、借貸平台和收益聚合器都依賴 Solana 的吞吐量(截至 2024 年,最佳條件下約為每秒 65,000 筆交易)和低廉的費用(通常每筆交易低於 0.001 美元)來執行其核心操作。鏈上金融工具和現實世界資產代幣化的成長反映了這種基礎設施的成熟度。

根據 Electric Capital 的開發者報告(最新數據請參閱 electriccapital.com),Solana 的活躍開發者數量在 2023 年全年持續增長並延續至 2024 年。開發者採用的增加通常有助於區塊鏈生態系統的健康發展,但這不應被視為投資建議。

SOL 作為 Solana 網路的費用貨幣。每次呼叫鏈上程式的交易都需要少量 SOL(可在包括 Coinbase 和 Binance 在內的主要加密貨幣交易所進行交易)。如需目前的 SOL 市場數據,請參閱 CoinGecko 上的目前 SOL 價格和市場數據.)

更多使用 Anchor 進行開發的開發者意味著部署更多程式,進而意味著更活躍的 Solana 網路使用量。這種關聯是關於生態系統動態的觀察,而非投資訊號。


關於 Solana Anchor Framework 的常見問題

Solana 使用哪種程式語言?

Solana 程式是以 Rust 編寫的。運行時也支援 C 和 C++,但 Rust 是生產環境開發的標準。對於客戶端程式碼,包括測試、前端互動和呼叫您程式的腳本,則透過 @coral-xyz/anchor npm 套件使用 TypeScript 和 JavaScript。Anchor Framework 同時涵蓋這兩個層面:Rust 用於程式邏輯,TypeScript 用於與其互動的所有內容。

是的。Anchor 程式是以 Rust 編寫的,沒有其他替代方案。也就是說,Anchor 的宏抽象了 Solana 開發中最需要 Rust 知識的部分:帳戶驗證和錯誤傳播。具備基本 Rust 知識(結構體、函數、特徵、基本所有權)的開發者可以建構並部署一個可運作的 Anchor 程式。入門不需要對 lifetimes、async 模式和進階泛型有深入的 Rust 專業知識。您的 TypeScript 或 JavaScript 技能可以直接應用於每個 Anchor 專案的客戶端。

程式衍生位址 (PDA) 是一個由程式控制而非由擁有私鑰的使用者控制的帳戶位址。由於 PDA 沒有私鑰,只有擁有者程式才能授權涉及它們的交易。給定相同的種子 (seeds) 和程式 ID,PDA 始終解析到相同的位址,這讓客戶端可以在不單獨儲存其位址的情況下找到程式控制的帳戶。Anchor 透過帳戶結構體中的 seeds 約束自動處理 PDA 推導。PDA 常被用於程式控制的金庫、託管帳戶和每位使用者的資料儲存。

是的。Solana 的大部分 NFT 基礎設施都是基於 Anchor 建構的。Metaplex 標準(定義了 Solana NFT 的代幣元資料、收藏結構和版稅)本身就是一個基於 Anchor 的程式。Solana 上的任何 NFT 平台都透過 anchor-spl crate 的跨程式呼叫 (CPI) 與 Metaplex 的程式互動,該 crate 為 SPL Token 指令提供了類型安全的包裝。

是的。Anchor 由 Coral XYZ 維護,且截至 2024 年是 Solana 程式開發的事實標準。目前的穩定版本為 v0.29.x。Anchor 的 GitHub 儲存庫 (coral-xyz/anchor) 定期發布更新,以追蹤 Solana 的運行環境更新。主要的生產級協定(包括 Raydium、Marinade Finance 和 Metaplex)持續在 Anchor 上構建,證實了其生產就緒性。

Anchor 與 Ethereum 的 Hardhat 相比如何?

Anchor 和 Hardhat 在各自的區塊鏈上扮演類似的角色:兩者都提供專案腳手架、測試環境和部署工具。兩者的差異始於底層架構。Hardhat 針對 Ethereum 的有狀態合約模型,其中智能合約同時持有其邏輯和狀態。Anchor 則針對 Solana 的無狀態程式模型,其中程式和數據儲存在不同的帳戶中。開發語言也不同:Hardhat 專案使用 Solidity 編寫合約,並使用 JavaScript 或 TypeScript 進行測試;Anchor 專案使用 Rust 編寫程式,並使用 TypeScript 進行測試。交易成本也有顯著差異:Solana 交易成本通常低於 0.001 美元,而 Ethereum 的 Gas 費用則有所不同,每筆交易可能達到 1 到 50 美元或更多。如需完整的對比,請參閱 Anchor 與替代方案的比較表。

學習 Anchor 框架需要多長時間?

具備 TypeScript 或 JavaScript 經驗且先前沒有 Rust 知識的開發人員,通常可以在一到兩天內使用結構化指南構建基本 Anchor 程式並將其部署到測試網 (devnet)。從基本程式過渡到生產級 DeFi 協定則需要數週到數月的時間,具體取決於程式的複雜性和 Rust 的掌握深度。Rust 的學習曲線是主要的變數。在學習 Anchor 的同時投入時間學習 Rust 基礎知識的開發人員,在開發非平凡程式方面會看到更快的進展。

什麼是 Solana 程式庫 (SPL)?

Solana 程式庫 (SPL) 是 Solana 上針對常用操作的一系列預先部署、經過審計的程式集合。SPL Token 程式相當於 Solana 的 ERC-20 標準,它以共享鏈上程式的形式實作,而非每個專案獨立部署的合約模板。Anchor 透過 anchor-spl crate 與 SPL 整合,該 crate 為 SPL Token 指令(包括 transfer、mint_to 和 burn)提供類型安全的封裝。SPL 也是透過 Metaplex 程式實作 NFT 代幣元數據的基礎。

什麼是 Solana 的歷史證明 (PoH)?

歷史證明 (PoH) 是一種加密計時機制,Solana 使用它在交易到達驗證者進行共識之前對交易進行排序。PoH 透過在區塊鏈中直接嵌入可驗證的時間戳序列,而不需要驗證者之間進行通訊以達成交易順序共識。這減少了傳統共識協定的協調開銷,也是截至 2024 年 Solana 在理想條件下每秒能達到約 65,000 筆交易的原因之一。

Solana 適合智能合約開發嗎?

Solana 非常適合交易吞吐量和成本為主要限制因素的應用:高頻 DeFi 協定、具有大量小額交易的 NFT 市場以及鏈上遊戲。其帳戶模型比 Ethereum 需要更多的前期學習,但 Anchor 框架顯著降低了這種摩擦。Solana 是否為正確選擇取決於您的應用情境。Ethereum 提供更大的生態系統和更完善的安全審計歷史,而 Solana 則為受益於低費用和高吞吐量的使用場景提供支援。


結論:Anchor 框架適合您的 Solana 專案嗎?

對於許多 Solana 應用開發流程,Anchor 框架是一個常見的起點,而當需要更低階的控制時,原生 Rust 仍然具備其價值。

Anchor 大幅減少了原始 Rust 開發的樣板程式碼。在原生 Solana 程式碼中需要數百行的程式,使用 Anchor 的宏 (macro) 系統只需一小部分即可完成。它透過其約束系統強制執行帳戶安全性,在編譯時或運行時(而非在生產環境中)捕捉所有權和授權錯誤。此外,它能從 IDL 自動生成 TypeScript 用戶端,讓 JavaScript 開發人員無需在用戶端編寫 Rust 即可獲得一個型別化、熟悉的介面來與程式互動。

只有當您的程式需要精確控制計算單元 (compute unit) 消耗,或者 Anchor 的抽象確實與特殊的架構發生衝突時,才值得考慮使用原生 Rust 開發。對於絕大多數程式,包括 DeFi 協定、NFT 平台、DAO 和代幣閘控系統,Anchor 是您的首選起點。

後續步驟:

  1. 使用 anchor deploy --provider.cluster devnet 將本指南中的餘額追蹤程式部署到測試網 (devnet)
  2. 訪問 anchor-lang.com 探索 Anchor 的官方文件,獲取完整的 API 參考和進階功能
  3. 瀏覽 Anchor GitHub 範例儲存庫,尋找生產模式的程式
  4. 當遇到問題時,加入 Anchor Discord 或 Solana Stack Exchange 尋求社群支援

您在本指南中部署的程式是真實存在的:它運行在 Solana 的測試網上,接受交易並在鏈上儲存狀態。這是每個生產級 Solana 應用程式構建的基礎。

相關 Solana 技術指南

在追蹤生態系統工具的同時關注 SOL 的開發者可以參考 SOL 價格頁面) 作為獨立的市場參考;它不具備驗證程式安全性或部署就緒性的功能。