Extensions de métadonnées et pointeur de métadonnées
Comment activer l'extension MetadataPointerExtension et l'extension TokenMetadata
La
MetadataPointerExtension
permet à un mint account de spécifier l'adresse de son compte de métadonnées. Ce
pointeur peut référencer n'importe quel compte détenu par un programme
implémentant
l'interface Token Metadata,
offrant ainsi une flexibilité quant à l'emplacement de stockage des métadonnées.
Le Token Extensions Program implémente directement l'interface Token Metadata, permettant ainsi de stocker les métadonnées sur le mint account lui-même. Cela inclut le nom du token, son symbole, son URI et des données descriptives supplémentaires. Lorsque les extensions de pointeur pointent vers le mint lui-même, les extensions TokenMetadata peuvent être utilisées pour stocker toutes les métadonnées dans le mint account.
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);
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,signature::{Keypair, Signer},system_instruction::create_account,transaction::Transaction,};use spl_token_2022::{ID as TOKEN_2022_PROGRAM_ID,extension::{ExtensionType, metadata_pointer::instruction::initialize as initialize_metadata_pointer,},instruction::initialize_mint,state::Mint,};use spl_token_metadata_interface::{instruction::initialize as initialize_token_metadata, state::TokenMetadata,}; // < 0.7.0#[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?;client.confirm_transaction(&airdrop_signature).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}// Generate keypair for the mintlet mint = Keypair::new();// Token metadatalet token_metadata = TokenMetadata {update_authority: Some(fee_payer.pubkey()).try_into().unwrap(),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);// Construct transaction with all instructionslet transaction = Transaction::new_signed_with_payer(&[create_mint_account_instruction,initialize_metadata_pointer_instruction,initialize_mint_instruction,initialize_metadata_instruction,],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],latest_blockhash,);// Send and confirm transactionlet transaction_signature = client.send_and_confirm_transaction(&transaction).await?;println!("Mint Address: {}", mint.pubkey());println!("\nSuccessfully created mint with metadata pointer and token metadata extensions");println!("Transaction Signature: {}", transaction_signature);Ok(())}
Is this page helpful?