gmsol_store/states/
user.rs

1use anchor_lang::prelude::*;
2use gmsol_utils::InitSpace;
3
4use crate::{
5    utils::pubkey::{optional_address, DEFAULT_PUBKEY},
6    CoreError,
7};
8
9use super::Seed;
10
11/// Header of `User` Account.
12#[account(zero_copy)]
13#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct UserHeader {
16    /// Version of the user account.
17    pub(crate) version: u8,
18    /// The bump seed.
19    pub(crate) bump: u8,
20    flags: UserFlagContainer,
21    #[cfg_attr(feature = "debug", debug(skip))]
22    padding_0: [u8; 13],
23    /// The owner of this user account.
24    pub(crate) owner: Pubkey,
25    /// The store.
26    pub(crate) store: Pubkey,
27    /// Referral.
28    pub(crate) referral: Referral,
29    /// GT State.
30    pub(crate) gt: UserGtState,
31    #[cfg_attr(feature = "debug", debug(skip))]
32    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
33    reserved: [u8; 128],
34}
35
36/// User flags.
37#[derive(num_enum::IntoPrimitive)]
38#[repr(u8)]
39#[non_exhaustive]
40pub enum UserFlag {
41    /// Is initialized.
42    Initialized,
43}
44
45impl UserFlag {
46    /// Max flags.
47    pub const MAX_FLAGS: usize = 8;
48}
49
50gmsol_utils::flags!(UserFlag, { UserFlag::MAX_FLAGS }, u8);
51
52impl UserHeader {
53    /// Return whether the user account is initialized.
54    pub fn is_initialized(&self) -> bool {
55        self.flags.get_flag(UserFlag::Initialized)
56    }
57
58    /// Initialize.
59    pub(crate) fn init(&mut self, store: &Pubkey, owner: &Pubkey, bump: u8) -> Result<()> {
60        require!(
61            !self.flags.get_flag(UserFlag::Initialized),
62            CoreError::UserAccountHasBeenInitialized
63        );
64        self.flags.set_flag(UserFlag::Initialized, true);
65
66        self.bump = bump;
67        self.owner = *owner;
68        self.store = *store;
69
70        Ok(())
71    }
72
73    /// Get User Account space.
74    pub fn space(_version: u8) -> usize {
75        std::mem::size_of::<Self>()
76    }
77
78    /// Get referral.
79    pub fn referral(&self) -> &Referral {
80        &self.referral
81    }
82
83    /// Transfer the ownership of the given code from this user to the receiver.
84    /// # CHECK
85    /// - `code` must be owned by current user.
86    /// - the store of `code` must be the same as current user and `receiver`.
87    /// # Errors
88    /// - `code` must be initialized.
89    /// - current user must be initialized.
90    /// - `receiver` must be initialized.
91    /// - the code of `receiver` must not have been set.
92    /// - the `next_owner` of the code must be the owner of the `receiver_user`.
93    pub(crate) fn unchecked_complete_code_transfer(
94        &mut self,
95        code: &mut ReferralCodeV2,
96        receiver_user: &mut Self,
97    ) -> Result<()> {
98        require!(
99            code.code != ReferralCodeBytes::default(),
100            CoreError::PreconditionsAreNotMet
101        );
102        require!(self.is_initialized(), CoreError::InvalidUserAccount);
103        require!(
104            receiver_user.is_initialized(),
105            CoreError::InvalidUserAccount
106        );
107        require_keys_eq!(
108            receiver_user.referral.code,
109            DEFAULT_PUBKEY,
110            CoreError::PreconditionsAreNotMet
111        );
112        require_keys_eq!(
113            receiver_user.owner,
114            code.next_owner,
115            CoreError::PreconditionsAreNotMet
116        );
117
118        // Transfer the ownership.
119        receiver_user.referral.code = self.referral.code;
120        code.owner = receiver_user.owner;
121        self.referral.code = DEFAULT_PUBKEY;
122        Ok(())
123    }
124
125    /// Transfer the ownership of the given code from this user to the receiver.
126    /// # CHECK
127    /// - `code` must be owned by current user.
128    /// - the store of `code` must be the same as current user and `receiver`.
129    /// # Errors
130    /// - `code` must be initialized.
131    /// - current user must be initialized.
132    /// - `receiver_user` must be initialized.
133    /// - the code of `receiver_user` must not have been set.
134    pub(crate) fn unchecked_transfer_code(
135        &self,
136        code: &mut ReferralCodeV2,
137        receiver_user: &Self,
138    ) -> Result<()> {
139        require!(
140            code.code != ReferralCodeBytes::default(),
141            CoreError::PreconditionsAreNotMet
142        );
143        require!(self.is_initialized(), CoreError::InvalidUserAccount);
144        require!(
145            receiver_user.is_initialized(),
146            CoreError::InvalidUserAccount
147        );
148        require_keys_eq!(
149            receiver_user.referral.code,
150            DEFAULT_PUBKEY,
151            CoreError::PreconditionsAreNotMet
152        );
153
154        code.set_next_owner(&receiver_user.owner)?;
155
156        Ok(())
157    }
158
159    /// Get GT state.
160    pub fn gt(&self) -> &UserGtState {
161        &self.gt
162    }
163}
164
165impl Seed for UserHeader {
166    const SEED: &'static [u8] = b"user";
167}
168
169/// Referral Code Bytes.
170pub type ReferralCodeBytes = [u8; 8];
171
172/// Referral.
173#[zero_copy]
174#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
175#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
176pub struct Referral {
177    /// The (owner) address of the referrer.
178    ///
179    /// [`DEFAULT_PUBKEY`] means no referrer.
180    pub(crate) referrer: Pubkey,
181    /// Referral Code Address.
182    pub(crate) code: Pubkey,
183    /// Number of referee.
184    referee_count: u128,
185    #[cfg_attr(feature = "debug", debug(skip))]
186    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
187    reserved: [u8; 64],
188}
189
190impl Referral {
191    pub(crate) fn set_code(&mut self, code: &Pubkey) -> Result<()> {
192        require_keys_eq!(self.code, DEFAULT_PUBKEY, CoreError::ReferralCodeHasBeenSet);
193
194        self.code = *code;
195
196        Ok(())
197    }
198
199    pub(crate) fn set_referrer(&mut self, referrer_user: &mut UserHeader) -> Result<()> {
200        require_keys_eq!(self.referrer, DEFAULT_PUBKEY, CoreError::ReferrerHasBeenSet,);
201
202        require!(
203            referrer_user.owner != DEFAULT_PUBKEY,
204            CoreError::InvalidArgument
205        );
206
207        self.referrer = referrer_user.owner;
208        referrer_user.referral.referee_count =
209            referrer_user.referral.referee_count.saturating_add(1);
210
211        Ok(())
212    }
213
214    /// Get the user account address of the referrer.
215    pub fn referrer(&self) -> Option<&Pubkey> {
216        optional_address(&self.referrer)
217    }
218
219    /// Get the referral code account address.
220    pub fn code(&self) -> Option<&Pubkey> {
221        optional_address(&self.code)
222    }
223}
224
225/// Referral Code.
226#[account(zero_copy)]
227#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229pub struct ReferralCodeV2 {
230    version: u8,
231    /// Bump.
232    pub(crate) bump: u8,
233    /// Code bytes.
234    pub code: ReferralCodeBytes,
235    /// Store.
236    pub store: Pubkey,
237    /// Owner.
238    pub owner: Pubkey,
239    /// Next owner.
240    next_owner: Pubkey,
241    #[cfg_attr(feature = "debug", debug(skip))]
242    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
243    reserved: [u8; 64],
244}
245
246impl ReferralCodeV2 {
247    /// The length of referral code.
248    pub const LEN: usize = std::mem::size_of::<ReferralCodeBytes>();
249
250    pub(crate) fn init(
251        &mut self,
252        bump: u8,
253        code: ReferralCodeBytes,
254        store: &Pubkey,
255        owner: &Pubkey,
256    ) {
257        self.bump = bump;
258        self.code = code;
259        self.store = *store;
260        self.owner = *owner;
261        self.next_owner = *owner;
262    }
263
264    /// Get next owner.
265    pub fn next_owner(&self) -> &Pubkey {
266        &self.next_owner
267    }
268
269    pub(crate) fn set_next_owner(&mut self, next_owner: &Pubkey) -> Result<()> {
270        require_keys_neq!(
271            self.next_owner,
272            *next_owner,
273            CoreError::PreconditionsAreNotMet
274        );
275        self.next_owner = *next_owner;
276        Ok(())
277    }
278
279    #[cfg(feature = "utils")]
280    /// Decode the given code string to code bytes.
281    pub fn decode(code: &str) -> Result<ReferralCodeBytes> {
282        require!(!code.is_empty(), CoreError::InvalidArgument);
283        let code = bs58::decode(code)
284            .into_vec()
285            .map_err(|_| error!(CoreError::InvalidArgument))?;
286        require_gte!(Self::LEN, code.len(), CoreError::InvalidArgument);
287        let padding = Self::LEN - code.len();
288        let mut code_bytes = ReferralCodeBytes::default();
289        code_bytes[padding..].copy_from_slice(&code);
290
291        Ok(code_bytes)
292    }
293
294    #[cfg(feature = "utils")]
295    /// Encode the given code to code string.
296    pub fn encode(code: &ReferralCodeBytes, skip_leading_ones: bool) -> String {
297        let code = bs58::encode(code).into_string();
298        if skip_leading_ones {
299            code.trim_start_matches('1').to_owned()
300        } else {
301            code
302        }
303    }
304}
305
306impl InitSpace for ReferralCodeV2 {
307    const INIT_SPACE: usize = std::mem::size_of::<Self>();
308}
309
310impl Seed for ReferralCodeV2 {
311    const SEED: &'static [u8] = b"referral_code";
312}
313
314/// GT State.
315#[zero_copy]
316#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
317#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
318pub struct UserGtState {
319    pub(crate) rank: u8,
320    padding_0: [u8; 7],
321    pub(crate) last_minted_at: i64,
322    pub(crate) total_minted: u64,
323    pub(crate) amount: u64,
324    #[cfg_attr(feature = "debug", debug(skip))]
325    padding_1: [u8; 32],
326    pub(crate) paid_fee_value: u128,
327    pub(crate) minted_fee_value: u128,
328    #[cfg_attr(feature = "debug", debug(skip))]
329    #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
330    reserved: [u8; 64],
331}
332
333impl UserGtState {
334    /// Get total paid fee value.
335    pub fn paid_fee_value(&self) -> u128 {
336        self.paid_fee_value
337    }
338
339    /// Get minted fee value.
340    pub fn minted_fee_value(&self) -> u128 {
341        self.minted_fee_value
342    }
343
344    /// Get current rank.
345    pub fn rank(&self) -> u8 {
346        self.rank
347    }
348
349    /// Get GT balance.
350    pub fn amount(&self) -> u64 {
351        self.amount
352    }
353}