0

I am trying to decode name and symbol for token mint 9ymawKvJmd3yeNpi6h3WaWvxkjQiGPW6od9ZTXZaVxGD. It's owner is token program 2022, this is a token from fluxbeam AMM.

It implements metadata extension, but first I need to find a way to retrive metadata pointer related to this Mint using RPC or js SDKs

How should this be done? I have not found a way using @solana/spl-token library

LIbrary versions in use:

"@solana/spl-token": "^0.3.9" "solana/spl-token-metadata": "^0.1.2"

Code snippet:

    const Web3 = require('@solana/web3.js');
    const SplTokenMetadata = require("@solana/spl-token-metadata")
    const SplToken = require('@solana/spl-token');
    const connection = new Web3.Connection('https://api.mainnet-beta.solana.com', 'confirmed');

    const mint = new Web3.PublicKey("9ymawKvJmd3yeNpi6h3WaWvxkjQiGPW6od9ZTXZaVxGD");

    const mintInfo = await SplToken.getMint(
        connection,
        mint,
        "confirmed",
        SplToken.TOKEN_2022_PROGRAM_ID
    );

    // Retrieve and log the metadata pointer state
    const metadataPointer = SplToken.getMetadataPointerState(mintInfo);
    console.log("\nMetadata Pointer:", JSON.stringify(metadataPointer, null, 2));

    // Extract and log the metadata
    const slicedBuffer = mintInfo.tlvData.subarray(72);
    const metadata = SplTokenMetadata.unpack(slicedBuffer);
    console.log("\nMetadata:", JSON.stringify(metadata, null, 2));

    console.log(metadataPointerState);

metadataPointer is always null and there is the problem

1 Answer 1

1

You can use @solana/spl-token-metadata.

It would look something like this:

import {
  Connection,
  Keypair,
  SystemProgram,
  Transaction,
  clusterApiUrl,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  ExtensionType,
  TOKEN_2022_PROGRAM_ID,
  createInitializeMintInstruction,
  getMintLen,
  createInitializeMetadataPointerInstruction,
  getMint,
  getMetadataPointerState,
} from "@solana/spl-token";
import {
  createInitializeInstruction,
  createUpdateFieldInstruction,
  createRemoveKeyInstruction,
  unpack,
  pack,
  TokenMetadata,
} from "@solana/spl-token-metadata";
import { getOrCreateKeypair } from "./utils";

// Local Keypair for payer
// This is a custom helper, replace with a keypair that is funded with SOL
const wallet_1 = getOrCreateKeypair("wallet_1");

// Connection to devnet cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

// Transaction to send
let transaction: Transaction;
// Transaction signature returned from sent transaction
let transactionSignature: string;

// Generate new keypair for Mint Account
const mintKeypair = Keypair.generate();
// Address for Mint Account
const mint = mintKeypair.publicKey;
// Decimals for Mint Account
const decimals = 2;
// Authority that can mint new tokens
const mintAuthority = wallet_1.publicKey;

const metaData: TokenMetadata = {
  updateAuthority: wallet_1.publicKey,
  mint: mint,
  name: "name",
  symbol: "symbol",
  uri: "uri",
  additionalMetadata: [["key1", "value1"]],
};

// Size of MetadataExtension 2 bytes for type, 2 bytes for length
const metadataExtension = 4;
// Size of metadata
const metadataLen = pack(metaData).length;

// Size of Mint Account with extension
const mintLen = getMintLen([ExtensionType.MetadataPointer]);

// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(
  mintLen + metadataExtension + metadataLen
);

// Instruction to invoke System Program to create new account
const createAccountInstruction = SystemProgram.createAccount({
  fromPubkey: wallet_1.publicKey, // Account that will transfer lamports to created account
  newAccountPubkey: mint, // Address of the account to create
  space: mintLen, // Amount of bytes to allocate to the created account
  lamports, // Amount of lamports transferred to created account
  programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});

// Instruction to initialize the MetadataPointer Extension
const initializeMetadataPointerInstruction =
  createInitializeMetadataPointerInstruction(
    mint, // Mint Account address
    wallet_1.publicKey, // Authority that can set the metadata address
    mint, // Account address that holds the metadata
    TOKEN_2022_PROGRAM_ID
  );

// Instruction to initialize Mint Account data
const initializeMintInstruction = createInitializeMintInstruction(
  mint, // Mint Account Address
  decimals, // Decimals of Mint
  mintAuthority, // Designated Mint Authority
  null, // Optional Freeze Authority
  TOKEN_2022_PROGRAM_ID // Token Extension Program ID
);

// Instruction to initialize Metadata Account data
const initializeMetadataInstruction = createInitializeInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Account address that holds the metadata
  updateAuthority: wallet_1.publicKey, // Authority that can update the metadata
  mint: mint, // Mint Account address
  mintAuthority: wallet_1.publicKey, // Designated Mint Authority
  name: metaData.name,
  symbol: metaData.symbol,
  uri: metaData.uri,
});

// Instruction to update metadata, adding custom field
const updateFieldInstruction = createUpdateFieldInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Account address that holds the metadata
  updateAuthority: wallet_1.publicKey, // Authority that can update the metadata
  field: metaData.additionalMetadata[0][0],
  value: metaData.additionalMetadata[0][1],
});

// Add instructions to new transaction
transaction = new Transaction().add(
  createAccountInstruction,
  initializeMetadataPointerInstruction,
  initializeMintInstruction,
  initializeMetadataInstruction,
  updateFieldInstruction
);

// Send transaction
transactionSignature = await sendAndConfirmTransaction(
  connection,
  transaction,
  [wallet_1, mintKeypair]
);

console.log(
  "\nCreate Mint Account:",
  `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);

await fetchAndLogMintInfo();

// Instruction to remove a key from the metadata
const removeKeyInstruction = createRemoveKeyInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Address of the metadata
  updateAuthority: wallet_1.publicKey, // Authority that can update the metadata
  key: metaData.additionalMetadata[0][0], // Key to remove from the metadata
  idempotent: true, /// If the idempotent flag is set to true, then the instruction will not error if the key does not exist
});

// Add instruction to new transaction
transaction = new Transaction().add(removeKeyInstruction);

// Send transaction
transactionSignature = await sendAndConfirmTransaction(
  connection,
  transaction,
  [wallet_1]
);

console.log(
  "\nRemove Additional Metadata Field:",
  `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);

await fetchAndLogMintInfo();

async function fetchAndLogMintInfo() {
  // Retrieve mint information
  const mintInfo = await getMint(
    connection,
    mint,
    "confirmed",
    TOKEN_2022_PROGRAM_ID
  );

  // Retrieve and log the metadata pointer state
  const metadataPointer = getMetadataPointerState(mintInfo);
  console.log("\nMetadata Pointer:", JSON.stringify(metadataPointer, null, 2));

  // Extract and log the metadata
  const slicedBuffer = mintInfo.tlvData.subarray(72);
  const metadata = unpack(slicedBuffer);
  console.log("\nMetadata:", JSON.stringify(metadata, null, 2));
}

For this specific example, the offset is 72 for where the metadata starts on the mint account.

  // Retrieve mint information
  const mintInfo = await getMint(
    connection,
    mint,
    "confirmed",
    TOKEN_2022_PROGRAM_ID
  );

  // Retrieve and log the metadata pointer state
  const metadataPointer = getMetadataPointerState(mintInfo);
  console.log("\nMetadata Pointer:", JSON.stringify(metadataPointer, null, 2));

  // Extract and log the metadata
  const slicedBuffer = mintInfo.tlvData.subarray(72);
  const metadata = unpack(slicedBuffer);
  console.log("\nMetadata:", JSON.stringify(metadata, null, 2));

As mentioned in the answer for this question,

At the time of this writing, the Token Metadata extension for @solana/spl-token is still in development.

However, once it's complete you'll be able to do this:

const tokenMetadata = getTokenMetadataState(mintInfo);

3
  • That is exactly what I am doing, but I just can't get it to work somehow, metadata pointer is always null:
    – Mario
    Dec 15, 2023 at 18:50
  • if you look at the JSON tab of the data, your mint address doesn't have the metadata pointer extension enabled solana.fm/address/…
    – john
    Dec 15, 2023 at 21:13
  • here's a devnet mint with the metadata pointer, and metadata stored on the mint account solana.fm/address/…
    – john
    Dec 15, 2023 at 21:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.