本文通过在 Solana 的 Anchor 框架中填充 seeds 参数,结合 Rust 和 Typescript 代码示例,展示了如何模拟 Solidity 中的映射表和嵌套映射表功能,利用 seeds 生成唯一账户地址实现键值存储。
在前述文章中,seeds=[] 始终为空。本文将通过填充 seeds 参数,模拟 Solidity 中的映射(mapping)功能。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!("BB7Z6YvBMPRKDutbVh8SGy8QS82e9GZc4jLrEt1Ym2o9");
#[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()],
bump
)]
val: Account<'info, Val>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
}
#[account]
pub struct Val {
value: u64,
}
关键点解析
类比:seeds 中的 key 类似 mapping(uint64 => Val) 中的键。
初始化测试代码:
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)];
const [valueAccount, _bump] = anchor.web3.PublicKey.findProgramAddressSync(
seeds,
program.programId
);
await program.methods
.initialize(key)
.accounts({ val: valueAccount })
.rpc();
});
});
新增 set 函数:
#[program]
pub mod example_map {
use super::*;
pub fn initialize(ctx: Context<Initialize>, key: u64) -> Result<()> {
Ok(())
}
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, seeds = [&key.to_le_bytes()], bump)]
val: Account<'info, Val>,
}
完整测试代码:
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 and set value", async () => {
const key = new anchor.BN(42);
const value = new anchor.BN(1337);
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();
await program.methods.set(key, value).accounts({ val: valueAccount }).rpc();
let result = await program.account.val.fetch(valueAccount);
console.log(`the value ${result.value} was stored in ${valueAccount.toBase58()}`);
});
});
Solidity vs. 真实嵌套
完整嵌套映射表代码:
use anchor_lang::prelude::*;
use std::mem::size_of;
declare_id!("BB7Z6YvBMPRKDutbVh8SGy8QS82e9GZc4jLrEt1Ym2o9");
#[program]
pub mod example_map {
use super::*;
pub fn initialize(ctx: Context<Initialize>, key1: u64, key2: u64) -> Result<()> {
Ok(())
}
pub fn set(ctx: Context<Set>, key1: u64, key2: u64, val: u64) -> Result<()> {
ctx.accounts.val.value = val;
Ok(())
}
}
#[derive(Accounts)]
#[instruction(key1: u64, key2: u64)]
pub struct Initialize<'info> {
#[account(
init,
payer = signer,
space = size_of::<Val>() + 8,
seeds = [&key1.to_le_bytes(), &key2.to_le_bytes()],
bump
)]
val: Account<'info, Val>,
#[account(mut)]
signer: Signer<'info>,
system_program: Program<'info, System>,
}
#[derive(Accounts)]
#[instruction(key1: u64, key2: u64)]
pub struct Set<'info> {
#[account(mut, seeds = [&key1.to_le_bytes(), &key2.to_le_bytes()], bump)]
val: Account<'info, Val>,
}
#[account]
pub struct Val {
value: u64,
}
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 and set value", async () => {
const key1 = new anchor.BN(42);
const key2 = new anchor.BN(43);
const value = new anchor.BN(1337);
const seeds = [key1.toArrayLike(Buffer, "le", 8), key2.toArrayLike(Buffer, "le", 8)];
let valueAccount = anchor.web3.PublicKey.findProgramAddressSync(
seeds,
program.programId,
)[0];
await program.methods.initialize(key1, key2).accounts({val: valueAccount}).rpc();
await program.methods.set(key1, key2, value).accounts({val: valueAccount}).rpc();
let result = await program.account.val.fetch(valueAccount);
console.log(`the value ${result.value} was stored in ${valueAccount.toBase58()}`);
});
});
【笔记配套代码】 https://github.com/0xE1337/rareskills_evm_to_solana 【参考资料】 https://learnblockchain.cn/column/119 https://www.rareskills.io/solana-tutorial
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!