Selaa lähdekoodia

Merge pull request #331 from metaplex-foundation/token-metadata/anchor-examples

Added some Anchor examples to TM burn and update.
Tony Boyle 7 kuukautta sitten
vanhempi
sitoutus
fee4c5cd40
3 muutettua tiedostoa jossa 676 lisäystä ja 34 poistoa
  1. 267 25
      src/pages/token-metadata/burn.md
  2. 10 7
      src/pages/token-metadata/mint.md
  3. 399 2
      src/pages/token-metadata/update.md

+ 267 - 25
src/pages/token-metadata/burn.md

@@ -26,7 +26,7 @@ Here is how you can use our SDKs to burn an asset on Token Metadata.
 ## NFT Burn
 
 {% dialect-switcher title="NFT Asset Burn" %}
-{% dialect title="JavaScript" id="js" %}
+{% dialect title="JavaScript - Umi" id="js" %}
 
 ```ts
 import { burnV1 } from '@metaplex-foundation/mpl-token-metadata'
@@ -36,27 +36,130 @@ await burnV1(umi, {
   authority: owner,
   tokenOwner: owner.publicKey,
   tokenStandard: TokenStandard.NonFungible,
+  // if your NFT is part of a collection you will also need to pass in the collection metadata address.
+  collectionMetadata: findMetadataPda( umi, { mint: collectionMintAddress })
 }).sendAndConfirm(umi)
 ```
 
 {% /dialect %}
-{% /dialect-switcher %}
 
-If the asset that you are trying to burn is part of a collection you additionally need to pass the collectionMetadata account into the function:
+{% dialect title="Rust MPL SDK - CPI" id="rust-metaplex-cpi" %}
 
-{% dialect-switcher title="Burning NFT Assets that are part of a collection" %}
-{% dialect title="JavaScript" id="js" %}
+```rust
+use mpl_token_metadata::instructions::BurnNftCpiBuilder;
 
-```ts
-import { burnV1, findMetadataPda } from '@metaplex-foundation/mpl-token-metadata'
+ BurnNftCpiBuilder::new(&metadata_program_id)
+    .metadata(&metadata)
+    // if your NFT is part of a collection you will need to pass in the collection metadata address.
+    .collection_metadata(collection_metadata.as_ref())
+    .owner(&owner)
+    .mint(&mint)
+    .token_account(&token)
+    .master_edition_account(&edition)
+    .spl_token_program(&spl_token)
+    .invoke()?;
+```
 
-await burnV1(umi, {
-  mint,
-  authority: owner,
-  tokenOwner: owner.publicKey,
-  collectionMetadata: findMetadataPda( umi, { mint: collectionMintAddress })
-  tokenStandard: TokenStandard.NonFungible,
-}).sendAndConfirm(umi)
+{% /dialect %}
+
+{% dialect title="Anchor - mpl-token-metadata" id="rs-anchor-mpl-token-metadata" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::token::Mint;
+use mpl_token_metadata::instructions::BurnNftCpiBuilder;
+
+#[derive(Accounts)]
+pub struct NftBurnMpl<'info> {
+    #[account(mut)]
+    owner: Signer<'info>,
+    #[account(mut)]
+    mint: Account<'info, Mint>,
+    #[account(mut)]
+    metadata: AccountInfo<'info>,
+    #[account(mut)]
+    token: AccountInfo<'info>,
+    #[account(mut)]
+    edition: AccountInfo<'info>,
+    collection_metadata: Option<AccountInfo<'info>>,
+    spl_token: AccountInfo<'info>,
+    metadata_program_id: AccountInfo<'info>,
+}
+
+pub fn burn_nft_mpl_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, NftBurnMpl<'info>>,
+) -> Result<()> {
+    let owner = ctx.accounts.owner.to_account_info();
+    let metadata = ctx.accounts.metadata.to_account_info();
+    let collection_metadata = ctx.accounts.collection_metadata.as_ref().map(|a| a.to_account_info());
+    let mint = ctx.accounts.mint.to_account_info();
+    let token = ctx.accounts.token.to_account_info();
+    let edition = ctx.accounts.edition.to_account_info();
+    let spl_token = ctx.accounts.spl_token.to_account_info();
+    let metadata_program_id = ctx.accounts.metadata_program_id.to_account_info();
+
+    BurnNftCpiBuilder::new(&metadata_program_id)
+        .metadata(&metadata)
+        // if your NFT is part of a collection you will also need to pass in the collection metadata address.
+        .collection_metadata(collection_metadata.as_ref())
+        .owner(&owner)
+        .mint(&mint)
+        .token_account(&token)
+        .master_edition_account(&edition)
+        .spl_token_program(&spl_token)
+        .invoke()?;
+
+    Ok(())
+}
+```
+{% /dialect %}
+
+{% dialect title="Anchor - anchor-spl 0.31.0" id="rs-anchor-anchor-spl" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::{metadata::BurnNft, token::Mint};
+
+
+#[derive(Accounts)]
+pub struct NftBurn<'info> {
+    #[account(mut)]
+    owner: Signer<'info>,
+    #[account(mut)]
+    mint: Account<'info, Mint>,
+    #[account(mut)]
+    metadata: AccountInfo<'info>,
+    #[account(mut)]
+    token: AccountInfo<'info>,
+    #[account(mut)]
+    edition: AccountInfo<'info>,
+    spl_token: AccountInfo<'info>,
+    metadata_program_id: AccountInfo<'info>,
+}
+
+pub fn burn_nft_instruction(ctx: Context<NftBurn>) {
+
+        let owner = ctx.accounts.owner.to_account_info();
+        let metadata = ctx.accounts.metadata.to_account_info();
+        let mint = ctx.accounts.mint.to_account_info();
+        let token = ctx.accounts.token.to_account_info();
+        let edition = ctx.accounts.edition.to_account_info();
+        let spl_token = ctx.accounts.spl_token.to_account_info();
+        let metadata_program_id = ctx.accounts.metadata_program_id.to_account_info();
+
+        CpiContext::new(
+            metadata_program_id,
+            BurnNft {
+                metadata,
+                owner,
+                mint,
+                token,
+                edition,
+                spl_token,
+            },
+        );
+
+}
 ```
 
 {% /dialect %}
@@ -64,8 +167,17 @@ await burnV1(umi, {
 
 ## pNFT Burn
 
+#### Additional Accounts
+
+`pNFTs` may require additional accounts to be passed in for the instruction to work. These may include:
+
+- tokenAccount
+- tokenRecord
+- authorizationRules
+- authorizationRulesProgram
+
 {% dialect-switcher title="pNFT Asset Burn" %}
-{% dialect title="JavaScript" id="js" %}
+{% dialect title="JavaScript - Umi" id="js-umi" %}
 
 ```ts
 import {
@@ -73,27 +185,27 @@ import {
   fetchDigitalAssetWithAssociatedToken,
   findMetadataPda,
   TokenStandard,
-} from "@metaplex-foundation/mpl-token-metadata";
-import { publicKey, unwrapOption } from "@metaplex-foundation/umi";
-import { base58 } from "@metaplex-foundation/umi/serializers";
+} from '@metaplex-foundation/mpl-token-metadata'
+import { publicKey, unwrapOption } from '@metaplex-foundation/umi'
+import { base58 } from '@metaplex-foundation/umi/serializers'
 
 // The pNFT mint ID
-const mintId = publicKey("11111111111111111111111111111111");
+const mintId = publicKey('11111111111111111111111111111111')
 
 // Fetch the pNFT Asset with the Token Account
 const assetWithToken = await fetchDigitalAssetWithAssociatedToken(
   umi,
   mintId,
   umi.identity.publicKey
-);
+)
 
 // Determine if the pNFT Asset is in a collection
-const collectionMint = unwrapOption(assetWithToken.metadata.collection);
+const collectionMint = unwrapOption(assetWithToken.metadata.collection)
 
 // If there's a collection find the collection metadata PDAs
 const collectionMetadata = collectionMint
   ? findMetadataPda(umi, { mint: collectionMint.key })
-  : null;
+  : null
 
 // Burn the pNFT Asset
 const res = await burnV1(umi, {
@@ -102,12 +214,142 @@ const res = await burnV1(umi, {
   token: assetWithToken.token.publicKey,
   tokenRecord: assetWithToken.tokenRecord?.publicKey,
   tokenStandard: TokenStandard.ProgrammableNonFungible,
-}).sendAndConfirm(umi);
-
+}).sendAndConfirm(umi)
 
-const signature = base58.deserialize(tx.signature)[0];
+const signature = base58.deserialize(tx.signature)[0]
 console.log('Transaction Signature: ' + signature)
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust MPL SDK - CPI" id="rust-metaplex-cpi" %}
+
+```rust
+use mpl_token_metadata::instructions::BurnNftCpiBuilder;
+
+BurnNftCpiBuilder::new(metadata_program_id.account_info())
+        .metadata(metadata.account_info())
+        .collection_metadata(Some(collection_metadata.account_info()))
+        .owner(owner.account_info())
+        .mint(mint.account_info())
+        .token_account(token.account_info())
+        .master_edition_account(edition.account_info())
+        .spl_token_program(spl_token.account_info())
+        .invoke()?;
+```
+
+{% /dialect %}
+
+{% dialect title="Anchor - mpl-token-metadata" id="rs-anchor-mpl-token-metadata" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::token::Mint;
+use mpl_token_metadata::instructions::BurnV1CpiBuilder;
+
+#[derive(Accounts)]
+pub struct PnftBurnMpl<'info> {
+    #[account(mut)]
+    owner: Signer<'info>,
+    #[account(mut)]
+    mint: Account<'info, Mint>,
+    #[account(mut)]
+    metadata: AccountInfo<'info>,
+    #[account(mut)]
+    token: AccountInfo<'info>,
+    #[account(mut)]
+    master_edition: AccountInfo<'info>,
+    #[account(mut)]
+    token_record: AccountInfo<'info>,
+    collection_metadata: Option<AccountInfo<'info>>,
+    spl_token: AccountInfo<'info>,
+    metadata_program_id: AccountInfo<'info>,
+}
+
+pub fn burn_pnft_mpl_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, PnftBurnMpl<'info>>,
+) -> Result<()> {
+let owner = ctx.accounts.owner.to_account_info();
+let metadata = ctx.accounts.metadata.to_account_info();
+let mint = ctx.accounts.mint.to_account_info();
+let token = ctx.accounts.token.to_account_info();
+let master_edition = ctx.accounts.master_edition.to_account_info();
+let collection_metadata = ctx
+    .accounts
+    .collection_metadata
+    .as_ref()
+    .map(|a| a.to_account_info());
+let spl_token = ctx.accounts.spl_token.to_account_info();
+let token_record = ctx.accounts.token_record.to_account_info();
+let metadata_program_id = ctx.accounts.metadata_program_id.to_account_info();
+
+BurnV1CpiBuilder::new(&metadata_program_id)
+    .metadata(&metadata)
+    .collection_metadata(collection_metadata.as_ref())
+    .authority(&owner)
+    .mint(&mint)
+    .token(&token)
+    .spl_token_program(&spl_token)
+    .token_record(Some(&token_record))
+    .master_edition(Some(&master_edition)) 
+    .invoke()?;
+
+Ok(())
+}
+```
+
+{% /dialect %}
+
+{% dialect title="Anchor - anchor-spl 0.31.0" id="rs-anchor-anchor-spl" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::{metadata::{BurnNft, burn_nft}, token::Mint};
+
+#[derive(Accounts)]
+pub struct PnftBurn<'info> {
+    #[account(mut)]
+    pub owner: Signer<'info>,
+    #[account(mut)]
+    pub mint: Account<'info, Mint>,
+    #[account(mut)]
+    pub metadata: AccountInfo<'info>,
+    #[account(mut)]
+    pub token: AccountInfo<'info>,
+    #[account(mut)]
+    pub edition: AccountInfo<'info>,
+    pub spl_token: AccountInfo<'info>,
+    pub metadata_program_id: AccountInfo<'info>,
+    /// CHECK: Optional collection metadata
+    #[account(mut)]
+    pub collection_metadata: Option<AccountInfo<'info>>,
+}
+
+pub fn burn_pnft_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, PnftBurn<'info>>,
+) {
+    // Create the accounts struct
+    let cpi_accounts = BurnNft {
+        metadata: ctx.accounts.metadata.clone(),
+        owner: ctx.accounts.owner.to_account_info().clone(),
+        mint: ctx.accounts.mint.to_account_info().clone(),
+        token: ctx.accounts.token.clone(),
+        edition: ctx.accounts.edition.clone(),
+        spl_token: ctx.accounts.spl_token.clone(),
+    };
+    
+    // Create CPI context
+    let cpi_ctx = CpiContext::new(
+        ctx.accounts.metadata_program_id.clone(),
+        cpi_accounts,
+    ).with_remaining_accounts(ctx.remaining_accounts.to_vec());
+    
+    // Get collection metadata pubkey if it exists
+    let collection_metadata = ctx.accounts.collection_metadata.as_ref().map(|a| a.key());
+    
+    // Execute the CPI
+    burn_nft(cpi_ctx, collection_metadata).expect("Failed to burn PNFT");
+}
+```
 {% /dialect %}
 {% /dialect-switcher %}

+ 10 - 7
src/pages/token-metadata/mint.md

@@ -142,12 +142,11 @@ This instruction accepts a variety of parameters and our SDKs do their best to p
 - **Token Standard**: The Token Standard of the asset.
 
 {% callout %}
-`createV1` is a helper function that can initialize the Mint Account and create the Metadata Account. If the mint exists already it will only create the metadata Account. If you are looking on how to use [`createMetadataAccountV3`](https://mpl-token-metadata-js-docs.vercel.app/functions/createMetadataAccountV3.html) you should be using this function instead.
-
+`createV1` is a helper function that can initialize the Mint Account and create the Metadata Account. If the mint exists already it will only create the metadata Account. If you are looking for how to use [`createMetadataAccountV3`](https://mpl-token-metadata-js-docs.vercel.app/functions/createMetadataAccountV3.html) you should be using this function instead.
 {% /callout %}
 
 {% dialect-switcher title="Create onchain Accounts" %}
-{% dialect title="JavaScript" id="js" %}
+{% dialect title="JavaScript - Umi" id="js-umi" %}
 
 ```ts
 import { generateSigner, percentAmount } from '@metaplex-foundation/umi'
@@ -169,7 +168,7 @@ await createV1(umi, {
 
 {% /dialect %}
 
-{% dialect title="Rust" id="rust" %}
+{% dialect title="Rust Script" id="rust-script" %}
 {% totem %}
 
 ```rust
@@ -214,7 +213,7 @@ client.send_and_confirm_transaction(&tx)?;
 
 {% totem-prose %}
 
-Note that when setting the `mint` account, it is require to specify a `bool` flag to indicate whether the account will be a signer or not – it need to be a signer if the `mint` account does not exist.
+Note that when setting the `mint` account, it is required to specify a `bool` flag to indicate whether the account will be a signer or not – it needs to be a signer if the `mint` account does not exist.
 
 {% /totem-prose %}
 
@@ -222,7 +221,7 @@ Note that when setting the `mint` account, it is require to specify a `bool` fla
 
 {% /dialect %}
 
-{% dialect title="Rust (CPI)" id="rust-cpi" %}
+{% dialect title="Rust MPL SDK - CPI" id="rust-cpi" %}
 
 ```rust
 use mpl_token_metadata::{
@@ -242,7 +241,7 @@ let create_cpi = CreateV1CpiBuilder::new(token_metadata_program_info)
     .master_edition(Some(master_edition_info))
     .system_program(system_program_info)
     .sysvar_instructions(sysvar_instructions_info)
-    .spl_token_program(spl_token_program_info);
+    .spl_token_program(spl_token_program_info)
     .token_standard(TokenStandard::NonFungible)
     .name(String::from("My NFT"))
     .uri(uri)
@@ -439,5 +438,9 @@ await createProgrammableNft(umi, {
 
 {% /totem-accordion  %}
 
+{% /dialect %}
+
+{% dialect title="Rust" id="rust" %}
+<!-- Rust helper examples are not provided in this version of the documentation -->
 {% /dialect %}
 {% /dialect-switcher %}

+ 399 - 2
src/pages/token-metadata/update.md

@@ -16,6 +16,9 @@ Below is an explanation of all the individual fields available for update in the
 
 The object that defines the Name, Symbol, URI, Seller Fee Basis Points and the array of Creators of the asset. Note that the update authority can only add and/or remove unverified creators from the Creators array. The only exception is if the creator is the update authority, in which case the added or removed creators can be verified.
 
+{% dialect-switcher title="Data Object" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 const data = {
   name: 'New Name',
@@ -26,28 +29,79 @@ const data = {
 }
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+pub struct DataV2 {
+    pub name: String,
+    pub symbol: String,
+    pub uri: String,
+    pub seller_fee_basis_points: u16,
+    pub creators: Option<Vec<Creator>>,
+    pub collection: Option<Collection>,
+    pub uses: Option<Uses>,
+}
+```
+
+{% /dialect %}
+
+{% /dialect-switcher %}
+
 ### Primary Sale Happened
 
 Primary Sale Happened: A boolean that indicates whether the asset has been sold before.
 
+{% dialect-switcher title="Primary Sale Happened" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 primarySaleHappened: true
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+primary_sale_happened: Option<bool>,
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 ### Is Mutable
 
 A boolean that indicates whether the asset can be updated again. When changing this to false, any future updates will fail.
 
+{% dialect-switcher title="Is Mutable" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 isMutable: true
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+is_mutable: Option<bool>,
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 ### Collection
 
-Collection: This attribute enables us to set or clear the collection of the asset. Note that when setting a new collection, the verified boolean must be set to false and [verified using another instruction](/token-metadata/collections).
+This attribute enables us to set or clear the collection of the asset. Note that when setting a new collection, the verified boolean must be set to false and [verified using another instruction](/token-metadata/collections).
 
 #### Setting A Collection
 
+{% dialect-switcher title="Setting A Collection" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 collection: collectionToggle('Set', [
   {
@@ -57,28 +111,84 @@ collection: collectionToggle('Set', [
 ])
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+collection: Some( Collection {
+  key: PubKey,
+  verified: Boolean,
+}),
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 #### Clearing a Collection
 
+{% dialect-switcher title="Clearing a Collection" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 collection: collectionToggle("Clear"),
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+collection: None,
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 ### New Update Authority
 
 A new update authority can be assigned to an Asset by passing in the `newUpdateAuthority` field.
 
+{% dialect-switcher title="New Update Authority" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
 newUpdateAuthority: publicKey('1111111111111111111111111111111')
 ```
 
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+new_update_authority: Option<PubKey>,
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 ### Programable RuleSets
 
 This attribute enables us to set or clear the rule set of the asset. This is only relevant for [Programmable Non-Fungibles](/token-metadata/pnfts).
 
+{% dialect-switcher title="Programable RuleSets" %}
+{% dialect title="JavaScript" id="js" %}
+
 ```ts
-newUpdateAuthority: publicKey('1111111111111111111111111111111')
+ruleSet: publicKey('1111111111111111111111111111111')
+```
+
+{% /dialect %}
+
+{% dialect title="Rust - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+// Not available in Rust anchor-spl SDK
 ```
 
+{% /dialect %}
+{% /dialect-switcher %}
+
 Here is how you can use our SDKs to update an asset on Token Metadata.
 
 ## Update As Update Authority
@@ -125,6 +235,134 @@ await updateV1(umi, {
 ```
 
 {% /totem %}
+
+{% /dialect %}
+
+{% dialect title="Anchor - mpl-token-metadata" id="rust-anchor-mpl-token-metadata" %}
+
+```rust
+use anchor_lang::prelude::*;
+use mpl_token_metadata::{
+    accounts::Metadata,
+    instructions::UpdateAsUpdateAuthorityV2CpiBuilder, types::Data,
+};
+
+#[derive(Accounts)]
+pub struct NftUpdateMpl<'info> {
+    pub mint: AccountInfo<'info>,
+    /// CHECK: Handled by CPI
+    #[account(mut)]
+    pub metadata: AccountInfo<'info>,
+    #[account(mut)]
+    pub update_authority: Signer<'info>,
+    /// CHECK: Handled by CPI
+    pub token_metadata_program: AccountInfo<'info>,
+}
+
+pub fn update_nft_mpl_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, NftUpdateMpl<'info>>,
+    new_name: Option<String>,
+    new_uri: Option<String>,
+) -> Result<()> {
+    let mint = ctx.accounts.mint.to_account_info();
+    let metadata = ctx.accounts.metadata.to_account_info();
+    let token_metadata_program = ctx.accounts.token_metadata_program.to_account_info();
+
+    // Get the original metadata values
+    let metadata_account = Metadata::try_from(&metadata)?;
+
+    let original_metadata = Data {
+        name: metadata_account.name,
+        symbol: metadata_account.symbol,
+        uri: metadata_account.uri,
+        seller_fee_basis_points: metadata_account.seller_fee_basis_points,
+        creators: metadata_account.creators,
+    };
+
+    let new_metadata = Data {
+        name: new_name.unwrap_or(original_metadata.name),
+        uri: new_uri.unwrap_or(original_metadata.uri),
+        ..original_metadata // Keep the rest of the metadata the same
+    };
+
+    UpdateAsUpdateAuthorityV2CpiBuilder::new(&token_metadata_program)
+        .mint(&mint)
+        .metadata(&metadata)
+        .authority(&ctx.accounts.update_authority)
+        .data(new_metadata)
+        // Add remaining data fields/accounts to be adjusted to the CPI if needed
+        // https://docs.rs/mpl-token-metadata/latest/mpl_token_metadata/instructions/struct.UpdateAsUpdateAuthorityV2CpiBuilder.html
+        .invoke()?;
+
+    Ok(())
+}
+```
+
+{% /dialect %}
+
+{% dialect title="Anchor - anchor-spl 0.31.0" id="rust-anchor" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::{
+    metadata::{mpl_token_metadata::types::DataV2, update_metadata_accounts_v2, MetadataAccount, UpdateMetadataAccountsV2},
+    token::Mint,
+};
+
+#[derive(Accounts)]
+pub struct UpdateNft<'info> {
+    #[account(mut)]
+    pub mint: Account<'info, Mint>,
+    /// CHECK: Handled by CPI
+    #[account(mut)]
+    pub metadata: Account<'info, MetadataAccount>,
+    #[account(mut)]
+    pub update_authority: Signer<'info>,
+    /// CHECK: Handled by CPI
+    pub token_metadata_program: AccountInfo<'info>,
+}
+
+pub fn update_nft_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, UpdateNft<'info>>,
+    new_name: Option<String>,
+    new_uri: Option<String>,
+) {
+    let cpi_accounts = UpdateMetadataAccountsV2 {
+        metadata: ctx.accounts.metadata.to_account_info().clone(),
+        update_authority: ctx.accounts.update_authority.to_account_info().clone(),
+    };
+
+    let cpi_ctx = CpiContext::new(
+        ctx.accounts.token_metadata_program.clone(),
+        cpi_accounts,
+    );
+
+    let original_metadata = DataV2 {
+        name: ctx.accounts.metadata.name.clone(),
+        symbol: ctx.accounts.metadata.symbol.clone(),
+        uri: ctx.accounts.metadata.uri.clone(),
+        seller_fee_basis_points: ctx.accounts.metadata.seller_fee_basis_points,
+        creators: ctx.accounts.metadata.creators.clone(),
+        collection: ctx.accounts.metadata.collection.clone(),
+        uses: ctx.accounts.metadata.uses.clone(),
+    };
+
+    let new_metadata = DataV2 {
+        name: new_name.clone().unwrap_or(original_metadata.name),
+        uri: new_uri.clone().unwrap_or(original_metadata.uri),
+        ..original_metadata
+    };
+
+    update_metadata_accounts_v2(
+        cpi_ctx,
+        None, // New update authority
+        Some(new_metadata), // Data
+        None, // Primary sale happened
+        None, // Is mutable
+    ).expect("Failed to update NFT metadata");
+}
+```
+
 {% /dialect %}
 {% /dialect-switcher %}
 
@@ -132,6 +370,15 @@ await updateV1(umi, {
 
 This example shows you how to update a Programable NFT (pNFT) Asset as the update Authority of the Asset.
 
+#### Additional Accounts
+
+`pNFTs` may require additional accounts to be passed in for the instruction to work. These include:
+
+- tokenAccount
+- tokenRecord
+- authorizationRules
+- authorizationRulesProgram
+
 {% dialect-switcher title="pNFT Asset Update" %}
 {% dialect title="JavaScript" id="js" %}
 {% totem %}
@@ -178,5 +425,155 @@ const txRes = await updateAsUpdateAuthorityV2(umi, {
 ```
 
 {% /totem %}
+{% /dialect %}
+
+{% dialect title="Anchor - mpl-token-metadata" id="rust-anchor-mpl-token-metadata" %}
+
+```rust
+use anchor_lang::prelude::*;
+use mpl_token_metadata::{
+    accounts::Metadata,
+    instructions::UpdateAsUpdateAuthorityV2CpiBuilder, types::Data,
+};
+
+#[derive(Accounts)]
+pub struct NftUpdateMpl<'info> {
+    pub mint: AccountInfo<'info>,
+    /// CHECK: Handled by CPI
+    #[account(mut)]
+    pub metadata: AccountInfo<'info>,
+    #[account(mut)]
+    pub update_authority: Signer<'info>,
+    /// CHECK: Handled by CPI
+    pub token_metadata_program: AccountInfo<'info>,
+    // Add additional accounts below if needed
+}
+
+pub fn update_nft_mpl_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, NftUpdateMpl<'info>>,
+    new_name: Option<String>,
+    new_uri: Option<String>,
+) -> Result<()> {
+    let mint = ctx.accounts.mint.to_account_info();
+    let metadata = ctx.accounts.metadata.to_account_info();
+    let token_metadata_program = ctx.accounts.token_metadata_program.to_account_info();
+
+    // Get the original metadata values
+    let metadata_account = Metadata::try_from(&metadata)?;
+
+    let original_metadata = Data {
+        name: metadata_account.name,
+        symbol: metadata_account.symbol,
+        uri: metadata_account.uri,
+        seller_fee_basis_points: metadata_account.seller_fee_basis_points,
+        creators: metadata_account.creators,
+    };
+
+    let new_metadata = Data {
+        name: new_name.unwrap_or(original_metadata.name),
+        uri: new_uri.unwrap_or(original_metadata.uri),
+        ..original_metadata // Keep the rest of the metadata the same
+    };
+
+    UpdateAsUpdateAuthorityV2CpiBuilder::new(&token_metadata_program)
+        .mint(&mint)
+        .metadata(&metadata)
+        .authority(&ctx.accounts.update_authority)
+        .data(new_metadata)
+        
+        // Add remaining data fields to be adjusted to the CPI if needed
+        // https://docs.rs/mpl-token-metadata/latest/mpl_token_metadata/instructions/struct.UpdateAsUpdateAuthorityV2CpiBuilder.html       
+        //
+        // .authorization_rules(authorization_rules)
+        // .authorization_rules_program(authorization_rules_program)
+        // .token_record(token_record)
+        .invoke()?;
+
+    Ok(())
+}
+
+```
+
+{% /dialect %}
+
+{% dialect title="Anchor - anchor-spl 0.31.0" id="rust-anchor-anchor-spl" %}
+
+```rust
+use anchor_lang::prelude::*;
+use anchor_spl::{
+    metadata::{
+        mpl_token_metadata::types::DataV2, update_metadata_accounts_v2, MetadataAccount,
+        UpdateMetadataAccountsV2,
+    },
+    token::Mint,
+};
+
+#[derive(Accounts)]
+pub struct UpdatePnft<'info> {
+    #[account(mut)]
+    pub mint: Account<'info, Mint>,
+    /// CHECK: Handled by CPI
+    #[account(mut)]
+    pub metadata: Account<'info, MetadataAccount>,
+    #[account(mut)]
+    pub update_authority: Signer<'info>,
+    /// CHECK: Handled by CPI
+    pub token_metadata_program: AccountInfo<'info>,
+    /// CHECK: Optional collection metadata
+    #[account(mut)]
+    pub collection_metadata: Option<AccountInfo<'info>>,
+}
+
+pub fn update_pnft_instruction<'info>(
+    ctx: Context<'_, '_, '_, 'info, UpdatePnft<'info>>,
+    new_name: Option<String>,
+    new_uri: Option<String>,
+) {
+    let cpi_accounts = UpdateMetadataAccountsV2 {
+        metadata: ctx.accounts.metadata.to_account_info().clone(),
+        update_authority: ctx.accounts.update_authority.to_account_info().clone(),
+    };
+
+    let remaining_accounts: Vec<AccountInfo> = ctx
+        .remaining_accounts
+        .iter()
+        .map(|a| (*a).clone())
+        .collect();
+
+    // Create CPI context
+    let cpi_ctx = CpiContext::new(ctx.accounts.token_metadata_program.clone(), cpi_accounts)
+        // The two remaining accounts to include (if Token Authorization Rules are used) are:
+        // Token Authorization Rules Program
+        // Token Authorization Rules account
+        .with_remaining_accounts(remaining_accounts);
+
+    let original_metadata = DataV2 {
+        name: ctx.accounts.metadata.name.clone(),
+        symbol: ctx.accounts.metadata.symbol.clone(),
+        uri: ctx.accounts.metadata.uri.clone(),
+        seller_fee_basis_points: ctx.accounts.metadata.seller_fee_basis_points,
+        creators: ctx.accounts.metadata.creators.clone(),
+        collection: ctx.accounts.metadata.collection.clone(),
+        uses: ctx.accounts.metadata.uses.clone(),
+    };
+
+    let new_metadata = DataV2 {
+        name: new_name.clone().unwrap_or(original_metadata.name),
+        uri: new_uri.clone().unwrap_or(original_metadata.uri),
+        ..original_metadata
+    };
+
+    // Update metadata for the NFT - correct parameter order
+    update_metadata_accounts_v2(
+        cpi_ctx,
+        None,               // New update authority
+        Some(new_metadata), // Data
+        None,               // Primary sale happened
+        None,               // Is mutable
+    )
+    .expect("Failed to update PNFT metadata");
+}
+```
+
 {% /dialect %}
 {% /dialect-switcher %}