O Que São as Extensões Ponteiro de Metadados e Metadados de Token?
A extensão de cunhagem MetadataPointer do Token Extensions Program
armazena dois campos numa cunhagem:
- Uma autoridade que pode atualizar o ponteiro
- O endereço da conta que armazena os metadados do token
Esse ponteiro pode referenciar qualquer conta pertencente a um programa que implemente a interface token-metadata.
O Token Extensions Program também implementa essa mesma interface diretamente
através da extensão de cunhagem TokenMetadata. Com TokenMetadata, a
cunhagem armazena o name, symbol, uri, update_authority
do token e metadados personalizados na própria conta de cunhagem.
URI de Metadados Off-chain
O campo uri aponta para metadados JSON off-chain. Consulte Formato de
Metadados Off-chain.
As duas extensões resolvem problemas diferentes:
MetadataPointerespecifica a conta que armazena os metadados.TokenMetadataarmazena os metadados diretamente na conta de cunhagem.
Extensão de Comprimento Variável
TokenMetadata é uma extensão TLV de comprimento variável.
InitializeTokenMetadata, UpdateField e RemoveKey podem
redimensionar os dados da conta de cunhagem, mas não transferem lamports
adicionais. A conta de cunhagem precisa de lamports suficientes para
permanecer isenta de renda para os metadados que estão a ser armazenados. Se
os metadados crescerem mais tarde, lamports adicionais precisam de ser
transferidos para a conta de cunhagem antes da instrução que a redimensiona.
Como Armazenar Metadados de Token na Conta de Cunhagem
Para armazenar metadados na conta de cunhagem:
- Calcule o tamanho da conta mint e o aluguel necessário para o mint, extensões e metadados.
- Crie a conta mint com
CreateAccount, inicializeMetadataPointere inicialize o mint comInitializeMint. - Inicialize
TokenMetadatana conta mint e, em seguida, useUpdateFieldpara adicionar ou atualizar metadados. - Use
RemoveKey,UpdateAuthorityeEmitpara remover metadados personalizados, alterar ou limpar a autoridade de atualização, ou retornar os metadados atuais nos dados de retorno da transação.
Calcular tamanho da conta
Calcule o tamanho da conta mint para o mint base mais a extensão
MetadataPointer. Este é o tamanho usado em CreateAccount.
Calcular aluguel
Calcule o aluguel usando o tamanho máximo necessário depois que
TokenMetadata for armazenado no mint.
Criar a conta mint
Crie a conta mint com o espaço e lamports calculados.
Inicializar MetadataPointer
Inicialize MetadataPointer e defina seu endereço de metadados como o
endereço do mint.
Inicializar o mint
Inicialize o mint com InitializeMint na mesma transação.
Inicializar e atualizar metadados
Inicialize TokenMetadata no mint. Use UpdateField para adicionar
metadados personalizados; se o campo não existir, UpdateField o adiciona.
Atualizar, remover ou emitir metadados
Após a inicialização do mint, use UpdateField para atualizar metadados,
UpdateMetadataPointer para atualizar o ponteiro de metadados,
RemoveKey para remover metadados personalizados, UpdateAuthority
para limpar a autoridade de atualização e Emit para retornar os metadados
atuais.
Ordem das Instruções
MetadataPointerInstruction::Initialize deve vir antes de
InitializeMint. CreateAccount,
MetadataPointerInstruction::Initialize e InitializeMint devem ser
incluídos na mesma transação.
Referência da Fonte
Ponteiro de Metadados
| Item | Descrição | Fonte |
|---|---|---|
MetadataPointer | Extensão de mint que armazena a autoridade do ponteiro de metadados e o endereço da conta de metadados. | Fonte |
MetadataPointerInstruction::Initialize | Inicializa a extensão de ponteiro de metadados antes de InitializeMint. | Fonte |
MetadataPointerInstruction::Update | Atualiza o endereço de metadados armazenado pela extensão de ponteiro de metadados do mint. | Fonte |
process_initialize (MetadataPointer) | Lógica do processador de ponteiro de metadados que requer pelo menos uma autoridade ou um endereço de metadados durante a inicialização. | Fonte |
process_update (MetadataPointer) | Valida a autoridade do ponteiro de metadados e reescreve o endereço de metadados armazenado no mint. | Fonte |
Metadados de Token
| Item | Descrição | Fonte |
|---|---|---|
TokenMetadata | Estado de interface de metadados de token de comprimento variável armazenado em uma entrada TLV. | Fonte |
Field | Enum de campo usado por UpdateField para segmentar name, symbol, uri ou uma chave personalizada. | Fonte |
TokenMetadataInstruction::Initialize | Inicializa os campos base name, symbol e uri para uma conta de metadados de token. | Fonte |
TokenMetadataInstruction::UpdateField | Adiciona ou atualiza um campo base ou campo de metadados personalizado nos metadados do token. | Fonte |
TokenMetadataInstruction::RemoveKey | Remove uma chave de metadados personalizada. Campos base não podem ser removidos com esta instrução. | Fonte |
TokenMetadataInstruction::UpdateAuthority | Alterna a autoridade de atualização de metadados ou a limpa completamente para tornar os metadados imutáveis. | Fonte |
TokenMetadataInstruction::Emit | Retorna os metadados serializados através dos dados de retorno da transação, opcionalmente para um intervalo de bytes. | Fonte |
process_initialize (TokenMetadata) | Lógica do processador de metadados de token que requer que a conta de metadados seja o próprio mint e que o mint tenha MetadataPointer. | Fonte |
process_update_field | Realoca e reescreve a entrada TLV de TokenMetadata de comprimento variável após uma atualização de campo. | Fonte |
process_remove_key | Valida a autoridade de atualização, remove uma chave de metadados personalizada e reescreve a entrada TLV. | Fonte |
process_update_authority | Valida a autoridade de atualização atual e alterna ou limpa a autoridade de metadados no local. | Fonte |
process_emit | Lê o TokenMetadata serializado do mint e grava o segmento solicitado nos dados de retorno. | Fonte |
Typescript
O exemplo abaixo usa as instruções geradas diretamente. Exemplos legados usando
@solana/web3.js, @solana/spl-token e @solana/spl-token-metadata estão
incluídos para referência.
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?