Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions programs/mpl-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ pub enum MplCoreError {
/// 50 - Bubblegum V2 Plugin limits other plugins
#[error("Bubblegum V2 Plugin limits other plugins")]
BlockedByBubblegumV2,

/// 51 - Group must be empty to be closed
#[error("Group must be empty to be closed")]
GroupMustBeEmpty,
}

impl PrintProgramError for MplCoreError {
Expand Down
150 changes: 142 additions & 8 deletions programs/mpl-core/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@ use borsh::{BorshDeserialize, BorshSerialize};
use shank::{ShankContext, ShankInstruction};

use crate::processor::{
AddCollectionExternalPluginAdapterV1Args, AddCollectionPluginV1Args,
AddExternalPluginAdapterV1Args, AddPluginV1Args, ApproveCollectionPluginAuthorityV1Args,
ApprovePluginAuthorityV1Args, BurnCollectionV1Args, BurnV1Args, CompressV1Args,
CreateCollectionV1Args, CreateCollectionV2Args, CreateV1Args, CreateV2Args, DecompressV1Args,
ExecuteV1Args, RemoveCollectionExternalPluginAdapterV1Args, RemoveCollectionPluginV1Args,
RemoveExternalPluginAdapterV1Args, RemovePluginV1Args, RevokeCollectionPluginAuthorityV1Args,
AddAssetsToGroupV1Args, AddCollectionExternalPluginAdapterV1Args, AddCollectionPluginV1Args,
AddCollectionsToGroupV1Args, AddExternalPluginAdapterV1Args,
AddGroupExternalPluginAdapterV1Args, AddGroupPluginV1Args, AddGroupsToGroupV1Args,
AddPluginV1Args, ApproveCollectionPluginAuthorityV1Args, ApproveGroupPluginAuthorityV1Args,
ApprovePluginAuthorityV1Args, BurnCollectionV1Args, BurnV1Args, CloseGroupV1Args,
CompressV1Args, CreateCollectionV1Args, CreateCollectionV2Args, CreateGroupV1Args,
CreateV1Args, CreateV2Args, DecompressV1Args, ExecuteV1Args, RemoveAssetsFromGroupV1Args,
RemoveCollectionExternalPluginAdapterV1Args, RemoveCollectionPluginV1Args,
RemoveCollectionsFromGroupV1Args, RemoveExternalPluginAdapterV1Args,
RemoveGroupExternalPluginAdapterV1Args, RemoveGroupPluginV1Args, RemoveGroupsFromGroupV1Args,
RemovePluginV1Args, RevokeCollectionPluginAuthorityV1Args, RevokeGroupPluginAuthorityV1Args,
RevokePluginAuthorityV1Args, TransferV1Args, UpdateCollectionExternalPluginAdapterV1Args,
UpdateCollectionInfoV1Args, UpdateCollectionPluginV1Args, UpdateCollectionV1Args,
UpdateExternalPluginAdapterV1Args, UpdatePluginV1Args, UpdateV1Args, UpdateV2Args,
WriteCollectionExternalPluginAdapterDataV1Args, WriteExternalPluginAdapterDataV1Args,
UpdateExternalPluginAdapterV1Args, UpdateGroupPluginV1Args, UpdateGroupV1Args,
UpdatePluginV1Args, UpdateV1Args, UpdateV2Args, WriteCollectionExternalPluginAdapterDataV1Args,
WriteExternalPluginAdapterDataV1Args, WriteGroupExternalPluginAdapterDataV1Args,
};

/// Instructions supported by the mpl-core program.
Expand All @@ -39,6 +45,27 @@ pub(crate) enum MplAssetInstruction {
#[account(3, name="system_program", desc = "The system program")]
CreateCollectionV1(CreateCollectionV1Args),

/// Create a new Group account.
#[account(0, writable, signer, name="group", desc = "The address of the new group")]
#[account(1, optional, name="update_authority", desc = "The authority of the new group")]
#[account(2, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(3, name="system_program", desc = "The system program")]
CreateGroupV1(CreateGroupV1Args),

/// Close an existing Group account. The group must have no parent or child relationships.
#[account(0, writable, name="group", desc = "The address of the group to close")]
#[account(1, writable, signer, name="payer", desc = "The account receiving reclaimed lamports")]
#[account(2, optional, signer, name="authority", desc = "The update authority or update delegate of the group")]
CloseGroupV1(CloseGroupV1Args),

/// Update an existing Group account.
#[account(0, writable, name="group", desc = "The address of the group to update")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or update delegate of the group")]
#[account(3, optional, name="new_update_authority", desc = "The new update authority of the group")]
#[account(4, name="system_program", desc = "The system program")]
UpdateGroupV1(UpdateGroupV1Args),

/// Add a plugin to an mpl-core.
#[account(0, writable, name="asset", desc = "The address of the asset")]
#[account(1, optional, writable, name="collection", desc = "The collection to which the asset belongs")]
Expand Down Expand Up @@ -307,4 +334,111 @@ pub(crate) enum MplAssetInstruction {
#[account(0, writable, name="collection", desc = "The address of the asset")]
#[account(1, signer, name="bubblegum_signer", desc = "Bubblegum PDA signer")]
UpdateCollectionInfoV1(UpdateCollectionInfoV1Args),

/// Add collections to a group.
#[account(0, writable, name="group", desc = "The address of the group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group/collections")]
#[account(3, name="system_program", desc = "The system program")]
AddCollectionsToGroupV1(AddCollectionsToGroupV1Args),

/// Remove collections from a group.
#[account(0, writable, name="group", desc = "The address of the group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group/collections")]
#[account(3, name="system_program", desc = "The system program")]
RemoveCollectionsFromGroupV1(RemoveCollectionsFromGroupV1Args),

/// Add assets to a group.
#[account(0, writable, name="group", desc = "The address of the group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group/assets")]
#[account(3, name="system_program", desc = "The system program")]
AddAssetsToGroupV1(AddAssetsToGroupV1Args),

/// Remove assets from a group.
#[account(0, writable, name="group", desc = "The address of the group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group/assets")]
#[account(3, name="system_program", desc = "The system program")]
RemoveAssetsFromGroupV1(RemoveAssetsFromGroupV1Args),

/// Add groups to a parent group.
#[account(0, writable, name="parent_group", desc = "The address of the parent group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the groups")]
#[account(3, name="system_program", desc = "The system program")]
AddGroupsToGroupV1(AddGroupsToGroupV1Args),

/// Remove groups from a parent group.
#[account(0, writable, name="parent_group", desc = "The address of the parent group to modify")]
#[account(1, writable, signer, name="payer", desc = "The account paying for storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the groups")]
#[account(3, name="system_program", desc = "The system program")]
RemoveGroupsFromGroupV1(RemoveGroupsFromGroupV1Args),

/// Add a plugin to a Group account.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
AddGroupPluginV1(AddGroupPluginV1Args),

/// Remove a plugin from a Group account.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
RemoveGroupPluginV1(RemoveGroupPluginV1Args),

/// Update a plugin of a Group account.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
UpdateGroupPluginV1(UpdateGroupPluginV1Args),

/// Approve an authority to a Group plugin.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
ApproveGroupPluginAuthorityV1(ApproveGroupPluginAuthorityV1Args),

/// Revoke an authority from a Group plugin.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
RevokeGroupPluginAuthorityV1(RevokeGroupPluginAuthorityV1Args),

/// Add an external plugin adapter to a Group.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
AddGroupExternalPluginAdapterV1(AddGroupExternalPluginAdapterV1Args),

/// Remove an external plugin adapter from a Group.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The update authority or delegate of the group")]
#[account(3, name="system_program", desc = "The system program")]
#[account(4, optional, name="log_wrapper", desc = "The SPL Noop Program")]
RemoveGroupExternalPluginAdapterV1(RemoveGroupExternalPluginAdapterV1Args),

/// Write data to a Group external plugin adapter.
#[account(0, writable, name="group", desc = "The address of the group")]
#[account(1, writable, signer, name="payer", desc = "The account paying for the storage fees")]
#[account(2, optional, signer, name="authority", desc = "The data authority or update authority of the group")]
#[account(3, optional, name="buffer", desc = "Buffer account containing data")]
#[account(4, name="system_program", desc = "The system program")]
#[account(5, optional, name="log_wrapper", desc = "The SPL Noop Program")]
WriteGroupExternalPluginAdapterDataV1(WriteGroupExternalPluginAdapterDataV1Args),
}
25 changes: 25 additions & 0 deletions programs/mpl-core/src/plugins/internal/authority_managed/groups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use crate::{plugins::PluginValidation, state::DataBlob};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::pubkey::Pubkey;

/// Groups plugin for collections. Stores the immediate parent group accounts this collection
/// belongs to. Validation is deferred to specialized group instructions, so the plugin itself has
/// no special lifecycle behaviour beyond the default `PluginValidation` implementation.
#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq, Default)]
pub struct Groups {
/// The list of parent group accounts for this collection.
pub groups: Vec<Pubkey>, // 4 + len * 32
}

impl Groups {
const BASE_LEN: usize = 4; // length of the groups vector
}

impl DataBlob for Groups {
fn len(&self) -> usize {
Self::BASE_LEN + self.groups.len() * 32
}
}

impl PluginValidation for Groups {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod add_blocker;
mod attributes;
mod groups;
mod immutable_metadata;
mod master_edition;
mod royalties;
Expand All @@ -8,6 +9,7 @@ mod verified_creators;

pub use add_blocker::*;
pub use attributes::*;
pub use groups::*;
pub use immutable_metadata::*;
pub use master_edition::*;
pub use royalties::*;
Expand Down
12 changes: 12 additions & 0 deletions programs/mpl-core/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum Plugin {
ImmutableMetadata(ImmutableMetadata),
/// VerifiedCreators plugin allows update auth to specify verified creators and additional creators to sign
VerifiedCreators(VerifiedCreators),
/// Groups plugin stores parent group memberships of a collection for taxonomy purposes
Groups(Groups),
/// Autograph plugin allows anybody to add their signature to the asset with an optional message
Autograph(Autograph),
/// The Bubblegum V2 plugin allows a Core collection to contain Compressed NFTs (cNFTs) from the Bubblegum program.
Expand Down Expand Up @@ -104,6 +106,7 @@ impl Plugin {
Plugin::AddBlocker(inner) => inner,
Plugin::ImmutableMetadata(inner) => inner,
Plugin::VerifiedCreators(inner) => inner,
Plugin::Groups(inner) => inner,
Plugin::Autograph(inner) => inner,
Plugin::BubblegumV2(inner) => inner,
}
Expand Down Expand Up @@ -136,6 +139,7 @@ impl DataBlob for Plugin {
Plugin::AddBlocker(add_blocker) => add_blocker.len(),
Plugin::ImmutableMetadata(immutable_metadata) => immutable_metadata.len(),
Plugin::VerifiedCreators(verified_creators) => verified_creators.len(),
Plugin::Groups(groups) => groups.len(),
Plugin::Autograph(autograph) => autograph.len(),
Plugin::BubblegumV2(bubblegum_v2) => bubblegum_v2.len(),
}
Expand Down Expand Up @@ -188,6 +192,8 @@ pub enum PluginType {
ImmutableMetadata,
/// VerifiedCreators plugin.
VerifiedCreators,
/// Groups plugin.
Groups,
/// Autograph plugin.
Autograph,
/// Bubblegum V2 plugin.
Expand Down Expand Up @@ -229,6 +235,7 @@ impl From<&Plugin> for PluginType {
Plugin::Edition(_) => PluginType::Edition,
Plugin::MasterEdition(_) => PluginType::MasterEdition,
Plugin::VerifiedCreators(_) => PluginType::VerifiedCreators,
Plugin::Groups(_) => PluginType::Groups,
Plugin::Autograph(_) => PluginType::Autograph,
Plugin::BubblegumV2(_) => PluginType::BubblegumV2,
}
Expand All @@ -253,6 +260,7 @@ impl PluginType {
PluginType::Edition => Authority::UpdateAuthority,
PluginType::MasterEdition => Authority::UpdateAuthority,
PluginType::VerifiedCreators => Authority::UpdateAuthority,
PluginType::Groups => Authority::UpdateAuthority,
PluginType::Autograph => Authority::Owner,
PluginType::BubblegumV2 => Authority::Address {
address: mpl_bubblegum::ID,
Expand Down Expand Up @@ -306,6 +314,7 @@ mod test {
Plugin::AddBlocker(AddBlocker {}),
Plugin::ImmutableMetadata(ImmutableMetadata {}),
Plugin::VerifiedCreators(VerifiedCreators { signatures: vec![] }),
Plugin::Groups(Groups { groups: vec![] }),
Plugin::Autograph(Autograph { signatures: vec![] }),
Plugin::BubblegumV2(BubblegumV2 {}),
];
Expand Down Expand Up @@ -417,6 +426,9 @@ mod test {
verified: true,
}],
})],
vec![Plugin::Groups(Groups {
groups: vec![Pubkey::default()],
})],
vec![Plugin::Autograph(Autograph {
signatures: vec![AutographSignature {
address: Pubkey::default(),
Expand Down
Loading
Loading