Solana 60 天课程

2025年02月27日更新 66 人订阅
原价: ¥ 28 限时优惠
专栏简介 开始 Solana - 安装与故障排除 Solana 和 Rust 中的算术与基本类型 Solana Anchor 程序 IDL Solana中的Require、Revert和自定义错误 Solana程序是可升级的,并且没有构造函数 Solidity开发者的Rust基础 Rust不寻常的语法 Rust 函数式过程宏 Rust 结构体与属性式和自定义派生宏 Rust 和 Solana 中的可见性与“继承” Solana时钟及其他“区块”变量 Solana 系统变量详解 Solana 日志、“事件”与交易历史 Tx.origin、msg.sender 和 onlyOwner 在 Solana 中:识别调用者 Solana 计算单元与交易费用介绍 在 Solana 和 Anchor 中初始化账户 Solana 计数器教程:在账户中读写数据 使用 Solana web3 js 和 Anchor 读取账户数据 在Solana中创建“映射”和“嵌套映射” Solana中的存储成本、最大存储容量和账户调整 在 Solana 中读取账户余额的 Anchor 方法:address(account).balance 功能修饰符(view、pure、payable)和回退函数在 Solana 中不存在的原因 在 Solana 上实现 SOL 转账及构建支付分配器 使用不同签名者修改账户 PDA(程序派生地址)与 Solana 中的密钥对账户 理解 Solana 中的账户所有权:从PDA中转移SOL Anchor 中的 Init if needed 与重初始化攻击 Solana 中的多重调用:批量交易与交易大小限制 Solana 中的所有者与权限 在Solana中删除和关闭账户与程序 在 Anchor 中:不同类型的账户 在链上读取另一个锚点程序账户数据 在 Anchor 中的跨程序调用(CPI)

在链上读取另一个锚点程序账户数据

  • RareSkills
  • 发布于 2024-05-09 13:32
  • 阅读 802

本文详细介绍了在Solana链上程序中如何读取不属于自己的账户数据,通过创建data_holderdata_reader两个程序,展示了如何初始化并读取PDA中的数据,并探讨了Anchor框架下的数据反序列化机制及其限制。

在 Solidity 中,读取另一个合约的存储需要调用 view 函数或者存储变量是公共的。在 Solana 中,离线客户端可以直接读取存储账户。这个教程展示了一个链上 Solana 程序如何读取它不拥有的账户中的数据。

我们将设置两个程序: data_holderdata_readerdata_holder 将初始化并拥有一个 PDA,其数据将被 data_reader 读取。

设置存储数据的 data_holder 程序: Shell 1

以下代码是一个基本的 Solana 程序,它初始化账户 Storage,其中包含 u64 字段 x,并在初始化时将值 9 存储在其中:

Typescript 代码:

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

describe("data-holder", () => {
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.DataHolder as Program<DataHolder>;

  it("Is initialized!", async () => {
    const seeds = [];
    const [storage, _bump] = anchor.web3.PublicKey.findProgramAddressSync(
      seeds,
      program.programId
    );

    await program.methods
      .initialize()
      .accounts({ storage: storage })
      .rpc();

    let storageStruct = await program.account.storage.fetch(
      storage
    );

    console.log("The value of x is: ",storageStruct.x.toString());

    console.log("Storage account address: ", storage.toBase58());
  });
});

测试将打印出 PDA 的地址,我们稍后会提到这个地址:

PDA 输出

读取器

为了让 data_reader 读取另一个账户,必须通过 Context 结构将该账户的公共密钥作为交易的一部分传递。这与传递任何其他类型的账户没有区别。

账户中的数据以序列化字节的形式存储。 为了反序列化账户,data_reader 程序需要读取它的 Rust 结构定义。我们需要以下账户定义,并且它与 data_holder 中的 Storage 结构完全相同:

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

这个结构与 data_reader 中的结构完全相同——连名称也必须相同(稍后我们会介绍为什么)。读取账户的代码在以下两行中:

let mut data_slice: &[u8] = &data_account.data.borrow();

let data_struct: Storage = 
    AccountDeserialize::try_deserialize(
        &mut data_slice,
    )?;

data_slice 是账户中数据的原始字节。如果你运行 solana account <pda address>(使用我们在部署 data_holder 时生成的 PDA 地址),你可以在那里看到数据,包括我们储存在红框中的数字 9:

终端输出 solana \\<pda address\\> 包含数字 9

黄框中前 8 个字节是账户鉴别符,稍后我们将对此进行描述。

反序列化发生在此步骤:

let data_struct: Storage =
    AccountDeserialize::try_deserialize(
        &mut data_slice,
    )?;

在这里传递类型 Storage(我们上面定义的结构),告诉 Solana 如何(尝试)反序列化数据。

现在让我们在新文件夹中创建一个单独的 anchor 项目 anchor new data_reader

完整的 Rust 代码如下:


use anchor_lang::prelude::*;

declare_id!("HjJ1Rqsth5uxA6HKNGy8VVRvwK4W7aFgmQsss7UxePBw");

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

    pub fn read_other_data(
        ctx: Context<ReadOtherData>,
    ) -> Result<()> {

        let data_account = &ctx.accounts.other_data;

        if data_account.data_is_empty() {
            return err!(MyError::NoData);
        }

        let mut data_slice: &[u8] = &data_account.data.borrow();

        let data_struct: Storage =
            AccountDeserialize::try_deserialize(
                &mut data_slice,
            )?;

        msg!("The value of x is: {}", data_struct.x);

        Ok(())
    }
}

#[error_code]
pub enum MyError {
    #[msg("No data")]
    NoData,
}

#[derive(Accounts)]...

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

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

0 条评论

请先 登录 后评论