From 8bdfa1fd808f1a8360fb9267ffae2615ed613a1f Mon Sep 17 00:00:00 2001
From: blockiosaurus <blockiosaurus@gmail.com>
Date: Fri, 8 Mar 2024 17:25:16 -0500
Subject: [PATCH] Minor helper and test changes.

---
 .../instructions/addCollectionPlugin.ts       | 19 ++++-
 .../src/generated/instructions/addPlugin.ts   | 19 ++++-
 clients/js/test/addPlugin.test.ts             | 76 ++++++++++++++++++-
 clients/js/test/approveAuthority.test.ts      |  3 +-
 clients/js/test/collect.test.ts               |  3 +-
 clients/js/test/createCollection.test.ts      | 13 ++--
 .../js/test/plugins/asset/delegate.test.ts    |  2 +
 .../plugins/asset/delegateTransfer.test.ts    |  1 +
 clients/js/test/plugins/asset/freeze.test.ts  |  1 +
 .../collectionUpdateDelegate.test.ts          |  1 +
 clients/js/test/removePlugin.test.ts          |  1 +
 clients/js/test/revokeAuthority.test.ts       |  5 ++
 clients/js/test/update.test.ts                |  2 +
 .../instructions/add_collection_plugin.rs     | 19 +++++
 .../src/generated/instructions/add_plugin.rs  | 19 +++++
 idls/mpl_core.json                            | 16 ++++
 programs/mpl-core/src/plugins/mod.rs          | 15 +---
 programs/mpl-core/src/plugins/utils.rs        | 48 +++++-------
 programs/mpl-core/src/processor/add_plugin.rs | 61 ++++++---------
 .../src/processor/approve_plugin_authority.rs | 18 +----
 programs/mpl-core/src/processor/create.rs     |  4 +-
 .../src/processor/create_collection.rs        |  4 +-
 .../mpl-core/src/processor/remove_plugin.rs   | 48 ++----------
 .../src/processor/revoke_plugin_authority.rs  | 48 ++----------
 programs/mpl-core/src/processor/update.rs     | 19 +----
 programs/mpl-core/src/utils.rs                | 56 +++++++++++++-
 26 files changed, 307 insertions(+), 214 deletions(-)

diff --git a/clients/js/src/generated/instructions/addCollectionPlugin.ts b/clients/js/src/generated/instructions/addCollectionPlugin.ts
index 55009253..3594ed55 100644
--- a/clients/js/src/generated/instructions/addCollectionPlugin.ts
+++ b/clients/js/src/generated/instructions/addCollectionPlugin.ts
@@ -8,6 +8,8 @@
 
 import {
   Context,
+  Option,
+  OptionOrNullable,
   Pda,
   PublicKey,
   Signer,
@@ -17,6 +19,7 @@ import {
 import {
   Serializer,
   mapSerializer,
+  option,
   struct,
   u8,
 } from '@metaplex-foundation/umi/serializers';
@@ -25,7 +28,14 @@ import {
   ResolvedAccountsWithIndices,
   getAccountMetasAndSigners,
 } from '../shared';
-import { Plugin, PluginArgs, getPluginSerializer } from '../types';
+import {
+  Authority,
+  AuthorityArgs,
+  Plugin,
+  PluginArgs,
+  getAuthoritySerializer,
+  getPluginSerializer,
+} from '../types';
 
 // Accounts.
 export type AddCollectionPluginInstructionAccounts = {
@@ -45,9 +55,13 @@ export type AddCollectionPluginInstructionAccounts = {
 export type AddCollectionPluginInstructionData = {
   discriminator: number;
   plugin: Plugin;
+  initAuthority: Option<Authority>;
 };
 
-export type AddCollectionPluginInstructionDataArgs = { plugin: PluginArgs };
+export type AddCollectionPluginInstructionDataArgs = {
+  plugin: PluginArgs;
+  initAuthority: OptionOrNullable<AuthorityArgs>;
+};
 
 export function getAddCollectionPluginInstructionDataSerializer(): Serializer<
   AddCollectionPluginInstructionDataArgs,
@@ -62,6 +76,7 @@ export function getAddCollectionPluginInstructionDataSerializer(): Serializer<
       [
         ['discriminator', u8()],
         ['plugin', getPluginSerializer()],
+        ['initAuthority', option(getAuthoritySerializer())],
       ],
       { description: 'AddCollectionPluginInstructionData' }
     ),
diff --git a/clients/js/src/generated/instructions/addPlugin.ts b/clients/js/src/generated/instructions/addPlugin.ts
index 9c84b988..80af0032 100644
--- a/clients/js/src/generated/instructions/addPlugin.ts
+++ b/clients/js/src/generated/instructions/addPlugin.ts
@@ -8,6 +8,8 @@
 
 import {
   Context,
+  Option,
+  OptionOrNullable,
   Pda,
   PublicKey,
   Signer,
@@ -17,6 +19,7 @@ import {
 import {
   Serializer,
   mapSerializer,
+  option,
   struct,
   u8,
 } from '@metaplex-foundation/umi/serializers';
@@ -25,7 +28,14 @@ import {
   ResolvedAccountsWithIndices,
   getAccountMetasAndSigners,
 } from '../shared';
-import { Plugin, PluginArgs, getPluginSerializer } from '../types';
+import {
+  Authority,
+  AuthorityArgs,
+  Plugin,
+  PluginArgs,
+  getAuthoritySerializer,
+  getPluginSerializer,
+} from '../types';
 
 // Accounts.
 export type AddPluginInstructionAccounts = {
@@ -47,9 +57,13 @@ export type AddPluginInstructionAccounts = {
 export type AddPluginInstructionData = {
   discriminator: number;
   plugin: Plugin;
+  initAuthority: Option<Authority>;
 };
 
-export type AddPluginInstructionDataArgs = { plugin: PluginArgs };
+export type AddPluginInstructionDataArgs = {
+  plugin: PluginArgs;
+  initAuthority: OptionOrNullable<AuthorityArgs>;
+};
 
 export function getAddPluginInstructionDataSerializer(): Serializer<
   AddPluginInstructionDataArgs,
@@ -64,6 +78,7 @@ export function getAddPluginInstructionDataSerializer(): Serializer<
       [
         ['discriminator', u8()],
         ['plugin', getPluginSerializer()],
+        ['initAuthority', option(getAuthoritySerializer())],
       ],
       { description: 'AddPluginInstructionData' }
     ),
diff --git a/clients/js/test/addPlugin.test.ts b/clients/js/test/addPlugin.test.ts
index 6a39fc81..173eab77 100644
--- a/clients/js/test/addPlugin.test.ts
+++ b/clients/js/test/addPlugin.test.ts
@@ -10,6 +10,7 @@ import {
   PluginType,
   addCollectionPlugin,
   addPlugin,
+  authority,
   create,
   createCollection,
   fetchAsset,
@@ -50,7 +51,8 @@ test('it can add a plugin to an asset', async (t) => {
     plugin: {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
-    }
+    },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   const asset1 = await fetchAssetWithPlugins(umi, assetAddress.publicKey);
@@ -87,6 +89,75 @@ test('it can add a plugin to an asset', async (t) => {
   });
 });
 
+test('it can add a plugin to an asset with a different authority than the default', async (t) => {
+  // Given a Umi instance and a new signer.
+  const umi = await createUmi();
+  const assetAddress = generateSigner(umi);
+  const delegateAddress = generateSigner(umi);
+
+  // When we create a new account.
+  await create(umi, {
+    dataState: DataState.AccountState,
+    asset: assetAddress,
+    name: 'Test Bread',
+    uri: 'https://example.com/bread',
+    plugins: [],
+  }).sendAndConfirm(umi);
+
+  // Then an account was created with the correct data.
+  const asset = await fetchAsset(umi, assetAddress.publicKey);
+  // console.log("Account State:", asset);
+  t.like(asset, <Asset>{
+    publicKey: assetAddress.publicKey,
+    updateAuthority: updateAuthority('Address', [umi.identity.publicKey]),
+    owner: umi.identity.publicKey,
+    name: 'Test Bread',
+    uri: 'https://example.com/bread',
+  });
+
+  await addPlugin(umi, {
+    asset: assetAddress.publicKey,
+    plugin: {
+      __kind: 'Freeze',
+      fields: [{ frozen: false }],
+    },
+    initAuthority: authority('Pubkey', { address: delegateAddress.publicKey }),
+  }).sendAndConfirm(umi);
+
+  const asset1 = await fetchAssetWithPlugins(umi, assetAddress.publicKey);
+  // console.log(JSON.stringify(asset1, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));
+  t.like(asset1, <AssetWithPlugins>{
+    publicKey: assetAddress.publicKey,
+    updateAuthority: updateAuthority('Address', [umi.identity.publicKey]),
+    owner: umi.identity.publicKey,
+    name: 'Test Bread',
+    uri: 'https://example.com/bread',
+    pluginHeader: {
+      key: 3,
+      pluginRegistryOffset: BigInt(120),
+    },
+    pluginRegistry: {
+      key: 4,
+      registry: [
+        {
+          pluginType: 2,
+          offset: BigInt(118),
+          authority: authority('Pubkey', { address: delegateAddress.publicKey })
+        },
+      ],
+    },
+    plugins: [
+      {
+        authority: authority('Pubkey', { address: delegateAddress.publicKey }),
+        plugin: {
+          __kind: 'Freeze',
+          fields: [{ frozen: false }],
+        },
+      },
+    ],
+  });
+});
+
 test('it can add a plugin to a collection', async (t) => {
   // Given a Umi instance and a new signer.
   const umi = await createUmi();
@@ -115,7 +186,8 @@ test('it can add a plugin to a collection', async (t) => {
     plugin: {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
-    }
+    },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   const asset1 = await fetchCollectionWithPlugins(umi, collectionAddress.publicKey);
diff --git a/clients/js/test/approveAuthority.test.ts b/clients/js/test/approveAuthority.test.ts
index 854a965d..41b91028 100644
--- a/clients/js/test/approveAuthority.test.ts
+++ b/clients/js/test/approveAuthority.test.ts
@@ -44,7 +44,8 @@ test('it can add an authority to a plugin', async (t) => {
 
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
-    plugin: plugin('Freeze', [{ frozen: false }])
+    plugin: plugin('Freeze', [{ frozen: false }]),
+    initAuthority: null
   })
     .append(
       approvePluginAuthority(umi, {
diff --git a/clients/js/test/collect.test.ts b/clients/js/test/collect.test.ts
index c20689be..b6f78baa 100644
--- a/clients/js/test/collect.test.ts
+++ b/clients/js/test/collect.test.ts
@@ -68,7 +68,8 @@ test('it can add asset plugin with collect amount', async (t) => {
 
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
-    plugin: plugin('Freeze', [{ frozen: true }])
+    plugin: plugin('Freeze', [{ frozen: true }]),
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   t.assert(await hasCollectAmount(umi, assetAddress.publicKey), 'Collect amount not found')
diff --git a/clients/js/test/createCollection.test.ts b/clients/js/test/createCollection.test.ts
index 2d6bd5f7..3964b68e 100644
--- a/clients/js/test/createCollection.test.ts
+++ b/clients/js/test/createCollection.test.ts
@@ -127,13 +127,14 @@ test('it can create a new asset with a collection with collection delegate', asy
   await addCollectionPlugin(umi, {
     collection: collectionAddress.publicKey,
     plugin: plugin('UpdateDelegate', [{}]),
+    initAuthority: null
+  }).sendAndConfirm(umi);
+
+  await approveCollectionPluginAuthority(umi, {
+    collection: collectionAddress.publicKey,
+    pluginType: PluginType.UpdateDelegate,
+    newAuthority: authority('Pubkey', { address: delegate.publicKey }),
   }).sendAndConfirm(umi);
-  
-await approveCollectionPluginAuthority(umi, {
-  collection: collectionAddress.publicKey,
-  pluginType: PluginType.UpdateDelegate,
-  newAuthority: authority('Pubkey', { address: delegate.publicKey}),
-}).sendAndConfirm(umi);
 
 
   // When we create a new account.
diff --git a/clients/js/test/plugins/asset/delegate.test.ts b/clients/js/test/plugins/asset/delegate.test.ts
index 780157e9..ba05bdd9 100644
--- a/clients/js/test/plugins/asset/delegate.test.ts
+++ b/clients/js/test/plugins/asset/delegate.test.ts
@@ -35,6 +35,7 @@ test('it can delegate a new authority', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
@@ -102,6 +103,7 @@ test('a delegate can freeze the token', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
diff --git a/clients/js/test/plugins/asset/delegateTransfer.test.ts b/clients/js/test/plugins/asset/delegateTransfer.test.ts
index dc539707..c383f214 100644
--- a/clients/js/test/plugins/asset/delegateTransfer.test.ts
+++ b/clients/js/test/plugins/asset/delegateTransfer.test.ts
@@ -35,6 +35,7 @@ test('a delegate can transfer the asset', async (t) => {
       __kind: 'Transfer',
       fields: [{}],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
diff --git a/clients/js/test/plugins/asset/freeze.test.ts b/clients/js/test/plugins/asset/freeze.test.ts
index 9e4ecb48..2b0cf0a2 100644
--- a/clients/js/test/plugins/asset/freeze.test.ts
+++ b/clients/js/test/plugins/asset/freeze.test.ts
@@ -28,6 +28,7 @@ test('it can freeze and unfreeze an asset', async (t) => {
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
     plugin: plugin('Freeze', [{ frozen: true }]),
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   const asset = await fetchAssetWithPlugins(umi, assetAddress.publicKey);
diff --git a/clients/js/test/plugins/collection/collectionUpdateDelegate.test.ts b/clients/js/test/plugins/collection/collectionUpdateDelegate.test.ts
index 1394d700..476cbed7 100644
--- a/clients/js/test/plugins/collection/collectionUpdateDelegate.test.ts
+++ b/clients/js/test/plugins/collection/collectionUpdateDelegate.test.ts
@@ -37,6 +37,7 @@ test('it can create a new asset with a collection if it is the collection update
       __kind: 'UpdateDelegate',
       fields: [{}],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   // console.log(JSON.stringify(await fetchCollectionWithPlugins(umi, collectionAddress.publicKey), (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));
diff --git a/clients/js/test/removePlugin.test.ts b/clients/js/test/removePlugin.test.ts
index 0d075379..89f24173 100644
--- a/clients/js/test/removePlugin.test.ts
+++ b/clients/js/test/removePlugin.test.ts
@@ -46,6 +46,7 @@ test('it can remove a plugin from an asset', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   const asset1 = await fetchAssetWithPlugins(umi, assetAddress.publicKey);
diff --git a/clients/js/test/revokeAuthority.test.ts b/clients/js/test/revokeAuthority.test.ts
index c7810f66..1eb98bff 100644
--- a/clients/js/test/revokeAuthority.test.ts
+++ b/clients/js/test/revokeAuthority.test.ts
@@ -50,6 +50,7 @@ test('it can remove an authority from a plugin', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   })
     .append(
       approvePluginAuthority(umi, {
@@ -168,6 +169,7 @@ test('it can remove the default authority from a plugin to make it immutable', a
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
@@ -230,6 +232,7 @@ test('it can remove a pubkey authority from a plugin if that pubkey is the signe
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
     plugin: plugin('Freeze', [{ frozen: false }]),
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
@@ -308,6 +311,7 @@ test('it can remove a owner authority from a plugin with other authority', async
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
     plugin: plugin('Freeze', [{ frozen: false }]),
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
@@ -384,6 +388,7 @@ test('it cannot remove a none authority from a plugin', async (t) => {
   await addPlugin(umi, {
     asset: assetAddress.publicKey,
     plugin: plugin('Freeze', [{ frozen: false }]),
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await approvePluginAuthority(umi, {
diff --git a/clients/js/test/update.test.ts b/clients/js/test/update.test.ts
index e1baf0a6..dafe8b40 100644
--- a/clients/js/test/update.test.ts
+++ b/clients/js/test/update.test.ts
@@ -95,6 +95,7 @@ test('it can update an asset with plugins to be larger', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await update(umi, {
@@ -158,6 +159,7 @@ test('it can update an asset with plugins to be smaller', async (t) => {
       __kind: 'Freeze',
       fields: [{ frozen: false }],
     },
+    initAuthority: null
   }).sendAndConfirm(umi);
 
   await update(umi, {
diff --git a/clients/rust/src/generated/instructions/add_collection_plugin.rs b/clients/rust/src/generated/instructions/add_collection_plugin.rs
index 5bda5b42..9587f355 100644
--- a/clients/rust/src/generated/instructions/add_collection_plugin.rs
+++ b/clients/rust/src/generated/instructions/add_collection_plugin.rs
@@ -5,6 +5,7 @@
 //! [https://github.com/metaplex-foundation/kinobi]
 //!
 
+use crate::generated::types::Authority;
 use crate::generated::types::Plugin;
 use borsh::BorshDeserialize;
 use borsh::BorshSerialize;
@@ -98,6 +99,7 @@ impl AddCollectionPluginInstructionData {
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 pub struct AddCollectionPluginInstructionArgs {
     pub plugin: Plugin,
+    pub init_authority: Option<Authority>,
 }
 
 /// Instruction builder for `AddCollectionPlugin`.
@@ -117,6 +119,7 @@ pub struct AddCollectionPluginBuilder {
     system_program: Option<solana_program::pubkey::Pubkey>,
     log_wrapper: Option<solana_program::pubkey::Pubkey>,
     plugin: Option<Plugin>,
+    init_authority: Option<Authority>,
     __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
 }
 
@@ -165,6 +168,12 @@ impl AddCollectionPluginBuilder {
         self.plugin = Some(plugin);
         self
     }
+    /// `[optional argument]`
+    #[inline(always)]
+    pub fn init_authority(&mut self, init_authority: Authority) -> &mut Self {
+        self.init_authority = Some(init_authority);
+        self
+    }
     /// Add an aditional account to the instruction.
     #[inline(always)]
     pub fn add_remaining_account(
@@ -196,6 +205,7 @@ impl AddCollectionPluginBuilder {
         };
         let args = AddCollectionPluginInstructionArgs {
             plugin: self.plugin.clone().expect("plugin is not set"),
+            init_authority: self.init_authority.clone(),
         };
 
         accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts)
@@ -381,6 +391,7 @@ impl<'a, 'b> AddCollectionPluginCpiBuilder<'a, 'b> {
             system_program: None,
             log_wrapper: None,
             plugin: None,
+            init_authority: None,
             __remaining_accounts: Vec::new(),
         });
         Self { instruction }
@@ -437,6 +448,12 @@ impl<'a, 'b> AddCollectionPluginCpiBuilder<'a, 'b> {
         self.instruction.plugin = Some(plugin);
         self
     }
+    /// `[optional argument]`
+    #[inline(always)]
+    pub fn init_authority(&mut self, init_authority: Authority) -> &mut Self {
+        self.instruction.init_authority = Some(init_authority);
+        self
+    }
     /// Add an additional account to the instruction.
     #[inline(always)]
     pub fn add_remaining_account(
@@ -480,6 +497,7 @@ impl<'a, 'b> AddCollectionPluginCpiBuilder<'a, 'b> {
     ) -> solana_program::entrypoint::ProgramResult {
         let args = AddCollectionPluginInstructionArgs {
             plugin: self.instruction.plugin.clone().expect("plugin is not set"),
+            init_authority: self.instruction.init_authority.clone(),
         };
         let instruction = AddCollectionPluginCpi {
             __program: self.instruction.__program,
@@ -513,6 +531,7 @@ struct AddCollectionPluginCpiBuilderInstruction<'a, 'b> {
     system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     log_wrapper: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     plugin: Option<Plugin>,
+    init_authority: Option<Authority>,
     /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
     __remaining_accounts: Vec<(
         &'b solana_program::account_info::AccountInfo<'a>,
diff --git a/clients/rust/src/generated/instructions/add_plugin.rs b/clients/rust/src/generated/instructions/add_plugin.rs
index b1a752c7..866f064b 100644
--- a/clients/rust/src/generated/instructions/add_plugin.rs
+++ b/clients/rust/src/generated/instructions/add_plugin.rs
@@ -5,6 +5,7 @@
 //! [https://github.com/metaplex-foundation/kinobi]
 //!
 
+use crate::generated::types::Authority;
 use crate::generated::types::Plugin;
 use borsh::BorshDeserialize;
 use borsh::BorshSerialize;
@@ -107,6 +108,7 @@ impl AddPluginInstructionData {
 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 pub struct AddPluginInstructionArgs {
     pub plugin: Plugin,
+    pub init_authority: Option<Authority>,
 }
 
 /// Instruction builder for `AddPlugin`.
@@ -128,6 +130,7 @@ pub struct AddPluginBuilder {
     system_program: Option<solana_program::pubkey::Pubkey>,
     log_wrapper: Option<solana_program::pubkey::Pubkey>,
     plugin: Option<Plugin>,
+    init_authority: Option<Authority>,
     __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
 }
 
@@ -183,6 +186,12 @@ impl AddPluginBuilder {
         self.plugin = Some(plugin);
         self
     }
+    /// `[optional argument]`
+    #[inline(always)]
+    pub fn init_authority(&mut self, init_authority: Authority) -> &mut Self {
+        self.init_authority = Some(init_authority);
+        self
+    }
     /// Add an aditional account to the instruction.
     #[inline(always)]
     pub fn add_remaining_account(
@@ -215,6 +224,7 @@ impl AddPluginBuilder {
         };
         let args = AddPluginInstructionArgs {
             plugin: self.plugin.clone().expect("plugin is not set"),
+            init_authority: self.init_authority.clone(),
         };
 
         accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts)
@@ -419,6 +429,7 @@ impl<'a, 'b> AddPluginCpiBuilder<'a, 'b> {
             system_program: None,
             log_wrapper: None,
             plugin: None,
+            init_authority: None,
             __remaining_accounts: Vec::new(),
         });
         Self { instruction }
@@ -482,6 +493,12 @@ impl<'a, 'b> AddPluginCpiBuilder<'a, 'b> {
         self.instruction.plugin = Some(plugin);
         self
     }
+    /// `[optional argument]`
+    #[inline(always)]
+    pub fn init_authority(&mut self, init_authority: Authority) -> &mut Self {
+        self.instruction.init_authority = Some(init_authority);
+        self
+    }
     /// Add an additional account to the instruction.
     #[inline(always)]
     pub fn add_remaining_account(
@@ -525,6 +542,7 @@ impl<'a, 'b> AddPluginCpiBuilder<'a, 'b> {
     ) -> solana_program::entrypoint::ProgramResult {
         let args = AddPluginInstructionArgs {
             plugin: self.instruction.plugin.clone().expect("plugin is not set"),
+            init_authority: self.instruction.init_authority.clone(),
         };
         let instruction = AddPluginCpi {
             __program: self.instruction.__program,
@@ -561,6 +579,7 @@ struct AddPluginCpiBuilderInstruction<'a, 'b> {
     system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     log_wrapper: Option<&'b solana_program::account_info::AccountInfo<'a>>,
     plugin: Option<Plugin>,
+    init_authority: Option<Authority>,
     /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
     __remaining_accounts: Vec<(
         &'b solana_program::account_info::AccountInfo<'a>,
diff --git a/idls/mpl_core.json b/idls/mpl_core.json
index eee448f3..bfccae2a 100644
--- a/idls/mpl_core.json
+++ b/idls/mpl_core.json
@@ -1537,6 +1537,14 @@
             "type": {
               "defined": "Plugin"
             }
+          },
+          {
+            "name": "initAuthority",
+            "type": {
+              "option": {
+                "defined": "Authority"
+              }
+            }
           }
         ]
       }
@@ -1551,6 +1559,14 @@
             "type": {
               "defined": "Plugin"
             }
+          },
+          {
+            "name": "initAuthority",
+            "type": {
+              "option": {
+                "defined": "Authority"
+              }
+            }
           }
         ]
       }
diff --git a/programs/mpl-core/src/plugins/mod.rs b/programs/mpl-core/src/plugins/mod.rs
index c52a72ba..ce469112 100644
--- a/programs/mpl-core/src/plugins/mod.rs
+++ b/programs/mpl-core/src/plugins/mod.rs
@@ -51,17 +51,8 @@ pub enum Plugin {
 
 impl Plugin {
     /// Get the default authority for a plugin which defines who must allow the plugin to be created.
-    pub fn default_authority(&self) -> Authority {
-        // match self {
-        //     Plugin::Reserved => Err(MplCoreError::InvalidPlugin.into()),
-        //     Plugin::Royalties(_) => Ok(Authority::UpdateAuthority),
-        //     Plugin::Freeze(_) => Ok(Authority::Owner),
-        //     Plugin::Burn(_) => Ok(Authority::Owner),
-        //     Plugin::Transfer(_) => Ok(Authority::Owner),
-        //     Plugin::UpdateDelegate(_) => Ok(Authority::UpdateAuthority),
-        // }
-
-        PluginType::from(self).default_authority()
+    pub fn manager(&self) -> Authority {
+        PluginType::from(self).manager()
     }
 
     /// Load and deserialize a plugin from an offset in the account.
@@ -141,7 +132,7 @@ impl From<&Plugin> for PluginType {
 
 impl PluginType {
     /// Get the default authority for a plugin which defines who must allow the plugin to be created.
-    pub fn default_authority(&self) -> Authority {
+    pub fn manager(&self) -> Authority {
         match self {
             PluginType::Reserved => Authority::None,
             PluginType::Royalties => Authority::UpdateAuthority,
diff --git a/programs/mpl-core/src/plugins/utils.rs b/programs/mpl-core/src/plugins/utils.rs
index a56f9609..fa0826e2 100644
--- a/programs/mpl-core/src/plugins/utils.rs
+++ b/programs/mpl-core/src/plugins/utils.rs
@@ -6,37 +6,20 @@ use solana_program::{
 
 use crate::{
     error::MplCoreError,
-    state::{Asset, Authority, Collection, CoreAsset, DataBlob, Key, SolanaAccount},
-    utils::{assert_authority, load_key, resize_or_reallocate_account},
+    state::{Asset, Authority, CoreAsset, DataBlob, Key, SolanaAccount},
+    utils::{assert_authority, resize_or_reallocate_account},
 };
 
 use super::{Plugin, PluginHeader, PluginRegistry, PluginType, RegistryRecord};
 
 /// Create plugin header and registry if it doesn't exist
-pub fn create_meta_idempotent<'a>(
+pub fn create_meta_idempotent<'a, T: SolanaAccount + DataBlob>(
     account: &AccountInfo<'a>,
     payer: &AccountInfo<'a>,
     system_program: &AccountInfo<'a>,
-) -> ProgramResult {
-    let header_offset = match load_key(account, 0)? {
-        Key::Asset => {
-            let asset = {
-                let mut bytes: &[u8] = &(*account.data).borrow();
-                Asset::deserialize(&mut bytes)?
-            };
-
-            asset.get_size()
-        }
-        Key::Collection => {
-            let collection = {
-                let mut bytes: &[u8] = &(*account.data).borrow();
-                Collection::deserialize(&mut bytes)?
-            };
-
-            collection.get_size()
-        }
-        _ => return Err(MplCoreError::IncorrectAccount.into()),
-    };
+) -> Result<(T, PluginHeader, PluginRegistry), ProgramError> {
+    let core = T::load(account, 0)?;
+    let header_offset = core.get_size();
 
     // Check if the plugin header and registry exist.
     if header_offset == account.data_len() {
@@ -60,9 +43,15 @@ pub fn create_meta_idempotent<'a>(
 
         header.save(account, header_offset)?;
         registry.save(account, header.plugin_registry_offset)?;
-    }
 
-    Ok(())
+        Ok((core, header, registry))
+    } else {
+        // They exist, so load them.
+        let header = PluginHeader::load(account, header_offset)?;
+        let registry = PluginRegistry::load(account, header.plugin_registry_offset)?;
+
+        Ok((core, header, registry))
+    }
 }
 
 /// Create plugin header and registry if it doesn't exist
@@ -101,7 +90,7 @@ pub fn create_plugin_meta<'a, T: SolanaAccount + DataBlob>(
 /// Assert that the Plugin metadata has been initialized.
 pub fn assert_plugins_initialized(account: &AccountInfo) -> ProgramResult {
     let mut bytes: &[u8] = &(*account.data).borrow();
-    let asset = Asset::deserialize(&mut bytes).unwrap();
+    let asset = Asset::deserialize(&mut bytes)?;
 
     if asset.get_size() == account.data_len() {
         return Err(MplCoreError::PluginsNotInitialized.into());
@@ -174,6 +163,7 @@ pub fn list_plugins(account: &AccountInfo) -> Result<Vec<PluginType>, ProgramErr
         .collect())
 }
 
+//TODO: Take in the header and registry so we don't have to reload it.
 /// Add a plugin to the registry and initialize it.
 pub fn initialize_plugin<'a, T: DataBlob + SolanaAccount>(
     plugin: &Plugin,
@@ -261,7 +251,7 @@ pub fn delete_plugin<'a, T: DataBlob>(
         let serialized_registry_record = registry_record.try_to_vec()?;
 
         // Only allow the default authority to delete the plugin.
-        if authority_type != &registry_record.plugin_type.default_authority() {
+        if authority_type != &registry_record.plugin_type.manager() {
             return Err(MplCoreError::InvalidAuthority.into());
         }
 
@@ -369,7 +359,7 @@ pub fn revoke_authority_on_plugin<'a>(
     solana_program::msg!("registry_record.authority: {:?}", registry_record.authority);
 
     // TODO inspect this logic
-    if (*authority_type != registry_record.plugin_type.default_authority() &&
+    if (*authority_type != registry_record.plugin_type.manager() &&
         // pubkey authorities can remove themselves if they are a signer
         authority_type != &registry_record.authority) ||
         // Unable to revoke a None authority
@@ -379,7 +369,7 @@ pub fn revoke_authority_on_plugin<'a>(
     }
 
     let authority_bytes = registry_record.authority.try_to_vec()?;
-    registry_record.authority = registry_record.plugin_type.default_authority();
+    registry_record.authority = registry_record.plugin_type.manager();
 
     let new_size = account
         .data_len()
diff --git a/programs/mpl-core/src/processor/add_plugin.rs b/programs/mpl-core/src/processor/add_plugin.rs
index ee781daf..209bd968 100644
--- a/programs/mpl-core/src/processor/add_plugin.rs
+++ b/programs/mpl-core/src/processor/add_plugin.rs
@@ -5,13 +5,15 @@ use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult};
 use crate::{
     instruction::accounts::{AddCollectionPluginAccounts, AddPluginAccounts},
     plugins::{create_meta_idempotent, initialize_plugin, Plugin},
-    state::{Asset, Collection},
+    state::{Asset, Authority, Collection, DataBlob, SolanaAccount},
+    utils::resolve_payer,
 };
 
 #[repr(C)]
 #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
 pub(crate) struct AddPluginArgs {
     plugin: Plugin,
+    init_authority: Option<Authority>,
 }
 
 pub(crate) fn add_plugin<'a>(
@@ -22,33 +24,22 @@ pub(crate) fn add_plugin<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
-    let _default_auth = args.plugin.default_authority();
-
-    create_meta_idempotent(ctx.accounts.asset, payer, ctx.accounts.system_program)?;
-
-    initialize_plugin::<Asset>(
-        &args.plugin,
-        &args.plugin.default_authority(),
+    process_add_plugin::<Asset>(
         ctx.accounts.asset,
         payer,
         ctx.accounts.system_program,
-    )?;
-
-    process_add_plugin()
+        &args.plugin,
+        &args.init_authority.unwrap_or(args.plugin.manager()),
+    )
 }
 
 #[repr(C)]
 #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)]
 pub(crate) struct AddCollectionPluginArgs {
     plugin: Plugin,
+    init_authority: Option<Authority>,
 }
 
 pub(crate) fn add_collection_plugin<'a>(
@@ -59,30 +50,24 @@ pub(crate) fn add_collection_plugin<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
-
-    let _default_auth = args.plugin.default_authority();
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
-    create_meta_idempotent(ctx.accounts.collection, payer, ctx.accounts.system_program)?;
-
-    initialize_plugin::<Collection>(
-        &args.plugin,
-        &args.plugin.default_authority(),
+    process_add_plugin::<Collection>(
         ctx.accounts.collection,
         payer,
         ctx.accounts.system_program,
-    )?;
-
-    process_add_plugin()
+        &args.plugin,
+        &args.init_authority.unwrap_or(args.plugin.manager()),
+    )
 }
 
-//TODO
-fn process_add_plugin() -> ProgramResult {
-    Ok(())
+fn process_add_plugin<'a, T: DataBlob + SolanaAccount>(
+    account: &AccountInfo<'a>,
+    payer: &AccountInfo<'a>,
+    system_program: &AccountInfo<'a>,
+    plugin: &Plugin,
+    authority: &Authority,
+) -> ProgramResult {
+    create_meta_idempotent::<T>(account, payer, system_program)?;
+    initialize_plugin::<T>(plugin, authority, account, payer, system_program)
 }
diff --git a/programs/mpl-core/src/processor/approve_plugin_authority.rs b/programs/mpl-core/src/processor/approve_plugin_authority.rs
index 3ef4e7c4..f28963f4 100644
--- a/programs/mpl-core/src/processor/approve_plugin_authority.rs
+++ b/programs/mpl-core/src/processor/approve_plugin_authority.rs
@@ -9,7 +9,7 @@ use crate::{
     },
     plugins::{approve_authority_on_plugin, PluginType},
     state::{Asset, Authority, Collection, CoreAsset, DataBlob, SolanaAccount},
-    utils::fetch_core_data,
+    utils::{fetch_core_data, resolve_payer},
 };
 
 #[repr(C)]
@@ -27,13 +27,7 @@ pub(crate) fn approve_plugin_authority<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     process_approve_plugin_authority::<Asset>(
         ctx.accounts.asset,
@@ -60,13 +54,7 @@ pub(crate) fn approve_collection_plugin_authority<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     process_approve_plugin_authority::<Collection>(
         ctx.accounts.collection,
diff --git a/programs/mpl-core/src/processor/create.rs b/programs/mpl-core/src/processor/create.rs
index 68337b91..8ab210e4 100644
--- a/programs/mpl-core/src/processor/create.rs
+++ b/programs/mpl-core/src/processor/create.rs
@@ -100,7 +100,7 @@ pub(crate) fn create<'a>(accounts: &'a [AccountInfo<'a>], args: CreateArgs) -> P
     );
 
     if args.data_state == DataState::AccountState {
-        create_meta_idempotent(
+        create_meta_idempotent::<Asset>(
             ctx.accounts.asset,
             ctx.accounts.payer,
             ctx.accounts.system_program,
@@ -109,7 +109,7 @@ pub(crate) fn create<'a>(accounts: &'a [AccountInfo<'a>], args: CreateArgs) -> P
         for plugin in &args.plugins {
             initialize_plugin::<Asset>(
                 plugin,
-                &plugin.default_authority(),
+                &plugin.manager(),
                 ctx.accounts.asset,
                 ctx.accounts.payer,
                 ctx.accounts.system_program,
diff --git a/programs/mpl-core/src/processor/create_collection.rs b/programs/mpl-core/src/processor/create_collection.rs
index effae81c..155be97a 100644
--- a/programs/mpl-core/src/processor/create_collection.rs
+++ b/programs/mpl-core/src/processor/create_collection.rs
@@ -77,7 +77,7 @@ pub(crate) fn create_collection<'a>(
 
     drop(serialized_data);
 
-    create_meta_idempotent(
+    create_meta_idempotent::<Collection>(
         ctx.accounts.collection,
         ctx.accounts.payer,
         ctx.accounts.system_program,
@@ -86,7 +86,7 @@ pub(crate) fn create_collection<'a>(
     for plugin in args.plugins {
         initialize_plugin::<Collection>(
             &plugin,
-            &plugin.default_authority(),
+            &plugin.manager(),
             ctx.accounts.collection,
             ctx.accounts.payer,
             ctx.accounts.system_program,
diff --git a/programs/mpl-core/src/processor/remove_plugin.rs b/programs/mpl-core/src/processor/remove_plugin.rs
index 38e973ec..1d6d6dc3 100644
--- a/programs/mpl-core/src/processor/remove_plugin.rs
+++ b/programs/mpl-core/src/processor/remove_plugin.rs
@@ -6,8 +6,8 @@ use crate::{
     error::MplCoreError,
     instruction::accounts::{RemoveCollectionPluginAccounts, RemovePluginAccounts},
     plugins::{delete_plugin, PluginType},
-    state::{Asset, Authority, Collection, SolanaAccount, UpdateAuthority},
-    utils::fetch_core_data,
+    state::{Asset, Authority, Collection},
+    utils::{fetch_core_data, resolve_payer, resolve_to_authority},
 };
 
 #[repr(C)]
@@ -24,13 +24,7 @@ pub(crate) fn remove_plugin<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (asset, plugin_header, plugin_registry) = fetch_core_data::<Asset>(ctx.accounts.asset)?;
 
@@ -40,32 +34,8 @@ pub(crate) fn remove_plugin<'a>(
     }
 
     //TODO: Make this better.
-    let authority_type = if ctx.accounts.authority.key == &asset.owner {
-        Authority::Owner
-    } else if let UpdateAuthority::Address(update_authority) = asset.update_authority {
-        if ctx.accounts.authority.key == &update_authority {
-            Authority::UpdateAuthority
-        } else {
-            return Err(MplCoreError::InvalidAuthority.into());
-        }
-    } else if let UpdateAuthority::Collection(collection_address) = asset.update_authority {
-        match ctx.accounts.collection {
-            Some(collection_info) => {
-                if collection_info.key != &collection_address {
-                    return Err(MplCoreError::InvalidCollection.into());
-                }
-                let collection = Collection::load(collection_info, 0)?;
-                if ctx.accounts.authority.key == &collection.update_authority {
-                    Authority::UpdateAuthority
-                } else {
-                    return Err(MplCoreError::InvalidAuthority.into());
-                }
-            }
-            None => return Err(MplCoreError::InvalidAuthority.into()),
-        }
-    } else {
-        return Err(MplCoreError::InvalidAuthority.into());
-    };
+    let authority_type =
+        resolve_to_authority(ctx.accounts.authority, ctx.accounts.collection, &asset)?;
 
     delete_plugin(
         &args.plugin_type,
@@ -93,13 +63,7 @@ pub(crate) fn remove_collection_plugin<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (collection, plugin_header, plugin_registry) =
         fetch_core_data::<Collection>(ctx.accounts.collection)?;
diff --git a/programs/mpl-core/src/processor/revoke_plugin_authority.rs b/programs/mpl-core/src/processor/revoke_plugin_authority.rs
index 07c40f6e..06a4dd72 100644
--- a/programs/mpl-core/src/processor/revoke_plugin_authority.rs
+++ b/programs/mpl-core/src/processor/revoke_plugin_authority.rs
@@ -8,8 +8,8 @@ use crate::{
         RevokeCollectionPluginAuthorityAccounts, RevokePluginAuthorityAccounts,
     },
     plugins::{revoke_authority_on_plugin, PluginHeader, PluginRegistry, PluginType},
-    state::{Asset, Authority, Collection, SolanaAccount, UpdateAuthority},
-    utils::fetch_core_data,
+    state::{Asset, Authority, Collection},
+    utils::{fetch_core_data, resolve_payer, resolve_to_authority},
 };
 
 #[repr(C)]
@@ -26,43 +26,13 @@ pub(crate) fn revoke_plugin_authority<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (asset, plugin_header, mut plugin_registry) = fetch_core_data::<Asset>(ctx.accounts.asset)?;
 
     //TODO: Make this better.
-    let authority_type = if ctx.accounts.authority.key == &asset.owner {
-        Authority::Owner
-    } else if asset.update_authority == UpdateAuthority::Address(*ctx.accounts.authority.key) {
-        Authority::UpdateAuthority
-    } else if let UpdateAuthority::Collection(collection_address) = asset.update_authority {
-        match ctx.accounts.collection {
-            Some(collection_info) => {
-                if collection_info.key != &collection_address {
-                    return Err(MplCoreError::InvalidCollection.into());
-                }
-                let collection: Collection = Collection::load(collection_info, 0)?;
-                if ctx.accounts.authority.key == &collection.update_authority {
-                    Authority::UpdateAuthority
-                } else {
-                    Authority::Pubkey {
-                        address: *ctx.accounts.authority.key,
-                    }
-                }
-            }
-            None => return Err(MplCoreError::InvalidCollection.into()),
-        }
-    } else {
-        Authority::Pubkey {
-            address: *ctx.accounts.authority.key,
-        }
-    };
+    let authority_type =
+        resolve_to_authority(ctx.accounts.authority, ctx.accounts.collection, &asset)?;
 
     process_revoke_plugin_authority(
         ctx.accounts.asset,
@@ -89,13 +59,7 @@ pub(crate) fn revoke_collection_plugin_authority<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (_, plugin_header, mut plugin_registry) =
         fetch_core_data::<Collection>(ctx.accounts.collection)?;
diff --git a/programs/mpl-core/src/processor/update.rs b/programs/mpl-core/src/processor/update.rs
index 4181593a..8062a719 100644
--- a/programs/mpl-core/src/processor/update.rs
+++ b/programs/mpl-core/src/processor/update.rs
@@ -10,7 +10,8 @@ use crate::{
     plugins::{Plugin, PluginType, RegistryRecord},
     state::{Asset, Collection, DataBlob, SolanaAccount, UpdateAuthority},
     utils::{
-        resize_or_reallocate_account, validate_asset_permissions, validate_collection_permissions,
+        resize_or_reallocate_account, resolve_payer, validate_asset_permissions,
+        validate_collection_permissions,
     },
 };
 
@@ -27,13 +28,7 @@ pub(crate) fn update<'a>(accounts: &'a [AccountInfo<'a>], args: UpdateArgs) -> P
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (mut asset, plugin_header, plugin_registry) = validate_asset_permissions(
         ctx.accounts.authority,
@@ -151,13 +146,7 @@ pub(crate) fn update_collection<'a>(
 
     // Guards.
     assert_signer(ctx.accounts.authority)?;
-    let payer = match ctx.accounts.payer {
-        Some(payer) => {
-            assert_signer(payer)?;
-            payer
-        }
-        None => ctx.accounts.authority,
-    };
+    let payer = resolve_payer(ctx.accounts.authority, ctx.accounts.payer)?;
 
     let (mut asset, plugin_header, plugin_registry) = validate_collection_permissions(
         ctx.accounts.authority,
diff --git a/programs/mpl-core/src/utils.rs b/programs/mpl-core/src/utils.rs
index 597c8fc3..9b751e57 100644
--- a/programs/mpl-core/src/utils.rs
+++ b/programs/mpl-core/src/utils.rs
@@ -1,11 +1,13 @@
+use std::collections::BTreeMap;
+
 use borsh::{BorshDeserialize, BorshSerialize};
+use mpl_utils::assert_signer;
 use num_traits::{FromPrimitive, ToPrimitive};
 use solana_program::{
     account_info::AccountInfo, entrypoint::ProgramResult, program::invoke,
     program_error::ProgramError, program_memory::sol_memcpy, rent::Rent, system_instruction,
     sysvar::Sysvar,
 };
-use std::collections::BTreeMap;
 
 use crate::{
     error::MplCoreError,
@@ -15,7 +17,7 @@ use crate::{
     },
     state::{
         Asset, Authority, Collection, Compressible, CompressionProof, CoreAsset, DataBlob,
-        HashablePluginSchema, HashedAsset, HashedAssetSchema, Key, SolanaAccount,
+        HashablePluginSchema, HashedAsset, HashedAssetSchema, Key, SolanaAccount, UpdateAuthority,
     },
 };
 
@@ -377,7 +379,7 @@ pub fn rebuild_account_state_from_proof_data<'a>(
 
     // Add the plugins.
     if !plugins.is_empty() {
-        create_meta_idempotent(asset_info, payer, system_program)?;
+        create_meta_idempotent::<Asset>(asset_info, payer, system_program)?;
 
         for plugin in plugins {
             initialize_plugin::<Asset>(
@@ -444,3 +446,51 @@ pub fn compress_into_account_space<'a>(
 
     Ok(compression_proof)
 }
+
+pub(crate) fn resolve_to_authority(
+    authority_info: &AccountInfo,
+    maybe_collection_info: Option<&AccountInfo>,
+    asset: &Asset,
+) -> Result<Authority, ProgramError> {
+    let authority_type = if authority_info.key == &asset.owner {
+        Authority::Owner
+    } else if asset.update_authority == UpdateAuthority::Address(*authority_info.key) {
+        Authority::UpdateAuthority
+    } else if let UpdateAuthority::Collection(collection_address) = asset.update_authority {
+        match maybe_collection_info {
+            Some(collection_info) => {
+                if collection_info.key != &collection_address {
+                    return Err(MplCoreError::InvalidCollection.into());
+                }
+                let collection: Collection = Collection::load(collection_info, 0)?;
+                if authority_info.key == &collection.update_authority {
+                    Authority::UpdateAuthority
+                } else {
+                    Authority::Pubkey {
+                        address: *authority_info.key,
+                    }
+                }
+            }
+            None => return Err(MplCoreError::InvalidCollection.into()),
+        }
+    } else {
+        Authority::Pubkey {
+            address: *authority_info.key,
+        }
+    };
+    Ok(authority_type)
+}
+
+/// Resolves the payer for the transaction for an optional payer pattern.
+pub(crate) fn resolve_payer<'a>(
+    authority: &'a AccountInfo<'a>,
+    payer: Option<&'a AccountInfo<'a>>,
+) -> Result<&'a AccountInfo<'a>, ProgramError> {
+    match payer {
+        Some(payer) => {
+            assert_signer(payer).unwrap();
+            Ok(payer)
+        }
+        None => Ok(authority),
+    }
+}