n_token_models/states/
user_portfolio.rs

1//! State transition types
2use 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/// struct of SPLU of PPU
32#[repr(C)]
33#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
34pub struct SpluStruct {
35    /// SPLU secondary address.
36    pub splu_secondary: Pubkey,
37    /// State of SPLU secondary.
38    pub state_splu_secondary: u8,
39    /// First SPLU tertiary address.
40    pub splu_tertiary1: Pubkey,
41    /// State of first SPLU tertiary.
42    pub state_splu_tertiary1: u8,
43    /// Second SPLU tertiary address.
44    pub splu_tertiary2: Pubkey,
45    /// State of second SPLU tertiary.
46    pub state_splu_tertiary2: u8,
47    /// Authority of SPLU.
48    pub authority_splu: Pubkey,
49    /// Account_program of SPLU.
50    pub program_account_splu: Pubkey,
51    /// Nonce of SPLU.
52    pub nonce_splu: u8,
53}
54
55impl SpluStruct {
56    /// Create new SPLU Struct
57    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    /// update the state of splu secondary
74    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    /// reset the state of splu secondary
84    pub fn reset_splu_secondary(&mut self) -> ProgramResult {
85        self.state_splu_secondary = 0;
86
87        Ok(())
88    }
89
90    /// add new splu teriary
91    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    /// reset the state of splu teriary
112    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    /// update the state splu teriary
120    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///  User Portfolio data.
129#[repr(C)]
130#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
131
132pub struct UserPortfolio {
133    /// The type of account.
134    pub type_account: u8,
135    /// The owner of user portfolio.
136    pub owner: Pubkey,
137    /// Portfolio depends of user portfolio.
138    pub portfolio_address: Pubkey,
139    /// State of PPU to know which step was completed.
140    pub state: u8,
141    /// Last amount deposit or withraw.
142    pub amount: u64,
143    /// Version of PPU struct.
144    pub version: u8,
145    /// Account to be used to save extra data.
146    pub extended_data: Pubkey,
147    /// Splm primary which user deposit from.
148    pub splm_primary: Pubkey,
149    /// Authority of PPU.
150    pub authority: Pubkey,
151    /// Nonce used in cross program.
152    pub nonce: u8,
153    /// Create account program used in cross program.
154    pub create_account_program: Pubkey,
155    /// List of splu contain the amount transfered after swap.
156    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
167/// pack and unpack for user portfolio
168pub trait PackUserPortfolio {
169    /// unpack user portfolio
170    fn unpack_user_portfolio(src: &[u8]) -> Result<UserPortfolio, ProgramError>;
171    /// pack user portfolio
172    fn pack_user_portfolio(&self, dst: &mut [u8]);
173}
174
175impl PackUserPortfolio for UserPortfolio {
176    /// unpack user portfolio
177    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        // unpack list of splu struct
215        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    /// pack user portfolio
272    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)]; // MAXIMUM 10 SPLU
288
289        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]; // maximum 10 splu
383
384        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]); // 0..8 : 8 bytes contain length of struct
389        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); // 204: @préfix + 164:splu_len * 1
433        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}