Browse Source

add update core plugin examples and page (#385)

* add update plugin examples and page

* remove update section

* add to menu bar

* update page

* fix code examples

* Apply suggestions from code review

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/pages/core/plugins/transfer-delegate.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
MarkSackerberg 2 months ago
parent
commit
bac7909300

+ 4 - 0
src/components/products/core/index.js

@@ -119,6 +119,10 @@ export const core = {
               title: 'Adding Plugins',
               title: 'Adding Plugins',
               href: '/core/plugins/adding-plugins',
               href: '/core/plugins/adding-plugins',
             },
             },
+            {
+              title: 'Updating Plugins',
+              href: '/core/plugins/update-plugins',
+            },
             {
             {
               title: 'Removing Plugins',
               title: 'Removing Plugins',
               href: '/core/plugins/removing-plugins',
               href: '/core/plugins/removing-plugins',

+ 59 - 6
src/pages/core/plugins/burn-delegate.md

@@ -30,12 +30,14 @@ The Burn Plugin doesn't contain any arguments to pass in.
 import { publicKey } from '@metaplex-foundation/umi'
 import { publicKey } from '@metaplex-foundation/umi'
 import { addPlugin } from '@metaplex-foundation/mpl-core'
 import { addPlugin } from '@metaplex-foundation/mpl-core'
 
 
-const asset = publicKey('11111111111111111111111111111111')
-
-await addPlugin(umi, {
-  asset: asset,
-  plugin: { type: 'BurnDelegate' },
-}).sendAndConfirm(umi)
+(async () => {
+    const asset = publicKey('11111111111111111111111111111111')
+
+    await addPlugin(umi, {
+    asset: asset,
+    plugin: { type: 'BurnDelegate' },
+    }).sendAndConfirm(umi)
+})();
 ```
 ```
 
 
 {% /dialect %}
 {% /dialect %}
@@ -85,3 +87,54 @@ pub async fn add_burn_delegate_plugin() {
 
 
 {% /dialect %}
 {% /dialect %}
 {% /dialect-switcher %}
 {% /dialect-switcher %}
+
+## Delegating Burn Authority
+
+The Burn Delegate plugin authority can be delegated to a different address using the `approvePluginAuthority` function. This allows you to change who can burn the Asset.
+
+{% dialect-switcher title="Delegate Burn Authority" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { approvePluginAuthority } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+    const assetAddress = publicKey('11111111111111111111111111111111')
+    const newDelegate = publicKey('22222222222222222222222222222222')
+
+    await approvePluginAuthority(umi, {
+    asset: assetAddress,
+    plugin: { type: 'BurnDelegate' },
+    newAuthority: { type: 'Address', address: newDelegate },
+    }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+
+{% /dialect-switcher %}
+
+## Revoking Burn Authority
+
+The burn authority can be revoked using the `revokePluginAuthority` function, returning control to the asset owner.
+
+{% dialect-switcher title="Revoke Burn Authority" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { revokePluginAuthority } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+    const assetAddress = publicKey('11111111111111111111111111111111')
+
+    await revokePluginAuthority(umi, {
+    asset: assetAddress,
+    plugin: { type: 'BurnDelegate' },
+    }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}

+ 4 - 0
src/pages/core/plugins/freeze-delegate.md

@@ -199,6 +199,10 @@ pub async fn approve_plugin_authority() {
 {% /dialect %}
 {% /dialect %}
 {% /dialect-switcher %}
 {% /dialect-switcher %}
 
 
+## Updating the Freeze Delegate Plugin
+
+The Freeze Delegate Plugin can be updated to change the frozen state of the asset. This is the same as using the [Freezing an Asset](#freezing-an-asset) and [Thawing a Frozen Asset](#thawing-a-frozen-asset) functions shown below.
+
 ### Freezing an Asset
 ### Freezing an Asset
 
 
 The `freezeAsset` command freezes an Asset, preventing it from being transferred or burned. This is useful for escrowless staking or marketplace listings.
 The `freezeAsset` command freezes an Asset, preventing it from being transferred or burned. This is useful for escrowless staking or marketplace listings.

+ 189 - 0
src/pages/core/plugins/royalties.md

@@ -72,6 +72,195 @@ let creators = vec![
 
 
 {% /dialect-switcher %}
 {% /dialect-switcher %}
 
 
+## Updating the Royalties Plugin on an Asset
+
+The Royalties Plugin can be updated to modify basis points, creators, or rulesets for an existing Asset.
+
+{% dialect-switcher title="Update Royalties Plugin on Asset" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin, ruleSet } from '@metaplex-foundation/mpl-core'
+
+const assetAddress = publicKey('11111111111111111111111111111111')
+const creator1 = publicKey('11111111111111111111111111111111')
+const creator2 = publicKey('2222222222222222222222222222222')
+
+await updatePlugin(umi, {
+  asset: assetAddress,
+  plugin: {
+    type: 'Royalties',
+    basisPoints: 750, // Updated from 500 to 750 (7.5%)
+    creators: [
+      { address: creator1, percentage: 60 }, // Updated distribution
+      { address: creator2, percentage: 40 },
+    ],
+    ruleSet: ruleSet('ProgramAllowList', [
+      [
+        publicKey('66666666666666666666666666666666'), // Updated ruleset
+        publicKey('77777777777777777777777777777777'),
+      ],
+    ]),
+  },
+}).sendAndConfirm(umi)
+```
+
+{% /dialect %}
+
+{% dialect title="Rust" id="rust" %}
+
+```rust
+use mpl_core::{
+    instructions::UpdatePluginV1Builder,
+    types::{Creator, Plugin, Royalties, RuleSet},
+};
+use solana_client::nonblocking::rpc_client;
+use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
+use std::str::FromStr;
+
+pub async fn update_royalties_plugin() {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com".to_string());
+
+    let authority = Keypair::new();
+    let asset = Pubkey::from_str("11111111111111111111111111111111").unwrap();
+
+    let creator1 = Pubkey::from_str("22222222222222222222222222222222").unwrap();
+    let creator2 = Pubkey::from_str("33333333333333333333333333333333").unwrap();
+
+    let update_royalties_plugin_ix = UpdatePluginV1Builder::new()
+        .asset(asset)
+        .payer(authority.pubkey())
+        .plugin(Plugin::Royalties(Royalties {
+            basis_points: 750, // Updated from 500 to 750 (7.5%)
+            creators: vec![
+                Creator {
+                    address: creator1,
+                    percentage: 60, // Updated distribution
+                },
+                Creator {
+                    address: creator2,
+                    percentage: 40,
+                },
+            ],
+            rule_set: RuleSet::None,
+        }))
+        .instruction();
+
+    let signers = vec![&authority];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await.unwrap();
+
+    let update_royalties_plugin_tx = Transaction::new_signed_with_payer(
+        &[update_royalties_plugin_ix],
+        Some(&authority.pubkey()),
+        &signers,
+        last_blockhash,
+    );
+
+    let res = rpc_client
+        .send_and_confirm_transaction(&update_royalties_plugin_tx)
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res)
+}
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Updating the Royalties Plugin on a Collection
+
+{% dialect-switcher title="Update Royalties Plugin on Collection" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updateCollectionPlugin, ruleSet } from '@metaplex-foundation/mpl-core'
+
+const collectionAddress = publicKey('11111111111111111111111111111111')
+const creator1 = publicKey('11111111111111111111111111111111')
+const creator2 = publicKey('2222222222222222222222222222222')
+
+await updateCollectionPlugin(umi, {
+  collection: collectionAddress,
+  plugin: {
+    type: 'Royalties',
+    basisPoints: 600, // Updated royalty percentage
+    creators: [
+      { address: creator1, percentage: 70 }, // Updated distribution
+      { address: creator2, percentage: 30 },
+    ],
+    ruleSet: ruleSet('None'),
+  },
+}).sendAndConfirm(umi)
+```
+
+{% /dialect %}
+
+{% dialect title="Rust" id="rust" %}
+
+```rust
+use mpl_core::{
+    instructions::UpdateCollectionPluginV1Builder,
+    types::{Creator, Plugin, Royalties, RuleSet},
+};
+use solana_client::nonblocking::rpc_client;
+use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
+use std::str::FromStr;
+
+pub async fn update_collection_royalties_plugin() {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com".to_string());
+
+    let authority = Keypair::new();
+    let collection = Pubkey::from_str("11111111111111111111111111111111").unwrap();
+
+    let creator1 = Pubkey::from_str("22222222222222222222222222222222").unwrap();
+    let creator2 = Pubkey::from_str("33333333333333333333333333333333").unwrap();
+
+    let update_royalties_plugin_ix = UpdateCollectionPluginV1Builder::new()
+        .collection(collection)
+        .payer(authority.pubkey())
+        .plugin(Plugin::Royalties(Royalties {
+            basis_points: 600, // Updated royalty percentage
+            creators: vec![
+                Creator {
+                    address: creator1,
+                    percentage: 70, // Updated distribution
+                },
+                Creator {
+                    address: creator2,
+                    percentage: 30,
+                },
+            ],
+            rule_set: RuleSet::None,
+        }))
+        .instruction();
+
+    let signers = vec![&authority];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await.unwrap();
+
+    let update_collection_royalties_plugin_tx = Transaction::new_signed_with_payer(
+        &[update_royalties_plugin_ix],
+        Some(&authority.pubkey()),
+        &signers,
+        last_blockhash,
+    );
+
+    let res = rpc_client
+        .send_and_confirm_transaction(&update_collection_royalties_plugin_tx)
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res)
+}
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
 ## RuleSets
 ## RuleSets
 
 
 RuleSets allow you to control what programs can or can not perform actions on the MPL Core Assets the Royalties plugin is assigned to.
 RuleSets allow you to control what programs can or can not perform actions on the MPL Core Assets the Royalties plugin is assigned to.

+ 60 - 1
src/pages/core/plugins/transfer-delegate.md

@@ -253,6 +253,65 @@ TransferV1CpiBuilder::new(&ctx.accounts.mpl_core_program.to_account_info())
     .invoke()?;
     .invoke()?;
 ```
 ```
 
 
-{% /dialect %}`
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Updating Transfer Delegate Authority
+
+Since the Transfer Delegate plugin doesn't contain plugin data to update (it's an empty object `{}`), the main "update" operation is changing the plugin authority. This allows you to delegate transfer permissions to different addresses.
+
+### Changing the Transfer Delegate Authority
+
+You can change who has transfer authority using the `approvePluginAuthority` function:
+
+{% dialect-switcher title="Update Transfer Delegate Authority" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { approvePluginAuthority } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+    const assetAddress = publicKey('11111111111111111111111111111111')
+    const newDelegate = publicKey('44444444444444444444444444444444')
+
+    // Change the transfer delegate to a new address
+    await approvePluginAuthority(umi, {
+    asset: assetAddress,
+    plugin: { type: 'TransferDelegate' },
+    newAuthority: { type: 'Address', address: newDelegate },
+    }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### Revoking Transfer Delegate Authority
+
+The transfer authority can be revoked using the `revokePluginAuthority` function, returning transfer control to the asset owner.
+
+{% dialect-switcher title="Revoke Transfer Delegate Authority" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { revokePluginAuthority } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+    const assetAddress = publicKey('11111111111111111111111111111111')
+(async () => {
+    const assetAddress = publicKey('11111111111111111111111111')
+
+    await revokePluginAuthority(umi, {
+
+    await revokePluginAuthority(umi, {
+    asset: assetAddress,
+    plugin: { type: 'TransferDelegate' },
+    }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
 {% /dialect-switcher %}
 {% /dialect-switcher %}
 
 

+ 152 - 0
src/pages/core/plugins/update-delegate.md

@@ -103,3 +103,155 @@ pub async fn add_update_delegate_plugin() {
 
 
 {% /dialect %}
 {% /dialect %}
 {% /dialect-switcher %}
 {% /dialect-switcher %}
+
+## Updating the Update Delegate Plugin
+
+The Update Delegate Plugin can be updated to modify the list of additional delegates or change the plugin authority.
+
+{% dialect-switcher title="Updating Update Delegate Plugin on Asset" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin } from '@metaplex-foundation/mpl-core'
+
+const assetAddress = publicKey('11111111111111111111111111111111')
+const newDelegate = publicKey('33333333333333333333333333333333')
+const existingDelegate = publicKey('22222222222222222222222222222222')
+
+await updatePlugin(umi, {
+  asset: assetAddress,
+  plugin: {
+    type: 'UpdateDelegate',
+    additionalDelegates: [existingDelegate, newDelegate], // Add or remove delegates
+  },
+}).sendAndConfirm(umi)
+```
+
+{% /dialect %}
+
+{% dialect title="Rust" id="rust" %}
+
+```rust
+use mpl_core::{
+    instructions::UpdatePluginV1Builder,
+    types::{Plugin, UpdateDelegate},
+};
+use solana_client::nonblocking::rpc_client;
+use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
+use std::str::FromStr;
+
+pub async fn update_update_delegate_plugin() {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com".to_string());
+
+    let authority = Keypair::new();
+    let asset = Pubkey::from_str("11111111111111111111111111111111").unwrap();
+    
+    let new_delegate = Pubkey::from_str("33333333333333333333333333333333").unwrap();
+    let existing_delegate = Pubkey::from_str("22222222222222222222222222222222").unwrap();
+
+    let update_update_delegate_plugin_ix = UpdatePluginV1Builder::new()
+        .asset(asset)
+        .payer(authority.pubkey())
+        .plugin(Plugin::UpdateDelegate(UpdateDelegate {
+            additional_delegates: vec![existing_delegate, new_delegate], // Add or remove delegates
+        }))
+        .instruction();
+
+    let signers = vec![&authority];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await.unwrap();
+
+    let update_update_delegate_plugin_tx = Transaction::new_signed_with_payer(
+        &[update_update_delegate_plugin_ix],
+        Some(&authority.pubkey()),
+        &signers,
+        last_blockhash,
+    );
+
+    let res = rpc_client
+        .send_and_confirm_transaction(&update_update_delegate_plugin_tx)
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res)
+}
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Updating Update Delegate Plugin on Collection
+
+{% dialect-switcher title="Updating Update Delegate Plugin on Collection" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updateCollectionPlugin } from '@metaplex-foundation/mpl-core'
+
+const collectionAddress = publicKey('11111111111111111111111111111111')
+const delegate1 = publicKey('22222222222222222222222222222222')
+const delegate2 = publicKey('33333333333333333333333333333333')
+
+await updateCollectionPlugin(umi, {
+  collection: collectionAddress,
+  plugin: {
+    type: 'UpdateDelegate',
+    additionalDelegates: [delegate1, delegate2], // Updated delegates list
+  },
+}).sendAndConfirm(umi)
+```
+
+{% /dialect %}
+
+{% dialect title="Rust" id="rust" %}
+
+```rust
+use mpl_core::{
+    instructions::UpdateCollectionPluginV1Builder,
+    types::{Plugin, UpdateDelegate},
+};
+use solana_client::nonblocking::rpc_client;
+use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
+use std::str::FromStr;
+
+pub async fn update_collection_update_delegate_plugin() {
+    let rpc_client = rpc_client::RpcClient::new("https://api.devnet.solana.com".to_string());
+
+    let authority = Keypair::new();
+    let collection = Pubkey::from_str("11111111111111111111111111111111").unwrap();
+    
+    let delegate1 = Pubkey::from_str("22222222222222222222222222222222").unwrap();
+    let delegate2 = Pubkey::from_str("33333333333333333333333333333333").unwrap();
+
+    let update_collection_update_delegate_plugin_ix = UpdateCollectionPluginV1Builder::new()
+        .collection(collection)
+        .payer(authority.pubkey())
+        .plugin(Plugin::UpdateDelegate(UpdateDelegate {
+            additional_delegates: vec![delegate1, delegate2], // Updated delegates list
+        }))
+        .instruction();
+
+    let signers = vec![&authority];
+
+    let last_blockhash = rpc_client.get_latest_blockhash().await.unwrap();
+
+    let update_collection_update_delegate_plugin_tx = Transaction::new_signed_with_payer(
+        &[update_collection_update_delegate_plugin_ix],
+        Some(&authority.pubkey()),
+        &signers,
+        last_blockhash,
+    );
+
+    let res = rpc_client
+        .send_and_confirm_transaction(&update_collection_update_delegate_plugin_tx)
+        .await
+        .unwrap();
+
+    println!("Signature: {:?}", res)
+}
+```
+
+{% /dialect %}
+{% /dialect-switcher %}

+ 298 - 0
src/pages/core/plugins/update-plugins.md

@@ -0,0 +1,298 @@
+---
+title: Updating Plugins
+metaTitle: Updating Plugins | Core
+description: Learn how to update existing plugins on MPL Core Assets and Collections using the updatePlugin function.
+---
+
+Many plugins on MPL Core Assets and Collections can be updated after they've been added. The `updatePlugin` function allows you to modify plugin data, such as changing attributes, updating royalties, or modifying freeze states.
+
+{% totem %}
+{% totem-accordion title="Technical Instruction Details" %}
+
+**Instruction Accounts List**
+
+| Account       | Description                                     |
+| ------------- | ----------------------------------------------- |
+| asset         | The address of the MPL Core Asset.              |
+| collection    | The collection to which the Core Asset belongs. |
+| payer         | The account paying for the storage fees.        |
+| authority     | The owner or delegate with update permissions.  |
+| systemProgram | The System Program account.                     |
+| logWrapper    | The SPL Noop Program.                           |
+
+**Instruction Arguments**
+
+| Args   | Description                            |
+| ------ | -------------------------------------- |
+| plugin | The plugin type and data to update. |
+
+Some of the accounts/args may be abstracted out and/or optional in our SDKs for ease of use.
+For detailed TypeDoc documentation, see:
+- [updatePlugin](https://mpl-core.typedoc.metaplex.com/functions/updatePlugin.html)
+- [updateCollectionPlugin](https://mpl-core.typedoc.metaplex.com/functions/updateCollectionPlugin.html)
+
+Note: In the JavaScript SDK, updatePlugin expects the plugin data without a data wrapper (e.g., `{ type: 'FreezeDelegate', frozen: true }`). By contrast, addPlugin wraps data under `data` (e.g., `{ type: 'FreezeDelegate', data: { frozen: true } }`). This mirrors the shape used in createAsset/createCollection plugin lists.
+
+{% /totem-accordion %}
+{% /totem %}
+
+## Updating Plugins on Assets
+
+### Basic Plugin Update Example
+
+Here's how to update a plugin on an MPL Core Asset using the Attributes plugin as an example:
+
+{% dialect-switcher title="Update Plugin on Asset" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin, fetchAsset } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const assetAddress = publicKey('11111111111111111111111111111111')
+
+  // Fetch the current asset to see existing plugin data
+  const asset = await fetchAsset(umi, assetAddress, {
+    skipDerivePlugins: false,
+  })
+
+  // Update the Attributes plugin with new data
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'Attributes',
+      attributeList: [
+        { key: 'level', value: '5' },        // Updated value
+        { key: 'rarity', value: 'legendary' }, // New attribute
+        { key: 'power', value: '150' },      // New attribute
+      ],
+    },
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### Updating Royalties Plugin
+
+{% dialect-switcher title="Update Royalties Plugin" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin, ruleSet } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const assetAddress = publicKey('11111111111111111111111111111111')
+  const creator1 = publicKey('22222222222222222222222222222222')
+  const creator2 = publicKey('33333333333333333333333333333333')
+
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'Royalties',
+      basisPoints: 750, // Updated from 500 to 750 (7.5%)
+      creators: [
+        { address: creator1, percentage: 70 }, // Updated distribution
+        { address: creator2, percentage: 30 },
+      ],
+      ruleSet: ruleSet('ProgramAllowList', [
+        [
+          publicKey('44444444444444444444444444444444'),
+          publicKey('55555555555555555555555555555555'),
+        ],
+      ]),
+    },
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### Updating State-Based Plugins
+
+Some plugins store simple state that can be toggled, like the Freeze Delegate plugin:
+
+{% dialect-switcher title="Update Freeze State" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const assetAddress = publicKey('11111111111111111111111111111111')
+
+  // Freeze the asset
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'FreezeDelegate',
+      frozen: true, // Set to true to freeze, false to unfreeze
+    },
+  }).sendAndConfirm(umi)
+
+  // Later, unfreeze the asset
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'FreezeDelegate',
+      frozen: false, // Unfreeze the asset
+    },
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Updating Plugins on Collections
+
+Collection plugins work similarly to asset plugins, but use the `updateCollectionPlugin` function:
+
+{% dialect-switcher title="Update Plugin on Collection" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updateCollectionPlugin, ruleSet } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const collectionAddress = publicKey('11111111111111111111111111111111')
+  const creator1 = publicKey('22222222222222222222222222222222')
+  const creator2 = publicKey('33333333333333333333333333333333')
+
+  // Update collection-wide royalties
+  await updateCollectionPlugin(umi, {
+    collection: collectionAddress,
+    plugin: {
+      type: 'Royalties',
+      basisPoints: 600, // 6% royalty for the collection
+      creators: [
+        { address: creator1, percentage: 80 },
+        { address: creator2, percentage: 20 },
+      ],
+      ruleSet: ruleSet('None'),
+    },
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Working with Complex Plugin Data
+
+### Managing Lists in Plugins
+
+Some plugins like Autograph and Verified Creators maintain lists of data. When updating these plugins, you need to pass the complete list you want to maintain:
+
+{% dialect-switcher title="Update List-Based Plugin" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin, fetchAsset } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const assetAddress = publicKey('11111111111111111111111111111111')
+
+  // First, fetch the current asset to see existing autographs
+  const asset = await fetchAsset(umi, assetAddress, {
+    skipDerivePlugins: false,
+  })
+
+  // Add a new autograph while keeping existing ones
+  const newAutograph = {
+    address: umi.identity.publicKey,
+    message: "Amazing NFT! Signed by collector."
+  }
+
+  // Include all existing autographs plus the new one
+  const updatedAutographs = [...asset.autograph.signatures, newAutograph]
+
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'Autograph',
+      signatures: updatedAutographs, // Complete list including new addition
+    },
+    authority: umi.identity,
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+### Removing Items from Lists
+
+{% dialect-switcher title="Remove Items from Plugin Lists" %}
+{% dialect title="JavaScript" id="js" %}
+
+```ts
+import { publicKey } from '@metaplex-foundation/umi'
+import { updatePlugin, fetchAsset } from '@metaplex-foundation/mpl-core'
+
+(async () => {
+  const assetAddress = publicKey('11111111111111111111111111111111')
+  const autographToRemove = publicKey('44444444444444444444444444444444')
+
+  // Fetch current asset data
+  const asset = await fetchAsset(umi, assetAddress, {
+    skipDerivePlugins: false,
+  })
+
+  // Filter out the autograph we want to remove
+  const filteredAutographs = asset.autograph.signatures.filter(
+    (autograph) => autograph.address !== autographToRemove
+  )
+
+  await updatePlugin(umi, {
+    asset: assetAddress,
+    plugin: {
+      type: 'Autograph',
+      signatures: filteredAutographs, // List without the removed item
+    },
+    authority: umi.identity,
+  }).sendAndConfirm(umi)
+})();
+```
+
+{% /dialect %}
+{% /dialect-switcher %}
+
+## Authority Requirements
+
+Different plugins require different authorities to update:
+
+- **Authority Managed Plugins** (Royalties, Attributes, Update Delegate): Require the **authority** of the asset or collection
+- **Owner Managed Plugins** (Autograph, Freeze Delegate): Require the **owner** of the asset or the plugin's specific authority
+- **Verified Creators Plugin**: Requires the **update authority** to add/remove creators, but individual **creators can verify themselves**
+
+## Error Handling
+
+Common errors when updating plugins:
+
+- **Authority mismatch**: Ensure you're signing with the correct authority for the plugin type
+- **Plugin not found**: The plugin must exist on the asset/collection before it can be updated
+- **Invalid data**: Plugin data must conform to the expected structure and constraints
+- **Collection mismatch**: If the asset is part of a collection, you may need to include the collection in the update
+
+## Best Practices
+
+1. **Fetch before updating**: Always fetch the current asset/collection state to see existing plugin data
+2. **Preserve existing data**: When updating list-based plugins, include existing data you want to keep
+3. **Use proper authorities**: Ensure you're using the correct signing authority for each plugin type
+4. **Batch updates**: If updating multiple plugins, consider batching operations for efficiency
+5. **Validate data**: Ensure your update data meets the plugin's requirements (e.g., creator percentages sum to 100%)
+
+## Next Steps
+
+- Learn about specific plugin updates in individual plugin documentation
+- Explore [Plugin Overview](/core/plugins) for all available plugins
+- Check out [Adding Plugins](/core/plugins/adding-plugins) and [Removing Plugins](/core/plugins/removing-plugins)
+- Visit the [MPL Core TypeDoc](https://mpl-core.typedoc.metaplex.com) for detailed API documentation.