module_ntoken/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    /// The asset minted while join.
140    pub splm_asset: Pubkey,
141    /// State of PPU to know which step was completed.
142    pub state: u8,
143    /// Last amount deposit or withraw.
144    pub amount: u64,
145    /// Version of PPU struct.
146    pub version: u8,
147    /// Account to be used to save extra data.
148    pub extended_data: Pubkey,
149    /// Splm primary which user deposit from.
150    pub splm_primary: Pubkey,
151    /// Authority of PPU.
152    pub authority: Pubkey,
153    /// Nonce used in cross program.
154    pub nonce: u8,
155    /// Create account program used in cross program.
156    pub create_account_program: Pubkey,
157    /// List of splu contain the amount transfered after swap.
158    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
169/// pack and unpack for user portfolio
170pub trait PackUserPortfolio {
171    /// unpack user portfolio
172    fn unpack_user_portfolio(src: &[u8]) -> Result<UserPortfolio, ProgramError>;
173    /// pack user portfolio
174    fn pack_user_portfolio(&self, dst: &mut [u8]);
175}
176
177impl PackUserPortfolio for UserPortfolio {
178    /// unpack user portfolio
179    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        // unpack list of splu struct
219        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    /// pack user portfolio
277    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)]; // MAXIMUM 10 SPLU
294
295        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]; // maximum 10 splu
400
401        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]); // 0..8 : 8 bytes contain length of struct
406        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); // 204: @préfix + 164:splu_len * 1
451        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}