gmsol_store/instructions/
user.rs

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/// The accounts definitions for [`prepare_user`](crate::gmsol_store::prepare_user) instruction.
13#[derive(Accounts)]
14pub struct PrepareUser<'info> {
15    /// Owner.
16    #[account(mut)]
17    pub owner: Signer<'info>,
18    /// Store.
19    pub store: AccountLoader<'info, Store>,
20    /// User Account.
21    #[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/// The accounts definition for [`initialize_referral_code`](crate::gmsol_store::initialize_referral_code)
62/// instruction.
63#[derive(Accounts)]
64#[instruction(code: [u8; 8])]
65pub struct InitializeReferralCode<'info> {
66    /// Owner.
67    #[account(mut)]
68    pub owner: Signer<'info>,
69    /// Store.
70    pub store: AccountLoader<'info, Store>,
71    /// Referral Code Account.
72    #[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    /// User Account.
81    #[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    // Initialize Referral Code Account.
103    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    // Set referral code address.
111    ctx.accounts
112        .user
113        .load_mut()?
114        .referral
115        .set_code(&ctx.accounts.referral_code.key())?;
116    Ok(())
117}
118
119/// The accounts definitions for [`set_referrer`](crate::gmsol_store::set_referrer)
120/// instruction.
121#[derive(Accounts)]
122#[instruction(code: [u8; 8])]
123pub struct SetReferrer<'info> {
124    pub owner: Signer<'info>,
125    pub store: AccountLoader<'info, Store>,
126    /// User Account.
127    #[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    /// Referral Code Account.
137    #[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    /// Referrer.
145    #[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/// The accounts definitions for [`accept_referral_code`](crate::gmsol_store::accept_referral_code) instruction.
172#[derive(Accounts)]
173pub struct AcceptReferralCode<'info> {
174    pub next_owner: Signer<'info>,
175    pub store: AccountLoader<'info, Store>,
176    /// User Account.
177    #[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    /// Referral Code Account.
187    #[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    /// Receiver.
195    #[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/// The accounts definitions for [`transfer_referral_code`](crate::gmsol_store::transfer_referral_code) instruction.
224#[derive(Accounts)]
225pub struct TransferReferralCode<'info> {
226    pub owner: Signer<'info>,
227    pub store: AccountLoader<'info, Store>,
228    /// User Account.
229    #[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    /// Referral Code Account.
239    #[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    /// Receiver.
247    #[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/// The accounts definitions for [`cancel_referral_code_transfer`](crate::gmsol_store::cancel_referral_code_transfer) instruction.
274#[derive(Accounts)]
275pub struct CancelReferralCodeTransfer<'info> {
276    pub owner: Signer<'info>,
277    pub store: AccountLoader<'info, Store>,
278    /// User Account.
279    #[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    /// Referral Code Account.
289    #[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}