module_ntoken/states/
distribute_token.rs

1//! State transition types
2use crate::constants::account_size::{DISTRIBUTE_TOKEN_PREFIX_SIZE, NUMBER_USERS_DISTRIBUTE, LIST_DISTRIBUTE_USER_SIZE, USER_DISTRIBUTE_SIZE};
3use crate::constants::constant::PUBKEY_SIZE;
4use crate::{
5    constants::{account_size::DISTRIBUTE_TOKEN_SIZE, account_type::TYPE_ACCOUNT_AIRDROP_ACCOUNT},
6    error::PortfolioError,
7};
8use arrayref::{array_ref, array_refs};
9use serde::{Deserialize, Serialize};
10use solana_program::{
11    entrypoint_deprecated::ProgramResult,
12    program_error::ProgramError,
13    program_pack::{IsInitialized, Sealed},
14    pubkey::Pubkey,
15};
16use std::vec::Vec;
17
18const TYPE_SIZE: usize = 1;
19const STATE_DISTIBUTE_TOKENS_SIZE: usize = 1;
20const AMOUNT_SIZE: usize = 8;
21const NFT_FIND_SIZE: usize = 1;
22const STATE_USER_DISTRIBUTE_SIZE: usize = 1;
23
24#[repr(C)]
25#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
26
27pub struct UserDistributeStruct {
28    /// The public address owned by user .
29    pub user_portfolio_account: Pubkey,
30    ///  The last amount deposit or withraw.
31    pub amount: u64,
32    /// _0. if he wasn't staked an nft token.
33    /// _1. if he was staked an nft token.
34    pub nft_find: u8,
35    /// state of the user portfolio.
36    pub state_user_distribute: u8,
37}
38impl UserDistributeStruct {
39    /// add user distribute
40    pub fn add_user_distribute(
41        &mut self,
42        user_portfolio_account: Pubkey,
43        amount: u64,
44        nft_find: u8,
45        state_user_distribute: u8,
46    ) -> ProgramResult {
47        self.user_portfolio_account = user_portfolio_account;
48        self.amount = self
49            .amount
50            .checked_add(amount)
51            .ok_or(PortfolioError::InvalidAmount)?;
52
53        self.nft_find = nft_find;
54        self.state_user_distribute = state_user_distribute;
55        Ok(())
56    }
57    /// update the state of  user distribute
58    pub fn update_state_user_distribute(&mut self, state_user_distribute: u8) -> ProgramResult {
59        self.state_user_distribute = state_user_distribute;
60        Ok(())
61    }
62}
63
64///  distribute account data.
65#[repr(C)]
66#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
67pub struct DistributeTokens {
68    /// The type of account .
69    pub type_account: u8,
70    /// state of the user portfolio.
71    pub state_distribute_tokens: u8,
72    /// The portfolio depends of distribute token.
73    pub portfolio_address: Pubkey,
74    /// list of [`UserDistributeStruct`]
75    pub list_user_distribute: Vec<UserDistributeStruct>,
76}
77
78impl Sealed for DistributeTokens {}
79
80impl IsInitialized for DistributeTokens {
81    fn is_initialized(&self) -> bool {
82        return self.type_account == TYPE_ACCOUNT_AIRDROP_ACCOUNT;
83    }
84}
85/// pack and unpack for distribute token
86pub trait PackDistributeTokens {
87    /// unpack distribute token
88    fn unpack_distribute_token(src: &[u8]) -> Result<DistributeTokens, ProgramError>;
89    /// pack diestribute token
90    fn pack_distribute_token(&self, dst: &mut [u8]);
91}
92
93impl PackDistributeTokens for DistributeTokens {
94    /// unpack distribute token
95    fn unpack_distribute_token(src: &[u8]) -> Result<Self, ProgramError> {
96        let src_fix = array_ref![&src, 0, DISTRIBUTE_TOKEN_PREFIX_SIZE];
97        let (type_account, state_distribute_tokens, portfolio_address) =
98            array_refs![src_fix, TYPE_SIZE, STATE_DISTIBUTE_TOKENS_SIZE, PUBKEY_SIZE];
99        let mut user_distribute_vec: Vec<UserDistributeStruct> =
100            Vec::with_capacity(NUMBER_USERS_DISTRIBUTE);
101
102        // unpack the list of user distribure
103        let list_user_distribute_data = &src[DISTRIBUTE_TOKEN_PREFIX_SIZE
104            ..DISTRIBUTE_TOKEN_PREFIX_SIZE + LIST_DISTRIBUTE_USER_SIZE as usize];
105        let mut offset = 0;
106        for _ in 0..NUMBER_USERS_DISTRIBUTE {
107            let user_data = array_ref![list_user_distribute_data, offset, USER_DISTRIBUTE_SIZE];
108            #[allow(clippy::ptr_offset_with_cast)]
109            let (user_portfolio_account, amount, nft_find, state_user_distribute) = array_refs![
110                user_data,
111                PUBKEY_SIZE,
112                AMOUNT_SIZE,
113                NFT_FIND_SIZE,
114                STATE_USER_DISTRIBUTE_SIZE
115            ];
116            user_distribute_vec.push(UserDistributeStruct {
117                user_portfolio_account: Pubkey::new_from_array(*user_portfolio_account),
118                amount: u64::from_le_bytes(*amount),
119                nft_find: u8::from_le_bytes(*nft_find),
120                state_user_distribute: u8::from_le_bytes(*state_user_distribute),
121            });
122            offset += USER_DISTRIBUTE_SIZE;
123        }
124
125        Ok(DistributeTokens {
126            type_account: u8::from_le_bytes(*type_account),
127            state_distribute_tokens: u8::from_le_bytes(*state_distribute_tokens),
128            portfolio_address: Pubkey::new_from_array(*portfolio_address),
129            list_user_distribute: user_distribute_vec.to_vec(),
130        })
131    }
132
133    /// pack distribute token
134    fn pack_distribute_token(&self, dst: &mut [u8]) {
135        let DistributeTokens {
136            type_account,
137            state_distribute_tokens,
138            portfolio_address,
139            list_user_distribute,
140        } = self;
141
142        let mut buffer = [0; DISTRIBUTE_TOKEN_SIZE];
143        buffer[0] = *type_account;
144        let state_distribute_tokens_range = TYPE_SIZE;
145        buffer[state_distribute_tokens_range] = *state_distribute_tokens;
146        let portfolio_address_range = TYPE_SIZE + STATE_DISTIBUTE_TOKENS_SIZE
147            ..TYPE_SIZE + STATE_DISTIBUTE_TOKENS_SIZE + PUBKEY_SIZE;
148        buffer[portfolio_address_range].clone_from_slice(portfolio_address.as_ref());
149        let user_distribute_vec_tmp = bincode::serialize(&list_user_distribute).unwrap();
150        let mut user_distribute_data_tmp = [0; LIST_DISTRIBUTE_USER_SIZE]; // maximum 44
151        let len_user_distribute = user_distribute_vec_tmp.len();
152        let nbre_user_distribute = len_user_distribute / USER_DISTRIBUTE_SIZE; //44
153        let len_slu_reel = nbre_user_distribute * USER_DISTRIBUTE_SIZE;
154        user_distribute_data_tmp[0..len_slu_reel as usize]
155            .clone_from_slice(&user_distribute_vec_tmp[8..len_user_distribute]); // 0..8 : 8 bytes contain length of struct
156
157        buffer[DISTRIBUTE_TOKEN_PREFIX_SIZE..DISTRIBUTE_TOKEN_PREFIX_SIZE + len_slu_reel as usize]
158            .clone_from_slice(&user_distribute_data_tmp[0..len_slu_reel as usize]);
159
160        dst[0..DISTRIBUTE_TOKEN_PREFIX_SIZE + len_slu_reel as usize]
161            .copy_from_slice(&buffer[0..DISTRIBUTE_TOKEN_PREFIX_SIZE + len_slu_reel as usize]);
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use crate::constants::account_type::TYPE_ACCOUNT_AIRDROP_ACCOUNT;
169    #[test]
170    fn test_pack_distribute_token() {
171        let user_portfolio_account = Pubkey::new_unique();
172        let amount = 12;
173        let nft_find = 0;
174        let state_user_distribute = 0;
175
176        let _user_distribute = UserDistributeStruct {
177            user_portfolio_account,
178            amount,
179            nft_find,
180            state_user_distribute,
181        };
182        let mut list_user_distribute = Vec::new();
183        for _i in 0..NUMBER_USERS_DISTRIBUTE {
184            let user_distribute = UserDistributeStruct {
185                user_portfolio_account,
186                amount,
187                nft_find,
188                state_user_distribute,
189            };
190            list_user_distribute.push(user_distribute);
191        }
192
193        let distribute_token = DistributeTokens {
194            type_account: TYPE_ACCOUNT_AIRDROP_ACCOUNT,
195            state_distribute_tokens: 0,
196            portfolio_address: Pubkey::new_unique(),
197            list_user_distribute,
198        };
199
200        const LEN: usize = DISTRIBUTE_TOKEN_SIZE;
201        let mut packed = [0u8; LEN];
202        PackDistributeTokens::pack_distribute_token(&distribute_token, &mut packed[..]);
203        let unpacked = DistributeTokens::unpack_distribute_token(&packed).unwrap();
204        assert_eq!(distribute_token, unpacked);
205        assert_eq!(unpacked.type_account,TYPE_ACCOUNT_AIRDROP_ACCOUNT);
206    }
207}