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#[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 pub(crate) version: u8,
21 pub(crate) bump: u8,
23 flags: UserFlagContainer,
24 #[cfg_attr(feature = "debug", debug(skip))]
25 padding_0: [u8; 13],
26 pub(crate) owner: Pubkey,
28 pub(crate) store: Pubkey,
30 pub(crate) referral: Referral,
32 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 pub fn is_initialized(&self) -> bool {
44 self.flags.get_flag(UserFlag::Initialized)
45 }
46
47 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 pub fn space(_version: u8) -> usize {
64 std::mem::size_of::<Self>()
65 }
66
67 pub fn referral(&self) -> &Referral {
69 &self.referral
70 }
71
72 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 receiver_user.referral.code = self.referral.code;
109 code.owner = receiver_user.owner;
110 self.referral.code = DEFAULT_PUBKEY;
111 Ok(())
112 }
113
114 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 pub fn gt(&self) -> &UserGtState {
150 &self.gt
151 }
152}
153
154impl Seed for UserHeader {
155 const SEED: &'static [u8] = b"user";
156}
157
158pub type ReferralCodeBytes = [u8; 8];
160
161#[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 pub(crate) referrer: Pubkey,
170 pub(crate) code: Pubkey,
172 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 pub fn referrer(&self) -> Option<&Pubkey> {
205 optional_address(&self.referrer)
206 }
207
208 pub fn code(&self) -> Option<&Pubkey> {
210 optional_address(&self.code)
211 }
212}
213
214#[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 pub(crate) bump: u8,
222 pub code: ReferralCodeBytes,
224 pub store: Pubkey,
226 pub owner: Pubkey,
228 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 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 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 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 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#[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 pub fn paid_fee_value(&self) -> u128 {
325 self.paid_fee_value
326 }
327
328 pub fn minted_fee_value(&self) -> u128 {
330 self.minted_fee_value
331 }
332
333 pub fn rank(&self) -> u8 {
335 self.rank
336 }
337
338 pub fn amount(&self) -> u64 {
340 self.amount
341 }
342}