gmsol_store/states/
user.rs

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