在令牌扩展计划和 令牌元数据接口之前,向 Mint 帐户添加额外数据的过程需要通过 Metaplex 元数据计划创建元数据帐户。
该MetadataPointer
扩展现在允许 Mint 帐户指定其相应元数据帐户的地址。这种灵活性允许铸币帐户指向实现令牌元数据接口的程序所拥有的任何帐户。
令牌扩展程序直接实现令牌元数据接口,可通过TokenMetadata
扩展进行访问。通过TokenMetadata
扩展,Mint 帐户本身现在可以存储元数据。
在本指南中,我们将演示如何创建支持MetadataPointer
和TokenMetadata
扩展的 Mint 帐户。此设置通过将所有数据存储在单个帐户上,简化了向 Mint 帐户添加元数据的过程。这是 最终的脚本。
令牌元数据接口概述#
令牌 元数据接口 旨在通过定义用于处理元数据的数据结构和指令集来标准化和简化向令牌添加元数据的过程。
令牌元数据接口可以由任何程序实现。这使开发人员能够灵活地创建自定义元数据程序,同时减少与其程序的生态系统集成相关的挑战。
通过这个通用接口,钱包、dApp 和链上程序可以普遍访问代币元数据,并且用于创建或修改元数据的工具变得普遍兼容。
元数据接口字段#
令牌元数据接口定义了一组标准的数据字段 TokenMetadata
,如下所述。此外,它还允许在该部分中包含自定义数据字段additional_metadata
,格式为键值对。
pub struct TokenMetadata {
/// The authority that can sign to update the metadata
pub update_authority: OptionalNonZeroPubkey,
/// The associated mint, used to counter spoofing to be sure that metadata
/// belongs to a particular mint
pub mint: Pubkey,
/// The longer name of the token
pub name: String,
/// The shortened symbol for the token
pub symbol: String,
/// The URI pointing to richer metadata
pub uri: String,
/// Any additional metadata about the token as key-value pairs. The program
/// must avoid storing the same key twice.
pub additional_metadata: Vec<(String, String)>,
}
元数据接口说明#
元数据接口指定以下 指令:
- 初始化:初始化基本令牌元数据字段(名称、符号、URI)。
- UpdateField:更新现有令牌元数据字段或添加到该字段
additional_metadata
(如果尚不存在)。需要调整帐户大小以容纳额外的空间。 - RemoveKey:从 .txt 文件中删除键值对
additional_metadata
。此指令不适用于所需的名称、符号和 URI 字段。 - UpdateAuthority:更新允许更改令牌元数据的权限。
- Emit:以结构体的格式发出令牌元数据
TokenMetadata
。这允许帐户数据以不同的格式存储,同时保持与接口标准的兼容性。
入门#
首先使用 以下起始代码打开此 Solana Playground链接。
// Client
console.log("My address:", pg.wallet.publicKey.toString());
const balance = await pg.connection.getBalance(pg.wallet.publicKey);
console.log(`My balance: ${balance / web3.LAMPORTS_PER_SOL} SOL`);
如果这是您第一次使用 Solana Playground,您首先需要创建一个 Playground 钱包并使用 devnet SOL 为钱包充值。
如果您没有 Playground 钱包,您可能会在编辑器中看到pg.wallet.publicKey
. 创建 Playground 钱包后,此类型错误将会清除。
要获取 devnet SOL,请solana airdrop
在 Playground 的终端中运行命令,或访问此devnet faucet。
solana airdrop 5
创建并资助 Playground 钱包后,单击“运行”按钮来运行起始代码。
添加依赖项#
让我们从设置脚本开始。我们将使用@solana/web3.js
、 @solana/spl-token
和@solana/spl-token-metadata
库。
将起始代码替换为以下内容:
import {
Connection,
Keypair,
SystemProgram,
Transaction,
clusterApiUrl,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
ExtensionType,
TOKEN_2022_PROGRAM_ID,
createInitializeMintInstruction,
getMintLen,
createInitializeMetadataPointerInstruction,
getMint,
getMetadataPointerState,
getTokenMetadata,
TYPE_SIZE,
LENGTH_SIZE,
} from "@solana/spl-token";
import {
createInitializeInstruction,
createUpdateFieldInstruction,
createRemoveKeyInstruction,
pack,
TokenMetadata,
} from "@solana/spl-token-metadata";
// Playground wallet
const payer = pg.wallet.keypair;
// Connection to devnet cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// Transaction to send
let transaction: Transaction;
// Transaction signature returned from sent transaction
let transactionSignature: string;
薄荷设置#
接下来,定义我们将在下一步中创建的 Mint 帐户的属性。
// Generate new keypair for Mint Account
const mintKeypair = Keypair.generate();
// Address for Mint Account
const mint = mintKeypair.publicKey;
// Decimals for Mint Account
const decimals = 2;
// Authority that can mint new tokens
const mintAuthority = pg.wallet.publicKey;
// Authority that can update the metadata pointer and token metadata
const updateAuthority = pg.wallet.publicKey;
// Metadata to store in Mint Account
const metaData: TokenMetadata = {
updateAuthority: updateAuthority,
mint: mint,
name: "OPOS",
symbol: "OPOS",
uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
additionalMetadata: [["description", "Only Possible On Solana"]],
};
接下来,确定新铸币账户的大小,并计算免除租金所需的最少 lamports。
在下面的代码片段中,我们为TokenMetadata
扩展分配了4个字节,然后计算元数据所需的空间。
// Size of MetadataExtension 2 bytes for type, 2 bytes for length
const metadataExtension = TYPE_SIZE + LENGTH_SIZE;
// Size of metadata
const metadataLen = pack(metaData).length;
// Size of Mint Account with extension
const mintLen = getMintLen([ExtensionType.MetadataPointer]);
// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(
mintLen + metadataExtension + metadataLen,
);
对于令牌扩展,Mint 帐户的大小将根据启用的扩展而有所不同。
构建说明#
接下来,让我们构建一组指令来:
- 创建一个新账户
- 初始化
MetadataPointer
扩展 - 初始化剩余的Mint账户数据
- 初始化
TokenMetadata
扩展和令牌元数据 - 使用自定义字段更新令牌元数据
首先,构建指令来调用系统程序来创建帐户并将所有权分配给令牌扩展程序。
// Instruction to invoke System Program to create new account
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey, // Account that will transfer lamports to created account
newAccountPubkey: mint, // Address of the account to create
space: mintLen, // Amount of bytes to allocate to the created account
lamports, // Amount of lamports transferred to created account
programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});
接下来,构建指令来初始化MetadataPointer
Mint 帐户的扩展。在这个例子中,元数据指针将指向Mint地址,表明元数据将直接存储在Mint账户上。
// Instruction to initialize the MetadataPointer Extension
const initializeMetadataPointerInstruction =
createInitializeMetadataPointerInstruction(
mint, // Mint Account address
updateAuthority, // Authority that can set the metadata address
mint, // Account address that holds the metadata
TOKEN_2022_PROGRAM_ID,
);
接下来,构建指令来初始化 Mint 账户数据的其余部分。这与原始令牌计划相同。
// Instruction to initialize Mint Account data
const initializeMintInstruction = createInitializeMintInstruction(
mint, // Mint Account Address
decimals, // Decimals of Mint
mintAuthority, // Designated Mint Authority
null, // Optional Freeze Authority
TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
);
接下来,构建指令来初始化TokenMetadata
扩展和所需的元数据字段(名称、符号、URI)。
对于此指令,使用令牌扩展程序作为programId
,其功能相当于“元数据程序”。此外,铸币厂账户的地址用作表明metadata
铸币厂本身是“元数据账户”。
// Instruction to initialize Metadata Account data
const initializeMetadataInstruction = createInitializeInstruction({
programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
metadata: mint, // Account address that holds the metadata
updateAuthority: updateAuthority, // Authority that can update the metadata
mint: mint, // Mint Account address
mintAuthority: mintAuthority, // Designated Mint Authority
name: metaData.name,
symbol: metaData.symbol,
uri: metaData.uri,
});
UpdateField
接下来,使用令牌元数据接口中的指令构建指令以使用自定义字段更新元数据 。
该指令将更新现有字段的值,或者将其添加到 additional_metadata
(如果该字段尚不存在)。请注意,您可能需要为帐户重新分配更多空间以容纳附加数据。在此示例中,我们在创建帐户时预先分配了租金所需的所有 lamport。
// Instruction to update metadata, adding custom field
const updateFieldInstruction = createUpdateFieldInstruction({
programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
metadata: mint, // Account address that holds the metadata
updateAuthority: updateAuthority, // Authority that can update the metadata
field: metaData.additionalMetadata[0][0], // key
value: metaData.additionalMetadata[0][1], // value
});
发送交易#
接下来,将指令添加到新交易中并将其发送到网络。这将创建一个启用了MetadataPointer
和TokenMetadata
扩展的 Mint 帐户,并将元数据存储在 Mint 帐户上。
一些令牌扩展指令需要在初始化铸币之前以原子方式排序。而其他人一定在后面。如果这些说明“无序”,可能会导致您的交易失败。
// Add instructions to new transaction
transaction = new Transaction().add(
createAccountInstruction,
initializeMetadataPointerInstruction,
// note: the above instructions are required before initializing the mint
initializeMintInstruction,
initializeMetadataInstruction,
updateFieldInstruction,
);
// Send transaction
transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[payer, mintKeypair], // Signers
);
console.log(
"\nCreate Mint Account:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,
);
从 Mint 帐户读取元数据#
接下来,检查元数据是否已存储在 Mint 帐户中。
首先获取 Mint 帐户并读取MetadataPointer
帐户数据的扩展部分:
// Retrieve mint information
const mintInfo = await getMint(
connection,
mint,
"confirmed",
TOKEN_2022_PROGRAM_ID,
);
// Retrieve and log the metadata pointer state
const metadataPointer = getMetadataPointerState(mintInfo);
console.log("\nMetadata Pointer:", JSON.stringify(metadataPointer, null, 2));
接下来,读取帐户数据的元数据部分:
// Retrieve and log the metadata state
const metadata = await getTokenMetadata(
connection,
mint, // Mint Account address
);
console.log("\nMetadata:", JSON.stringify(metadata, null, 2));
单击按钮运行脚本Run
。然后您可以在 SolanaFM 上检查交易详细信息。
您还应该看到类似于以下内容的控制台输出:
Metadata Pointer: {
"authority": "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
"metadataAddress": "BFqmKEm12CrDbcFAncjL34Anu5w18LruxQrgvy7aExzV"
}
Metadata: {
"updateAuthority": "3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R",
"mint": "BFqmKEm12CrDbcFAncjL34Anu5w18LruxQrgvy7aExzV",
"name": "OPOS",
"symbol": "OPOS",
"uri": "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
"additionalMetadata": [
[
"description",
"Only Possible On Solana"
]
]
}
删除自定义字段#
要从元数据中删除自定义字段,请使用RemoveKey
令牌元数据接口中的指令。
该idempotent
标志用于指定如果账户上不存在该密钥,交易是否应该失败。如果幂等标志设置为true
,那么即使键不存在,指令也不会出错。
// Instruction to remove a key from the metadata
const removeKeyInstruction = createRemoveKeyInstruction({
programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
metadata: mint, // Address of the metadata
updateAuthority: updateAuthority, // Authority that can update the metadata
key: metaData.additionalMetadata[0][0], // Key to remove from the metadata
idempotent: true, // If the idempotent flag is set to true, then the instruction will not error if the key does not exist
});
// Add instruction to new transaction
transaction = new Transaction().add(removeKeyInstruction);
// Send transaction
transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[payer],
);
console.log(
"\nRemove Additional Metadata Field:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,
);
// Retrieve and log the metadata state
const updatedMetadata = await getTokenMetadata(
connection,
mint, // Mint Account address
);
console.log("\nUpdated Metadata:", JSON.stringify(updatedMetadata, null, 2));
console.log(
"\nMint Account:",
`https://solana.fm/address/${mint}?cluster=devnet-solana`,
);
单击按钮运行脚本Run
。然后,您可以在 SolanaFM 上检查交易详细信息和铸币账户。
您还应该看到类似于以下内容的控制台输出:
Updated Metadata: {
"updateAuthority": "Ehqz1TAMboGbY5oBWqKKWmv5hhvQuwcpkaWbVjkU96cZ",
"mint": "9wdvSnsqgYo4HFBYMtiCvVNQfFBYdzSeACjLuxVCDcjB",
"name": "OPOS",
"symbol": "OPOS",
"uri": "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
"additionalMetadata": []
}
结论#
通过启用MetadataPointer
和TokenMetadata
扩展,Mint 账户现在可以直接存储代币元数据。此功能简化了向 Mint 帐户添加元数据的过程。
pdf+视频Solana链SOL发币教程及多模式组合合约源代码下载:
Solana链SOL发币(合约部署、开源、锁仓、LP、参数配置、开发、故障处理、工具使用)教程下载:
多模式(燃烧、回流指定营销地址、分红本币及任意币种,邀请推广八代收益,LP加池分红、交易分红、复利分红、NFT分红、自动筑池、动态手续费、定时开盘、回购)组合合约源代码下载:
pdf+视频Solana链SOL发币教程及多模式组合合约源代码下载地址:
添加VX或者telegram获取全程线上免费指导
评论前必须登录!
注册