当前位置: 首页 > news >正文

在 Solana 中实现映射表与嵌套映射表

本文将通过填充 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,实现类似功能。


初始化映射表

Rust 实现

首先展示初始化代码,引入新语法:

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

关键点解析

  1. seeds = [&key.to_le_bytes()]
    • seeds 需为字节数组,key(u64)通过 to_le_bytes() 转换为 8 字节小端数组(little-endian)。
    • 可选大端(to_be_bytes()),只需保持一致。
    • seeds 类似映射表的键,生成唯一的程序派生地址(PDA)。
  2. #[instruction(key: u64)]
    • 使 initialize 的参数 key 在 Initialize 结构中可见,用于 seeds 计算。
    • 缺少此宏,init 将无法访问 key。

类比:seeds 中的 key 类似 mapping(uint64 => Val) 中的键。

Typescript 实现

初始化测试代码:

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();
  });
});
  • key.toArrayLike(Buffer, "le", 8) 将 u64 转为 8 字节小端缓冲区,与 Rust 保持一致。
  • 每个映射表项对应一个独立账户,需单独初始化。

设置映射表值

Rust 实现

新增 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>,
}
  • Set 使用相同 seeds 定位账户,mut 表示可写。

Typescript 读写

完整测试代码:

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()}`);

  });
});
  • 客户端通过 seeds 推导地址,读写语法与前文一致。

嵌套映射表解析

Solidity vs. 真实嵌套

  • 真实嵌套(如 Python):map[key1] 返回另一映射表。
  • Solidity 嵌套:mapping(uint => mapping(uint => uint)) 需提供所有键(如 map[key1][key2])获取值,非真正嵌套。
  • Solana 限制:seeds 模拟 Solidity 风格,需完整提供所有键。

Rust 实现

完整嵌套映射表代码:

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,
}
  • seeds 包含两个键,模拟 mapping(uint => mapping(uint => uint))。

Typescript 实现

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://t.me/gtokentool  。

相关文章:

  • PolarDB数据库表恢复实战指南:通过控制台恢复表的完整操作流程
  • 【Kafka】消费者幂等性保障全解析
  • 大模型LLMs框架Langchain之链详解
  • 一、Python编程语言介绍
  • 微服务集成测试 -华为OD机试真题(A卷、Python)
  • 数据结构C语言练习(单双链表)
  • 深度学习 机器学习和强化学习算法的关系
  • flink 分组窗口聚合 与 窗口表值函数聚合 的区别
  • 【算法手记6】NC1 大数加法 NC40 链表相加(二) NC10 大数乘法
  • 营销库存系统设计方案
  • 《Linux运维总结:基于银河麒麟V10+ARM64架构CPU源码编译部署单实例redis7.2.6》
  • 基础认证-单选题(一)
  • Golang 的 GMP 调度机制常见问题及解答
  • 刘裕的简介
  • ResNet(残差网络)中的残差是什么?
  • 今日行情明日机会——20250328
  • zynq7000 + ucos3 + lwip202_v1_2调试过程
  • 从混乱思绪到清晰表达:记录想法如何改变你的学习人生
  • Problem A: 计算奇数和
  • 央视少儿APP V2.6.2
  • 101岁陕西省军区原司令员冀廷璧逝世,曾参加百团大战
  • 女生“生理期请病假要脱裤子证明”?高校回应:视频经处理后有失真等问题
  • 舱位已排到月底,跨境电商忙补货!美线订单大增面临爆舱,6月运价或翻倍
  • 国税总局上海市税务局通报:收到王某对刘某某及相关企业涉税问题举报,正依法依规办理
  • 河南:响鼓重锤对违规吃喝问题露头就打、反复敲打、人人喊打
  • 《求是》杂志发表习近平总书记重要文章《锲而不舍落实中央八项规定精神,以优良党风引领社风民风》