1use arrayref::{array_ref, array_refs};
3use serde::{Deserialize, Serialize};
4use solana_program::{
5 entrypoint_deprecated::ProgramResult,
6 program_error::ProgramError,
7 program_pack::{IsInitialized, Sealed},
8 pubkey::Pubkey,
9};
10use std::vec::Vec;
11
12use crate::{
13 constants::{
14 account_size::{SPLU_LEN, USER_PORTFOLIO_PREFIX},
15 constant::{NULL_PUBKEY, PUBKEY_SIZE},
16 account_type::TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT,
17 },
18 error::PortfolioError,
19};
20
21const TYPE_SIZE: usize = 1;
22const STATE_SIZE: usize = 1;
23const AMOUNT_SIZE: usize = 8;
24const VERSION_SIZE: usize = 1;
25const NONCE_SIZE: usize = 1;
26const STATE_SPLU_SECONDARY: usize = 1;
27const STATE_SPLU_TERTIARY1: usize = 1;
28const STATE_SPLU_TERTIARY2: usize = 1;
29const NONCE_SPLU: usize = 1;
30
31#[repr(C)]
33#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
34pub struct SpluStruct {
35 pub splu_secondary: Pubkey,
37 pub state_splu_secondary: u8,
39 pub splu_tertiary1: Pubkey,
41 pub state_splu_tertiary1: u8,
43 pub splu_tertiary2: Pubkey,
45 pub state_splu_tertiary2: u8,
47 pub authority_splu: Pubkey,
49 pub program_account_splu: Pubkey,
51 pub nonce_splu: u8,
53}
54
55impl SpluStruct {
56 pub fn add_new_splu_secondary(
58 &mut self,
59 splu_secondary_address: Pubkey,
60 authority_splu: Pubkey,
61 program_account_splu: Pubkey,
62 nonce_splu: u8,
63 ) -> ProgramResult {
64 self.splu_secondary = splu_secondary_address;
65 self.state_splu_secondary = 1;
66 self.authority_splu = authority_splu;
67 self.program_account_splu = program_account_splu;
68 self.nonce_splu = nonce_splu;
69
70 Ok(())
71 }
72
73 pub fn update_splu_secondary(&mut self) -> ProgramResult {
75 self.state_splu_secondary = self
76 .state_splu_secondary
77 .checked_add(1)
78 .ok_or(PortfolioError::InvalidAmount)?;
79
80 Ok(())
81 }
82
83 pub fn reset_splu_secondary(&mut self) -> ProgramResult {
85 self.state_splu_secondary = 0;
86
87 Ok(())
88 }
89
90 pub fn add_splu_tertiary(
92 &mut self,
93 splu_tertiary1: Pubkey,
94 splu_tertiary2: Pubkey,
95 ) -> ProgramResult {
96 self.splu_tertiary1 = splu_tertiary1;
97 self.state_splu_tertiary1 = self
98 .state_splu_tertiary1
99 .checked_add(1)
100 .ok_or(PortfolioError::InvalidAmount)?;
101 self.splu_tertiary2 = splu_tertiary2;
102 if splu_tertiary2.as_ref() != NULL_PUBKEY {
103 self.state_splu_tertiary2 = self
104 .state_splu_tertiary2
105 .checked_add(1)
106 .ok_or(PortfolioError::InvalidAmount)?;
107 }
108 Ok(())
109 }
110
111 pub fn reset_splu_tertiary(&mut self) -> ProgramResult {
113 self.state_splu_tertiary1 = 0;
114 self.state_splu_tertiary2 = 0;
115
116 Ok(())
117 }
118
119 pub fn update_state_tertiary(&mut self, state1: u8, state2: u8) -> ProgramResult {
121 self.state_splu_tertiary1 = state1;
122 self.state_splu_tertiary2 = state2;
123
124 Ok(())
125 }
126}
127
128#[repr(C)]
130#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
131
132pub struct UserPortfolio {
133 pub type_account: u8,
135 pub owner: Pubkey,
137 pub portfolio_address: Pubkey,
139 pub splm_asset: Pubkey,
141 pub state: u8,
143 pub amount: u64,
145 pub version: u8,
147 pub extended_data: Pubkey,
149 pub splm_primary: Pubkey,
151 pub authority: Pubkey,
153 pub nonce: u8,
155 pub create_account_program: Pubkey,
157 pub splu_list: Vec<SpluStruct>,
159}
160
161impl Sealed for UserPortfolio {}
162
163impl IsInitialized for UserPortfolio {
164 fn is_initialized(&self) -> bool {
165 return self.type_account == TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT;
166 }
167}
168
169pub trait PackUserPortfolio {
171 fn unpack_user_portfolio(src: &[u8]) -> Result<UserPortfolio, ProgramError>;
173 fn pack_user_portfolio(&self, dst: &mut [u8]);
175}
176
177impl PackUserPortfolio for UserPortfolio {
178 fn unpack_user_portfolio(src: &[u8]) -> Result<Self, ProgramError> {
180 let numbre_splu = (&src.len() - USER_PORTFOLIO_PREFIX) / SPLU_LEN;
181 let len_splu_data = &src.len() - USER_PORTFOLIO_PREFIX;
182
183 let src_fix = array_ref![&src, 0, USER_PORTFOLIO_PREFIX];
184 let (
185 type_account,
186 owner,
187 portfolio_address,
188 splm_asset,
189 state,
190 amount,
191 version,
192 extended_data,
193 splm_primary,
194 authority,
195 nonce,
196 create_account_program,
197 ) = array_refs![
198 src_fix,
199 TYPE_SIZE,
200 PUBKEY_SIZE,
201 PUBKEY_SIZE,
202 PUBKEY_SIZE,
203 STATE_SIZE,
204 AMOUNT_SIZE,
205 VERSION_SIZE,
206 PUBKEY_SIZE,
207 PUBKEY_SIZE,
208 PUBKEY_SIZE,
209 NONCE_SIZE,
210 PUBKEY_SIZE
211 ];
212
213 let mut splu_vec: Vec<SpluStruct> = Vec::with_capacity(numbre_splu);
214
215 let list_splu_data =
216 &src[USER_PORTFOLIO_PREFIX..USER_PORTFOLIO_PREFIX + (len_splu_data) as usize];
217
218 let mut offset = 0;
220 for _ in 0..numbre_splu {
221 let splu_data = array_ref![list_splu_data, offset, SPLU_LEN];
222 #[allow(clippy::ptr_offset_with_cast)]
223 let (
224 splu_secondary,
225 state_splu_secondary,
226 splu_tertiary1,
227 state_splu_tertiary1,
228 splu_tertiary2,
229 state_splu_tertiary2,
230 authority_splu,
231 program_account_splu,
232 nonce_splu,
233 ) = array_refs![
234 splu_data,
235 PUBKEY_SIZE,
236 STATE_SPLU_SECONDARY,
237 PUBKEY_SIZE,
238 STATE_SPLU_TERTIARY1,
239 PUBKEY_SIZE,
240 STATE_SPLU_TERTIARY2,
241 PUBKEY_SIZE,
242 PUBKEY_SIZE,
243 NONCE_SPLU
244 ];
245 splu_vec.push(SpluStruct {
246 splu_secondary: Pubkey::new_from_array(*splu_secondary),
247 state_splu_secondary: u8::from_le_bytes(*state_splu_secondary),
248 splu_tertiary1: Pubkey::new_from_array(*splu_tertiary1),
249 state_splu_tertiary1: u8::from_le_bytes(*state_splu_tertiary1),
250 splu_tertiary2: Pubkey::new_from_array(*splu_tertiary2),
251 state_splu_tertiary2: u8::from_le_bytes(*state_splu_tertiary2),
252 authority_splu: Pubkey::new_from_array(*authority_splu),
253 program_account_splu: Pubkey::new_from_array(*program_account_splu),
254 nonce_splu: u8::from_le_bytes(*nonce_splu),
255 });
256 offset += SPLU_LEN;
257 }
258
259 Ok(UserPortfolio {
260 type_account: u8::from_le_bytes(*type_account),
261 owner: Pubkey::new_from_array(*owner),
262 portfolio_address: Pubkey::new_from_array(*portfolio_address),
263 splm_asset: Pubkey::new_from_array(*splm_asset),
264 state: u8::from_le_bytes(*state),
265 amount: u64::from_le_bytes(*amount),
266 version: u8::from_le_bytes(*version),
267 extended_data: Pubkey::new_from_array(*extended_data),
268 splm_primary: Pubkey::new_from_array(*splm_primary),
269 authority: Pubkey::new_from_array(*authority),
270 nonce: u8::from_le_bytes(*nonce),
271 create_account_program: Pubkey::new_from_array(*create_account_program),
272 splu_list: splu_vec.to_vec(),
273 })
274 }
275
276 fn pack_user_portfolio(&self, dst: &mut [u8]) {
278 let UserPortfolio {
279 type_account,
280 owner,
281 portfolio_address,
282 splm_asset,
283 state,
284 amount,
285 version,
286 extended_data,
287 splm_primary,
288 authority,
289 nonce,
290 create_account_program,
291 splu_list,
292 } = self;
293 let mut buffer = [0; USER_PORTFOLIO_PREFIX + (SPLU_LEN * 10)]; buffer[0] = *type_account;
296 let owner_range = TYPE_SIZE..TYPE_SIZE + PUBKEY_SIZE;
297 buffer[owner_range].clone_from_slice(owner.as_ref());
298 let portfolio_address_range =
299 TYPE_SIZE + PUBKEY_SIZE..TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE;
300 buffer[portfolio_address_range].clone_from_slice(portfolio_address.as_ref());
301 let splm_asset_range =
302 TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE..TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE;
303 buffer[splm_asset_range].clone_from_slice(splm_asset.as_ref());
304 let state_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE;
305 buffer[state_range] = *state;
306 let amount_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE
307 ..TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE;
308 let amount_to_array = amount.to_le_bytes();
309 buffer[amount_range].clone_from_slice(&amount_to_array[0..]);
310 let version_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE;
311 buffer[version_range] = *version;
312 let extended_data_range =
313 TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE + VERSION_SIZE
314 ..TYPE_SIZE
315 + PUBKEY_SIZE
316 + PUBKEY_SIZE
317 + PUBKEY_SIZE
318 + STATE_SIZE
319 + AMOUNT_SIZE
320 + VERSION_SIZE
321 + PUBKEY_SIZE;
322 buffer[extended_data_range].clone_from_slice(extended_data.as_ref());
323 let splm_primary_range = TYPE_SIZE
324 + PUBKEY_SIZE
325 + PUBKEY_SIZE
326 + PUBKEY_SIZE
327 + STATE_SIZE
328 + AMOUNT_SIZE
329 + VERSION_SIZE
330 + PUBKEY_SIZE
331 ..TYPE_SIZE
332 + PUBKEY_SIZE
333 + PUBKEY_SIZE
334 + PUBKEY_SIZE
335 + STATE_SIZE
336 + AMOUNT_SIZE
337 + VERSION_SIZE
338 + PUBKEY_SIZE
339 + PUBKEY_SIZE;
340 buffer[splm_primary_range].clone_from_slice(splm_primary.as_ref());
341 let authority_range = TYPE_SIZE
342 + PUBKEY_SIZE
343 + PUBKEY_SIZE
344 + PUBKEY_SIZE
345 + STATE_SIZE
346 + AMOUNT_SIZE
347 + VERSION_SIZE
348 + PUBKEY_SIZE
349 + PUBKEY_SIZE
350 ..TYPE_SIZE
351 + PUBKEY_SIZE
352 + PUBKEY_SIZE
353 + PUBKEY_SIZE
354 + STATE_SIZE
355 + AMOUNT_SIZE
356 + VERSION_SIZE
357 + PUBKEY_SIZE
358 + PUBKEY_SIZE
359 + PUBKEY_SIZE;
360 buffer[authority_range].clone_from_slice(authority.as_ref());
361 let nonce_range = TYPE_SIZE
362 + PUBKEY_SIZE
363 + PUBKEY_SIZE
364 + PUBKEY_SIZE
365 + STATE_SIZE
366 + AMOUNT_SIZE
367 + VERSION_SIZE
368 + PUBKEY_SIZE
369 + PUBKEY_SIZE
370 + PUBKEY_SIZE;
371 buffer[nonce_range] = *nonce;
372 let create_account_program_range = TYPE_SIZE
373 + PUBKEY_SIZE
374 + PUBKEY_SIZE
375 + PUBKEY_SIZE
376 + STATE_SIZE
377 + AMOUNT_SIZE
378 + VERSION_SIZE
379 + PUBKEY_SIZE
380 + PUBKEY_SIZE
381 + PUBKEY_SIZE
382 + NONCE_SIZE
383 ..TYPE_SIZE
384 + PUBKEY_SIZE
385 + PUBKEY_SIZE
386 + PUBKEY_SIZE
387 + STATE_SIZE
388 + AMOUNT_SIZE
389 + VERSION_SIZE
390 + PUBKEY_SIZE
391 + PUBKEY_SIZE
392 + PUBKEY_SIZE
393 + NONCE_SIZE
394 + PUBKEY_SIZE;
395 buffer[create_account_program_range].clone_from_slice(create_account_program.as_ref());
396
397 let splu_vec_tmp = bincode::serialize(&splu_list).unwrap();
398
399 let mut splu_data_tmp = [0; SPLU_LEN * 10]; let len_splu = splu_vec_tmp.len();
402 let nbre_splu = len_splu / SPLU_LEN;
403 let len_slu_reel = nbre_splu * SPLU_LEN;
404
405 splu_data_tmp[0..len_slu_reel as usize].clone_from_slice(&splu_vec_tmp[8..len_splu]); buffer[USER_PORTFOLIO_PREFIX..USER_PORTFOLIO_PREFIX + len_slu_reel as usize]
407 .clone_from_slice(&splu_data_tmp[0..len_slu_reel as usize]);
408
409 dst[0..USER_PORTFOLIO_PREFIX + len_slu_reel as usize]
410 .copy_from_slice(&buffer[0..USER_PORTFOLIO_PREFIX + len_slu_reel as usize]);
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_pack_unpack_user_portfolio() {
420 let splu_struct = SpluStruct {
421 splu_secondary: Pubkey::new_unique(),
422 state_splu_secondary: 1,
423 splu_tertiary1: Pubkey::new_unique(),
424 state_splu_tertiary1: 1,
425 splu_tertiary2: Pubkey::new_unique(),
426 state_splu_tertiary2: 1,
427 authority_splu: Pubkey::new_unique(),
428 program_account_splu: Pubkey::new_unique(),
429 nonce_splu: 1,
430 };
431
432 let mut splu_list = Vec::new();
433 splu_list.push(splu_struct);
434
435 let user_portfolio = UserPortfolio {
436 type_account: TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT,
437 owner: Pubkey::new_unique(),
438 portfolio_address: Pubkey::new_unique(),
439 splm_asset: Pubkey::new_unique(),
440 state: 0,
441 amount: 3,
442 version: 1,
443 extended_data: Pubkey::new_unique(),
444 splm_primary: Pubkey::new_unique(),
445 authority: Pubkey::new_unique(),
446 nonce: 1,
447 create_account_program: Pubkey::new_unique(),
448 splu_list,
449 };
450 const LEN: usize = USER_PORTFOLIO_PREFIX + (SPLU_LEN * 1); let mut packed = [0u8; LEN];
452 UserPortfolio::pack_user_portfolio(&user_portfolio, &mut packed[..]);
453 let unpacked = UserPortfolio::unpack_user_portfolio(&packed).unwrap();
454 assert_eq!(user_portfolio, unpacked);
455 assert_eq!(unpacked.type_account, TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT);
456 }
457}