How to fetch and modify Solana NFT metadata using JavaScript

I’m working with Solana NFTs and need help with two main tasks. First, I want to retrieve metadata for a specific NFT token, and second, I need to update that metadata programmatically.

For context, I know that NFT metadata on Solana lives in accounts controlled by the Token Metadata Program. I’m trying to work with a specific token as my test case: 7hGCdGVc2AnthVRwqHdTXQS4uKdLhLkN9Qp1XcF8VmDt

Currently I’m using the metaplex SDK and can pull all metadata using this approach:

const allAccounts = await fetchProgramAccounts(rpcConnection, TOKEN_METADATA_PROGRAM, 'confirmed');

But this grabs everything which is way too much data. I need to filter for just one specific NFT instead of getting all metadata accounts.

I found some filtering logic that targets creators:

const creatorFilters = [
  {
    memcmp: {
      offset: 
        1 + // account type
        32 + // update authority  
        32 + // mint key
        4 + // name length
        NAME_MAX_SIZE + // name data
        4 + // metadata uri length  
        URI_MAX_SIZE + // uri data
        4 + // symbol length
        SYMBOL_MAX_SIZE + // symbol data
        2 + // royalty basis points
        1 + // has creators flag
        4 + // creators array length
        index * CREATOR_MAX_SIZE,
      bytes: targetCreatorAddress,
    }
  }
];

I think I can modify this filter to target the mint address instead, but I’m not sure about the correct byte offset calculation.

Once I can successfully read the metadata, I also need to know how to update it, particularly changing the URI field to point to new metadata JSON.

Oh interesting approach with the byte offsets! I’ve been messing around with solana nft stuff lately too and ran into similar issues.

Actually, i’m curious - have you tried using the getTokenAccountsByOwner RPC method first to get the token account, then derive the metadata PDA from there? might be easier than calculating all those offsets manually. Something like:

const metadataPDA = await PublicKey.findProgramAddress(
  [
    Buffer.from('metadata'),
    TOKEN_METADATA_PROGRAM.toBuffer(), 
    mintAddress.toBuffer()
  ],
  TOKEN_METADATA_PROGRAM
);

Then you can just fetch that specific account directly instead of filtering through everything.

For the updating part - are you the update authority on this nft? because if not, you wont be able to modify the metadata anyway. Also what exactly are you trying to change in the uri? just wondering if there’s a specific usecase your working on.

One thing that caught my attention - when you say “changing the URI field”, do you mean updating the on-chain metadata pointer or actually modifying the json content that the uri points to? because those are kinda different operations with different implications.

btw have you checked if the nft is mutable in the first place? some nfts have the isMutable flag set to false which would block any updates entirely.

You’re overcomplicating this with manual offset calculations. The much simpler approach is to derive the metadata PDA directly from your mint address since each NFT has a deterministic metadata account address.

const [metadataAddress] = await PublicKey.findProgramAddress(
  [
    Buffer.from('metadata'),
    TOKEN_METADATA_PROGRAM.toBuffer(),
    new PublicKey('7hGCdGVc2AnthVRwqHdTXQS4uKdLhLkN9Qp1XcF8VmDt').toBuffer()
  ],
  TOKEN_METADATA_PROGRAM
);

const metadataAccount = await connection.getAccountInfo(metadataAddress);

For updating metadata, you’ll need to verify you have update authority first. The updateMetadataAccount instruction requires the update authority to sign the transaction. Most production NFTs have this locked to prevent changes, so check the update authority field in your fetched metadata.

Regarding URI updates - remember that changing the on-chain URI field requires a blockchain transaction and fees. If you just need to modify the JSON content, updating the file at the existing URI endpoint might be more practical depending on your hosting setup.

yeah the PDA approach is definitly the way to go here. but just heads up - if your testing with that specific mint address, make sure it actually exists first by checking getAccountInfo on the mint itself before trying to derive metadata. i’ve wasted time debugging non-existent tokens lol. also worth mentioning that some older nfts might not follow the standard metadata program structure so you could get weird results