¿Qué son las Extensiones de Puntero de Metadatos y Metadatos de Token?
La extensión de mint MetadataPointer del Programa de Token Extensions
almacena dos campos en un mint:
- Una autoridad que puede actualizar el puntero
- La dirección de la cuenta que almacena los metadatos del token
Ese puntero puede hacer referencia a cualquier cuenta que pertenezca a un programa que implemente la interfaz token-metadata.
El Programa de Token Extensions también implementa esa misma interfaz
directamente a través de la extensión de mint TokenMetadata. Con
TokenMetadata, el mint almacena el name, symbol, uri,
update_authority, y metadatos personalizados del token en la propia cuenta
del mint.
URI de Metadatos Fuera de Cadena
El campo uri apunta a metadatos JSON fuera de cadena. Consulta Formato
de Metadatos Fuera de
Cadena.
Las dos extensiones resuelven problemas diferentes:
MetadataPointerespecifica la cuenta que almacena los metadatos.TokenMetadataalmacena los metadatos directamente en la cuenta del mint.
Extensión de Longitud Variable
TokenMetadata es una extensión TLV de longitud variable.
InitializeTokenMetadata, UpdateField y RemoveKey pueden
redimensionar los datos de la cuenta del mint, pero no transfieren lamports
adicionales. La cuenta del mint necesita suficientes lamports para permanecer
exenta de renta para los metadatos que se están almacenando. Si los metadatos
crecen posteriormente, se deben transferir lamports adicionales a la cuenta
del mint antes de la instrucción que la redimensione.
Cómo Almacenar Metadatos de Token en la Cuenta del Mint
Para almacenar metadatos en la cuenta del mint:
- Calcula el tamaño de la cuenta mint y el rent necesario para el mint, las extensiones y los metadatos.
- Crea la cuenta mint con
CreateAccount, inicializaMetadataPointere inicializa el mint conInitializeMint. - Inicializa
TokenMetadataen la cuenta mint y luego usaUpdateFieldpara agregar o actualizar metadatos. - Usa
RemoveKey,UpdateAuthorityyEmitpara eliminar metadatos personalizados, cambiar o borrar la autoridad de actualización, o devolver los metadatos actuales en los datos de retorno de la transacción.
Calcular el tamaño de la cuenta
Calcula el tamaño de la cuenta mint para el mint base más la extensión
MetadataPointer. Este es el tamaño utilizado en CreateAccount.
Calcular el rent
Calcula el rent utilizando el tamaño máximo necesario después de que
TokenMetadata se almacene en el mint.
Crear la cuenta mint
Crea la cuenta mint con el espacio y los lamports calculados.
Inicializar MetadataPointer
Inicializa MetadataPointer y establece su dirección de metadatos en la
dirección del mint.
Inicializar el mint
Inicializa el mint con InitializeMint en la misma transacción.
Inicializar y actualizar metadatos
Inicializa TokenMetadata en el mint. Usa UpdateField para agregar
metadatos personalizados; si el campo no existe, UpdateField lo agrega.
Actualizar, eliminar o emitir metadatos
Después de inicializar el mint, usa UpdateField para actualizar metadatos,
UpdateMetadataPointer para actualizar el puntero de metadatos,
RemoveKey para eliminar metadatos personalizados, UpdateAuthority
para limpiar la autoridad de actualización, y Emit para devolver los
metadatos actuales.
Orden de las instrucciones
MetadataPointerInstruction::Initialize debe ejecutarse antes de
InitializeMint. CreateAccount,
MetadataPointerInstruction::Initialize y InitializeMint deben
incluirse en la misma transacción.
Referencia del código fuente
Puntero de metadatos
| Elemento | Descripción | Fuente |
|---|---|---|
MetadataPointer | Extensión del mint que almacena la autoridad del puntero de metadatos y la dirección de la cuenta de metadatos. | Fuente |
MetadataPointerInstruction::Initialize | Inicializa la extensión del puntero de metadatos antes de InitializeMint. | Fuente |
MetadataPointerInstruction::Update | Actualiza la dirección de metadatos almacenada por la extensión del puntero de metadatos del mint. | Fuente |
process_initialize (MetadataPointer) | Lógica del procesador del puntero de metadatos que requiere al menos una autoridad o una dirección de metadatos durante la inicialización. | Fuente |
process_update (MetadataPointer) | Valida la autoridad del puntero de metadatos y luego reescribe la dirección de metadatos almacenada en el mint. | Fuente |
Metadatos del token
| Elemento | Descripción | Fuente |
|---|---|---|
TokenMetadata | Estado de la interfaz de metadatos del token de longitud variable almacenado en una entrada TLV. | Fuente |
Field | Enum de campo utilizado por UpdateField para dirigirse a name, symbol, uri o una clave personalizada. | Fuente |
TokenMetadataInstruction::Initialize | Inicializa los campos base name, symbol y uri para una cuenta de metadatos de token. | Fuente |
TokenMetadataInstruction::UpdateField | Agrega o actualiza un campo base o un campo de metadatos personalizado en los metadatos del token. | Fuente |
TokenMetadataInstruction::RemoveKey | Elimina una clave de metadatos personalizada. Los campos base no se pueden eliminar con esta instrucción. | Fuente |
TokenMetadataInstruction::UpdateAuthority | Rota la autoridad de actualización de metadatos o la elimina por completo para hacer que los metadatos sean inmutables. | Fuente |
TokenMetadataInstruction::Emit | Devuelve los metadatos serializados a través de los datos de retorno de la transacción, opcionalmente para un rango de bytes. | Fuente |
process_initialize (TokenMetadata) | Lógica del procesador de metadatos de token que requiere que la cuenta de metadatos sea el mint en sí y que el mint tenga MetadataPointer. | Fuente |
process_update_field | Reasigna y reescribe la entrada TLV de longitud variable TokenMetadata después de una actualización de campo. | Fuente |
process_remove_key | Valida la autoridad de actualización, elimina una clave de metadatos personalizada y reescribe la entrada TLV. | Fuente |
process_update_authority | Valida la autoridad de actualización actual y luego rota o elimina la autoridad de metadatos in situ. | Fuente |
process_emit | Lee el TokenMetadata serializado del mint y escribe el segmento solicitado en los datos de retorno. | Fuente |
Typescript
El ejemplo Kit a continuación utiliza las instrucciones generadas
directamente. Los ejemplos heredados que usan @solana/web3.js,
@solana/spl-token e @solana/spl-token-metadata se incluyen como referencia.
Kit
import {lamports,createClient,generateKeyPairSigner,unwrapOption} from "@solana/kit";import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";import { unpack as unpackTokenMetadata } from "@solana/spl-token-metadata";import { getCreateAccountInstruction } from "@solana-program/system";import {extension,fetchMint,getEmitTokenMetadataInstruction,getInitializeMetadataPointerInstruction,getInitializeMintInstruction,getInitializeTokenMetadataInstruction,getMintSize,getRemoveTokenMetadataKeyInstruction,getUpdateMetadataPointerInstruction,getUpdateTokenMetadataFieldInstruction,getUpdateTokenMetadataUpdateAuthorityInstruction,isExtension,tokenMetadataField,TOKEN_2022_PROGRAM_ADDRESS} from "@solana-program/token-2022";const client = await createClient().use(generatedPayer()).use(solanaRpc({rpcUrl: "http://localhost:8899",rpcSubscriptionsUrl: "ws://localhost:8900"})).use(rpcAirdrop()).use(airdropPayer(lamports(1_000_000_000n)));const mint = await generateKeyPairSigner();const metadataPointerExtension = extension("MetadataPointer", {authority: client.payer.address,metadataAddress: mint.address});const mintSpace = BigInt(getMintSize([metadataPointerExtension]));const maxTokenMetadataExtension = extension("TokenMetadata", {updateAuthority: client.payer.address,mint: mint.address,name: "Example Token v2",symbol: "EXMPL",uri: "https://example.com/token.json",additionalMetadata: new Map([["description", "Metadata stored on mint account"]])});const maxMintSpace = BigInt(getMintSize([metadataPointerExtension, maxTokenMetadataExtension]));const mintRent = await client.rpc.getMinimumBalanceForRentExemption(maxMintSpace).send();await client.sendTransaction([getCreateAccountInstruction({payer: client.payer, // Account funding account creation.newAccount: mint, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint plus MetadataPointer.programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.}),getInitializeMetadataPointerInstruction({mint: mint.address, // Mint account that stores the MetadataPointer extension.authority: client.payer.address, // Authority allowed to update the metadata pointer later.metadataAddress: mint.address // Account address that stores the metadata.}),getInitializeMintInstruction({mint: mint.address, // Mint account to initialize.decimals: 0, // Number of decimals for the token.mintAuthority: client.payer.address, // Authority allowed to mint new tokens.freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.}),getInitializeTokenMetadataInstruction({metadata: mint.address, // Mint account that stores the metadata.updateAuthority: client.payer.address, // Authority allowed to update metadata later.mint: mint.address, // Mint that the metadata describes.mintAuthority: client.payer, // Signer authorizing metadata initialization for the mint.name: "Example Token", // Token name stored in metadata.symbol: "EXMPL", // Token symbol stored in metadata.uri: "https://example.com/token.json" // URI pointing to off-chain JSON metadata.}),getUpdateTokenMetadataFieldInstruction({metadata: mint.address, // Mint account that stores the metadata.updateAuthority: client.payer, // Signer authorized to update metadata fields.field: tokenMetadataField("Key", ["description"]), // Custom metadata field to add.value: "Metadata stored on mint account" // Value stored for the custom metadata field.})]);const initialMintAccount = await fetchMint(client.rpc, mint.address);const initialExtensions =unwrapOption(initialMintAccount.data.extensions) ?? [];const initialTokenMetadata = initialExtensions.find((item) =>isExtension("TokenMetadata", item));console.dir({mint: mint.address,tokenMetadata: initialTokenMetadata},{ depth: null });const updateMetadataTransaction = await client.sendTransaction([getUpdateTokenMetadataFieldInstruction({metadata: mint.address, // Mint account that stores the metadata.updateAuthority: client.payer, // Signer authorized to update metadata fields.field: tokenMetadataField("Name"), // Base metadata field to update.value: "Example Token v2" // Updated value for the token name.}),getUpdateMetadataPointerInstruction({mint: mint.address, // Mint account that stores the MetadataPointer extension.metadataPointerAuthority: client.payer, // Signer authorized to update the metadata pointer.metadataAddress: mint.address // Account address that stores the metadata.}),getRemoveTokenMetadataKeyInstruction({metadata: mint.address, // Mint account that stores the metadata.updateAuthority: client.payer, // Signer authorized to remove custom metadata.key: "description" // Custom metadata key to remove.}),getUpdateTokenMetadataUpdateAuthorityInstruction({metadata: mint.address, // Mint account that stores the metadata.updateAuthority: client.payer, // Current signer authorized to change the update authority.newUpdateAuthority: null // Clear the update authority so metadata can no longer be changed.}),getEmitTokenMetadataInstruction({metadata: mint.address // Mint account that stores the metadata to emit.})]);const updateMetadataResult = await client.rpc.getTransaction(updateMetadataTransaction.context.signature, {encoding: "json",maxSupportedTransactionVersion: 0}).send();const emittedDataBase64 = updateMetadataResult?.meta?.returnData?.data?.[0];if (!emittedDataBase64) {throw new Error("Expected token metadata return data");}const emittedTokenMetadata = unpackTokenMetadata(Buffer.from(emittedDataBase64, "base64"));const mintAccount = await fetchMint(client.rpc, mint.address);const extensions = unwrapOption(mintAccount.data.extensions) ?? [];const metadataPointer = extensions.find((item) =>isExtension("MetadataPointer", item));const tokenMetadata = extensions.find((item) =>isExtension("TokenMetadata", item));console.dir({mint: mint.address,metadataPointer,tokenMetadata,emittedTokenMetadata},{ depth: null });
Web3.js
import {Connection,Keypair,LAMPORTS_PER_SOL,sendAndConfirmTransaction,SystemProgram,Transaction} from "@solana/web3.js";import {createInitializeMetadataPointerInstruction,createInitializeMintInstruction,createUpdateMetadataPointerInstruction,ExtensionType,getMetadataPointerState,getMint,getMintLen,getTokenMetadata,LENGTH_SIZE,TOKEN_2022_PROGRAM_ID,TYPE_SIZE} from "@solana/spl-token";import {createInitializeInstruction,createEmitInstruction,createRemoveKeyInstruction,unpack as unpackTokenMetadata,createUpdateAuthorityInstruction,createUpdateFieldInstruction,pack,type TokenMetadata} from "@solana/spl-token-metadata";const connection = new Connection("http://localhost:8899", "confirmed");const latestBlockhash = await connection.getLatestBlockhash();const feePayer = Keypair.generate();const mint = Keypair.generate();const airdropSignature = await connection.requestAirdrop(feePayer.publicKey,LAMPORTS_PER_SOL);await connection.confirmTransaction({blockhash: latestBlockhash.blockhash,lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,signature: airdropSignature});const maxMetadata: TokenMetadata = {updateAuthority: feePayer.publicKey,mint: mint.publicKey,name: "Example Token v2",symbol: "EXMPL",uri: "https://example.com/token.json",additionalMetadata: [["description", "Metadata stored on mint account"]]};const mintSpace = getMintLen([ExtensionType.MetadataPointer]);const metadataSpace = TYPE_SIZE + LENGTH_SIZE + pack(maxMetadata).length;const mintRent = await connection.getMinimumBalanceForRentExemption(mintSpace + metadataSpace);await sendAndConfirmTransaction(connection,new Transaction().add(SystemProgram.createAccount({fromPubkey: feePayer.publicKey, // Account funding account creation.newAccountPubkey: mint.publicKey, // New mint account to create.lamports: mintRent, // Lamports funding the mint account rent.space: mintSpace, // Account size in bytes for the mint plus MetadataPointer.programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.}),createInitializeMetadataPointerInstruction(mint.publicKey, // Mint account that stores the MetadataPointer extension.feePayer.publicKey, // Authority allowed to update the metadata pointer later.mint.publicKey, // Account address that stores the metadata.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.),createInitializeMintInstruction(mint.publicKey, // Mint account to initialize.0, // Number of decimals for the token.feePayer.publicKey, // Authority allowed to mint new tokens.feePayer.publicKey, // Authority allowed to freeze token accounts.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.),createInitializeInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the mint and metadata.metadata: mint.publicKey, // Mint account that stores the metadata.updateAuthority: feePayer.publicKey, // Authority allowed to update metadata later.mint: mint.publicKey, // Mint that the metadata describes.mintAuthority: feePayer.publicKey, // Signer authorizing metadata initialization for the mint.name: "Example Token", // Token name stored in metadata.symbol: "EXMPL", // Token symbol stored in metadata.uri: "https://example.com/token.json" // URI pointing to off-chain JSON metadata.}),createUpdateFieldInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.metadata: mint.publicKey, // Mint account that stores the metadata.updateAuthority: feePayer.publicKey, // Authority allowed to update metadata fields.field: "description", // Custom metadata field to add.value: "Metadata stored on mint account" // Value stored for the custom metadata field.})),[feePayer, mint],{ commitment: "confirmed" });const initialTokenMetadata = await getTokenMetadata(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);console.log(JSON.stringify({mint: mint.publicKey,tokenMetadata: initialTokenMetadata},null,2));const updateMetadataSignature = await sendAndConfirmTransaction(connection,new Transaction().add(createUpdateFieldInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.metadata: mint.publicKey, // Mint account that stores the metadata.updateAuthority: feePayer.publicKey, // Authority allowed to update metadata fields.field: "name", // Base metadata field to update.value: "Example Token v2" // Updated value for the token name.}),createUpdateMetadataPointerInstruction(mint.publicKey, // Mint account that stores the MetadataPointer extension.feePayer.publicKey, // Authority allowed to update the metadata pointer.mint.publicKey, // Account address that stores the metadata.[], // Additional signer accounts required by the instruction.TOKEN_2022_PROGRAM_ID // Program that owns the mint account.),createRemoveKeyInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.metadata: mint.publicKey, // Mint account that stores the metadata.updateAuthority: feePayer.publicKey, // Authority allowed to remove custom metadata.key: "description", // Custom metadata key to remove.idempotent: false // Fail if the custom metadata key does not exist.}),createUpdateAuthorityInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.metadata: mint.publicKey, // Mint account that stores the metadata.oldAuthority: feePayer.publicKey, // Current authority allowed to change the update authority.newAuthority: null // Clear the update authority so metadata can no longer be changed.}),createEmitInstruction({programId: TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.metadata: mint.publicKey // Mint account that stores the metadata to emit.})),[feePayer],{ commitment: "confirmed" });const updateMetadataTransaction = (await connection.getTransaction(updateMetadataSignature,{maxSupportedTransactionVersion: 0})) as any;const emittedDataBase64 =updateMetadataTransaction?.meta?.returnData?.data?.[0];if (!emittedDataBase64) {throw new Error("Expected token metadata return data");}const emittedTokenMetadata = unpackTokenMetadata(Buffer.from(emittedDataBase64, "base64"));const mintAccount = await getMint(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);const metadataPointer = getMetadataPointerState(mintAccount);const tokenMetadata = await getTokenMetadata(connection,mint.publicKey,"confirmed",TOKEN_2022_PROGRAM_ID);console.log(JSON.stringify({mint: mint.publicKey,metadataPointer,tokenMetadata,emittedTokenMetadata},null,2));
Rust
use anyhow::{anyhow, Result};use base64::prelude::{Engine as _, BASE64_STANDARD};use solana_client::nonblocking::rpc_client::RpcClient;use solana_client::rpc_config::RpcTransactionConfig;use solana_commitment_config::CommitmentConfig;use solana_sdk::{pubkey::Pubkey,signature::{Keypair, Signer},transaction::Transaction,};use solana_transaction_status_client_types::UiTransactionEncoding;use solana_system_interface::instruction::create_account;use spl_token_2022_interface::{extension::{metadata_pointer::{instruction::{initialize as initialize_metadata_pointer,update as update_metadata_pointer,},MetadataPointer,},BaseStateWithExtensions, ExtensionType, StateWithExtensions,},instruction::initialize_mint,state::Mint,ID as TOKEN_2022_PROGRAM_ID,};use spl_token_metadata_interface::{borsh::BorshDeserialize,instruction::{emit as emit_token_metadata, initialize as initialize_token_metadata,remove_key as remove_token_metadata_key,update_authority as update_token_metadata_authority,update_field as update_token_metadata_field,},state::{Field, TokenMetadata},};#[tokio::main]async fn main() -> Result<()> {let client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let fee_payer = Keypair::new();let airdrop_signature = client.request_airdrop(&fee_payer.pubkey(), 1_000_000_000).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}let mint = Keypair::new();let max_token_metadata = TokenMetadata {update_authority: Some(fee_payer.pubkey()).try_into()?,mint: mint.pubkey(),name: "Example Token v2".to_string(),symbol: "EXMPL".to_string(),uri: "https://example.com/token.json".to_string(),additional_metadata: vec![("description".to_string(),"Metadata stored on mint account".to_string(),)],};let mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::MetadataPointer])?;let mint_rent = client.get_minimum_balance_for_rent_exemption(mint_space + max_token_metadata.tlv_size_of()?).await?;let create_mint_transaction = Transaction::new_signed_with_payer(&[create_account(&fee_payer.pubkey(), // Account funding account creation.&mint.pubkey(), // New mint account to create.mint_rent, // Lamports funding the mint account rent.mint_space as u64, // Account size in bytes for the mint plus MetadataPointer.&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.),initialize_metadata_pointer(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account that stores the MetadataPointer extension.Some(fee_payer.pubkey()), // Authority allowed to update the metadata pointer later.Some(mint.pubkey()), // Account address that stores the metadata.)?,initialize_mint(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account to initialize.&fee_payer.pubkey(), // Authority allowed to mint new tokens.Some(&fee_payer.pubkey()), // Authority allowed to freeze token accounts.0, // Number of decimals for the token.)?,initialize_token_metadata(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint and metadata.&mint.pubkey(), // Mint account that stores the metadata.&fee_payer.pubkey(), // Authority allowed to update metadata later.&mint.pubkey(), // Mint that the metadata describes.&fee_payer.pubkey(), // Signer authorizing metadata initialization for the mint."Example Token".to_string(), // Token name stored in metadata."EXMPL".to_string(), // Token symbol stored in metadata."https://example.com/token.json".to_string(), // URI pointing to off-chain JSON metadata.),update_token_metadata_field(&TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.&mint.pubkey(), // Mint account that stores the metadata.&fee_payer.pubkey(), // Authority allowed to update metadata fields.Field::Key("description".to_string()), // Custom metadata field to add."Metadata stored on mint account".to_string(), // Value stored for the custom metadata field.),],Some(&fee_payer.pubkey()),&[&fee_payer, &mint],client.get_latest_blockhash().await?,);client.send_and_confirm_transaction(&create_mint_transaction).await?;let initial_mint_account = client.get_account(&mint.pubkey()).await?;let initial_mint_state = StateWithExtensions::<Mint>::unpack(&initial_mint_account.data)?;let initial_token_metadata = initial_mint_state.get_variable_len_extension::<TokenMetadata>()?;println!("Mint: {}", mint.pubkey());println!("Token Metadata: {:#?}", initial_token_metadata);let update_metadata_transaction = Transaction::new_signed_with_payer(&[update_token_metadata_field(&TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.&mint.pubkey(), // Mint account that stores the metadata.&fee_payer.pubkey(), // Authority allowed to update metadata fields.Field::Name, // Base metadata field to update."Example Token v2".to_string(), // Updated value for the token name.),update_metadata_pointer(&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.&mint.pubkey(), // Mint account that stores the MetadataPointer extension.&fee_payer.pubkey(), // Authority allowed to update the metadata pointer.&[], // Additional signer accounts required by the instruction.Some(mint.pubkey()), // Account address that stores the metadata.)?,remove_token_metadata_key(&TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.&mint.pubkey(), // Mint account that stores the metadata.&fee_payer.pubkey(), // Authority allowed to remove custom metadata."description".to_string(), // Custom metadata key to remove.false, // Fail if the custom metadata key does not exist.),update_token_metadata_authority(&TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.&mint.pubkey(), // Mint account that stores the metadata.&fee_payer.pubkey(), // Current authority allowed to change the update authority.None::<Pubkey>.try_into()?, // Clear the update authority so metadata can no longer be changed.),emit_token_metadata(&TOKEN_2022_PROGRAM_ID, // Program that owns the metadata.&mint.pubkey(), // Mint account that stores the metadata to emit.None, // Start offset for the emitted metadata slice.None, // End offset for the emitted metadata slice.),],Some(&fee_payer.pubkey()),&[&fee_payer],client.get_latest_blockhash().await?,);let update_metadata_signature = client.send_and_confirm_transaction(&update_metadata_transaction).await?;let update_metadata_result = client.get_transaction_with_config(&update_metadata_signature,RpcTransactionConfig {encoding: Some(UiTransactionEncoding::Json),commitment: Some(CommitmentConfig::confirmed()),max_supported_transaction_version: Some(0),},).await?;let emitted_data = BASE64_STANDARD.decode(update_metadata_result.transaction.meta.and_then(|meta| meta.return_data.map(|return_data| return_data.data.0)).ok_or_else(|| anyhow!("Expected token metadata return data"))?,)?;let emitted_token_metadata = TokenMetadata::try_from_slice(&emitted_data)?;let mint_account = client.get_account(&mint.pubkey()).await?;let mint_state = StateWithExtensions::<Mint>::unpack(&mint_account.data)?;let metadata_pointer = mint_state.get_extension::<MetadataPointer>()?;let token_metadata = mint_state.get_variable_len_extension::<TokenMetadata>()?;println!("Mint: {}", mint.pubkey());println!("Metadata Pointer: {:#?}", metadata_pointer);println!("Token Metadata: {:#?}", token_metadata);println!("Emitted Token Metadata: {:#?}", emitted_token_metadata);Ok(())}
Is this page helpful?