mpl-token-metadata icon indicating copy to clipboard operation
mpl-token-metadata copied to clipboard

Cannot update "Update Authority" on SPL token created with Metaplex

Open 0xlucyy opened this issue 1 year ago • 13 comments

I create an SPL token using metaplex with specific metadata using the following code. In the process I also revoke Mint and Freeze authority.

  const createToken = async () => {
    if (!userPublicKey) {
        console.error('User public key not found');
        return;
    }

    try {
      const metadata = {
          name: "RNG Gaming",
          symbol: "RNG",
          uri: "https://ensagent.mypinata.cloud/ipfs/Qm..."
      };

      const mint = generateSigner(umi);
      const initialSupply = 1000000000000000;

      console.log('Creating token: ', metadata.name)
      await createAndMint(umi, {
        mint,
        authority: umi.identity,
        name: metadata.name,
        symbol: metadata.symbol,
        uri: metadata.uri,
        sellerFeeBasisPoints: percentAmount(0),
        decimals: 7,
        amount: initialSupply,
        tokenOwner: userPublicKey,
        tokenStandard: TokenStandard.Fungible,
      }).sendAndConfirm(umi);

      const token_mint_pub_key = new PublicKey(mint.publicKey)
      await setToken(token_mint_pub_key);

      // Revoke the mint & freeze authority
      const transaction = new Transaction().add(
        createSetAuthorityInstruction(
          token_mint_pub_key,
          userPublicKey,
          AuthorityType.MintTokens,
          null,
          [],
          TOKEN_PROGRAM_ID
        ),
        createSetAuthorityInstruction(
          token_mint_pub_key,
          userPublicKey,
          AuthorityType.FreezeAccount,
          null,
          [],
          TOKEN_PROGRAM_ID
        )
      );

      const { blockhash } = await connection.getRecentBlockhash();
      transaction.recentBlockhash = blockhash;
      transaction.feePayer = userPublicKey;

      const signature = await sendTransaction(transaction, connection);
      console.log('Transaction signature:', signature);

    } catch (error) {
       ...
       ...
    }
  };

This successfully creates the appropriate token

Screenshot 2024-07-31 at 11 06 36 PM

Now, what I am attempting but failing at is revoking the Update Authority.

This is my code to revoke Update Authority

  const revokeUpdate = async () => {
    try {
      const mintAddress = publicKey(MAINNET_RNG_TOKEN_MINT);
  
      const initialMetadata = await fetchMetadataFromSeeds(umi, { mint: mintAddress });
      console.log("Initial metadata:", initialMetadata);
  
      const updateInstruction = updateV1(umi, {
        mint: mintAddress,
        authority: metadata_wallet_signer,
        data: {
          ...initialMetadata,
        },
        newUpdateAuthority: null,
        isMutable: false,
      });
  
      const result = await updateInstruction.sendAndConfirm(umi);
  
      const newMetadata = await fetchMetadataFromSeeds(umi, { mint: mintAddress });
      console.log("new metadata:", newMetadata);
    } catch (error) {
      console.error("Error in revokeAuthorities:", error);
      if (error.stack) {
        console.error("Stack trace:", error.stack);
      }
    }
  };

Please note the relevant console logs

Initial metadata: {
  name : "RNG Gaming",
  symbol: "RNG",
  isMutable: true,
  updateAuthority: "DQ3...",
  uri : "https://ensagent.mypinata.cloud/ipfs/Qm..."
  ....
  ....
}

AND 

new metadata: {
  name : "RNG Gaming",
  symbol: "RNG",
  isMutable: false,
  updateAuthority: "DQ3...",
  uri : "https://ensagent.mypinata.cloud/ipfs/Qm..."
  ....
  ....
}

Please note that the ONLY thing that changes is the isMutable field. The updateAuthority field remains unchanged. This behavior is true even if I send two separate updateInstruction, the first ONLY changing the updateAuthority and the second ONLY changing the isMutable fields.

I should note, the account I am using to sign/send the transactions is the same account which has the Update Authority on the token.

What is happening, what am I doing wrong? And how can I fix it!

0xlucyy avatar Aug 01 '24 05:08 0xlucyy

Hey, To remove the update authority account key you probably need to pass in none() instead of null. In general this should not be required though. As soon as the metadata is set to isMutable: false the update authority cannot be used for anything either way.

MarkSackerberg avatar Aug 01 '24 12:08 MarkSackerberg

Thank you for your feedback.

Unfortunetly your provided solution does not work...

  import { none } from '@metaplex-foundation/umi'

  const revokUpdateAuthority = async () => {
    try {
      const mintAddress = publicKey(MAINNET_RNG_TOKEN_MINT);
  
      const initialMetadata = await fetchMetadataFromSeeds(umi, { mint: mintAddress });
      console.log("Initial metadata:", initialMetadata);
      
      const updateInstruction = updateV1(umi, {
        mint: mintAddress,
        authority: metadata_wallet_signer,
        data: {
          ...initialMetadata,
        },
        newUpdateAuthority: none(),
        isMutable: false,
      });
  
      const result = await updateInstruction.sendAndConfirm(umi);
     
      const newMetadata = await fetchMetadataFromSeeds(umi, { mint: mintAddress });
      console.log("new metadata:", newMetadata);
    } catch (error) {
          ...
          ...
      }
    }
  };

This produces the exact same Initial metadata & new metadata as before.

Please open this ticket, as there has been no working solution provided.

0xlucyy avatar Aug 01 '24 21:08 0xlucyy

Let’s start over. What is it that’s not working? You can not update metadata after isMutable was set to false, as mentioned before. So what do you want to achieve in addition with removing your address?

MarkSackerberg avatar Aug 01 '24 21:08 MarkSackerberg

I need to ensure that a token successfully has its Update Authority set to None/null.

Right now, every SPL token I create with metaplex has its Update Authority as the minter of the token. I can successfully revoke Mint & Freeze Authority, meaning those two authorities are set to null.

So when a wallet checks those Authorities, the wallets correctly show the user that this token has renounced those Authorities and that the token is safe from those attacks.

Several popular wallets, including Phantom, show the user if the token has renounced its Update Authority. Even though I can set isMutable: false,, the wallets and rug checking websites show that the token metadata is mutable because an Update Authority is set.

So from a users perspective, they are buying into a token that can change its picture and other metadata.

That is highly problematic for me. All I need to do is set the Update Authority to null just as I have for Freeze & Mint Authorities.

Does that make sense friend? I am struggling with setting the Update Authority to None/null and I have a feeling that metaplex is at fault here. This should be a trivial matter to do, yet it is not.

I want to create the safest token possible for users, and I want that token to be presented accurately to users... meaning that all relevant Authorities are revoked and set to None/null. This will present the token in the best possible light in wallets and rug checking websites.

The fact I can set the token to isMutable: false does nothing for how the token is presented and I should be able to change the update authority if I want to. But right now I cannot and ive tried for several days now, that is why I am here asking for help.

I appreciate your response but I need a viable replicable solution and I still do not have one after days of trying. This is very bad for me.

0xlucyy avatar Aug 01 '24 22:08 0xlucyy

hi @0xlucyy can u give me a clue on how to revoke the Mint & Freeze? Because when i use Token Metadata Program to mint NFT, it auto set the PDA as mint & freeze key, when i try to unlock/thaw, it need the mint & freeze authority signer and they are derived key and i don't know how to sign with it.

cd11103 avatar Sep 11 '24 02:09 cd11103

Hey, To remove the update authority account key you probably need to pass in none() instead of null. In general this should not be required though. As soon as the metadata is set to isMutable: false the update authority cannot be used for anything either way.

Passing none() as newUpdateAuthority does not work as you said.

import { none } from "@metaplex-foundation/umi";
import { updateAsUpdateAuthorityV2 } from "@metaplex-foundation/mpl-token-metadata";

const newOnChainMetadata = {/*...*/}
const txBuilder = updateAsUpdateAuthorityV2(umi, {
    mint: fromWeb3JsPublicKey(mint),
    data: newOnChainMetadata,
    newUpdateAuthority: none(),
})

Upon executing this, updateAuthority will not be changed and explorer.solana.com will show the same authority as it was.

rogaldh avatar Sep 11 '24 19:09 rogaldh

Also having this issue. Either passing null or none() does not change the metadata's update authority address.

rileyg98 avatar Sep 19 '24 18:09 rileyg98

Did this ever get fixed, if so, how?

captin-neo avatar Jan 19 '25 12:01 captin-neo

Any news on this?

defijoker avatar Feb 12 '25 17:02 defijoker

I am experiencing the same issue. As a temporary workaround, I assigned the update authority to the system program. By doing this, the update authority is essentially prevented from being updated.

const newAuthority = publicKey("11111111111111111111111111111111");

updateV1(umi, {
  isMutable: false,
  mint: mint.publicKey,
  authority: authority,
  newUpdateAuthority: newAuthority
})

Resister-boy avatar Feb 14 '25 12:02 Resister-boy

serializedData Uint8Array(11) [
  50, 0, 0, 0, 0,
   0, 0, 0, 0, 0,
   0
]

Here is the instruction data returned when the updateV1 function is called with mint and authority, while updateAuthority is set to none() or null. When deserialized, it produces the following result:

deserializedData [
  {
    discriminator: 50,
    updateV1Discriminator: 0,
    newUpdateAuthority: { __option: 'None' },
    data: { __option: 'None' },
    primarySaleHappened: { __option: 'None' },
    isMutable: { __option: 'None' },
    collection: { __kind: 'None' },
    collectionDetails: { __kind: 'None' },
    uses: { __kind: 'None' },
    ruleSet: { __kind: 'None' },
    authorizationData: { __option: 'None' }
  },
  11
]

When a valid address is provided, the updateAuthority is transferred correctly. However, an exceptional bug occurs when none or null is used.

Resister-boy avatar Feb 15 '25 10:02 Resister-boy

Hey guys, looking at the Metaplex Token Metadata program source code,

the update_authority must be a Pubkey

pub struct Metadata {
    /// Account discriminator.
    pub key: Key,
    /// Address of the update authority.
    #[cfg_attr(feature = "serde-feature", serde(with = "As::<DisplayFromStr>"))]
    pub update_authority: Pubkey,

and there are no state variables to keep track of whether a Metadata account's authority is renounced.

Hence, the way to renounce the update authority is to set it to the System Program address like what @Resister-boy has done above.

I also confirmed this with @blockiosaurus

nazreen avatar Mar 17 '25 19:03 nazreen