You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Solana Anchor框架中PDA使用zero_copy的实现与报错解决

问题原因与修复方案

首先明确:zero_copy 特性完全支持PDA账户,不存在仅能用于钱包所有账户的限制,你遇到的报错和zero_copy对PDA的兼容性无关,是代码存在几处明显错误导致的。


存在的具体问题

  • Rust端账户类型写错:你将zero_copy对应的账户类型写成了拼写错误的AccountInfor,实际上所有标记zero_copy的账户,无论是否为PDA,都必须使用AccountLoader类型包裹,否则Anchor无法正确识别账户类型。
  • 指令参数位置错误:你的create_aff指令定义了一个u8类型的指令参数_i,但TS端调用RPC时没有传入该参数,直接将账户配置对象作为第一个参数传入,Anchor会将该对象识别为_i的参数值,后续解析账户时自然找不到affAccount字段,直接抛出参数缺失错误。
  • 账户空间计算错误:zero_copy要求结构体按8字节内存对齐,你定义的Aff结构除了8字节Anchor账户鉴别器、32字节公钥、1字节bump外,还需要7字节的对齐填充,总空间应为48字节,你写的41字节会导致初始化时空间不足。
  • 测试代码笔误:初始化账户后你调用的是program.account.counts.fetch,但你定义的账户名称是aff,会导致后续查询失败。

修复后的代码

Rust端代码

// PDA种子常量
pub const AFFSEED: &[u8] = b"aff_seed";

#[program]
mod myapp {
    use super::*;

    pub fn create_aff(ctx: Context<CreateAff>, _i: u8) -> Result<()> {
        let acc = &mut ctx.accounts.aff_account.load_init()?;
        acc.aff_account = ctx.accounts.user.key();
        acc.bump = *ctx.bumps.get("aff_account").unwrap();
        Ok(())
    }
}

// 显式标注内存布局与对齐,避免zero_copy内存解析错误
#[account(zero_copy)]
#[repr(C, align(8))]
pub struct Aff {
    aff_account: Pubkey,
    bump: u8,
}

#[derive(Accounts)]
#[instruction(i: u8)]
pub struct CreateAff<'info> {
    // 修正类型为AccountLoader,修正空间为48字节
    #[account(init, space = 48, payer = user, seeds = [AFFSEED], bump)]
    aff_account: AccountLoader<'info, Aff>,
    #[account(mut)]
    user: Signer<'info>,
    /// CHECK: 仅作为系统程序调用,无需额外校验
    system_program: AccountInfo<'info>,
}

测试端JS代码

it("Creates the aff account if doesn't exist", async () => {
  const [affPDA, bump] = await PublicKey.findProgramAddress(
    [anchor.utils.bytes.utf8.encode(AFFSEED)],
    program.programId
  );
  console.log("CreateAff: affPDA is [", affPDA.toString(), "], bump is", bump);
  const contents = await program.account.aff.fetchNullable(affPDA);
  if (contents == null) {
    // 注意:先按顺序传入指令参数,最后再传入账户配置对象
    await program.rpc.createAff(
      0, // 对应Rust端的_i:u8参数,传任意u8值即可
      {
        accounts: {
          affAccount: affPDA,
          user: usr,
          systemProgram: SystemProgram.programId,
        },
        signers: [],
      }
    );
    // 修正查询的账户类型为aff
    const affe = await program.account.aff.fetch(affPDA);
    console.log("affe: ", affe);
    assert.ok(true);
  } else {
    assert.ok(true);
  }
});

补充说明

使用AccountLoader+zero_copy操作PDA时,和操作普通用户持有账户没有任何区别:初始化时用load_init()获取可写引用,读取时用load(),修改时用load_mut()即可。该模式下Anchor不会对账户做全量序列化/反序列化,直接通过内存映射访问账户数据,完全不会触发BPF 4KB栈限制,单账户最多可存储10KB数据(Solana默认单账户大小上限,需要更大容量可手动调用账户扩容指令)。

内容的提问来源于stack exchange,提问作者AllBlooming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 05:30:47