使用 Solana web3 js 和 Anchor 读取账户数据

  • RareSkills
  • 发布于 2024-02-28 17:50
  • 阅读 182

本教程详细介绍了如何通过Solana的web3 Javascript客户端直接读取账户数据,并展示了如何在Web应用的前端实现这一功能。教程中首先使用Rust代码初始化并写入数据,然后通过TypeScript读取和反序列化数据,并进一步演示了如何读取由其他Anchor程序创建的账户数据。

读取账户数据 本教程展示了如何直接从 Solana web3 Javascript 客户端读取账户数据,以便 web 应用能够在前端读取它。

在上一个教程中,我们使用 solana account <account address> 来读取我们写入的数据,但如果我们正在网站上构建一个 dApp,这种方法是行不通的。

相反,我们必须计算存储账户的地址,读取数据,并从 Solana web3 客户端反序列化数据。

想象一下,如果在 Ethereum 中我们希望避免使用公共变量或视图函数,但仍然想在前端显示它们的值。要在不公开它们或添加视图函数的情况下查看存储变量中的值,我们将使用 getStorageAt(contract_address, slot) API。在 Solana 中,我们将做类似的事情,不过我们只需传入程序的地址,并派生出其存储账户的地址。

以下是上一个教程中的 Rust 代码。它初始化 MyStorage 并使用 set 函数写入 x。在本教程中我们不修改它:

use anchor_lang::prelude::*;
use std::mem::size_of;

declare_id!("GLKUcCtHx6nkuDLTz5TNFrR4tt4wDNuk24Aid2GrDLC6");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }

    pub fn set(ctx: Context<Set>, new_x: u64) -> Result<()> {
        ctx.accounts.my_storage.x = new_x;
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Set<'info> {
    #[account(mut, seeds = [], bump)]
    pub my_storage: Account<'info, MyStorage>,
}

#[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>,
}

#[account]
pub struct MyStorage {
    x: u64,
}

以下是 Typescript 单元测试,它:

  1. 初始化账户
  2. 170 写入存储
  3. 使用 fetch 函数读取值:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { BasicStorage } from "../target/types/basic_storage";

describe("basic_storage", () => { 
    anchor.setProvider(anchor.AnchorProvider.env());

    const program = anchor.workspace.BasicStorage as Program;

    it("已初始化!", async () => { 
        const seeds = [] 
        const [myStorage, _bump] = anchor.web3.PublicKey.findProgramAddressSync(seeds, program.programId);

        console.log("存储账户地址是", myStorage.toBase58());

        await program.methods.initialize().accounts({myStorage: myStorage}).rpc();
        await program.methods.set(new anchor.BN(170)).accounts({myStorage: myStorage}).rpc();

        // ***********************************
        // *** 新代码以读取结构 ***
        // ***********************************
        let myStorageStruct = await program.account.myStorage.fetch(myStorage);
        console.log("x 的值是:", myStorageStruct.x.toString());

    }); 
});

在 Anchor 中查看账户可以使用:

```typescript
let myStorageStruct = await program.account.myStorage.fetch(myStorage);
console.log("x 的值是:", myStorageStruct.x.toString());

Anchor 会自动计算 MyStorage 账户的地址,读取它并将其格式化为 Typescript 对象。

要理解 Anchor 如何神奇地将 Rust 结构转换为 Typescript 结构,我们来看看 target/idl/basic_storage.json 中的 IDL。在 JSON 的底部,我们可以看到程序正在创建的结构定义:

"accounts": [
  {
    "name": "MyStorage",
    "type": {
      "kind": "struct",
      "fields": [
        {
          "name": "x",
          "type": "u64"
        }
      ]
    }
  }
],

此方法仅适用于你的程序或客户端初始化或创建的账户,并且具有 IDL,不适用于任意账户。

也就是说,如果你选择 Solana 上的一个随机账户并使用上述代码,反序列化几乎肯定会失败。稍后在本文中,我们将以一种更“原始”的方式读取该账户。

fetch 函数并不是魔法。那么,我们如何对于一个我们没有创建的账户进行操作呢?

从 Anchor Solana 程序创建的账户中提取...

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

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

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/