Metadata & Metadata Pointer -laajennukset
Miten otetaan käyttöön MetadataPointerExtension ja TokenMetadata -laajennus
MetadataPointerExtension
mahdollistaa mint account -tilin määrittää metadata-tilinsä osoitteen. Tämä
osoitin voi viitata mihin tahansa tiliin, jonka omistaa
Token Metadata Interface
-rajapintaa toteuttava ohjelma, mikä tarjoaa joustavuutta metadatan
tallennuspaikan suhteen.
Token Extensions Program toteuttaa Token Metadata Interface -rajapinnan suoraan, mikä mahdollistaa metadatan tallentamisen itse mint account -tilille. Tämä sisältää tokenin nimen, symbolin, URI:n ja lisäkuvaukset. Kun osoitinlaajennukset osoittavat itse mint-tiliin, TokenMetadata -laajennuksia voidaan käyttää kaiken metadatan tallentamiseen mint account -tilille.
Typescript
import { getCreateAccountInstruction } from "@solana-program/system";import {extension,getInitializeAccountInstruction,getInitializeMintInstruction,getInitializeMetadataPointerInstruction,getMintSize,getTokenSize,TOKEN_2022_PROGRAM_ADDRESS,getInitializeTokenMetadataInstruction} from "@solana-program/token-2022";import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners,some} from "@solana/kit";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate the authority for the mint (also acts as fee payer)const authority = await generateKeyPairSigner();// Fund authority/fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: authority.address,lamports: lamports(5_000_000_000n), // 5 SOLcommitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Enable Metadata and Metadata Pointer extensionsconst metadataExtension = extension("TokenMetadata", {updateAuthority: some(authority.address),mint: mint.address,name: "OPOS",symbol: "OPS",uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",additionalMetadata: new Map().set("description", "Only possible on Solana")});const metadataPointerExtension = extension("MetadataPointer", {authority: authority.address,metadataAddress: mint.address // can also point to another account if desired});// Get mint account size with the metadata pointer extension aloneconst spaceWithoutTokenMetadataExtension = BigInt(getMintSize([metadataPointerExtension]));// Get mint account size with all extensions(metadata && metadataPointer)const spaceWithTokenMetadataExtension = BigInt(getMintSize([metadataPointerExtension, metadataExtension]));// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(spaceWithTokenMetadataExtension).send();// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Instruction to create new account for mintconst createMintAccountInstruction = getCreateAccountInstruction({payer: authority,newAccount: mint,lamports: rent,space: spaceWithoutTokenMetadataExtension,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Initialize metadata extensionconst initializeMetadataInstruction = getInitializeTokenMetadataInstruction({metadata: mint.address, // Account address that holds the metadataupdateAuthority: authority.address, // Authority that can update the metadatamint: mint.address, // Mint Account addressmintAuthority: authority, // Designated Mint Authorityname: "OPOS",symbol: "OPS",uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json"});// Initialize metadata pointer extensionconst initializeMetadataPointerInstruction =getInitializeMetadataPointerInstruction({mint: mint.address,authority: authority.address,metadataAddress: mint.address});// Initialize mint account dataconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 9,mintAuthority: authority.address,freezeAuthority: authority.address});// Generate keypair to use as address of token accountconst tokenAccount = await generateKeyPairSigner();// Get token account size (basic)const tokenAccountLen = BigInt(getTokenSize([]));// Get minimum balance for rent exemptionconst tokenAccountRent = await rpc.getMinimumBalanceForRentExemption(tokenAccountLen).send();// Instruction to create new token accountconst createTokenAccountInstruction = getCreateAccountInstruction({payer: authority,newAccount: tokenAccount,lamports: tokenAccountRent,space: tokenAccountLen,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize the created token accountconst initializeTokenAccountInstruction = getInitializeAccountInstruction({account: tokenAccount.address,mint: mint.address,owner: authority.address});// Build the instruction listconst instructions = [createMintAccountInstruction,initializeMetadataPointerInstruction,initializeMintInstruction,initializeMetadataInstruction,createTokenAccountInstruction,initializeTokenAccountInstruction];// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions(instructions, tx));// Sign transaction message with all required signersconst signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed", skipPreflight: true });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address:", mint.address.toString());console.log("Token account with Metadata + Metadata Pointer:",tokenAccount.address.toString());console.log("Transaction Signature:", transactionSignature);
Console
Click to execute the code.
Rust
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_commitment_config::CommitmentConfig;use solana_sdk::{signature::{Keypair, Signer},transaction::Transaction,};use solana_system_interface::instruction::create_account;use spl_token_2022_interface::{extension::{metadata_pointer::{instruction::initialize as initialize_metadata_pointer, MetadataPointer,},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::initialize_mint,state::Mint,ID as TOKEN_2022_PROGRAM_ID,};use spl_token_metadata_interface::{instruction::{initialize as initialize_token_metadata, update_field},state::{Field, TokenMetadata},};#[tokio::main]async fn main() -> Result<()> {// Create connection to local validatorlet client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let latest_blockhash = client.get_latest_blockhash().await?;// Generate a new keypair for the fee payerlet fee_payer = Keypair::new();// Airdrop 5 SOL to fee payerlet airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 5_000_000_000).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}// Generate keypair for the mintlet mint = Keypair::new();// Define Token metadatalet token_metadata = TokenMetadata {update_authority: Some(fee_payer.pubkey()).try_into()?,mint: mint.pubkey(),name: "OPOS".to_string(),symbol : "OPS".to_string(),uri : "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json".to_string(),additional_metadata: vec![("description".to_string(),"only possible on Solana".to_string())]};// Calculate space for mint with metadata pointer and token metadata extensionslet mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MetadataPointer])?;let metadata_len = token_metadata.tlv_size_of()?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space + metadata_len).await?;// Instruction to create new account for mint (token22)let create_mint_account_instruction = create_account(&fee_payer.pubkey(), // payer&mint.pubkey(), // new account (mint)mint_rent, // lamportsmint_space as u64, // space&TOKEN_2022_PROGRAM_ID, // program id);// Instruction to initialize metadata pointer (pointing to itself for self-managed metadata)let initialize_metadata_pointer_instruction = initialize_metadata_pointer(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),Some(fee_payer.pubkey()), // authoritySome(mint.pubkey()), // metadata address (pointing to self))?;// Instruction to initialize mint account datalet initialize_mint_instruction = initialize_mint(&TOKEN_2022_PROGRAM_ID, // program id&mint.pubkey(), // mint&fee_payer.pubkey(), // mint authoritySome(&fee_payer.pubkey()), // freeze authority9, // decimals)?;// Instruction to initialize token metadatalet initialize_metadata_instruction = initialize_token_metadata(&TOKEN_2022_PROGRAM_ID, // program id&mint.pubkey(), //metadata&fee_payer.pubkey(), // update authority&mint.pubkey(), // mint&fee_payer.pubkey(), // mint authoritytoken_metadata.name.to_string(), // nametoken_metadata.symbol.to_string(), // symboltoken_metadata.uri.to_string(), // uri);// Create update field instructions from token_metadata.additional_metadata// Additional metadata must be initialized separately using the update_field instruction// If the field already exists, it will be updated instead of creating a new fieldlet update_field_instructions: Vec<_> = token_metadata.additional_metadata.iter().map(|(key, value)| {update_field(&TOKEN_2022_PROGRAM_ID,&mint.pubkey(),&fee_payer.pubkey(),Field::Key(key.clone()),value.clone(),)}).collect();// Construct transaction with all instructionslet mut instructions = vec![create_mint_account_instruction,initialize_metadata_pointer_instruction,initialize_mint_instruction,initialize_metadata_instruction,];instructions.extend(update_field_instructions);let transaction = Transaction::new_signed_with_payer(&instructions,Some(&fee_payer.pubkey()),&[&fee_payer, &mint],latest_blockhash,);let transaction_signature = client.send_and_confirm_transaction(&transaction).await?;println!("Mint Address: {}", mint.pubkey());println!("Transaction Signature: {}", transaction_signature);// Fetch mint accountlet mint_account = client.get_account(&mint.pubkey()).await?;// Deserialize the mint account with extensionslet mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;// Get all extension types enabled on this mintlet extension_types = mint_state.get_extension_types()?;println!("\nExtensions enabled: {:?}", extension_types);// Deserialize the MetadataPointer extension datalet metadata_pointer = mint_state.get_extension::<MetadataPointer>()?;println!("\n{:#?}", metadata_pointer);// Deserialize the TokenMetadata extension data (variable-length)let token_metadata = mint_state.get_variable_len_extension::<TokenMetadata>()?;println!("\n{:#?}", token_metadata);Ok(())}
Console
Click to execute the code.
Is this page helpful?