Skip to content

Commit

Permalink
Adding UpdateDelegate for collections.
Browse files Browse the repository at this point in the history
  • Loading branch information
blockiosaurus committed Feb 28, 2024
1 parent cadab57 commit 401743a
Show file tree
Hide file tree
Showing 24 changed files with 490 additions and 44 deletions.
1 change: 1 addition & 0 deletions clients/js/src/generated/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from './registryRecord';
export * from './royalties';
export * from './ruleSet';
export * from './transfer';
export * from './updateDelegate';
19 changes: 17 additions & 2 deletions clients/js/src/generated/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ import {
RoyaltiesArgs,
Transfer,
TransferArgs,
UpdateDelegate,
UpdateDelegateArgs,
getBurnSerializer,
getCollectionSerializer,
getFreezeSerializer,
getRoyaltiesSerializer,
getTransferSerializer,
getUpdateDelegateSerializer,
} from '.';

export type Plugin =
Expand All @@ -39,15 +42,17 @@ export type Plugin =
| { __kind: 'Freeze'; fields: [Freeze] }
| { __kind: 'Burn'; fields: [Burn] }
| { __kind: 'Transfer'; fields: [Transfer] }
| { __kind: 'Collection'; fields: [Collection] };
| { __kind: 'Collection'; fields: [Collection] }
| { __kind: 'UpdateDelegate'; fields: [UpdateDelegate] };

export type PluginArgs =
| { __kind: 'Reserved' }
| { __kind: 'Royalties'; fields: [RoyaltiesArgs] }
| { __kind: 'Freeze'; fields: [FreezeArgs] }
| { __kind: 'Burn'; fields: [BurnArgs] }
| { __kind: 'Transfer'; fields: [TransferArgs] }
| { __kind: 'Collection'; fields: [CollectionArgs] };
| { __kind: 'Collection'; fields: [CollectionArgs] }
| { __kind: 'UpdateDelegate'; fields: [UpdateDelegateArgs] };

export function getPluginSerializer(): Serializer<PluginArgs, Plugin> {
return dataEnum<Plugin>(
Expand Down Expand Up @@ -83,6 +88,12 @@ export function getPluginSerializer(): Serializer<PluginArgs, Plugin> {
['fields', tuple([getCollectionSerializer()])],
]),
],
[
'UpdateDelegate',
struct<GetDataEnumKindContent<Plugin, 'UpdateDelegate'>>([
['fields', tuple([getUpdateDelegateSerializer()])],
]),
],
],
{ description: 'Plugin' }
) as Serializer<PluginArgs, Plugin>;
Expand Down Expand Up @@ -112,6 +123,10 @@ export function plugin(
kind: 'Collection',
data: GetDataEnumKindContent<PluginArgs, 'Collection'>['fields']
): GetDataEnumKind<PluginArgs, 'Collection'>;
export function plugin(
kind: 'UpdateDelegate',
data: GetDataEnumKindContent<PluginArgs, 'UpdateDelegate'>['fields']
): GetDataEnumKind<PluginArgs, 'UpdateDelegate'>;
export function plugin<K extends PluginArgs['__kind']>(
kind: K,
data?: any
Expand Down
1 change: 1 addition & 0 deletions clients/js/src/generated/types/pluginType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum PluginType {
Burn,
Transfer,
Collection,
UpdateDelegate,
}

export type PluginTypeArgs = PluginType;
Expand Down
22 changes: 22 additions & 0 deletions clients/js/src/generated/types/updateDelegate.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 UpdateDelegate = {};

export type UpdateDelegateArgs = UpdateDelegate;

export function getUpdateDelegateSerializer(): Serializer<
UpdateDelegateArgs,
UpdateDelegate
> {
return struct<UpdateDelegate>([], {
description: 'UpdateDelegate',
}) as Serializer<UpdateDelegateArgs, UpdateDelegate>;
}
136 changes: 136 additions & 0 deletions clients/js/test/collectionUpdateDelegate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { generateSigner } from '@metaplex-foundation/umi';
import test from 'ava';
import {
AssetWithPlugins,
CollectionWithPlugins,
DataState,
PluginType,
addAuthority,
addPlugin,
create,
createCollection,
fetchAssetWithPlugins,
fetchCollectionWithPlugins,
} from '../src';
import { createUmi } from './_setup';

test('it can create a new asset with a collection if it is the collection update delegate', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();
const collectionAddress = generateSigner(umi);
const assetAddress = generateSigner(umi);
const updateDelegate = generateSigner(umi);

// When we create a new account.
await createCollection(umi, {
collectionAddress,
name: 'Test Bread Collection',
uri: 'https://example.com/bread',
plugins: []
}).sendAndConfirm(umi);

await addPlugin(umi, {
assetAddress: collectionAddress.publicKey,
plugin: {
__kind: 'UpdateDelegate',
fields: [{}],
}
}).sendAndConfirm(umi);

// console.log(JSON.stringify(await fetchCollectionWithPlugins(umi, collectionAddress.publicKey), (_, v) => typeof v === 'bigint' ? v.toString() : v, 2));

await addAuthority(umi, {
assetAddress: collectionAddress.publicKey,
pluginType: PluginType.UpdateDelegate,
newAuthority: {
__kind: 'Pubkey',
address: updateDelegate.publicKey,
}
}).sendAndConfirm(umi);

const collection = await fetchCollectionWithPlugins(umi, collectionAddress.publicKey);
// console.log("Account State:", collection);
t.like(collection, <CollectionWithPlugins>{
publicKey: collectionAddress.publicKey,
updateAuthority: umi.identity.publicKey,
name: 'Test Bread Collection',
uri: 'https://example.com/bread',
pluginHeader: {
key: 3,
pluginRegistryOffset: BigInt(105),
},
pluginRegistry: {
key: 4,
registry: [
{
pluginType: PluginType.UpdateDelegate,
offset: BigInt(104),
authorities: [
{ __kind: 'UpdateAuthority' },
{ __kind: 'Pubkey', address: updateDelegate.publicKey }
],
},
],
},
plugins: [
{
authorities: [
{ __kind: 'UpdateAuthority' },
{ __kind: 'Pubkey', address: updateDelegate.publicKey }
],
plugin: {
__kind: 'UpdateDelegate',
fields: [{}],
},
},
],
});

// When we create a new account.
await create(umi, {
dataState: DataState.AccountState,
assetAddress,
name: 'Test Bread',
uri: 'https://example.com/bread',
collection: collectionAddress.publicKey,
plugins: [{
__kind: 'Collection', fields: [{
collectionAddress: collectionAddress.publicKey,
managed: true
}]
}],
}).sendAndConfirm(umi);

const asset = await fetchAssetWithPlugins(umi, assetAddress.publicKey);
console.log("Asset State:", asset);
t.like(asset, <AssetWithPlugins>{
publicKey: assetAddress.publicKey,
updateAuthority: umi.identity.publicKey,
owner: umi.identity.publicKey,
name: 'Test Bread',
uri: 'https://example.com/bread',
pluginHeader: {
key: 3,
pluginRegistryOffset: BigInt(151),
},
pluginRegistry: {
key: 4,
registry: [
{
pluginType: PluginType.Collection,
offset: BigInt(117),
authorities: [{ __kind: 'UpdateAuthority' }],
},
],
},
plugins: [
{
authorities: [{ __kind: 'UpdateAuthority' }],
plugin: {
__kind: 'Collection',
fields: [{ collectionAddress: collectionAddress.publicKey, managed: true }],
},
},
],
});
});
2 changes: 2 additions & 0 deletions clients/rust/src/generated/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub(crate) mod registry_record;
pub(crate) mod royalties;
pub(crate) mod rule_set;
pub(crate) mod transfer;
pub(crate) mod update_delegate;

pub use self::authority::*;
pub use self::burn::*;
Expand All @@ -42,3 +43,4 @@ pub use self::registry_record::*;
pub use self::royalties::*;
pub use self::rule_set::*;
pub use self::transfer::*;
pub use self::update_delegate::*;
2 changes: 2 additions & 0 deletions clients/rust/src/generated/types/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::generated::types::Collection;
use crate::generated::types::Freeze;
use crate::generated::types::Royalties;
use crate::generated::types::Transfer;
use crate::generated::types::UpdateDelegate;
use borsh::BorshDeserialize;
use borsh::BorshSerialize;

Expand All @@ -22,4 +23,5 @@ pub enum Plugin {
Burn(Burn),
Transfer(Transfer),
Collection(Collection),
UpdateDelegate(UpdateDelegate),
}
1 change: 1 addition & 0 deletions clients/rust/src/generated/types/plugin_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ pub enum PluginType {
Burn,
Transfer,
Collection,
UpdateDelegate,
}
13 changes: 13 additions & 0 deletions clients/rust/src/generated/types/update_delegate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! 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.
//!
//! [https://github.com/metaplex-foundation/kinobi]
//!
use borsh::BorshDeserialize;
use borsh::BorshSerialize;

#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UpdateDelegate {}
18 changes: 18 additions & 0 deletions idls/mpl_core_program.json
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,13 @@
"fields": []
}
},
{
"name": "UpdateDelegate",
"type": {
"kind": "struct",
"fields": []
}
},
{
"name": "AddAuthorityArgs",
"type": {
Expand Down Expand Up @@ -1396,6 +1403,14 @@
"defined": "Collection"
}
]
},
{
"name": "UpdateDelegate",
"fields": [
{
"defined": "UpdateDelegate"
}
]
}
]
}
Expand All @@ -1422,6 +1437,9 @@
},
{
"name": "Collection"
},
{
"name": "UpdateDelegate"
}
]
}
Expand Down
16 changes: 13 additions & 3 deletions programs/mpl-core/src/plugins/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use crate::{
BurnAccounts, CompressAccounts, CreateAccounts, DecompressAccounts, TransferAccounts,
UpdateAccounts,
},
plugins::{fetch_plugin, PluginType},
processor::{BurnArgs, CompressArgs, CreateArgs, DecompressArgs, TransferArgs, UpdateArgs},
state::{Authority, CollectionData, DataBlob, SolanaAccount},
utils::assert_collection_authority,
};

use super::{PluginValidation, ValidationResult};
Expand Down Expand Up @@ -43,8 +45,8 @@ impl PluginValidation for Collection {
_authorities: &[Authority],
) -> Result<super::ValidationResult, solana_program::program_error::ProgramError> {
match ctx.collection {
Some(collection) => {
let collection = CollectionData::load(collection, 0)?;
Some(collection_info) => {
let collection = CollectionData::load(collection_info, 0)?;
solana_program::msg!("Collection: {:?}", collection);
// Check that the collection update authority is a signer.
let authority = match ctx.update_authority {
Expand All @@ -54,7 +56,15 @@ impl PluginValidation for Collection {

assert_signer(authority)?;

if authority.key != &collection.update_authority {
let maybe_update_delegate =
fetch_plugin(collection_info, PluginType::UpdateDelegate);

if let Ok((mut authorities, _, _)) = maybe_update_delegate {
authorities.push(Authority::UpdateAuthority);
if assert_collection_authority(&collection, authority, &authorities).is_err() {
return Ok(ValidationResult::Rejected);
}
} else if authority.key != &collection.update_authority {
return Ok(ValidationResult::Rejected);
}

Expand Down
Loading

0 comments on commit 401743a

Please sign in to comment.