metaplex-foundation / mpl-token-metadata

Program to attach additional data to Fungible or Non-Fungible tokens on Solana.
https://developers.metaplex.com/token-metadata
Other
118 stars 59 forks source link

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

Open 0xlucyy opened 1 month ago

0xlucyy commented 1 month ago

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!

MarkSackerberg commented 1 month ago

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.

0xlucyy commented 1 month ago

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.

MarkSackerberg commented 1 month ago

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?

0xlucyy commented 1 month ago

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.

cd11103 commented 2 days ago

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.

rogaldh commented 1 day ago

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.