gmsol_treasury/instructions/
store.rs

1use anchor_lang::prelude::*;
2use anchor_spl::{
3    associated_token::AssociatedToken,
4    token_interface::{Mint, TokenAccount, TokenInterface},
5};
6use gmsol_store::{
7    cpi::{
8        accounts::{ClaimFeesFromMarket, ConfigurateGt, TransferReceiver as StoreTransferReceiver},
9        claim_fees_from_market, gt_set_referral_reward_factors, transfer_receiver,
10    },
11    program::GmsolStore,
12    utils::{CpiAuthentication, WithStore},
13    CoreError,
14};
15
16use crate::{
17    constants,
18    states::{config::ReceiverSigner, Config},
19};
20
21/// The accounts definition for [`transfer_receiver`](crate::gmsol_treasury::transfer_receiver).
22#[derive(Accounts)]
23pub struct TransferReceiver<'info> {
24    /// Authority.
25    #[account(mut)]
26    pub authority: Signer<'info>,
27    /// Store.
28    /// CHECK: check by CPI.
29    #[account(mut)]
30    pub store: UncheckedAccount<'info>,
31    /// Config.
32    #[account(has_one = store)]
33    pub config: AccountLoader<'info, Config>,
34    /// Receiver.
35    #[account(
36        seeds = [constants::RECEIVER_SEED, config.key().as_ref()],
37        bump = config.load()?.receiver_bump,
38    )]
39    pub receiver: SystemAccount<'info>,
40    /// The new receiver.
41    /// CHECK: only used as an identifier.
42    pub next_receiver: UncheckedAccount<'info>,
43    /// Store program.
44    pub store_program: Program<'info, GmsolStore>,
45    /// The system program.
46    pub system_program: Program<'info, System>,
47}
48
49/// Claim fees from a market.
50/// # CHECK
51/// Only [`TREASURY_OWNER`](crate::roles::TREASURY_OWNER) can use.
52pub(crate) fn unchecked_transfer_receiver(ctx: Context<TransferReceiver>) -> Result<()> {
53    let config = &ctx.accounts.config;
54    let signer = ReceiverSigner::new(config.key(), config.load()?.receiver_bump);
55    let cpi_ctx = ctx.accounts.set_receiver_ctx();
56    transfer_receiver(cpi_ctx.with_signer(&[&signer.as_seeds()]))?;
57    Ok(())
58}
59
60impl<'info> WithStore<'info> for TransferReceiver<'info> {
61    fn store_program(&self) -> AccountInfo<'info> {
62        self.store_program.to_account_info()
63    }
64
65    fn store(&self) -> AccountInfo<'info> {
66        self.store.to_account_info()
67    }
68}
69
70impl<'info> CpiAuthentication<'info> for TransferReceiver<'info> {
71    fn authority(&self) -> AccountInfo<'info> {
72        self.authority.to_account_info()
73    }
74
75    fn on_error(&self) -> Result<()> {
76        err!(CoreError::PermissionDenied)
77    }
78}
79
80impl<'info> TransferReceiver<'info> {
81    fn set_receiver_ctx(&self) -> CpiContext<'_, '_, '_, 'info, StoreTransferReceiver<'info>> {
82        CpiContext::new(
83            self.store_program.to_account_info(),
84            StoreTransferReceiver {
85                authority: self.receiver.to_account_info(),
86                store: self.store.to_account_info(),
87                next_receiver: self.next_receiver.to_account_info(),
88            },
89        )
90    }
91}
92
93/// The accounts definition for [`claim_fees`](crate::gmsol_treasury::claim_fees).
94#[derive(Accounts)]
95pub struct ClaimFees<'info> {
96    /// Authority.
97    #[account(mut)]
98    pub authority: Signer<'info>,
99    /// Store.
100    /// CHECK: check by CPI.
101    pub store: UncheckedAccount<'info>,
102    /// Config to initialize with.
103    #[account(has_one = store)]
104    pub config: AccountLoader<'info, Config>,
105    /// Receiver.
106    #[account(
107        seeds = [constants::RECEIVER_SEED, config.key().as_ref()],
108        bump = config.load()?.receiver_bump,
109    )]
110    pub receiver: SystemAccount<'info>,
111    /// Market to claim fees from.
112    /// CHECK: check by CPI.
113    #[account(mut)]
114    pub market: UncheckedAccount<'info>,
115    /// Token.
116    pub token: InterfaceAccount<'info, Mint>,
117    /// Vault.
118    /// CHECK: check by CPI.
119    #[account(mut)]
120    pub vault: UncheckedAccount<'info>,
121    /// Reciever vault.
122    #[account(
123        init_if_needed,
124        payer = authority,
125        associated_token::authority = receiver,
126        associated_token::mint = token,
127    )]
128    pub receiver_vault: InterfaceAccount<'info, TokenAccount>,
129    /// Event authority.
130    /// CHECK: check by CPI.
131    pub event_authority: UncheckedAccount<'info>,
132    /// Store program.
133    pub store_program: Program<'info, GmsolStore>,
134    /// The token program.
135    pub token_program: Interface<'info, TokenInterface>,
136    /// Associated token program.
137    pub associated_token_program: Program<'info, AssociatedToken>,
138    /// The system program.
139    pub system_program: Program<'info, System>,
140}
141
142/// Claim fees from a market.
143/// # CHECK
144/// Only [`TREASURY_KEEPER`](crate::roles::TREASURY_KEEPER) can use.
145pub(crate) fn unchecked_claim_fees(ctx: Context<ClaimFees>, min_amount: u64) -> Result<()> {
146    let config = &ctx.accounts.config;
147    let signer = ReceiverSigner::new(config.key(), config.load()?.receiver_bump);
148    let cpi_ctx = ctx.accounts.claim_fees_from_market_ctx();
149    let amount = claim_fees_from_market(cpi_ctx.with_signer(&[&signer.as_seeds()]))?;
150
151    require_gte!(amount.get(), min_amount, CoreError::NotEnoughTokenAmount);
152
153    msg!("[Treasury] claimed {} tokens from the market", amount.get());
154    Ok(())
155}
156
157impl<'info> WithStore<'info> for ClaimFees<'info> {
158    fn store_program(&self) -> AccountInfo<'info> {
159        self.store_program.to_account_info()
160    }
161
162    fn store(&self) -> AccountInfo<'info> {
163        self.store.to_account_info()
164    }
165}
166
167impl<'info> CpiAuthentication<'info> for ClaimFees<'info> {
168    fn authority(&self) -> AccountInfo<'info> {
169        self.authority.to_account_info()
170    }
171
172    fn on_error(&self) -> Result<()> {
173        err!(CoreError::PermissionDenied)
174    }
175}
176
177impl<'info> ClaimFees<'info> {
178    fn claim_fees_from_market_ctx(
179        &self,
180    ) -> CpiContext<'_, '_, '_, 'info, ClaimFeesFromMarket<'info>> {
181        CpiContext::new(
182            self.store_program.to_account_info(),
183            ClaimFeesFromMarket {
184                authority: self.receiver.to_account_info(),
185                store: self.store.to_account_info(),
186                market: self.market.to_account_info(),
187                token_mint: self.token.to_account_info(),
188                vault: self.vault.to_account_info(),
189                target: self.receiver_vault.to_account_info(),
190                token_program: self.token_program.to_account_info(),
191                event_authority: self.event_authority.to_account_info(),
192                program: self.store_program.to_account_info(),
193            },
194        )
195    }
196}
197
198/// The accounts definition for [`set_referral_reward`](crate::gmsol_treasury::set_referral_reward).
199#[derive(Accounts)]
200pub struct SetReferralReward<'info> {
201    /// Authority.
202    #[account(mut)]
203    pub authority: Signer<'info>,
204    /// Store.
205    /// CHECK: check by CPI.
206    #[account(mut)]
207    pub store: UncheckedAccount<'info>,
208    /// Config.
209    #[account(has_one = store)]
210    pub config: AccountLoader<'info, Config>,
211    /// Store program.
212    pub store_program: Program<'info, GmsolStore>,
213}
214
215/// Set referral reward.
216/// # CHECK
217/// Only [`TREASURY_ADMIN`](crate::roles::TREASURY_ADMIN) can use.
218pub(crate) fn unchecked_set_referral_reward(
219    ctx: Context<SetReferralReward>,
220    factors: Vec<u128>,
221) -> Result<()> {
222    let signer = ctx.accounts.config.load()?.signer();
223    let cpi_ctx = ctx.accounts.configurate_gt_ctx();
224    gt_set_referral_reward_factors(cpi_ctx.with_signer(&[&signer.as_seeds()]), factors)?;
225    Ok(())
226}
227
228impl<'info> WithStore<'info> for SetReferralReward<'info> {
229    fn store_program(&self) -> AccountInfo<'info> {
230        self.store_program.to_account_info()
231    }
232
233    fn store(&self) -> AccountInfo<'info> {
234        self.store.to_account_info()
235    }
236}
237
238impl<'info> CpiAuthentication<'info> for SetReferralReward<'info> {
239    fn authority(&self) -> AccountInfo<'info> {
240        self.authority.to_account_info()
241    }
242
243    fn on_error(&self) -> Result<()> {
244        err!(CoreError::PermissionDenied)
245    }
246}
247
248impl<'info> SetReferralReward<'info> {
249    fn configurate_gt_ctx(&self) -> CpiContext<'_, '_, '_, 'info, ConfigurateGt<'info>> {
250        CpiContext::new(
251            self.store_program.to_account_info(),
252            ConfigurateGt {
253                authority: self.config.to_account_info(),
254                store: self.store.to_account_info(),
255            },
256        )
257    }
258}