Стандартные SPL-токены не включают метаданные, такие как название, символ или изображение. Программа Metaplex Token Metadata решает эту проблему, создавая аккаунт метаданных, связанный с каждым mint account токена.
Это руководство охватывает программу Metaplex Token Metadata, которая может использоваться для стандартных SPL-токенов и Token-2022. Для использования расширения Metadata Extension в Token-2022 см. руководство по Metadata Extension, которое хранит метаданные непосредственно на аккаунте mint.
Как работают метаданные токенов
Программа Token Metadata создает Program Derived Address (PDA) для каждого mint account токена. Этот аккаунт метаданных хранит on-chain информацию, такую как название токена и символ, а также URI, указывающий на off-chain метаданные в формате JSON (изображения, описания и т.д.).
┌─────────────────┐ ┌─────────────────────┐│ Mint Account │ │ Metadata Account ││ │ │ (PDA) ││ - Supply │◄──────│ - Name ││ - Decimals │ │ - Symbol ││ - Authority │ │ - URI │└─────────────────┘ │ - Seller Fee ││ - Creators │└─────────────────────┘
PDA метаданных выводится из seeds: ["metadata", program_id, mint_address]
Создание токена с метаданными
Инструкция createV1 создает как аккаунт mint, так и его метаданные в одной
транзакции.
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);
Получение метаданных токена
Чтобы узнать, как получить метаданные для существующих токенов, см. рецепт в кулинарной книге Fetch Token Metadata.
Обновление метаданных токена
Update authority может изменять метаданные, если аккаунт является изменяемым.
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");
Стандарты токенов
Программа Token Metadata поддерживает различные стандарты токенов:
| Стандарт | Описание | Сценарий использования |
|---|---|---|
Fungible | Стандартный взаимозаменяемый токен с метаданными | Валюты, баллы |
FungibleAsset | Взаимозаменяемый токен, представляющий уникальный актив | Полувзаимозаменяемые предметы |
NonFungible | NFT с Master Edition | Уникальные произведения искусства |
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:
{"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?