core_preview/hooked/
plugin.rs

1use borsh::BorshDeserialize;
2use solana_program::account_info::AccountInfo;
3
4use crate::{
5    accounts::{BaseAssetV1, PluginHeaderV1, PluginRegistryV1},
6    errors::MplCoreError,
7    types::{Plugin, PluginAuthority, PluginType, RegistryRecord},
8    AttributesPlugin, BaseAuthority, BasePlugin, BurnDelegatePlugin, DataBlob,
9    FreezeDelegatePlugin, PermanentBurnDelegatePlugin, PermanentFreezeDelegatePlugin,
10    PermanentTransferDelegatePlugin, PluginsList, RoyaltiesPlugin, SolanaAccount,
11    TransferDelegatePlugin, UpdateDelegatePlugin,
12};
13
14/// Fetch the plugin from the registry.
15pub fn fetch_plugin<T: DataBlob + SolanaAccount, U: BorshDeserialize>(
16    account: &AccountInfo,
17    plugin_type: PluginType,
18) -> Result<(PluginAuthority, U, usize), std::io::Error> {
19    let asset = T::load(account, 0)?;
20
21    if asset.get_size() == account.data_len() {
22        return Err(std::io::Error::new(
23            std::io::ErrorKind::Other,
24            MplCoreError::PluginNotFound.to_string(),
25        ));
26    }
27
28    let header = PluginHeaderV1::load(account, asset.get_size())?;
29    let PluginRegistryV1 { registry, .. } =
30        PluginRegistryV1::load(account, header.plugin_registry_offset as usize)?;
31
32    // Find the plugin in the registry.
33    let registry_record = registry
34        .iter()
35        .find(|record| record.plugin_type == plugin_type)
36        .ok_or(std::io::Error::new(
37            std::io::ErrorKind::Other,
38            MplCoreError::PluginNotFound.to_string(),
39        ))?;
40
41    // Deserialize the plugin.
42    let plugin =
43        Plugin::deserialize(&mut &(*account.data).borrow()[(registry_record.offset as usize)..])?;
44
45    if PluginType::from(&plugin) != plugin_type {
46        return Err(std::io::Error::new(
47            std::io::ErrorKind::Other,
48            MplCoreError::PluginNotFound.to_string(),
49        ));
50    }
51
52    let inner = U::deserialize(
53        &mut &(*account.data).borrow()[registry_record.offset.checked_add(1).ok_or(
54            std::io::Error::new(
55                std::io::ErrorKind::Other,
56                MplCoreError::NumericalOverflow.to_string(),
57            ),
58        )? as usize..],
59    )?;
60
61    // Return the plugin and its authority.
62    Ok((
63        registry_record.authority.clone(),
64        inner,
65        registry_record.offset as usize,
66    ))
67}
68
69/// Fetch the plugin registry.
70pub fn fetch_plugins(account: &[u8]) -> Result<Vec<RegistryRecord>, std::io::Error> {
71    let asset = BaseAssetV1::from_bytes(account)?;
72
73    let header = PluginHeaderV1::from_bytes(&account[asset.get_size()..])?;
74    let PluginRegistryV1 { registry, .. } =
75        PluginRegistryV1::from_bytes(&account[(header.plugin_registry_offset as usize)..])?;
76
77    Ok(registry)
78}
79
80/// Create plugin header and registry if it doesn't exist
81pub fn list_plugins(account: &[u8]) -> Result<Vec<PluginType>, std::io::Error> {
82    let asset = BaseAssetV1::from_bytes(account)?;
83
84    let header = PluginHeaderV1::from_bytes(&account[asset.get_size()..])?;
85    let PluginRegistryV1 { registry, .. } =
86        PluginRegistryV1::from_bytes(&account[(header.plugin_registry_offset as usize)..])?;
87
88    Ok(registry
89        .iter()
90        .map(|registry_record| registry_record.plugin_type.clone())
91        .collect())
92}
93
94pub fn registry_records_to_plugin_list(
95    registry_records: &[RegistryRecord],
96    account_data: &[u8],
97) -> Result<PluginsList, std::io::Error> {
98    let result = registry_records
99        .iter()
100        .try_fold(PluginsList::default(), |mut acc, record| {
101            let authority: BaseAuthority = record.authority.clone().into();
102            let base = BasePlugin {
103                authority,
104                offset: Some(record.offset),
105            };
106            let plugin = Plugin::deserialize(&mut &account_data[record.offset as usize..])?;
107
108            match plugin {
109                Plugin::Royalties(royalties) => {
110                    acc.royalties = Some(RoyaltiesPlugin { base, royalties });
111                }
112                Plugin::FreezeDelegate(freeze_delegate) => {
113                    acc.freeze_delegate = Some(FreezeDelegatePlugin {
114                        base,
115                        freeze_delegate,
116                    });
117                }
118                Plugin::BurnDelegate(burn_delegate) => {
119                    acc.burn_delegate = Some(BurnDelegatePlugin {
120                        base,
121                        burn_delegate,
122                    });
123                }
124                Plugin::TransferDelegate(transfer_delegate) => {
125                    acc.transfer_delegate = Some(TransferDelegatePlugin {
126                        base,
127                        transfer_delegate,
128                    });
129                }
130                Plugin::UpdateDelegate(update_delegate) => {
131                    acc.update_delegate = Some(UpdateDelegatePlugin {
132                        base,
133                        update_delegate,
134                    });
135                }
136                Plugin::PermanentFreezeDelegate(permanent_freeze_delegate) => {
137                    acc.permanent_freeze_delegate = Some(PermanentFreezeDelegatePlugin {
138                        base,
139                        permanent_freeze_delegate,
140                    });
141                }
142                Plugin::Attributes(attributes) => {
143                    acc.attributes = Some(AttributesPlugin { base, attributes });
144                }
145                Plugin::PermanentTransferDelegate(permanent_transfer_delegate) => {
146                    acc.permanent_transfer_delegate = Some(PermanentTransferDelegatePlugin {
147                        base,
148                        permanent_transfer_delegate,
149                    })
150                }
151                Plugin::PermanentBurnDelegate(permanent_burn_delegate) => {
152                    acc.permanent_burn_delegate = Some(PermanentBurnDelegatePlugin {
153                        base,
154                        permanent_burn_delegate,
155                    })
156                }
157            };
158
159            Ok(acc)
160        });
161
162    result
163}