1use crate::constants::{constant::PUBKEY_SIZE, account_type::TYPE_ACCOUNT_PORTFOLIO_ACCOUNT, account_size::{PORTFOLIO_PREFIX, ASSET_LEN}};
3use arrayref::{array_ref, array_refs};
4use serde::{Deserialize, Serialize};
5use solana_program::{
6 entrypoint_deprecated::ProgramResult,
7 program_error::ProgramError,
8 program_pack::{IsInitialized, Sealed},
9 pubkey::Pubkey,
10};
11use std::vec::Vec;
12
13const METADATAHASH_SIZE: usize = 16;
14const METADATAURL_SIZE: usize = 128;
15const TYPE_SIZE: usize = 1;
16const IS_INITIALIZED: usize = 1;
17const ASSET_DATA_LEN_SIZE: usize = 1;
18const NFT_TOKEN_SIZE: usize = 1;
19const VERSION_SIZE: usize = 1;
20const AMOUNT_SIZE: usize = 1;
21const PERIODE_SIZE: usize = 1;
22const PERCENTAGE_SIZE: usize = 1;
23
24#[repr(C)]
26#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
27pub struct AssetStruct {
28 pub amount: u8,
30 pub address_asset: Pubkey,
32 pub periode: u8,
34 pub asset_to_sold_into_asset: Pubkey,
36 pub percentage: u8,
38}
39
40impl AssetStruct {
41 pub fn add_new_asset(
43 &mut self,
44 amount: u8,
45 address_asset: Pubkey,
46 periode: u8,
47 asset_to_sold_into_asset: Pubkey,
48 percentage: u8,
49 ) -> ProgramResult {
50 self.amount = amount;
51 self.address_asset = address_asset;
52 self.periode = periode;
53 self.asset_to_sold_into_asset = asset_to_sold_into_asset;
54 self.percentage = percentage;
55
56 Ok(())
57 }
58}
59
60#[repr(C)]
62#[derive(Clone, Debug, Default, PartialEq)]
63pub struct Portfolio {
64 pub type_account: u8,
66 pub creator_portfolio: Pubkey,
68 pub meta_data_url: Vec<u8>,
70 pub meta_data_hash: Vec<u8>,
72 pub is_initialized: u8,
74 pub asset_data_len: u8,
76 pub nft_token: u8,
78 pub version: u8,
80 pub splm_stake: Pubkey,
82 pub extended_data: Pubkey,
84 pub asset_data: Vec<AssetStruct>,
86}
87
88impl Sealed for Portfolio {}
89impl IsInitialized for Portfolio {
91 fn is_initialized(&self) -> bool {
92 return self.type_account == TYPE_ACCOUNT_PORTFOLIO_ACCOUNT;
93 }
94}
95
96pub trait PackPortfolio {
98 fn unpack_portfolio(src: &[u8]) -> Result<Portfolio, ProgramError>;
100 fn pack_portfolio(&self, dst: &mut [u8]);
102}
103
104impl PackPortfolio for Portfolio {
106 fn unpack_portfolio(src: &[u8]) -> Result<Self, ProgramError> {
107 let len_asset_nbre = (&src.len() - PORTFOLIO_PREFIX) / ASSET_LEN;
108
109 let src_fix = array_ref![&src, 0, PORTFOLIO_PREFIX];
110 let (
111 type_account,
112 creator_portfolio,
113 meta_data_url,
114 meta_data_hash,
115 is_initialized,
116 asset_data_len,
117 nft_token,
118 version,
119 splm_stake,
120 extended_data,
121 ) = array_refs![
122 src_fix,
123 TYPE_SIZE,
124 PUBKEY_SIZE,
125 METADATAURL_SIZE,
126 METADATAHASH_SIZE,
127 IS_INITIALIZED,
128 ASSET_DATA_LEN_SIZE,
129 NFT_TOKEN_SIZE,
130 VERSION_SIZE,
131 PUBKEY_SIZE,
132 PUBKEY_SIZE
133 ];
134
135 let len_data: usize = ASSET_LEN * len_asset_nbre;
136
137 let list_asset_data = &src[PORTFOLIO_PREFIX..PORTFOLIO_PREFIX + (len_data) as usize];
138
139 let mut asset_vec: Vec<AssetStruct> = Vec::with_capacity(len_asset_nbre);
140
141 let mut offset = 0;
144 for _ in 0..len_asset_nbre {
145 let asset_data = array_ref![list_asset_data, offset, ASSET_LEN];
146 #[allow(clippy::ptr_offset_with_cast)]
147 let (amount, address_asset, periode, asset_to_sold_into_asset, percentage) = array_refs![
148 asset_data,
149 AMOUNT_SIZE,
150 PUBKEY_SIZE,
151 PERIODE_SIZE,
152 PUBKEY_SIZE,
153 PERCENTAGE_SIZE
154 ];
155 asset_vec.push(AssetStruct {
156 amount: u8::from_le_bytes(*amount),
157 address_asset: Pubkey::new_from_array(*address_asset),
158 periode: u8::from_le_bytes(*periode),
159 asset_to_sold_into_asset: Pubkey::new_from_array(*asset_to_sold_into_asset),
160 percentage: u8::from_le_bytes(*percentage),
161 });
162 offset += ASSET_LEN;
163 }
164
165 return Ok(Portfolio {
166 type_account: u8::from_le_bytes(*type_account),
167 creator_portfolio: Pubkey::new(creator_portfolio),
168 meta_data_url: meta_data_url.to_vec(),
169 meta_data_hash: meta_data_hash.to_vec(),
170 is_initialized: u8::from_le_bytes(*is_initialized),
171 asset_data_len: u8::from_le_bytes(*asset_data_len),
172 nft_token: u8::from_le_bytes(*nft_token),
173 version: u8::from_le_bytes(*version),
174 splm_stake: Pubkey::new(splm_stake),
175 extended_data: Pubkey::new(extended_data),
176 asset_data: asset_vec.to_vec(),
177 });
178 }
179
180 fn pack_portfolio(&self, dst: &mut [u8]) {
181 let Portfolio {
182 ref creator_portfolio,
183 meta_data_url,
184 meta_data_hash,
185 type_account,
186 is_initialized,
187 asset_data_len,
188 nft_token,
189 version,
190 splm_stake,
191 extended_data,
192 asset_data,
193 } = self;
194
195 let asset_data_len_usize = *asset_data_len as usize;
196 let len_data_asset = ASSET_LEN * asset_data_len_usize;
197
198 let mut buffer = [0; PORTFOLIO_PREFIX + ASSET_LEN * 10];
199
200 buffer[0] = *type_account;
201 let creator_portfolio_range = TYPE_SIZE..TYPE_SIZE + PUBKEY_SIZE;
202 buffer[creator_portfolio_range].clone_from_slice(creator_portfolio.as_ref());
203 let meta_data_url_range =
204 TYPE_SIZE + PUBKEY_SIZE..TYPE_SIZE + PUBKEY_SIZE + METADATAURL_SIZE;
205 buffer[meta_data_url_range].clone_from_slice(meta_data_url);
206
207 let meta_data_hash_range = TYPE_SIZE + PUBKEY_SIZE + METADATAURL_SIZE
208 ..TYPE_SIZE + PUBKEY_SIZE + METADATAURL_SIZE + METADATAHASH_SIZE;
209 buffer[meta_data_hash_range].clone_from_slice(&meta_data_hash);
210
211 let is_initialized_range = TYPE_SIZE + PUBKEY_SIZE + METADATAURL_SIZE + METADATAHASH_SIZE;
212 buffer[is_initialized_range] = *is_initialized;
213 let asset_data_len_range =
214 TYPE_SIZE + PUBKEY_SIZE + METADATAURL_SIZE + METADATAHASH_SIZE + IS_INITIALIZED;
215 buffer[asset_data_len_range] = *asset_data_len;
216
217 let nft_token_range = TYPE_SIZE
218 + PUBKEY_SIZE
219 + METADATAURL_SIZE
220 + METADATAHASH_SIZE
221 + IS_INITIALIZED
222 + ASSET_DATA_LEN_SIZE;
223 buffer[nft_token_range] = *nft_token;
224 let version_range = TYPE_SIZE
225 + PUBKEY_SIZE
226 + METADATAURL_SIZE
227 + METADATAHASH_SIZE
228 + IS_INITIALIZED
229 + ASSET_DATA_LEN_SIZE
230 + NFT_TOKEN_SIZE;
231 buffer[version_range] = *version;
232 let splm_stake_range = TYPE_SIZE
233 + PUBKEY_SIZE
234 + METADATAURL_SIZE
235 + METADATAHASH_SIZE
236 + IS_INITIALIZED
237 + ASSET_DATA_LEN_SIZE
238 + NFT_TOKEN_SIZE
239 + VERSION_SIZE
240 ..TYPE_SIZE
241 + PUBKEY_SIZE
242 + METADATAURL_SIZE
243 + METADATAHASH_SIZE
244 + IS_INITIALIZED
245 + ASSET_DATA_LEN_SIZE
246 + NFT_TOKEN_SIZE
247 + VERSION_SIZE
248 + PUBKEY_SIZE;
249 buffer[splm_stake_range].clone_from_slice(splm_stake.as_ref());
250 let extended_data_range = TYPE_SIZE
251 + PUBKEY_SIZE
252 + METADATAURL_SIZE
253 + METADATAHASH_SIZE
254 + IS_INITIALIZED
255 + ASSET_DATA_LEN_SIZE
256 + NFT_TOKEN_SIZE
257 + VERSION_SIZE
258 + PUBKEY_SIZE
259 ..TYPE_SIZE
260 + PUBKEY_SIZE
261 + METADATAURL_SIZE
262 + METADATAHASH_SIZE
263 + IS_INITIALIZED
264 + ASSET_DATA_LEN_SIZE
265 + NFT_TOKEN_SIZE
266 + VERSION_SIZE
267 + PUBKEY_SIZE
268 + PUBKEY_SIZE;
269 buffer[extended_data_range].clone_from_slice(extended_data.as_ref());
270
271 let asset_vec_tmp = bincode::serialize(&asset_data).unwrap();
272
273 let mut asset_data_tmp = [0; ASSET_LEN * 10]; let len_asset = asset_vec_tmp.len();
276
277 asset_data_tmp[0..len_data_asset as usize].clone_from_slice(&asset_vec_tmp[8..len_asset]); buffer[PORTFOLIO_PREFIX..PORTFOLIO_PREFIX + len_data_asset as usize]
280 .clone_from_slice(&asset_data_tmp[0..len_data_asset as usize]);
281
282 let mut buffer_tranformed: Vec<u8> =
283 Vec::with_capacity(PORTFOLIO_PREFIX + (len_data_asset as usize));
284 buffer_tranformed.resize(PORTFOLIO_PREFIX + (len_data_asset as usize), 0);
285 buffer_tranformed[0..PORTFOLIO_PREFIX + len_data_asset as usize]
286 .copy_from_slice(&buffer[0..PORTFOLIO_PREFIX + (len_data_asset as usize)]);
287
288 dst[0..PORTFOLIO_PREFIX + len_data_asset as usize].copy_from_slice(&buffer_tranformed);
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 #[test]
296 fn test_pack_asset_portfolio() {
297 let amount = 12;
298 let address_asset = Pubkey::new_unique();
299 let periode = 2;
300 let asset_to_sold_into_asset = Pubkey::new_unique();
301 let percentage = 1;
302 let number_assets: u8 = 1;
303 let asset = AssetStruct {
304 amount,
305 address_asset,
306 periode,
307 asset_to_sold_into_asset,
308 percentage,
309 };
310 let meta_data_url = [1; METADATAURL_SIZE];
311 let meta_data_hash = [1; METADATAHASH_SIZE];
312 let mut asset_data = Vec::new();
313 asset_data.push(asset);
314 let portfolio = Portfolio {
315 type_account: TYPE_ACCOUNT_PORTFOLIO_ACCOUNT,
316 creator_portfolio: Pubkey::new_unique(),
317 meta_data_url: meta_data_url.to_vec(),
318 meta_data_hash: meta_data_hash.to_vec(),
319 is_initialized: 1,
320 asset_data_len: number_assets,
321 nft_token: 1,
322 version: 2,
323 splm_stake: Pubkey::new_unique(),
324 extended_data: Pubkey::new_unique(),
325 asset_data,
326 };
327 const LEN: usize = PORTFOLIO_PREFIX + (1 * ASSET_LEN);
328 let mut packed = [0u8; LEN];
329 PackPortfolio::pack_portfolio(&portfolio, &mut packed[..]);
330 let unpacked = Portfolio::unpack_portfolio(&packed).unwrap();
331 assert_eq!(portfolio, unpacked);
332 assert_eq!(unpacked.type_account,TYPE_ACCOUNT_PORTFOLIO_ACCOUNT);
333 }
334}