#[cfg(feature = "anchor")]
use anchor_lang::prelude::AnchorDeserialize as CrateDeserialize;
#[cfg(not(feature = "anchor"))]
use borsh::BorshDeserialize as CrateDeserialize;
use num_traits::FromPrimitive;
use solana_program::account_info::AccountInfo;
use crate::{
accounts::{BaseAssetV1, PluginHeaderV1},
errors::MplCoreError,
types::{
ExternalPluginAdapter, ExternalPluginAdapterType, Plugin, PluginAuthority, PluginType,
RegistryRecord,
},
AddBlockerPlugin, AttributesPlugin, AutographPlugin, BaseAuthority, BasePlugin,
BurnDelegatePlugin, DataBlob, EditionPlugin, ExternalPluginAdaptersList,
ExternalRegistryRecordSafe, FreezeDelegatePlugin, ImmutableMetadataPlugin, MasterEditionPlugin,
PermanentBurnDelegatePlugin, PermanentFreezeDelegatePlugin, PermanentTransferDelegatePlugin,
PluginRegistryV1Safe, PluginsList, RegistryRecordSafe, RoyaltiesPlugin, SolanaAccount,
TransferDelegatePlugin, UpdateDelegatePlugin, VerifiedCreatorsPlugin,
};
pub fn fetch_plugin<T: DataBlob + SolanaAccount, U: CrateDeserialize>(
account: &AccountInfo,
plugin_type: PluginType,
) -> Result<(PluginAuthority, U, usize), std::io::Error> {
let asset = T::load(account, 0)?;
if asset.get_size() == account.data_len() {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
MplCoreError::PluginNotFound.to_string(),
));
}
let header = PluginHeaderV1::from_bytes(&(*account.data).borrow()[asset.get_size()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&(*account.data).borrow()[header.plugin_registry_offset as usize..],
)?;
let registry_record = plugin_registry
.registry
.iter()
.find(|record| {
if let Some(plugin) = PluginType::from_u8(record.plugin_type) {
plugin == plugin_type
} else {
false
}
})
.ok_or(std::io::Error::new(
std::io::ErrorKind::Other,
MplCoreError::PluginNotFound.to_string(),
))?;
let plugin =
Plugin::deserialize(&mut &(*account.data).borrow()[(registry_record.offset as usize)..])?;
if PluginType::from(&plugin) != plugin_type {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
MplCoreError::PluginNotFound.to_string(),
));
}
let inner = U::deserialize(
&mut &(*account.data).borrow()[registry_record.offset.checked_add(1).ok_or(
std::io::Error::new(
std::io::ErrorKind::Other,
MplCoreError::NumericalOverflow.to_string(),
),
)? as usize..],
)?;
Ok((
registry_record.authority.clone(),
inner,
registry_record.offset as usize,
))
}
pub fn fetch_plugins(account_data: &[u8]) -> Result<Vec<RegistryRecord>, std::io::Error> {
let asset = BaseAssetV1::from_bytes(account_data)?;
let header = PluginHeaderV1::from_bytes(&account_data[asset.get_size()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&account_data[(header.plugin_registry_offset as usize)..],
)?;
let filtered_plugin_registry = plugin_registry
.registry
.iter()
.filter_map(|record| {
PluginType::from_u8(record.plugin_type).map(|plugin_type| RegistryRecord {
plugin_type,
authority: record.authority.clone(),
offset: record.offset,
})
})
.collect();
Ok(filtered_plugin_registry)
}
pub fn list_plugins(account_data: &[u8]) -> Result<Vec<PluginType>, std::io::Error> {
let asset = BaseAssetV1::from_bytes(account_data)?;
let header = PluginHeaderV1::from_bytes(&account_data[asset.get_size()..])?;
let plugin_registry = PluginRegistryV1Safe::from_bytes(
&account_data[(header.plugin_registry_offset as usize)..],
)?;
Ok(plugin_registry
.registry
.iter()
.filter_map(|registry_record| PluginType::from_u8(registry_record.plugin_type))
.collect())
}
pub(crate) fn registry_records_to_plugin_list(
registry_records: &[RegistryRecordSafe],
account_data: &[u8],
) -> Result<PluginsList, std::io::Error> {
let result = registry_records
.iter()
.try_fold(PluginsList::default(), |mut acc, record| {
if PluginType::from_u8(record.plugin_type).is_some() {
let authority: BaseAuthority = record.authority.clone().into();
let base = BasePlugin {
authority,
offset: Some(record.offset),
};
let plugin = Plugin::deserialize(&mut &account_data[record.offset as usize..])?;
match plugin {
Plugin::Royalties(royalties) => {
acc.royalties = Some(RoyaltiesPlugin { base, royalties });
}
Plugin::FreezeDelegate(freeze_delegate) => {
acc.freeze_delegate = Some(FreezeDelegatePlugin {
base,
freeze_delegate,
});
}
Plugin::BurnDelegate(burn_delegate) => {
acc.burn_delegate = Some(BurnDelegatePlugin {
base,
burn_delegate,
});
}
Plugin::TransferDelegate(transfer_delegate) => {
acc.transfer_delegate = Some(TransferDelegatePlugin {
base,
transfer_delegate,
});
}
Plugin::UpdateDelegate(update_delegate) => {
acc.update_delegate = Some(UpdateDelegatePlugin {
base,
update_delegate,
});
}
Plugin::PermanentFreezeDelegate(permanent_freeze_delegate) => {
acc.permanent_freeze_delegate = Some(PermanentFreezeDelegatePlugin {
base,
permanent_freeze_delegate,
});
}
Plugin::Attributes(attributes) => {
acc.attributes = Some(AttributesPlugin { base, attributes });
}
Plugin::PermanentTransferDelegate(permanent_transfer_delegate) => {
acc.permanent_transfer_delegate = Some(PermanentTransferDelegatePlugin {
base,
permanent_transfer_delegate,
})
}
Plugin::PermanentBurnDelegate(permanent_burn_delegate) => {
acc.permanent_burn_delegate = Some(PermanentBurnDelegatePlugin {
base,
permanent_burn_delegate,
})
}
Plugin::Edition(edition) => acc.edition = Some(EditionPlugin { base, edition }),
Plugin::MasterEdition(master_edition) => {
acc.master_edition = Some(MasterEditionPlugin {
base,
master_edition,
})
}
Plugin::AddBlocker(add_blocker) => {
acc.add_blocker = Some(AddBlockerPlugin { base, add_blocker })
}
Plugin::ImmutableMetadata(immutable_metadata) => {
acc.immutable_metadata = Some(ImmutableMetadataPlugin {
base,
immutable_metadata,
})
}
Plugin::VerifiedCreators(verified_creators) => {
acc.verified_creators = Some(VerifiedCreatorsPlugin {
base,
verified_creators,
})
}
Plugin::Autograph(autograph) => {
acc.autograph = Some(AutographPlugin { base, autograph })
}
}
}
Ok(acc)
});
result
}
pub(crate) fn registry_records_to_external_plugin_adapter_list(
registry_records: &[ExternalRegistryRecordSafe],
account_data: &[u8],
) -> Result<ExternalPluginAdaptersList, std::io::Error> {
let result = registry_records.iter().try_fold(
ExternalPluginAdaptersList::default(),
|mut acc, record| {
if ExternalPluginAdapterType::from_u8(record.plugin_type).is_some() {
let plugin = ExternalPluginAdapter::deserialize(
&mut &account_data[record.offset as usize..],
)?;
match plugin {
ExternalPluginAdapter::LifecycleHook(lifecycle_hook) => {
acc.lifecycle_hooks.push(lifecycle_hook)
}
ExternalPluginAdapter::Oracle(oracle) => acc.oracles.push(oracle),
ExternalPluginAdapter::DataStore(data_store) => {
acc.data_stores.push(data_store)
}
}
}
Ok(acc)
},
);
result
}