Преглед изворни кода

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 месеци
родитељ
комит
bac7909300

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

@@ -119,6 +119,10 @@ export const core = {
               title: 'Adding Plugins',
               href: '/core/plugins/adding-plugins',
             },
+            {
+              title: 'Updating Plugins',
+              href: '/core/plugins/update-plugins',
+            },
             {
               title: '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 { 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 %}
@@ -85,3 +87,54 @@ pub async fn add_burn_delegate_plugin() {
 
 {% /dialect %}
 {% /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-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
 
 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 %}
 
+## 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 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()?;
 ```
 
-{% /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 %}
 

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

@@ -103,3 +103,155 @@ pub async fn add_update_delegate_plugin() {
 
 {% /dialect %}
 {% /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.