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#[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 pub(crate) version: u8,
18 pub(crate) bump: u8,
20 flags: UserFlagContainer,
21 #[cfg_attr(feature = "debug", debug(skip))]
22 padding_0: [u8; 13],
23 pub(crate) owner: Pubkey,
25 pub(crate) store: Pubkey,
27 pub(crate) referral: Referral,
29 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#[derive(num_enum::IntoPrimitive)]
38#[repr(u8)]
39#[non_exhaustive]
40pub enum UserFlag {
41 Initialized,
43}
44
45impl UserFlag {
46 pub const MAX_FLAGS: usize = 8;
48}
49
50gmsol_utils::flags!(UserFlag, { UserFlag::MAX_FLAGS }, u8);
51
52impl UserHeader {
53 pub fn is_initialized(&self) -> bool {
55 self.flags.get_flag(UserFlag::Initialized)
56 }
57
58 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 pub fn space(_version: u8) -> usize {
75 std::mem::size_of::<Self>()
76 }
77
78 pub fn referral(&self) -> &Referral {
80 &self.referral
81 }
82
83 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 receiver_user.referral.code = self.referral.code;
120 code.owner = receiver_user.owner;
121 self.referral.code = DEFAULT_PUBKEY;
122 Ok(())
123 }
124
125 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 pub fn gt(&self) -> &UserGtState {
161 &self.gt
162 }
163}
164
165impl Seed for UserHeader {
166 const SEED: &'static [u8] = b"user";
167}
168
169pub type ReferralCodeBytes = [u8; 8];
171
172#[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 pub(crate) referrer: Pubkey,
181 pub(crate) code: Pubkey,
183 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 pub fn referrer(&self) -> Option<&Pubkey> {
216 optional_address(&self.referrer)
217 }
218
219 pub fn code(&self) -> Option<&Pubkey> {
221 optional_address(&self.code)
222 }
223}
224
225#[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 pub(crate) bump: u8,
233 pub code: ReferralCodeBytes,
235 pub store: Pubkey,
237 pub owner: Pubkey,
239 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 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 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 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 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#[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 pub fn paid_fee_value(&self) -> u128 {
336 self.paid_fee_value
337 }
338
339 pub fn minted_fee_value(&self) -> u128 {
341 self.minted_fee_value
342 }
343
344 pub fn rank(&self) -> u8 {
346 self.rank
347 }
348
349 pub fn amount(&self) -> u64 {
351 self.amount
352 }
353}