diff --git a/clients/rust/src/hooked/advanced_types.rs b/clients/rust/src/hooked/advanced_types.rs index 081d35f0..b4149623 100644 --- a/clients/rust/src/hooked/advanced_types.rs +++ b/clients/rust/src/hooked/advanced_types.rs @@ -198,6 +198,7 @@ pub struct Asset { pub struct Collection { pub base: BaseCollectionV1, pub plugin_list: PluginsList, + pub external_plugin_adapter_list: ExternalPluginAdaptersList, pub plugin_header: Option, } diff --git a/clients/rust/src/hooked/asset.rs b/clients/rust/src/hooked/asset.rs index 8131e986..4dcd849a 100644 --- a/clients/rust/src/hooked/asset.rs +++ b/clients/rust/src/hooked/asset.rs @@ -6,57 +6,63 @@ use borsh::BorshSerialize; use crate::{ accounts::{BaseAssetV1, PluginHeaderV1}, registry_records_to_external_plugin_adapter_list, registry_records_to_plugin_list, Asset, - PluginRegistryV1Safe, + ExternalPluginAdaptersList, PluginRegistryV1Safe, PluginsList, }; impl Asset { - pub fn deserialize(data: &[u8]) -> Result { + pub fn deserialize(data: &[u8]) -> Result, std::io::Error> { let base = BaseAssetV1::from_bytes(data)?; let base_data = base.try_to_vec()?; - let (plugin_header, plugin_list, external_plugin_adapter_list) = if base_data.len() - != data.len() - { - let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?; - let plugin_registry = PluginRegistryV1Safe::from_bytes( - &data[plugin_header.plugin_registry_offset as usize..], - )?; - - let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?; - let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list( - &plugin_registry.external_registry, - data, - )?; - - ( - Some(plugin_header), - Some(plugin_list), - Some(external_plugin_adapter_list), - ) - } else { - (None, None, None) - }; - - Ok(Self { + + if base_data.len() != data.len() { + return Self::deserialize_with_plugins(data, base, base_data); + } + + Ok(Box::new(Self { + base, + plugin_list: PluginsList::default(), + external_plugin_adapter_list: ExternalPluginAdaptersList::default(), + plugin_header: None, + })) + } + + fn deserialize_with_plugins( + data: &[u8], + base: BaseAssetV1, + base_data: Vec, + ) -> Result, std::io::Error> { + let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?; + let plugin_registry = PluginRegistryV1Safe::from_bytes( + &data[plugin_header.plugin_registry_offset as usize..], + )?; + + let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?; + let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list( + &plugin_registry.external_registry, + data, + )?; + + Ok(Box::new(Self { base, - plugin_list: plugin_list.unwrap_or_default(), - external_plugin_adapter_list: external_plugin_adapter_list.unwrap_or_default(), - plugin_header, - }) + plugin_list, + external_plugin_adapter_list, + plugin_header: Some(plugin_header), + })) } #[inline(always)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result, std::io::Error> { Self::deserialize(data) } } -impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Asset { +impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Box { type Error = std::io::Error; fn try_from( account_info: &solana_program::account_info::AccountInfo<'a>, ) -> Result { let data: &[u8] = &(*account_info.data).borrow(); - Self::deserialize(data) + Asset::deserialize(data) } } diff --git a/clients/rust/src/hooked/collection.rs b/clients/rust/src/hooked/collection.rs index 82ca0877..899edf1c 100644 --- a/clients/rust/src/hooked/collection.rs +++ b/clients/rust/src/hooked/collection.rs @@ -5,46 +5,64 @@ use borsh::BorshSerialize; use crate::{ accounts::{BaseCollectionV1, PluginHeaderV1}, - registry_records_to_plugin_list, Collection, PluginRegistryV1Safe, + registry_records_to_external_plugin_adapter_list, registry_records_to_plugin_list, Collection, + ExternalPluginAdaptersList, PluginRegistryV1Safe, PluginsList, }; impl Collection { - pub fn deserialize(data: &[u8]) -> Result { + pub fn deserialize(data: &[u8]) -> Result, std::io::Error> { let base = BaseCollectionV1::from_bytes(data)?; let base_data = base.try_to_vec()?; - let (plugin_header, plugin_list) = if base_data.len() != data.len() { - let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?; - let plugin_registry = PluginRegistryV1Safe::from_bytes( - &data[plugin_header.plugin_registry_offset as usize..], - )?; - let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?; + if base_data.len() != data.len() { + return Self::deserialize_with_plugins(data, base, base_data); + } - (Some(plugin_header), Some(plugin_list)) - } else { - (None, None) - }; + Ok(Box::new(Self { + base, + plugin_list: PluginsList::default(), + external_plugin_adapter_list: ExternalPluginAdaptersList::default(), + plugin_header: None, + })) + } + + fn deserialize_with_plugins( + data: &[u8], + base: BaseCollectionV1, + base_data: Vec, + ) -> Result, std::io::Error> { + let plugin_header = PluginHeaderV1::from_bytes(&data[base_data.len()..])?; + let plugin_registry = PluginRegistryV1Safe::from_bytes( + &data[plugin_header.plugin_registry_offset as usize..], + )?; - Ok(Self { + let plugin_list = registry_records_to_plugin_list(&plugin_registry.registry, data)?; + let external_plugin_adapter_list = registry_records_to_external_plugin_adapter_list( + &plugin_registry.external_registry, + data, + )?; + + Ok(Box::new(Self { base, - plugin_list: plugin_list.unwrap_or_default(), - plugin_header, - }) + plugin_list, + external_plugin_adapter_list, + plugin_header: Some(plugin_header), + })) } #[inline(always)] - pub fn from_bytes(data: &[u8]) -> Result { + pub fn from_bytes(data: &[u8]) -> Result, std::io::Error> { Self::deserialize(data) } } -impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Collection { +impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Box { type Error = std::io::Error; fn try_from( account_info: &solana_program::account_info::AccountInfo<'a>, ) -> Result { let data: &[u8] = &(*account_info.data).borrow(); - Self::deserialize(data) + Collection::deserialize(data) } } diff --git a/clients/rust/src/hooked/mod.rs b/clients/rust/src/hooked/mod.rs index 8628a463..bbae32c7 100644 --- a/clients/rust/src/hooked/mod.rs +++ b/clients/rust/src/hooked/mod.rs @@ -59,6 +59,61 @@ impl BaseCollectionV1 { pub const BASE_LENGTH: usize = 1 + 32 + 4 + 4 + 4 + 4; } +/// Anchor implementations that enable using `Account` and `Account` +/// in Anchor programs. +#[cfg(feature = "anchor")] +mod anchor_impl { + use super::*; + use anchor_lang::{ + prelude::{Owner, Pubkey}, + AccountDeserialize, AccountSerialize, Discriminator, + }; + + impl AccountDeserialize for BaseAssetV1 { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + let base_asset = Self::from_bytes(buf)?; + Ok(base_asset) + } + } + + // Not used as an Anchor program using Account would not have permission to + // reserialize the account as it's owned by mpl-core. + impl AccountSerialize for BaseAssetV1 {} + + // Not used but needed for Anchor. + impl Discriminator for BaseAssetV1 { + const DISCRIMINATOR: [u8; 8] = [0; 8]; + } + + impl Owner for BaseAssetV1 { + fn owner() -> Pubkey { + crate::ID + } + } + + impl AccountDeserialize for BaseCollectionV1 { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + let base_asset = Self::from_bytes(buf)?; + Ok(base_asset) + } + } + + // Not used as an Anchor program using Account would not have permission to + // reserialize the account as it's owned by mpl-core. + impl AccountSerialize for BaseCollectionV1 {} + + // Not used but needed for Anchor. + impl Discriminator for BaseCollectionV1 { + const DISCRIMINATOR: [u8; 8] = [0; 8]; + } + + impl Owner for BaseCollectionV1 { + fn owner() -> Pubkey { + crate::ID + } + } +} + impl DataBlob for BaseAssetV1 { fn get_initial_size() -> usize { BaseAssetV1::BASE_LENGTH