1use anchor_lang::prelude::*;
2use gmsol_utils::InitSpace;
3
4use crate::{
5 states::{
6 user::{ReferralCodeBytes, ReferralCodeV2, UserHeader},
7 Seed, Store,
8 },
9 CoreError,
10};
11
12#[derive(Accounts)]
14pub struct PrepareUser<'info> {
15 #[account(mut)]
17 pub owner: Signer<'info>,
18 pub store: AccountLoader<'info, Store>,
20 #[account(
22 init_if_needed,
23 payer = owner,
24 space = 8 + UserHeader::space(0),
25 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
26 bump,
27 )]
28 pub user: AccountLoader<'info, UserHeader>,
29 pub system_program: Program<'info, System>,
30}
31
32pub(crate) fn prepare_user(ctx: Context<PrepareUser>) -> Result<()> {
33 let store = ctx.accounts.store.key();
34 let owner = ctx.accounts.owner.key;
35 {
36 match ctx.accounts.user.load_init() {
37 Ok(mut user) => {
38 user.init(&store, owner, ctx.bumps.user)?;
39 }
40 Err(Error::AnchorError(err)) => {
41 if err.error_code_number != ErrorCode::AccountDiscriminatorAlreadySet as u32 {
42 return Err(Error::AnchorError(err));
43 }
44 }
45 Err(err) => {
46 return Err(err);
47 }
48 }
49 }
50 ctx.accounts.user.exit(&crate::ID)?;
51 {
52 let user = ctx.accounts.user.load()?;
53 require!(user.is_initialized(), CoreError::InvalidUserAccount);
54 require_keys_eq!(user.store, store, CoreError::InvalidUserAccount);
55 require_keys_eq!(user.owner, *owner, CoreError::InvalidUserAccount);
56 require_eq!(user.bump, ctx.bumps.user, CoreError::InvalidUserAccount);
57 }
58 Ok(())
59}
60
61#[derive(Accounts)]
64#[instruction(code: [u8; 8])]
65pub struct InitializeReferralCode<'info> {
66 #[account(mut)]
68 pub owner: Signer<'info>,
69 pub store: AccountLoader<'info, Store>,
71 #[account(
73 init,
74 payer = owner,
75 space = 8 + ReferralCodeV2::INIT_SPACE,
76 seeds = [ReferralCodeV2::SEED, store.key().as_ref(), &code],
77 bump,
78 )]
79 pub referral_code: AccountLoader<'info, ReferralCodeV2>,
80 #[account(
82 mut,
83 constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
84 has_one = owner,
85 has_one = store,
86 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
87 bump = user.load()?.bump,
88 )]
89 pub user: AccountLoader<'info, UserHeader>,
90 pub system_program: Program<'info, System>,
91}
92
93pub(crate) fn initialize_referral_code(
94 ctx: Context<InitializeReferralCode>,
95 code: ReferralCodeBytes,
96) -> Result<()> {
97 require!(
98 code != ReferralCodeBytes::default(),
99 CoreError::InvalidArgument
100 );
101
102 ctx.accounts.referral_code.load_init()?.init(
104 ctx.bumps.referral_code,
105 code,
106 &ctx.accounts.store.key(),
107 ctx.accounts.owner.key,
108 );
109
110 ctx.accounts
112 .user
113 .load_mut()?
114 .referral
115 .set_code(&ctx.accounts.referral_code.key())?;
116 Ok(())
117}
118
119#[derive(Accounts)]
122#[instruction(code: [u8; 8])]
123pub struct SetReferrer<'info> {
124 pub owner: Signer<'info>,
125 pub store: AccountLoader<'info, Store>,
126 #[account(
128 mut,
129 has_one = owner,
130 has_one = store,
131 constraint = user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
132 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
133 bump = user.load()?.bump,
134 )]
135 pub user: AccountLoader<'info, UserHeader>,
136 #[account(
138 has_one = store,
139 constraint = referral_code.load()?.code == code @ CoreError::ReferralCodeMismatched,
140 seeds = [ReferralCodeV2::SEED, store.key().as_ref(), &code],
141 bump = referral_code.load()?.bump,
142 )]
143 pub referral_code: AccountLoader<'info, ReferralCodeV2>,
144 #[account(
146 mut,
147 has_one = store,
148 constraint = referrer_user.load()?.is_initialized() @ CoreError::InvalidUserAccount,
149 constraint = referrer_user.load()?.owner == referral_code.load()?.owner @ CoreError::OwnerMismatched,
150 constraint = referrer_user.load()?.referral.code == referral_code.key() @ CoreError::ReferralCodeMismatched,
151 constraint = referrer_user.key() != user.key() @ CoreError::SelfReferral,
152 seeds = [UserHeader::SEED, store.key().as_ref(), referrer_user.load()?.owner.as_ref()],
153 bump = referrer_user.load()?.bump,
154 )]
155 pub referrer_user: AccountLoader<'info, UserHeader>,
156}
157
158pub(crate) fn set_referrer(ctx: Context<SetReferrer>, _code: ReferralCodeBytes) -> Result<()> {
159 require!(
160 ctx.accounts.referrer_user.load()?.referral.referrer != ctx.accounts.user.load()?.owner,
161 CoreError::MutualReferral
162 );
163 ctx.accounts
164 .user
165 .load_mut()?
166 .referral
167 .set_referrer(&mut *ctx.accounts.referrer_user.load_mut()?)?;
168 Ok(())
169}
170
171#[derive(Accounts)]
173pub struct AcceptReferralCode<'info> {
174 pub next_owner: Signer<'info>,
175 pub store: AccountLoader<'info, Store>,
176 #[account(
178 mut,
179 has_one = store,
180 constraint = user.load()?.owner == referral_code.load()?.owner @ CoreError::OwnerMismatched,
181 constraint = user.load()?.referral.code == referral_code.key() @ CoreError::ReferralCodeMismatched,
182 seeds = [UserHeader::SEED, store.key().as_ref(), referral_code.load()?.owner.as_ref()],
183 bump = user.load()?.bump,
184 )]
185 pub user: AccountLoader<'info, UserHeader>,
186 #[account(
188 mut,
189 has_one = store,
190 seeds = [ReferralCodeV2::SEED, store.key().as_ref(), &referral_code.load()?.code],
191 bump = referral_code.load()?.bump,
192 )]
193 pub referral_code: AccountLoader<'info, ReferralCodeV2>,
194 #[account(
196 mut,
197 has_one = store,
198 constraint = receiver_user.load()?.owner == next_owner.key() @ CoreError::OwnerMismatched,
199 constraint = receiver_user.key() != user.key() @ CoreError::SelfReferral,
200 seeds = [UserHeader::SEED, store.key().as_ref(), next_owner.key().as_ref()],
201 bump = receiver_user.load()?.bump,
202 )]
203 pub receiver_user: AccountLoader<'info, UserHeader>,
204}
205
206pub(crate) fn accept_referral_code(ctx: Context<AcceptReferralCode>) -> Result<()> {
207 let mut code = ctx.accounts.referral_code.load_mut()?;
208 let mut receiver_user = ctx.accounts.receiver_user.load_mut()?;
209 ctx.accounts
210 .user
211 .load_mut()?
212 .unchecked_complete_code_transfer(&mut code, &mut receiver_user)?;
213
214 msg!(
215 "[Referral] the owner of referral code `{:?}` is now {}",
216 code.code,
217 code.owner,
218 );
219
220 Ok(())
221}
222
223#[derive(Accounts)]
225pub struct TransferReferralCode<'info> {
226 pub owner: Signer<'info>,
227 pub store: AccountLoader<'info, Store>,
228 #[account(
230 has_one = owner,
231 has_one = store,
232 constraint = user.load()?.owner == referral_code.load()?.owner @ CoreError::OwnerMismatched,
233 constraint = user.load()?.referral.code == referral_code.key() @ CoreError::ReferralCodeMismatched,
234 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
235 bump = user.load()?.bump,
236 )]
237 pub user: AccountLoader<'info, UserHeader>,
238 #[account(
240 mut,
241 has_one = store,
242 seeds = [ReferralCodeV2::SEED, store.key().as_ref(), &referral_code.load()?.code],
243 bump = referral_code.load()?.bump,
244 )]
245 pub referral_code: AccountLoader<'info, ReferralCodeV2>,
246 #[account(
248 has_one = store,
249 constraint = receiver_user.key() != user.key() @ CoreError::SelfReferral,
250 seeds = [UserHeader::SEED, store.key().as_ref(), receiver_user.load()?.owner.as_ref()],
251 bump = receiver_user.load()?.bump,
252 )]
253 pub receiver_user: AccountLoader<'info, UserHeader>,
254}
255
256pub(crate) fn transfer_referral_code(ctx: Context<TransferReferralCode>) -> Result<()> {
257 let mut code = ctx.accounts.referral_code.load_mut()?;
258 let receiver_user = ctx.accounts.receiver_user.load()?;
259 ctx.accounts
260 .user
261 .load()?
262 .unchecked_transfer_code(&mut code, &receiver_user)?;
263
264 msg!(
265 "[Referral] the next owner of referral code `{:?}` is now {}",
266 code.code,
267 code.next_owner(),
268 );
269
270 Ok(())
271}
272
273#[derive(Accounts)]
275pub struct CancelReferralCodeTransfer<'info> {
276 pub owner: Signer<'info>,
277 pub store: AccountLoader<'info, Store>,
278 #[account(
280 has_one = owner,
281 has_one = store,
282 constraint = user.load()?.owner == referral_code.load()?.owner @ CoreError::OwnerMismatched,
283 constraint = user.load()?.referral.code == referral_code.key() @ CoreError::ReferralCodeMismatched,
284 seeds = [UserHeader::SEED, store.key().as_ref(), owner.key().as_ref()],
285 bump = user.load()?.bump,
286 )]
287 pub user: AccountLoader<'info, UserHeader>,
288 #[account(
290 mut,
291 has_one = store,
292 seeds = [ReferralCodeV2::SEED, store.key().as_ref(), &referral_code.load()?.code],
293 bump = referral_code.load()?.bump,
294 )]
295 pub referral_code: AccountLoader<'info, ReferralCodeV2>,
296}
297
298pub(crate) fn cancel_referral_code_transfer(
299 ctx: Context<CancelReferralCodeTransfer>,
300) -> Result<()> {
301 let mut code = ctx.accounts.referral_code.load_mut()?;
302 code.set_next_owner(ctx.accounts.owner.key)?;
303 msg!(
304 "[Referral] the next owner of referral code `{:?}` is now {}",
305 code.code,
306 code.next_owner(),
307 );
308
309 Ok(())
310}