gpl_token/
state.rs

1//! State transition types
2
3use crate::instruction::MAX_SIGNERS;
4use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs};
5use num_enum::TryFromPrimitive;
6use gemachain_program::{
7    program_error::ProgramError,
8    program_option::COption,
9    program_pack::{IsInitialized, Pack, Sealed},
10    pubkey::Pubkey,
11};
12
13/// Mint data.
14#[repr(C)]
15#[derive(Clone, Copy, Debug, Default, PartialEq)]
16pub struct Mint {
17    /// Optional authority used to mint new tokens. The mint authority may only be provided during
18    /// mint creation. If no mint authority is present then the mint has a fixed supply and no
19    /// further tokens may be minted.
20    pub mint_authority: COption<Pubkey>,
21    /// Total supply of tokens.
22    pub supply: u64,
23    /// Number of base 10 digits to the right of the decimal place.
24    pub decimals: u8,
25    /// Is `true` if this structure has been initialized
26    pub is_initialized: bool,
27    /// Optional authority to freeze token accounts.
28    pub freeze_authority: COption<Pubkey>,
29}
30impl Sealed for Mint {}
31impl IsInitialized for Mint {
32    fn is_initialized(&self) -> bool {
33        self.is_initialized
34    }
35}
36impl Pack for Mint {
37    const LEN: usize = 82;
38    fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
39        let src = array_ref![src, 0, 82];
40        let (mint_authority, supply, decimals, is_initialized, freeze_authority) =
41            array_refs![src, 36, 8, 1, 1, 36];
42        let mint_authority = unpack_coption_key(mint_authority)?;
43        let supply = u64::from_le_bytes(*supply);
44        let decimals = decimals[0];
45        let is_initialized = match is_initialized {
46            [0] => false,
47            [1] => true,
48            _ => return Err(ProgramError::InvalidAccountData),
49        };
50        let freeze_authority = unpack_coption_key(freeze_authority)?;
51        Ok(Mint {
52            mint_authority,
53            supply,
54            decimals,
55            is_initialized,
56            freeze_authority,
57        })
58    }
59    fn pack_into_slice(&self, dst: &mut [u8]) {
60        let dst = array_mut_ref![dst, 0, 82];
61        let (
62            mint_authority_dst,
63            supply_dst,
64            decimals_dst,
65            is_initialized_dst,
66            freeze_authority_dst,
67        ) = mut_array_refs![dst, 36, 8, 1, 1, 36];
68        let &Mint {
69            ref mint_authority,
70            supply,
71            decimals,
72            is_initialized,
73            ref freeze_authority,
74        } = self;
75        pack_coption_key(mint_authority, mint_authority_dst);
76        *supply_dst = supply.to_le_bytes();
77        decimals_dst[0] = decimals;
78        is_initialized_dst[0] = is_initialized as u8;
79        pack_coption_key(freeze_authority, freeze_authority_dst);
80    }
81}
82
83/// Account data.
84#[repr(C)]
85#[derive(Clone, Copy, Debug, Default, PartialEq)]
86pub struct Account {
87    /// The mint associated with this account
88    pub mint: Pubkey,
89    /// The owner of this account.
90    pub owner: Pubkey,
91    /// The amount of tokens this account holds.
92    pub amount: u64,
93    /// If `delegate` is `Some` then `delegated_amount` represents
94    /// the amount authorized by the delegate
95    pub delegate: COption<Pubkey>,
96    /// The account's state
97    pub state: AccountState,
98    /// If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account
99    /// is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped
100    /// GEMA accounts do not drop below this threshold.
101    pub is_native: COption<u64>,
102    /// The amount delegated
103    pub delegated_amount: u64,
104    /// Optional authority to close the account.
105    pub close_authority: COption<Pubkey>,
106}
107impl Account {
108    /// Checks if account is frozen
109    pub fn is_frozen(&self) -> bool {
110        self.state == AccountState::Frozen
111    }
112    /// Checks if account is native
113    pub fn is_native(&self) -> bool {
114        self.is_native.is_some()
115    }
116}
117impl Sealed for Account {}
118impl IsInitialized for Account {
119    fn is_initialized(&self) -> bool {
120        self.state != AccountState::Uninitialized
121    }
122}
123impl Pack for Account {
124    const LEN: usize = 165;
125    fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
126        let src = array_ref![src, 0, 165];
127        let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) =
128            array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36];
129        Ok(Account {
130            mint: Pubkey::new_from_array(*mint),
131            owner: Pubkey::new_from_array(*owner),
132            amount: u64::from_le_bytes(*amount),
133            delegate: unpack_coption_key(delegate)?,
134            state: AccountState::try_from_primitive(state[0])
135                .or(Err(ProgramError::InvalidAccountData))?,
136            is_native: unpack_coption_u64(is_native)?,
137            delegated_amount: u64::from_le_bytes(*delegated_amount),
138            close_authority: unpack_coption_key(close_authority)?,
139        })
140    }
141    fn pack_into_slice(&self, dst: &mut [u8]) {
142        let dst = array_mut_ref![dst, 0, 165];
143        let (
144            mint_dst,
145            owner_dst,
146            amount_dst,
147            delegate_dst,
148            state_dst,
149            is_native_dst,
150            delegated_amount_dst,
151            close_authority_dst,
152        ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36];
153        let &Account {
154            ref mint,
155            ref owner,
156            amount,
157            ref delegate,
158            state,
159            ref is_native,
160            delegated_amount,
161            ref close_authority,
162        } = self;
163        mint_dst.copy_from_slice(mint.as_ref());
164        owner_dst.copy_from_slice(owner.as_ref());
165        *amount_dst = amount.to_le_bytes();
166        pack_coption_key(delegate, delegate_dst);
167        state_dst[0] = state as u8;
168        pack_coption_u64(is_native, is_native_dst);
169        *delegated_amount_dst = delegated_amount.to_le_bytes();
170        pack_coption_key(close_authority, close_authority_dst);
171    }
172}
173
174/// Account state.
175#[repr(u8)]
176#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)]
177pub enum AccountState {
178    /// Account is not yet initialized
179    Uninitialized,
180    /// Account is initialized; the account owner and/or delegate may perform permitted operations
181    /// on this account
182    Initialized,
183    /// Account has been frozen by the mint freeze authority. Neither the account owner nor
184    /// the delegate are able to perform operations on this account.
185    Frozen,
186}
187
188impl Default for AccountState {
189    fn default() -> Self {
190        AccountState::Uninitialized
191    }
192}
193
194/// Multisignature data.
195#[repr(C)]
196#[derive(Clone, Copy, Debug, Default, PartialEq)]
197pub struct Multisig {
198    /// Number of signers required
199    pub m: u8,
200    /// Number of valid signers
201    pub n: u8,
202    /// Is `true` if this structure has been initialized
203    pub is_initialized: bool,
204    /// Signer public keys
205    pub signers: [Pubkey; MAX_SIGNERS],
206}
207impl Sealed for Multisig {}
208impl IsInitialized for Multisig {
209    fn is_initialized(&self) -> bool {
210        self.is_initialized
211    }
212}
213impl Pack for Multisig {
214    const LEN: usize = 355;
215    fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
216        let src = array_ref![src, 0, 355];
217        #[allow(clippy::ptr_offset_with_cast)]
218        let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS];
219        let mut result = Multisig {
220            m: m[0],
221            n: n[0],
222            is_initialized: match is_initialized {
223                [0] => false,
224                [1] => true,
225                _ => return Err(ProgramError::InvalidAccountData),
226            },
227            signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS],
228        };
229        for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) {
230            *dst = Pubkey::new(src);
231        }
232        Ok(result)
233    }
234    fn pack_into_slice(&self, dst: &mut [u8]) {
235        let dst = array_mut_ref![dst, 0, 355];
236        #[allow(clippy::ptr_offset_with_cast)]
237        let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS];
238        *m = [self.m];
239        *n = [self.n];
240        *is_initialized = [self.is_initialized as u8];
241        for (i, src) in self.signers.iter().enumerate() {
242            let dst_array = array_mut_ref![signers_flat, 32 * i, 32];
243            dst_array.copy_from_slice(src.as_ref());
244        }
245    }
246}
247
248// Helpers
249fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 36]) {
250    let (tag, body) = mut_array_refs![dst, 4, 32];
251    match src {
252        COption::Some(key) => {
253            *tag = [1, 0, 0, 0];
254            body.copy_from_slice(key.as_ref());
255        }
256        COption::None => {
257            *tag = [0; 4];
258        }
259    }
260}
261fn unpack_coption_key(src: &[u8; 36]) -> Result<COption<Pubkey>, ProgramError> {
262    let (tag, body) = array_refs![src, 4, 32];
263    match *tag {
264        [0, 0, 0, 0] => Ok(COption::None),
265        [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))),
266        _ => Err(ProgramError::InvalidAccountData),
267    }
268}
269fn pack_coption_u64(src: &COption<u64>, dst: &mut [u8; 12]) {
270    let (tag, body) = mut_array_refs![dst, 4, 8];
271    match src {
272        COption::Some(amount) => {
273            *tag = [1, 0, 0, 0];
274            *body = amount.to_le_bytes();
275        }
276        COption::None => {
277            *tag = [0; 4];
278        }
279    }
280}
281fn unpack_coption_u64(src: &[u8; 12]) -> Result<COption<u64>, ProgramError> {
282    let (tag, body) = array_refs![src, 4, 8];
283    match *tag {
284        [0, 0, 0, 0] => Ok(COption::None),
285        [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))),
286        _ => Err(ProgramError::InvalidAccountData),
287    }
288}