本文详细介绍了如何在 Solana 中使用 seeds
参数来实现类似于 Solidity 中的映射和嵌套映射,并提供了 Rust 和 Typescript 的代码示例。
在之前的教程中,seeds=[]
参数总是为空。如果我们向其中放入数据,它的表现就像 Solidity 映射中的一个或多个键。
考虑以下示例:
contract ExampleMapping {
struct SomeNum {
uint64 num;
}
mapping(uint64 => SomeNum) public exampleMap;
function setExampleMap(uint64 key, uint64 val) public {
exampleMap[key] = SomeNum(val);
}
}
我们现在创建一个 Solana Anchor 程序 example_map
。
起初,我们只会展示初始化步骤,因为它将引入一些需要解释的新语法。
use anchor_lang::prelude::*;
use std::mem::size_of;
declare_id!("DntexDPByFxpVeBSjd6nLqQQSqZmSaDkP8TUbcJ9jAgt");
#[program]
pub mod example_map {
use super::*;
pub fn initialize(ctx: Context<Initialize>, key: u64) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
#[instruction(key: u64)]
pub struct Initialize<'info> {
#[account(init,
payer = signer,
space = size_of::<Val>() + 8,
seeds=[&key.to_le_bytes().as_ref()],
bump)]
val: Account<'info, Val>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
}
#[account]
pub struct Val {
value: u64,
}
以下是你可以将映射视为的方式:
&key.to_le_bytes().as_ref()
中的 seeds 参数 key
可以看作是一个类似于 Solidity 构造形式的“键”:
mapping(uint256 => uint256) myMap;
myMap[key] = val
代码中不熟悉的部分是 #[instruction(key: u64)]
和 seeds=[&key.to_le_bytes().as_ref()]
。
seeds
中的项预期是字节。然而,我们传入的是一个 u64
,它不是字节类型。为了将其转换为字节,我们使用 to_le_bytes()
。“le” 是指 “小端”。seeds 不必编码为小端字节,我们只是在这个例子中选择了它。只要你保持一致,大端也可以使用。如果要转换为大端,我们将使用 to_be_bytes()
。
为了在 initialize(ctx: Context<Initialize>, key: u64)
中“传递”参数 key
,我们需要使用 instruction
宏,否则我们的 init
宏没有办法“看到”来自 initialize
的 key
参数。
以下代码展示了如何初始化账户:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { ExampleMap } from "../target/types/example_map";
describe("example_map", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.ExampleMap as Program<ExampleMap>;
it("Initialize mapping storage", async () => {
const key = new anchor.BN(42);
const seeds = [key.toArrayLike(Buffer, "le", 8)];
let valueAccount = anchor.web3.PublicKey.findProgramAddressSync(
seeds,
program.programId
)[0];
await program.methods.initialize(key).accounts({val: valueAccount}).rpc();
});
});
代码 key.toArrayLike(Buffer, "le", 8)
指定我们试图使用来自 key
的值创建一个大小为 8 字节的字节缓冲区。我们选择 8 字节,因为我们的键是 64 位,64 位是 8 字节。“le” 是小端,以便与 Rust 代码匹配。
映射中的每个“值”都是一个单独的账户,必须单独初始化。
我们需要的额外 Rust 代码来设置值。这里的语法应该是熟悉的。
// 在 #[program] 模块内
pub fn set(ctx: Context<Set>, key: u64, val: u64) -> Result<()> {
ctx.accounts.val.value = val;
Ok(())
}
//...
#[derive(Accounts)]
#[instruction(key: u64)]
pub struct Set<'info> {
#[account(mut)]
val: Account<'info, Val>,
}
因为我们在客户端(Typescript)推导出存储值的账户地址,所以我们就像处理 seeds
数组为空的账户那样读取和写入它。读取和写入 Solana 账户数据 的语法与之前的教程是相同的:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@cora...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!