Anchor
Anchor for Dummies

Anchor for Dummies

Anchor 101

Anchor 简介

什么是 Anchor

Anchor 是 Solana 智能合约开发的首选框架,提供了一个完整的工作流程,用于编写、测试、部署和与链上程序交互。

主要优势

  • 减少样板代码:Anchor 抽象了账户管理、指令序列化和错误处理的重复工作,让您可以专注于核心业务逻辑。
  • 内置安全性:默认运行严格的检查,例如账户所有权验证和数据验证,在问题出现之前就能缓解许多常见漏洞。

Anchor 宏

  • declare_id!():声明程序所在的链上地址。
  • #[program]:标记包含每个指令入口点和业务逻辑函数的模块。
  • #[derive(Accounts)]:列出指令所需的账户并自动强制执行其约束条件。
  • #[error_code]:定义自定义的、可读性强的错误类型,使调试更清晰、更快速。

这些声明式宏共同抽象了底层字节管理,使您能够以更少的努力交付安全的、生产级的 Solana 程序。

程序结构

让我们从一个精简版本的程序开始,详细解释每个宏的实际作用:

declare_id!("22222222222222222222222222222222222222222222");
 
#[program]
pub mod blueshift_anchor_vault {
    use super::*;
 
    pub fn deposit(ctx: Context<VaultAction>, amount: u64) -> Result<()> {
        // ...
        Ok(())
    }
 
    pub fn withdraw(ctx: Context<VaultAction>) -> Result<()> {
        // ...
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct VaultAction<'info> {
    // ...
}
 
#[error_code]
pub enum VaultError {
    // ...
}

这将转变为专注的模块,而不是将所有内容都塞入 lib.rs,以实现更结构化的程序。程序文件夹树大致如下:

src
├── instructions
│       ├── instruction1.rs
│       ├── mod.rs
│       ├── instruction2.rs
│       └── instruction3.rs
├── errors.rs
├── lib.rs
└── state.rs

declare_id!()

declare_id!() 宏为您的程序分配其链上地址;这是一个从项目的 target 文件夹中的密钥对派生的唯一公钥。该密钥对对编译后的 .so 二进制文件(包含所有程序逻辑和数据)进行签名和部署。

注意: 我们在 Blueshift 示例中使用占位符 222222... 是因为我们的内部测试套件。在生产环境中,当您运行标准的构建和部署命令时,Anchor 会为您生成一个新的程序 ID。

#[program]#[derive(Accounts)]

每个指令都有自己的 Context 结构体,其中列出了所有账户,以及(可选的)指令所需的任何数据。

在此示例中,depositwithdraw 共享相同的账户;因此,我们将创建一个名为 VaultAction 的单一账户结构体,以提高效率并简化操作。

深入了解 #[derive(Accounts)]

#[derive(Accounts)]
pub struct VaultAction<'info> {
  #[account(mut)]
  pub signer: Signer<'info>,
 
  #[account(
    mut,
    seeds = [b"vault", signer.key().as_ref()],
    bump,
  )]
  pub vault: SystemAccount<'info>,
 
  pub system_program: Program<'info, System>,
}

从代码片段中可以看出,#[derive(Accounts)] 宏承担了三个关键职责:

  • 声明特定指令所需的所有账户。
  • 自动执行约束检查,在运行时阻止许多错误和潜在漏洞。
  • 生成帮助方法,让您可以安全地访问和修改账户。

它通过账户类型和内联属性的组合来实现这些功能。

示例中的账户类型

  • Signer<'info>:验证账户已签署交易;这对于安全性以及需要签名的 CPI 至关重要。
  • SystemAccount<'info>:确认账户由系统程序拥有。
  • Program<'info, System>:确保账户是可执行的,并与系统程序 ID 匹配,从而支持账户创建或 lamport 转账等 CPI。

您将遇到的内联属性

  • mut:将账户标记为可变;当账户的 lamport 余额或数据可能发生变化时,这是必需的。
  • seeds & bump:验证账户是由提供的种子加上一个 bump 字节生成的程序派生地址 (PDA)。

注意 PDA 很重要,因为:

  • 当由拥有它们的程序使用时,PDA 可以代表程序签署 CPI。
  • 它们为持久化程序状态提供了确定性和可验证的地址。

#[error_code]

#[error_code] 宏允许您在程序中定义清晰的自定义错误。

#[error_code]
pub enum VaultError {
    #[msg("Vault already exists")]
    VaultAlreadyExists,
    #[msg("Invalid amount")]
    InvalidAmount,
}

每个枚举变量都可以携带一个 #[msg(...)] 属性,该属性在错误发生时记录描述性字符串;在调试过程中比原始数字代码更有帮助。

Blueshift © 2025Commit: fd080b2