diff --git a/clients/js/test/removeAuthority.test.ts b/clients/js/test/removeAuthority.test.ts index a108a7c9..cb51d69e 100644 --- a/clients/js/test/removeAuthority.test.ts +++ b/clients/js/test/removeAuthority.test.ts @@ -15,6 +15,7 @@ import { plugin, removePluginAuthority, updateAuthority, + createCollection, } from '../src'; import { createUmi } from './_setup'; @@ -300,3 +301,93 @@ test('it can remove a pubkey authority from a plugin if that pubkey is the signe ], }); }); + +test('it can remove a pubkey authority from a asset in collection if that pubkey is the signer authority', async (t) => { + // Given a Umi instance and a new signer. + const umi = await createUmi(); + const assetAddress = generateSigner(umi); + const pubkeyAuth = await generateSignerWithSol(umi); + const collectionAddress = generateSigner(umi); + + await createCollection(umi, { + collection: collectionAddress, + name: 'Test Collection', + uri: 'https://example.com/collection', + plugins: [] + }).sendAndConfirm(umi); + + // When we create a new account. + await create(umi, { + dataState: DataState.AccountState, + collection: collectionAddress.publicKey, + asset: assetAddress, + name: 'Test Bread', + uri: 'https://example.com/bread', + plugins: [], + }).sendAndConfirm(umi); + + await addPlugin(umi, { + asset: assetAddress.publicKey, + plugin: plugin('Freeze', [{ frozen: false }]), + }).sendAndConfirm(umi); + + await addPluginAuthority(umi, { + asset: assetAddress.publicKey, + pluginType: PluginType.Freeze, + newAuthority: { + __kind: 'Pubkey', + address: pubkeyAuth.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, { + publicKey: assetAddress.publicKey, + updateAuthority: updateAuthority('Collection', [collectionAddress.publicKey]), + owner: umi.identity.publicKey, + name: 'Test Bread', + uri: 'https://example.com/bread', + plugins: [ + { + authorities: [{ __kind: 'Owner' }, { __kind: 'Pubkey', address: pubkeyAuth.publicKey }], + plugin: { + __kind: 'Freeze', + fields: [{ frozen: false }], + }, + }, + ], + }); + + const umi2 = await createUmi(); + + await removePluginAuthority(umi2, { + payer: umi2.identity, + asset: assetAddress.publicKey, + authority: pubkeyAuth, + pluginType: PluginType.Freeze, + authorityToRemove: { + __kind: 'Pubkey', + address: pubkeyAuth.publicKey, + }, + }).sendAndConfirm(umi); + + const asset2 = await fetchAssetWithPlugins(umi, assetAddress.publicKey); + // console.log(JSON.stringify(asset1, (_, v) => typeof v === 'bigint' ? v.toString() : v, 2)); + t.like(asset2, { + publicKey: assetAddress.publicKey, + updateAuthority: updateAuthority('Collection', [collectionAddress.publicKey]), + owner: umi.identity.publicKey, + name: 'Test Bread', + uri: 'https://example.com/bread', + plugins: [ + { + authorities: [{ __kind: 'Owner' }], + plugin: { + __kind: 'Freeze', + fields: [{ frozen: false }], + }, + }, + ], + }); +}); diff --git a/programs/mpl-core/src/processor/remove_plugin_authority.rs b/programs/mpl-core/src/processor/remove_plugin_authority.rs index 81b065cf..71ec16cd 100644 --- a/programs/mpl-core/src/processor/remove_plugin_authority.rs +++ b/programs/mpl-core/src/processor/remove_plugin_authority.rs @@ -36,6 +36,35 @@ pub(crate) fn remove_plugin_authority<'a>( }; let (asset, plugin_header, mut plugin_registry) = fetch_core_data::(ctx.accounts.asset)?; + + // pubkey authorities can remove themselves if they are a signer, skip subsequent checks + // TODO refactor this, duplicate code + let plugin_registry_some = match plugin_registry.as_ref() { + Some(registry) => registry, + None => return Err(MplCoreError::PluginsNotInitialized.into()), + }; + + let registry_record = &plugin_registry_some + .registry + .iter() + .find(|record| record.plugin_type == args.plugin_type) + .ok_or(MplCoreError::PluginNotFound)?; + if (Authority::Pubkey { address: ctx.accounts.authority.key.clone() } == args.authority_to_remove.clone() && + registry_record.authorities.contains(&args.authority_to_remove)) { + return process_remove_plugin_authority( + ctx.accounts.asset, + payer, + ctx.accounts.system_program, + &Authority::Pubkey { + address: *ctx.accounts.authority.key, + }, + &args.plugin_type, + &args.authority_to_remove, + plugin_header.as_ref(), + plugin_registry.as_mut(), + ); + } + // End really bad code //TODO: Make this better. let authority_type = if ctx.accounts.authority.key == &asset.owner {