Skip to content

Commit

Permalink
Immutability plugins (#96)
Browse files Browse the repository at this point in the history
* Add ImmutableMetadata && AddBlocker plugins

* Update autogenerated parts

* add tests

* change the order of enums

* update generated part

* added tests for ensuring that UA is the only one who can add the plugin

* added tests for ensuring that UA is the only one who can add the plugin for collection and nested plugins

* update tests

* add audit details to readme (#103)

* removed audit warning (#108)

* regenerated clients

* updated tests

* updated rust clients
  • Loading branch information
kstepanovdev authored May 8, 2024
1 parent e6a8331 commit 38774bf
Show file tree
Hide file tree
Showing 24 changed files with 958 additions and 10 deletions.
23 changes: 23 additions & 0 deletions clients/js/src/generated/types/addBlocker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* This code was AUTOGENERATED using the kinobi library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun kinobi to update it.
*
* @see https://github.com/metaplex-foundation/kinobi
*/

import { Serializer, struct } from '@metaplex-foundation/umi/serializers';

export type AddBlocker = {};

export type AddBlockerArgs = AddBlocker;

export function getAddBlockerSerializer(): Serializer<
AddBlockerArgs,
AddBlocker
> {
return struct<AddBlocker>([], { description: 'AddBlocker' }) as Serializer<
AddBlockerArgs,
AddBlocker
>;
}
22 changes: 22 additions & 0 deletions clients/js/src/generated/types/immutableMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* This code was AUTOGENERATED using the kinobi library.
* Please DO NOT EDIT THIS FILE, instead use visitors
* to add features, then rerun kinobi to update it.
*
* @see https://github.com/metaplex-foundation/kinobi
*/

import { Serializer, struct } from '@metaplex-foundation/umi/serializers';

export type ImmutableMetadata = {};

export type ImmutableMetadataArgs = ImmutableMetadata;

export function getImmutableMetadataSerializer(): Serializer<
ImmutableMetadataArgs,
ImmutableMetadata
> {
return struct<ImmutableMetadata>([], {
description: 'ImmutableMetadata',
}) as Serializer<ImmutableMetadataArgs, ImmutableMetadata>;
}
2 changes: 2 additions & 0 deletions clients/js/src/generated/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @see https://github.com/metaplex-foundation/kinobi
*/

export * from './addBlocker';
export * from './attribute';
export * from './attributes';
export * from './burnDelegate';
Expand All @@ -18,6 +19,7 @@ export * from './extraAccounts';
export * from './freezeDelegate';
export * from './hashablePluginSchema';
export * from './hashedAssetSchema';
export * from './immutableMetadata';
export * from './key';
export * from './masterEdition';
export * from './permanentBurnDelegate';
Expand Down
34 changes: 32 additions & 2 deletions clients/js/src/generated/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
tuple,
} from '@metaplex-foundation/umi/serializers';
import {
AddBlocker,
AddBlockerArgs,
Attributes,
AttributesArgs,
BurnDelegate,
Expand All @@ -23,6 +25,8 @@ import {
EditionArgs,
FreezeDelegate,
FreezeDelegateArgs,
ImmutableMetadata,
ImmutableMetadataArgs,
MasterEdition,
MasterEditionArgs,
PermanentBurnDelegate,
Expand All @@ -37,10 +41,12 @@ import {
TransferDelegateArgs,
UpdateDelegate,
UpdateDelegateArgs,
getAddBlockerSerializer,
getAttributesSerializer,
getBurnDelegateSerializer,
getEditionSerializer,
getFreezeDelegateSerializer,
getImmutableMetadataSerializer,
getMasterEditionSerializer,
getPermanentBurnDelegateSerializer,
getPermanentFreezeDelegateSerializer,
Expand All @@ -61,7 +67,9 @@ export type Plugin =
| { __kind: 'PermanentTransferDelegate'; fields: [PermanentTransferDelegate] }
| { __kind: 'PermanentBurnDelegate'; fields: [PermanentBurnDelegate] }
| { __kind: 'Edition'; fields: [Edition] }
| { __kind: 'MasterEdition'; fields: [MasterEdition] };
| { __kind: 'MasterEdition'; fields: [MasterEdition] }
| { __kind: 'AddBlocker'; fields: [AddBlocker] }
| { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadata] };

export type PluginArgs =
| { __kind: 'Royalties'; fields: [RoyaltiesArgs] }
Expand All @@ -77,7 +85,9 @@ export type PluginArgs =
}
| { __kind: 'PermanentBurnDelegate'; fields: [PermanentBurnDelegateArgs] }
| { __kind: 'Edition'; fields: [EditionArgs] }
| { __kind: 'MasterEdition'; fields: [MasterEditionArgs] };
| { __kind: 'MasterEdition'; fields: [MasterEditionArgs] }
| { __kind: 'AddBlocker'; fields: [AddBlockerArgs] }
| { __kind: 'ImmutableMetadata'; fields: [ImmutableMetadataArgs] };

export function getPluginSerializer(): Serializer<PluginArgs, Plugin> {
return dataEnum<Plugin>(
Expand Down Expand Up @@ -148,6 +158,18 @@ export function getPluginSerializer(): Serializer<PluginArgs, Plugin> {
['fields', tuple([getMasterEditionSerializer()])],
]),
],
[
'AddBlocker',
struct<GetDataEnumKindContent<Plugin, 'AddBlocker'>>([
['fields', tuple([getAddBlockerSerializer()])],
]),
],
[
'ImmutableMetadata',
struct<GetDataEnumKindContent<Plugin, 'ImmutableMetadata'>>([
['fields', tuple([getImmutableMetadataSerializer()])],
]),
],
],
{ description: 'Plugin' }
) as Serializer<PluginArgs, Plugin>;
Expand Down Expand Up @@ -201,6 +223,14 @@ export function plugin(
kind: 'MasterEdition',
data: GetDataEnumKindContent<PluginArgs, 'MasterEdition'>['fields']
): GetDataEnumKind<PluginArgs, 'MasterEdition'>;
export function plugin(
kind: 'AddBlocker',
data: GetDataEnumKindContent<PluginArgs, 'AddBlocker'>['fields']
): GetDataEnumKind<PluginArgs, 'AddBlocker'>;
export function plugin(
kind: 'ImmutableMetadata',
data: GetDataEnumKindContent<PluginArgs, 'ImmutableMetadata'>['fields']
): GetDataEnumKind<PluginArgs, 'ImmutableMetadata'>;
export function plugin<K extends PluginArgs['__kind']>(
kind: K,
data?: any
Expand Down
2 changes: 2 additions & 0 deletions clients/js/src/generated/types/pluginType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export enum PluginType {
PermanentBurnDelegate,
Edition,
MasterEdition,
AddBlocker,
ImmutableMetadata,
}

export type PluginTypeArgs = PluginType;
Expand Down
6 changes: 6 additions & 0 deletions clients/js/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export type CreatePluginArgs =
| {
type: 'MasterEdition';
data: MasterEditionArgs;
}
| {
type: 'ImmutableMetadata';
}
| {
type: 'AddBlocker';
};

export function createPlugin(args: CreatePluginArgs): BasePlugin {
Expand Down
6 changes: 6 additions & 0 deletions clients/js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
PermanentBurnDelegate,
Edition,
MasterEdition,
ImmutableMetadata,
AddBlocker,
} from './generated';

export type BasePluginAuthority = {
Expand Down Expand Up @@ -46,6 +48,8 @@ export type PermanentTransferDelegatePlugin = BasePlugin &
export type PermanentBurnDelegatePlugin = BasePlugin & PermanentBurnDelegate;
export type EditionPlugin = BasePlugin & Edition;
export type MasterEditionPlugin = BasePlugin & MasterEdition;
export type AddBlockerPlugin = BasePlugin & AddBlocker;
export type ImmutableMetadataPlugin = BasePlugin & ImmutableMetadata;

export type PluginsList = {
royalties?: RoyaltiesPlugin;
Expand All @@ -59,4 +63,6 @@ export type PluginsList = {
permanentBurnDelegate?: PermanentBurnDelegatePlugin;
edition?: EditionPlugin;
masterEdition?: MasterEditionPlugin;
addBlocker?: AddBlockerPlugin;
immutableMetadata?: ImmutableMetadataPlugin;
};
178 changes: 178 additions & 0 deletions clients/js/test/plugins/asset/addBlocker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import test from 'ava';
import { generateSigner } from '@metaplex-foundation/umi';

import { addPluginV1, createPlugin, pluginAuthorityPair } from '../../../src';
import {
DEFAULT_ASSET,
assertAsset,
createAsset,
createUmi,
} from '../../_setup';

test('it cannot add UA-managed plugin if addBlocker had been added on creation', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();

const asset = await createAsset(umi, {
plugins: [
pluginAuthorityPair({
type: 'AddBlocker',
}),
],
});

const result = addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'Attributes',
data: {
attributeList: [],
},
}),
}).sendAndConfirm(umi);

await t.throwsAsync(result, {
name: 'InvalidAuthority',
});
});

test('it can add plugins unless AddBlocker is added', async (t) => {
const umi = await createUmi();
const asset = await createAsset(umi);

await addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'Attributes',
data: {
attributeList: [],
},
}),
}).sendAndConfirm(umi);

await addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'AddBlocker',
}),
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: umi.identity.publicKey,
attributes: {
authority: {
type: 'UpdateAuthority',
},
attributeList: [],
},
addBlocker: {
authority: {
type: 'UpdateAuthority',
},
},
});

const result = addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'Attributes',
data: {
attributeList: [],
},
}),
}).sendAndConfirm(umi);

await t.throwsAsync(result, {
name: 'InvalidAuthority',
});
});

test('it can add owner-managed plugins even if AddBlocker had been added', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();

const asset = await createAsset(umi, {
plugins: [
pluginAuthorityPair({
type: 'AddBlocker',
}),
],
});

await addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({ type: 'FreezeDelegate', data: { frozen: false } }),
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: umi.identity.publicKey,
freezeDelegate: {
authority: {
type: 'Owner',
},
frozen: false,
},
addBlocker: {
authority: {
type: 'UpdateAuthority',
},
},
});
});

test('it states that UA is the only one who can add the AddBlocker', async (t) => {
const umi = await createUmi();
const updateAuthority = generateSigner(umi);
const randomUser = generateSigner(umi);
const asset = await createAsset(umi, {
updateAuthority: updateAuthority.publicKey,
});

// random keypair can't add AddBlocker
let result = addPluginV1(umi, {
authority: randomUser,
asset: asset.publicKey,
plugin: createPlugin({
type: 'AddBlocker',
}),
}).sendAndConfirm(umi);

await t.throwsAsync(result, {
name: 'NoApprovals',
});

// Owner can't add AddBlocker
result = addPluginV1(umi, {
authority: umi.identity,
asset: asset.publicKey,
plugin: createPlugin({
type: 'AddBlocker',
}),
}).sendAndConfirm(umi);

await t.throwsAsync(result, {
name: 'NoApprovals',
});

// UA CAN add AddBlocker
await addPluginV1(umi, {
authority: updateAuthority,
asset: asset.publicKey,
plugin: createPlugin({ type: 'AddBlocker' }),
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: umi.identity.publicKey,
addBlocker: {
authority: {
type: 'UpdateAuthority',
},
},
});
});
Loading

0 comments on commit 38774bf

Please sign in to comment.