SPL Token standar tidak menyertakan metadata seperti nama, simbol, atau gambar. Metaplex Token Metadata Program mengatasi hal ini dengan membuat akun metadata yang terhubung ke setiap mint token.
Panduan ini mencakup Metaplex Token Metadata Program yang dapat digunakan untuk SPL Token standar dan Token-2022. Untuk menggunakan Ekstensi Metadata Token-2022, lihat panduan Ekstensi Metadata yang menyimpan metadata langsung di akun mint.
Cara Kerja Metadata Token
Token Metadata Program membuat Program Derived Address (PDA) untuk setiap mint token. Akun metadata ini menyimpan informasi on-chain seperti nama dan simbol token, ditambah URI yang mengarah ke metadata JSON off-chain (gambar, deskripsi, dll.).
┌─────────────────┐ ┌─────────────────────┐│ Mint Account │ │ Metadata Account ││ │ │ (PDA) ││ - Supply │◄──────│ - Name ││ - Decimals │ │ - Symbol ││ - Authority │ │ - URI │└─────────────────┘ │ - Seller Fee ││ - Creators │└─────────────────────┘
PDA metadata diturunkan dari seed: ["metadata", program_id, mint_address]
Membuat Token dengan Metadata
Instruksi createV1 membuat mint account dan metadata-nya dalam satu transaksi.
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);
Mengambil Metadata Token
Untuk mempelajari cara mengambil metadata untuk token yang sudah ada, lihat resep cookbook Mengambil Metadata Token.
Memperbarui Metadata Token
Otoritas pembaruan dapat memodifikasi metadata jika akun bersifat mutable.
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");
Standar Token
Token Metadata Program mendukung berbagai standar token:
| Standar | Deskripsi | Kasus Penggunaan |
|---|---|---|
Fungible | Token fungible standar dengan metadata | Mata uang, poin |
FungibleAsset | Token fungible yang mewakili aset unik | Item semi-fungible |
NonFungible | NFT dengan Master Edition | Karya seni 1/1 |
ProgrammableNonFungible | NFT dengan royalti yang ditegakkan | Royalti kreator |
NonFungibleEdition | Salinan cetak dari NFT | Edisi terbatas |
ProgrammableNonFungibleEdition | Salinan cetak dengan royalti yang ditegakkan | Edisi terbatas |
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;
Format Metadata Off-Chain
Bidang uri menunjuk ke file JSON yang berisi metadata tambahan. Format standar
mengikuti
Standar Token Metadata 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"}]}}
Simpan JSON metadata Anda pada solusi penyimpanan yang andal dan permanen seperti Arweave, IPFS, atau CDN khusus. Jika URI menjadi tidak dapat diakses, dompet dan explorer tidak akan dapat menampilkan metadata token Anda.
Struktur Akun Metadata
Akun metadata on-chain berisi:
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}
Praktik Terbaik
- Tetapkan kemutabilitasan yang sesuai: Gunakan
isMutable: falseuntuk token yang tidak boleh diubah - Gunakan hosting URI yang andal: Metadata off-chain harus disimpan di penyimpanan permanen
- Verifikasi kreator: Alamat kreator harus diverifikasi untuk mengonfirmasi keaslian
- Pertimbangkan royalti: Tetapkan
sellerFeeBasisPointsuntuk royalti penjualan sekunder (marketplace mungkin atau mungkin tidak menerapkan ini)
Is this page helpful?