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 state: u8,
141 pub amount: u64,
143 pub version: u8,
145 pub extended_data: Pubkey,
147 pub splm_primary: Pubkey,
149 pub authority: Pubkey,
151 pub nonce: u8,
153 pub create_account_program: Pubkey,
155 pub splu_list: Vec<SpluStruct>,
157}
158
159impl Sealed for UserPortfolio {}
160
161impl IsInitialized for UserPortfolio {
162 fn is_initialized(&self) -> bool {
163 return self.type_account == TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT;
164 }
165}
166
167pub trait PackUserPortfolio {
169 fn unpack_user_portfolio(src: &[u8]) -> Result<UserPortfolio, ProgramError>;
171 fn pack_user_portfolio(&self, dst: &mut [u8]);
173}
174
175impl PackUserPortfolio for UserPortfolio {
176 fn unpack_user_portfolio(src: &[u8]) -> Result<Self, ProgramError> {
178 let numbre_splu = (&src.len() - USER_PORTFOLIO_PREFIX) / SPLU_LEN;
179 let len_splu_data = &src.len() - USER_PORTFOLIO_PREFIX;
180
181 let src_fix = array_ref![&src, 0, USER_PORTFOLIO_PREFIX];
182 let (
183 type_account,
184 owner,
185 portfolio_address,
186 state,
187 amount,
188 version,
189 extended_data,
190 splm_primary,
191 authority,
192 nonce,
193 create_account_program,
194 ) = array_refs![
195 src_fix,
196 TYPE_SIZE,
197 PUBKEY_SIZE,
198 PUBKEY_SIZE,
199 STATE_SIZE,
200 AMOUNT_SIZE,
201 VERSION_SIZE,
202 PUBKEY_SIZE,
203 PUBKEY_SIZE,
204 PUBKEY_SIZE,
205 NONCE_SIZE,
206 PUBKEY_SIZE
207 ];
208
209 let mut splu_vec: Vec<SpluStruct> = Vec::with_capacity(numbre_splu);
210
211 let list_splu_data =
212 &src[USER_PORTFOLIO_PREFIX..USER_PORTFOLIO_PREFIX + (len_splu_data) as usize];
213
214 let mut offset = 0;
216 for _ in 0..numbre_splu {
217 let splu_data = array_ref![list_splu_data, offset, SPLU_LEN];
218 #[allow(clippy::ptr_offset_with_cast)]
219 let (
220 splu_secondary,
221 state_splu_secondary,
222 splu_tertiary1,
223 state_splu_tertiary1,
224 splu_tertiary2,
225 state_splu_tertiary2,
226 authority_splu,
227 program_account_splu,
228 nonce_splu,
229 ) = array_refs![
230 splu_data,
231 PUBKEY_SIZE,
232 STATE_SPLU_SECONDARY,
233 PUBKEY_SIZE,
234 STATE_SPLU_TERTIARY1,
235 PUBKEY_SIZE,
236 STATE_SPLU_TERTIARY2,
237 PUBKEY_SIZE,
238 PUBKEY_SIZE,
239 NONCE_SPLU
240 ];
241 splu_vec.push(SpluStruct {
242 splu_secondary: Pubkey::new_from_array(*splu_secondary),
243 state_splu_secondary: u8::from_le_bytes(*state_splu_secondary),
244 splu_tertiary1: Pubkey::new_from_array(*splu_tertiary1),
245 state_splu_tertiary1: u8::from_le_bytes(*state_splu_tertiary1),
246 splu_tertiary2: Pubkey::new_from_array(*splu_tertiary2),
247 state_splu_tertiary2: u8::from_le_bytes(*state_splu_tertiary2),
248 authority_splu: Pubkey::new_from_array(*authority_splu),
249 program_account_splu: Pubkey::new_from_array(*program_account_splu),
250 nonce_splu: u8::from_le_bytes(*nonce_splu),
251 });
252 offset += SPLU_LEN;
253 }
254
255 Ok(UserPortfolio {
256 type_account: u8::from_le_bytes(*type_account),
257 owner: Pubkey::new_from_array(*owner),
258 portfolio_address: Pubkey::new_from_array(*portfolio_address),
259 state: u8::from_le_bytes(*state),
260 amount: u64::from_le_bytes(*amount),
261 version: u8::from_le_bytes(*version),
262 extended_data: Pubkey::new_from_array(*extended_data),
263 splm_primary: Pubkey::new_from_array(*splm_primary),
264 authority: Pubkey::new_from_array(*authority),
265 nonce: u8::from_le_bytes(*nonce),
266 create_account_program: Pubkey::new_from_array(*create_account_program),
267 splu_list: splu_vec.to_vec(),
268 })
269 }
270
271 fn pack_user_portfolio(&self, dst: &mut [u8]) {
273 let UserPortfolio {
274 type_account,
275 owner,
276 portfolio_address,
277 state,
278 amount,
279 version,
280 extended_data,
281 splm_primary,
282 authority,
283 nonce,
284 create_account_program,
285 splu_list,
286 } = self;
287 let mut buffer = [0; USER_PORTFOLIO_PREFIX + (SPLU_LEN * 10)]; buffer[0] = *type_account;
290 let owner_range = TYPE_SIZE..TYPE_SIZE + PUBKEY_SIZE;
291 buffer[owner_range].clone_from_slice(owner.as_ref());
292 let portfolio_address_range =
293 TYPE_SIZE + PUBKEY_SIZE..TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE;
294 buffer[portfolio_address_range].clone_from_slice(portfolio_address.as_ref());
295 let state_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE;
296 buffer[state_range] = *state;
297 let amount_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE
298 ..TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE;
299 let amount_to_array = amount.to_le_bytes();
300 buffer[amount_range].clone_from_slice(&amount_to_array[0..]);
301 let version_range = TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE;
302 buffer[version_range] = *version;
303 let extended_data_range =
304 TYPE_SIZE + PUBKEY_SIZE + PUBKEY_SIZE + STATE_SIZE + AMOUNT_SIZE + VERSION_SIZE
305 ..TYPE_SIZE
306 + PUBKEY_SIZE
307 + PUBKEY_SIZE
308 + STATE_SIZE
309 + AMOUNT_SIZE
310 + VERSION_SIZE
311 + PUBKEY_SIZE;
312 buffer[extended_data_range].clone_from_slice(extended_data.as_ref());
313 let splm_primary_range = TYPE_SIZE
314 + PUBKEY_SIZE
315 + PUBKEY_SIZE
316 + STATE_SIZE
317 + AMOUNT_SIZE
318 + VERSION_SIZE
319 + PUBKEY_SIZE
320 ..TYPE_SIZE
321 + PUBKEY_SIZE
322 + PUBKEY_SIZE
323 + STATE_SIZE
324 + AMOUNT_SIZE
325 + VERSION_SIZE
326 + PUBKEY_SIZE
327 + PUBKEY_SIZE;
328 buffer[splm_primary_range].clone_from_slice(splm_primary.as_ref());
329 let authority_range = TYPE_SIZE
330 + PUBKEY_SIZE
331 + PUBKEY_SIZE
332 + STATE_SIZE
333 + AMOUNT_SIZE
334 + VERSION_SIZE
335 + PUBKEY_SIZE
336 + PUBKEY_SIZE
337 ..TYPE_SIZE
338 + PUBKEY_SIZE
339 + PUBKEY_SIZE
340 + STATE_SIZE
341 + AMOUNT_SIZE
342 + VERSION_SIZE
343 + PUBKEY_SIZE
344 + PUBKEY_SIZE
345 + PUBKEY_SIZE;
346 buffer[authority_range].clone_from_slice(authority.as_ref());
347 let nonce_range = TYPE_SIZE
348 + PUBKEY_SIZE
349 + PUBKEY_SIZE
350 + STATE_SIZE
351 + AMOUNT_SIZE
352 + VERSION_SIZE
353 + PUBKEY_SIZE
354 + PUBKEY_SIZE
355 + PUBKEY_SIZE;
356 buffer[nonce_range] = *nonce;
357 let create_account_program_range = TYPE_SIZE
358 + PUBKEY_SIZE
359 + PUBKEY_SIZE
360 + STATE_SIZE
361 + AMOUNT_SIZE
362 + VERSION_SIZE
363 + PUBKEY_SIZE
364 + PUBKEY_SIZE
365 + PUBKEY_SIZE
366 + NONCE_SIZE
367 ..TYPE_SIZE
368 + PUBKEY_SIZE
369 + PUBKEY_SIZE
370 + STATE_SIZE
371 + AMOUNT_SIZE
372 + VERSION_SIZE
373 + PUBKEY_SIZE
374 + PUBKEY_SIZE
375 + PUBKEY_SIZE
376 + NONCE_SIZE
377 + PUBKEY_SIZE;
378 buffer[create_account_program_range].clone_from_slice(create_account_program.as_ref());
379
380 let splu_vec_tmp = bincode::serialize(&splu_list).unwrap();
381
382 let mut splu_data_tmp = [0; SPLU_LEN * 10]; let len_splu = splu_vec_tmp.len();
385 let nbre_splu = len_splu / SPLU_LEN;
386 let len_slu_reel = nbre_splu * SPLU_LEN;
387
388 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]
390 .clone_from_slice(&splu_data_tmp[0..len_slu_reel as usize]);
391
392 dst[0..USER_PORTFOLIO_PREFIX + len_slu_reel as usize]
393 .copy_from_slice(&buffer[0..USER_PORTFOLIO_PREFIX + len_slu_reel as usize]);
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn test_pack_unpack_user_portfolio() {
403 let splu_struct = SpluStruct {
404 splu_secondary: Pubkey::new_unique(),
405 state_splu_secondary: 1,
406 splu_tertiary1: Pubkey::new_unique(),
407 state_splu_tertiary1: 1,
408 splu_tertiary2: Pubkey::new_unique(),
409 state_splu_tertiary2: 1,
410 authority_splu: Pubkey::new_unique(),
411 program_account_splu: Pubkey::new_unique(),
412 nonce_splu: 1,
413 };
414
415 let mut splu_list = Vec::new();
416 splu_list.push(splu_struct);
417
418 let user_portfolio = UserPortfolio {
419 type_account: TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT,
420 owner: Pubkey::new_unique(),
421 portfolio_address: Pubkey::new_unique(),
422 state: 0,
423 amount: 3,
424 version: 1,
425 extended_data: Pubkey::new_unique(),
426 splm_primary: Pubkey::new_unique(),
427 authority: Pubkey::new_unique(),
428 nonce: 1,
429 create_account_program: Pubkey::new_unique(),
430 splu_list,
431 };
432 const LEN: usize = USER_PORTFOLIO_PREFIX + (SPLU_LEN * 1); let mut packed = [0u8; LEN];
434 UserPortfolio::pack_user_portfolio(&user_portfolio, &mut packed[..]);
435 let unpacked = UserPortfolio::unpack_user_portfolio(&packed).unwrap();
436 assert_eq!(user_portfolio, unpacked);
437 assert_eq!(unpacked.type_account, TYPE_ACCOUNT_USER_PORTFOLIO_ACCOUNT);
438 }
439}