本文介绍了如何使用Metaplex元数据标准为SPL代币附加元数据。
我们在之前的教程中介绍了 Metaplex 元数据标准。在本教程中,我们将创建一个 SPL token,并使用 Metaplex 标准将元数据附加到它。
我们将构建一个 Anchor 程序,该程序使用 Metaplex 标准创建带有附加元数据的 SPL token。这允许我们向 token 添加信息,例如名称、符号、图像和其他属性。
在开始构建之前,让我们了解 Metaplex 标准,该标准控制 token 元数据的结构方式。
当我们为 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。
这就是我们要完成的:
首先,使用 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 程序。
在这里,我们定义了一个 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(())
}
}
```...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!