在 Anchor 中的 #[derive(Accounts)] 不同类型的账户

  • 0xE
  • 发布于 2025-04-03 10:54
  • 阅读 1459

本文详细讲解了 Solana Anchor 框架中 [derive(Accounts)] 宏的作用及其四种常见账户类型(Account、UncheckedAccount、Signer、Program),通过代码示例阐明其功能与应用场景,并分析了 Solana 交易中账户预指定的必要性。

在 Solana 的 Anchor 框架中,#[derive(Accounts)] 是一个宏,用于定义结构体,该结构体包含程序执行期间访问的所有账户引用。本文将详细讲解其作用,分析 Solana 交易中账户指定的必要性,并介绍四种常见账户类型:Account、UncheckedAccount、Signer 和 Program,通过代码示例阐明其功能与应用场景。


Solana 交易中账户的预指定要求

Solana 的高性能源于交易的并行处理能力。例如,Alice 和 Bob 的交易若不冲突,可同时执行。但若两者试图修改同一账户,则并行化失败。为避免冲突,Solana 要求交易事先声明所有访问的账户。运行时根据这些声明判断是否存在竞争,确保交易顺序或失败处理(如优先级费用决定)。因此,每个 Anchor 函数需配备独立的 #[derive(Accounts)] 结构体,列出程序可能访问的账户。这种机制与以太坊的 EIP-2930 访问列表交易有相似之处。

常用的账户类型包括:

  • Account
  • UncheckedAccount
  • Signer
  • Program

以下初始化存储的代码展示了三种类型:

#[derive(Accounts)]
pub struct Initialize<'info> {

    #[account(init, 
        payer = signer, 
        space = size_of::<MyStorage>() + 8, 
        seeds = [], 
        bump)]
    pub my_storage: Account<'info, MyStorage>,

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

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

读取余额时则引入第四种类型:

#[derive(Accounts)]
pub struct ReadBalance<'info> {
    /// CHECK: although we read this account's balance, we don't do anything with the information
    #[account(mut)]
    pub acct: UncheckedAccount<'info>,
}

这些类型通过 use anchor_lang::prelude::*; 导入,各自承担验证和交互功能。


Account

Account 类型确保加载的账户由程序拥有,若所有者不匹配,则加载失败。这是防止误读非程序创建数据的重要安全措施。

以下示例尝试将非程序拥有的账户传入,结果失败:

Rust 代码

use anchor_lang::prelude::*;

declare_id!("7wCSwt6CxCCLnmcXhL3v96512N82EUZ9z7ayC1LtHPNj");

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

    pub fn foo(ctx: Context<Foo>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Foo<'info> {
    some_account: Account<'info, SomeAccount>,
}

#[account]
pub struct SomeAccount {}

Typescript 代码

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { AccountTypes } from "../target/types/account_types";

describe("account_types", () => {
  async function airdropSol(publicKey, amount) {
    let airdropTx = await anchor.getProvider().connection.requestAirdrop(
      publicKey,
      amount * anchor.web3.LAMPORTS_PER_SOL
    );
    await confirmTransaction(airdropTx);
  }

  async function confirmTransaction(tx) {
    const latestBlockHash = await anchor.getProvider().connection.getLatestBlockhash();
    await anchor.getProvider().connection.confirmTransaction({
      blockhash: latestBlockHash.blockhash,
      lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
      signature: tx,
    });
  }

  anchor.setProvider(anchor.AnchorProvider.env());
  const program = anchor.workspace.AccountTypes as Program<AccountTypes>;

  it("Wrong owner with Account", async () => {
    const newKeypair = anchor.web3.Keypair.generate();
    await airdropSol(newKeypair.publicKey, 10);
    await program.methods.foo().accounts({ someAccount: newKeypair.publicKey }).rpc();
  });
});

输出

  1) account_types
       Wrong owner with Account:
     Error: AnchorError caused by account: some_account. Error Code: AccountOwnedByWrongProgram. Error Number: 3007. Error Message: The given account is owned by a different program than expected.
Program log: Left:
Program log: 11111111111111111111111111111111
Program log: Right:
Program log: 7wCSwt6CxCCLnmcXhL3v96512N82EUZ9z7ayC1LtHPNj

若添加 init 宏,账户所有权会转移给程序,但此例未使用。更多细节见 [文档](https:...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。