Solana 60 天课程

2025年02月27日更新 77 人订阅
原价: ¥ 36 限时优惠
专栏简介 开始 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) SPL Token 的运作方式 使用 Anchor 和 Web3.js 转移 SPL Token Solana 教程 - 如何实现 Token 出售 基础银行教程 Metaplex Token 元数据工作原理 使用Metaplex实施代币元数据 使用 LiteSVM 进行时间旅行测试

使用Metaplex实施代币元数据

本文介绍了如何使用Metaplex元数据标准为SPL代币附加元数据。

我们在之前的教程中介绍了 Metaplex 元数据标准。在本教程中,我们将创建一个 SPL token,并使用 Metaplex 标准将元数据附加到它。

我们将构建一个 Anchor 程序,该程序使用 Metaplex 标准创建带有附加元数据的 SPL token。这允许我们向 token 添加信息,例如名称、符号、图像和其他属性。

在开始构建之前,让我们了解 Metaplex 标准,该标准控制 token 元数据的结构方式。

Metaplex Token 标准和 URI 格式

当我们为 token 创建元数据时,我们需要遵循 Metaplex 定义的特定 JSON 格式。结构取决于我们创建的 token 类型(NFT、同质化 token 等)。

主要有三个标准:

同质化标准(元数据账户 token_standard = 2

这是带有元数据的常规 SPL token。这是我们将在本文后面创建的示例。

它的元数据 JSON 模式定义如下:

 {
  "name": "Example Token",
  "symbol": "EXT",
  "description": "A basic fungible SPL token with minimal metadata.",
  "image": "https://example.com/images/ext-logo.png"
}

同质化资产标准(token_standard = 1

这类似于 Ethereum 上的 ERC-1155,用于游戏内货币或物品。它被定义为供应量大于 1 但小数位为零(即,没有小数单位)的同质化 SPL token。

它的 JSON 模式包括一些额外的字段,例如 attributes

{
  "name": "Game Sword",
  "description": "A rare in-game sword used in the battle arena.",
  "image": "https://example.com/images/sword.png",
  "animation_url": "https://example.com/animations/sword-spin.mp4",
  "external_url": "https://game.example.com/item/1234",
  "attributes": [\
    { "trait_type": "Damage", "value": "12" },\
    { "trait_type": "Durability", "value": "50" }\
  ],
  "properties": {
    "files": [\
      {\
        "uri": "https://example.com/images/sword.png",\
        "type": "image/png"\
      }\
    ],
    "category": "image"
  }
}

非同质化标准(token_standard = 0

这类似于 Ethereum 上的 ERC-721——它代表一个非同质化 Token (NFT)。但是,在 Solana 上,每个 NFT 都是一个单独的 mint,供应量为 1,小数位为 0,而在 Ethereum 上,ERC-721 在单个合约中使用唯一的 token ID。

非同质化标准的 JSON 模式与上面的同质化资产标准相同。这两个标准使用完全相同的元数据结构——区别仅在于链上(供应量和小数位),而不是在 JSON 格式中。

{
  "name": "Rare Art Piece",
  "description": "A one-of-one digital artwork by Artist X.",
  "image": "https://example.com/images/artwork.png",
  "animation_url": "https://example.com/animations/artwork-loop.mp4",
  "external_url": "https://artistx.example.com/rare-art-piece",
  "attributes": [\
    { "trait_type": "Artist", "value": "Artist X" },\
    { "trait_type": "Year", "value": "2025" }\
  ],
  "properties": {
    "files": [\
      {\
        "uri": "https://example.com/images/artwork.png",\
        "type": "image/png"\
      }\
    ],
    "category": "image"
  }
}

注意:在 Ethereum 上,NFT 集合通常存在于一个铸造和管理许多 NFT 的合约中。在 Solana 上,每个 NFT 都是它自己的 mint,集合是通过 Metaplex 元数据中的链上验证链接(我们在之前的教程中介绍的 collection 字段)形成的,而不是通过单个合约。

现在我们了解了这些标准,让我们构建我们的程序来创建一个带有元数据的同质化 token。

实现同质化标准Token

这就是我们要完成的:

  1. 我们将创建一个带有函数的 Anchor 程序,用于将元数据附加到 SPL token
  2. 创建一个 SPL token(mint 账户)
  3. 通过 CPI 使用 Metaplex Token Metadata Program 创建一个元数据账户并将其链接到 token
  4. 将 token URI 及其内容(如 token 图像)存储在永久存储中,token 的元数据账户将引用该存储

项目设置

首先,使用 anchor init spl_with_metadata 创建一个新的 Anchor 项目。

然后,将 Anchor.toml 文件更新为以下内容,以正确配置我们的项目。我们将集群设置为“devnet”,因为我们需要与实际的 Metaplex Token Metadata Program 进行交互,该程序不存在于我们的本地环境中。我们还将添加使用 SPL token 和元数据所需的依赖项:


[toolchain]
package_manager = "yarn"

[features]
resolution = true
skip-lint = false

[programs.localnet]
spl_token_with_metadata = "ApCjqNHgvuvsiQYpX4kGCxXTipcWJUe7NmnNfq3UKrwD"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "devnet"                  # 添加了这个
wallet = "~/.config/solana/id.json"

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

接下来,更新 programs/spl_token_with_metadata/Cargo.toml 文件。

[package]
name = "spl_token_with_metadata"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "spl_token_with_metadata"

[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] # 添加了 "anchor-spl/idl-build"

[dependencies]
anchor-lang = "0.31.0"
anchor-spl = { version = "0.31.0", features = ["token"] } # 添加了这个
mpl-token-metadata = "5.1.0"                              # 添加了这个

我们配置我们的项目以使用 Anchor SPL 和 Metaplex Token Metadata crate。

我们添加这些依赖项是为了特定目的:

  • anchor-spl: 为 Solana 的 SPL token 程序提供 Anchor 兼容的接口
  • mpl-token-metadata: 允许我们与 Metaplex 的 Token Metadata Program 交互,以便为我们的 SPL token 创建和管理元数据

我们已将 idl-build = ["anchor-spl/idl-build"] 功能添加到我们的 Cargo.toml 中,以便生成一个 IDL 文件,该文件包含 SPL token 类型,允许我们的 TypeScript 客户端正确地与我们的程序交互

添加 Anchor 程序代码

现在使用以下代码更新 Anchor 程序。

在这里,我们定义了一个 create_token_metadata 函数,用于将元数据附加到提供的 SPL token。当我们继续进行时,我们将详细解释代码。


// 导入程序所需的依赖项:Anchor、Anchor SPL 和 Metaplex Token Metadata crate
use anchor_lang::prelude::*;
use anchor_spl::token::Mint;
use mpl_token_metadata::instructions::{
    CreateMetadataAccountV3Cpi, CreateMetadataAccountV3CpiAccounts,
    CreateMetadataAccountV3InstructionArgs,
};
use mpl_token_metadata::types::{Creator, DataV2};
use mpl_token_metadata::ID as METADATA_PROGRAM_ID;

declare_id!("2SZvgGtgotJFy1aKd4Rnm7UEZNxUdP4sdXbeLDgKDiGM"); // 运行 Anchor sync 以更新你的程序 ID

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

    pub fn create_token_metadata(
        ctx: Context<CreateTokenMetadata>,
        name: String,
        symbol: String,
        uri: String,
        seller_fee_basis_points: u16,
        is_mutable: bool,
    ) -> Result<()> {
                // 使用同质化标准格式创建元数据指令参数
        // 这遵循我们之前讨论的 token_standard = 2 格式
        let data = DataV2 {
            name,
            symbol,
            uri, // 指向带有名称、符号、描述和图像的 JSON
            seller_fee_basis_points,
            creators: Some(vec![Creator {\
                address: ctx.accounts.payer.key(),\
                verified: true,\
                share: 100,\
            }]),
            collection: None,
            uses: None,
        };

        // 查找元数据账户地址 (PDA)
        let mint_key = ctx.accounts.mint.key();
        let seeds = &[\
            b"metadata".as_ref(),\
            METADATA_PROGRAM_ID.as_ref(),\
            mint_key.as_ref(),\
        ];
        let (metadata_pda, _) = Pubkey::find_program_address(seeds, &METADATA_PROGRAM_ID);

        // 确保提供的元数据账户与 PDA 匹配
        require!(
            metadata_pda == ctx.accounts.metadata.key(),
            MetaplexError::InvalidMetadataAccount
        );

        // 创建并执行 CPI 以创建元数据
        let token_metadata_program_info = ctx.accounts.token_metadata_program.to_account_info();
        let metadata_info = ctx.accounts.metadata.to_account_info();
        let mint_info = ctx.accounts.mint.to_account_info();
        let authority_info = ctx.accounts.authority.to_account_info();
        let payer_info = ctx.accounts.payer.to_account_info();
        let system_program_info = ctx.accounts.system_program.to_account_info();
        let rent_info = ctx.accounts.rent.to_account_info();

        let cpi = CreateMetadataAccountV3Cpi::new(
            &token_metadata_program_info,
            CreateMetadataAccountV3CpiAccounts {
                metadata: &metadata_info,
                mint: &mint_info,
                mint_authority: &authority_info,
                payer: &payer_info,
                update_authority: (&authority_info, true),
                system_program: &system_program_info,
                rent: Some(&rent_info),
            },
            CreateMetadataAccountV3InstructionArgs {
                data,
                is_mutable,
                collection_details: None,
            },
        );
        cpi.invoke()?;
        Ok(())
    }
}

```...

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

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

0 条评论

请先 登录 后评论