gmsol_store/instructions/migration/
referral_code.rs

1use anchor_lang::prelude::*;
2
3use crate::{
4    internal,
5    states::{user::ReferralCodeBytes, Store},
6};
7
8/// Referral Code.
9#[account(zero_copy)]
10#[cfg_attr(feature = "debug", derive(Debug))]
11pub struct ReferralCode {
12    /// Bump.
13    pub bump: u8,
14    /// Code bytes.
15    pub code: ReferralCodeBytes,
16    /// Store.
17    pub store: Pubkey,
18    /// Owner.
19    pub owner: Pubkey,
20}
21
22/// The accounts definitions for [`migrate_referral_code`](crate::gmsol_store::migrate_referral_code) instruction.
23#[derive(Accounts)]
24pub struct MigrateReferralCode<'info> {
25    /// Authority.
26    #[account(mut)]
27    pub authority: Signer<'info>,
28    /// Store.
29    pub store: AccountLoader<'info, Store>,
30    /// System program.
31    pub system: Program<'info, System>,
32}
33
34impl<'info> internal::Authentication<'info> for MigrateReferralCode<'info> {
35    fn authority(&self) -> &Signer<'info> {
36        &self.authority
37    }
38
39    fn store(&self) -> &AccountLoader<'info, Store> {
40        &self.store
41    }
42}
43
44#[cfg(feature = "migration")]
45pub(crate) use migration::unchecked_migrate_referral_code;
46
47#[cfg(feature = "migration")]
48mod migration {
49    use anchor_lang::system_program;
50    use gmsol_utils::InitSpace;
51
52    use crate::{
53        states::{user::ReferralCodeV2, Seed},
54        CoreError,
55    };
56
57    use super::*;
58
59    /// Migrate referral code.
60    /// # CHECK
61    /// Only MIGRATION_KEEPER is allowed to invoke.
62    pub(crate) fn unchecked_migrate_referral_code<'info>(
63        ctx: Context<'_, '_, 'info, 'info, MigrateReferralCode<'info>>,
64    ) -> Result<()> {
65        let code = &ctx.remaining_accounts[0];
66        let data = ctx.accounts.validate_and_clone(code)?;
67        ctx.accounts.initialize_code_v2_unchecked(&data, code)?;
68        Ok(())
69    }
70
71    impl<'info> MigrateReferralCode<'info> {
72        fn validate_and_clone(&self, code: &'info AccountInfo<'info>) -> Result<ReferralCode> {
73            let loader = AccountLoader::<ReferralCode>::try_from(code)?;
74            let pubkey = loader.key();
75            let store = self.store.key();
76            let code = loader.load()?;
77
78            // Store validation.
79            require_keys_eq!(code.store, store, CoreError::StoreMismatched);
80
81            // Seeds validation.
82            let expected_pubkey = Pubkey::create_program_address(
83                &[
84                    ReferralCodeV2::SEED,
85                    store.as_ref(),
86                    &code.code,
87                    &[code.bump],
88                ],
89                &crate::ID,
90            )
91            .map_err(|_| error!(CoreError::InvalidArgument))?;
92            require_keys_eq!(expected_pubkey, pubkey, ErrorCode::ConstraintSeeds);
93
94            Ok(*code)
95        }
96
97        fn uninitialize_code_and_realloc(&self, account: &AccountInfo<'info>) -> Result<()> {
98            let rent = Rent::get()?;
99
100            account.try_borrow_mut_data()?.fill(0);
101
102            let space = 8 + ReferralCodeV2::INIT_SPACE;
103            let required_lamports = rent
104                .minimum_balance(space)
105                .max(1)
106                .saturating_sub(account.lamports());
107
108            if required_lamports > 0 {
109                system_program::transfer(
110                    CpiContext::new(
111                        self.system.to_account_info(),
112                        system_program::Transfer {
113                            from: self.authority.to_account_info(),
114                            to: account.clone(),
115                        },
116                    ),
117                    required_lamports,
118                )?;
119            }
120
121            account.realloc(space, false)?;
122            Ok(())
123        }
124
125        /// # CHECK
126        /// - `code` must be a valid [`ReferralCode`] account with `data` as its data.
127        fn initialize_code_v2_unchecked(
128            &self,
129            data: &ReferralCode,
130            code: &'info AccountInfo<'info>,
131        ) -> Result<()> {
132            self.uninitialize_code_and_realloc(code)?;
133            let referral_code_v2 =
134                AccountLoader::<ReferralCodeV2>::try_from_unchecked(&crate::ID, code)?;
135            referral_code_v2
136                .load_init()?
137                .init(data.bump, data.code, &data.store, &data.owner);
138            referral_code_v2.exit(&crate::ID)?;
139            Ok(())
140        }
141    }
142}