标准的 SPL 代币不包含名称、符号或图片等元数据。Metaplex 代币元数据程序通过为每个代币铸造账户创建关联的元数据账户来解决这个问题。
本指南涵盖了可用于标准 SPL 代币和 Token-2022 的 Metaplex 代币元数据程序。如需使用 Token-2022 元数据扩展,请参阅元数据扩展指南,该扩展将元数据直接存储在铸造账户上。
代币元数据的工作原理
代币元数据程序为每个代币铸造账户创建一个 Program Derived Address (PDA)。该元数据账户存储链上信息,如代币的名称和符号,以及指向链外 JSON 元数据(图片、描述等)的 URI。
┌─────────────────┐ ┌─────────────────────┐│ Mint Account │ │ Metadata Account ││ │ │ (PDA) ││ - Supply │◄──────│ - Name ││ - Decimals │ │ - Symbol ││ - Authority │ │ - URI │└─────────────────┘ │ - Seller Fee ││ - Creators │└─────────────────────┘
元数据 PDA 由以下种子派生:["metadata", program_id, mint_address]
创建带元数据的代币
createV1 指令在单个交易中同时创建铸造账户及其元数据。
Typescript
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import {getCreateV1InstructionAsync,TokenStandard} from "@metaplex-foundation/mpl-token-metadata-kit";// Create connectionconst rpc = createSolanaRpc("http://127.0.0.1:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://127.0.0.1:8900");// Generate keypairsconst payer = await generateKeyPairSigner();const mint = await generateKeyPairSigner();// Fund payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: payer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Create fungible token with metadataconst createInstruction = await getCreateV1InstructionAsync({mint,authority: payer,payer,name: "My Token",symbol: "MTK",uri: "https://example.com/token.json",sellerFeeBasisPoints: 0,tokenStandard: TokenStandard.Fungible});// Build and send transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();const transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(payer, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([createInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });console.log("Mint Address:", mint.address);
获取代币元数据
要了解如何获取现有代币的元数据,请参阅获取代币元数据实用指南。
更新代币元数据
如果账户是可变的,更新权限可以修改元数据。
Kit
import {appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import {getUpdateV1InstructionAsync,findMetadataPda,fetchMetadata} from "@metaplex-foundation/mpl-token-metadata-kit";const rpc = createSolanaRpc("http://127.0.0.1:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://127.0.0.1:8900");// authority must be a KeyPairSigner with update authorityconst mintAddress = "YOUR_MINT_ADDRESS";// Fetch current metadata to preserve existing valuesconst [metadataAddress] = await findMetadataPda({ mint: mintAddress });const currentMetadata = await fetchMetadata(rpc, metadataAddress);// Update metadata (must provide all data fields)const updateInstruction = await getUpdateV1InstructionAsync({mint: mintAddress,authority, // Update authority signerpayer: authority,data: {name: "Updated Token Name",symbol: "UPD",uri: "https://example.com/updated-token.json",sellerFeeBasisPoints: 100, // 1%creators:currentMetadata.data.creators.__option === "Some"? currentMetadata.data.creators.value: null}});// Build and send transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();const transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([updateInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });console.log("Metadata updated successfully");
代币标准
代币元数据程序支持不同的代币标准:
| 标准 | 描述 | 使用场景 |
|---|---|---|
Fungible | 带元数据的标准同质化代币 | 货币、积分 |
FungibleAsset | 代表独特资产的同质化代币 | 半同质化物品 |
NonFungible | 带主版本的 NFT | 1/1 艺术品 |
ProgrammableNonFungible | 强制版税的 NFT | 创作者版税 |
NonFungibleEdition | NFT 的印刷副本 | 限量版 |
ProgrammableNonFungibleEdition | 强制版税的印刷副本 | 限量版 |
import { TokenStandard } from "@metaplex-foundation/mpl-token-metadata-kit";// For fungible tokenstokenStandard: TokenStandard.Fungible;// For NFTstokenStandard: TokenStandard.NonFungible;// For programmable NFTs (enforced royalties)tokenStandard: TokenStandard.ProgrammableNonFungible;
链下元数据格式
uri 字段指向一个包含扩展元数据的 JSON 文件。标准格式遵循
Metaplex 代币元数据标准:
token-metadata.json
{"name": "My Token","symbol": "MTK","description": "A description of the token","image": "https://example.com/token-image.png","external_url": "https://example.com","attributes": [{"trait_type": "Category","value": "Utility"}],"properties": {"files": [{"uri": "https://example.com/token-image.png","type": "image/png"}]}}
将您的元数据 JSON 存储在可靠的永久存储解决方案上,例如 Arweave、IPFS 或专用 CDN。如果 URI 变得无法访问,钱包和浏览器将无法显示您的代币元数据。
元数据账户结构
链上元数据账户包含:
pub struct Metadata {pub key: Key, // Account type identifierpub update_authority: Pubkey, // Can update metadatapub mint: Pubkey, // Associated mintpub name: String, // Token name (max 32 chars)pub symbol: String, // Token symbol (max 10 chars)pub uri: String, // URI to off-chain JSON (max 200 chars)pub seller_fee_basis_points: u16, // Royalty % (100 = 1%)pub creators: Option<Vec<Creator>>, // Creator list with sharespub primary_sale_happened: bool, // Primary sale flagpub is_mutable: bool, // Can metadata be updatedpub edition_nonce: Option<u8>, // Edition noncepub token_standard: Option<TokenStandard>, // Token typepub collection: Option<Collection>, // Collection infopub uses: Option<Uses>, // Use tracking}
最佳实践
- 设置适当的可变性:对于永远不应更改的代币,使用
isMutable: false - 使用可靠的 URI 托管:链下元数据应存储在永久存储上
- 验证创建者:应验证创建者地址以确认真实性
- 考虑版税:为二次销售版税设置
sellerFeeBasisPoints(市场可能会也可能不会强制执行这些版税)
Is this page helpful?