gmsol_store/instructions/
token.rs

1use anchor_lang::prelude::*;
2use anchor_spl::{
3    associated_token::AssociatedToken,
4    token::{Mint, Token, TokenAccount},
5    token_interface,
6};
7
8use crate::{
9    constants,
10    states::Store,
11    utils::{internal, token::must_be_uninitialized},
12};
13
14/// The accounts definition for [`initialize_market_vault`](crate::gmsol_store::initialize_market_vault).
15///
16/// *[See also the documentation for the instruction.](crate::gmsol_store::initialize_market_vault)*
17#[derive(Accounts)]
18pub struct InitializeMarketVault<'info> {
19    /// The caller.
20    #[account(mut)]
21    pub authority: Signer<'info>,
22    /// Store.
23    pub store: AccountLoader<'info, Store>,
24    /// Token mint.
25    pub mint: Account<'info, Mint>,
26    /// The vault to create.
27    #[account(
28        init_if_needed,
29        payer = authority,
30        token::mint = mint,
31        // We use the store as the authority of the token account.
32        token::authority = store,
33        seeds = [
34            constants::MARKET_VAULT_SEED,
35            store.key().as_ref(),
36            mint.key().as_ref(),
37        ],
38        bump,
39    )]
40    pub vault: Account<'info, TokenAccount>,
41    /// System Program.
42    pub system_program: Program<'info, System>,
43    /// Token Program.
44    pub token_program: Program<'info, Token>,
45}
46
47/// Initialize a vault of the given token for a market.
48/// The address is derived from token mint addresses (the `market_token_mint` seed is optional).
49///
50/// ## CHECK
51/// - Only MARKET_KEEPER can initialize market vault.
52#[allow(unused_variables)]
53pub(crate) fn unchecked_initialize_market_vault(ctx: Context<InitializeMarketVault>) -> Result<()> {
54    Ok(())
55}
56
57impl<'info> internal::Authentication<'info> for InitializeMarketVault<'info> {
58    fn authority(&self) -> &Signer<'info> {
59        &self.authority
60    }
61
62    fn store(&self) -> &AccountLoader<'info, Store> {
63        &self.store
64    }
65}
66
67/// The accounts definition for [`use_claimable_account`](crate::gmsol_store::use_claimable_account).
68///
69/// *[See also the documentation for the instruction.](crate::gmsol_store::use_claimable_account)*
70#[derive(Accounts)]
71#[instruction(timestamp: i64)]
72pub struct UseClaimableAccount<'info> {
73    /// The caller.
74    #[account(mut)]
75    pub authority: Signer<'info>,
76    /// Store.
77    pub store: AccountLoader<'info, Store>,
78    /// Mint.
79    pub mint: Account<'info, Mint>,
80    /// Owner.
81    /// CHECK: check by CPI.
82    pub owner: UncheckedAccount<'info>,
83    /// The claimble account.
84    #[account(
85        init_if_needed,
86        payer = authority,
87        token::mint = mint,
88        // We use the store as the authority of the token account.
89        token::authority = store,
90        seeds = [
91            constants::CLAIMABLE_ACCOUNT_SEED,
92            store.key().as_ref(),
93            mint.key().as_ref(),
94            owner.key().as_ref(),
95            &store.load()?.claimable_time_key(timestamp)?,
96        ],
97        bump,
98    )]
99    pub account: Account<'info, TokenAccount>,
100    /// System Program.
101    pub system_program: Program<'info, System>,
102    /// Token Program.
103    pub token_program: Program<'info, Token>,
104}
105
106/// Prepare claimable account.
107///
108/// ## CHECK
109/// - Only ORDER_KEEPER can use claimable account.
110pub(crate) fn unchecked_use_claimable_account(
111    ctx: Context<UseClaimableAccount>,
112    _timestamp: i64,
113    amount: u64,
114) -> Result<()> {
115    if ctx.accounts.account.delegate.is_none() || ctx.accounts.account.delegated_amount != amount {
116        anchor_spl::token::approve(
117            CpiContext::new_with_signer(
118                ctx.accounts.token_program.to_account_info(),
119                anchor_spl::token::Approve {
120                    to: ctx.accounts.account.to_account_info(),
121                    delegate: ctx.accounts.owner.to_account_info(),
122                    authority: ctx.accounts.store.to_account_info(),
123                },
124                &[&ctx.accounts.store.load()?.signer_seeds()],
125            ),
126            amount,
127        )?;
128    }
129    Ok(())
130}
131
132impl<'info> internal::Authentication<'info> for UseClaimableAccount<'info> {
133    fn authority(&self) -> &Signer<'info> {
134        &self.authority
135    }
136
137    fn store(&self) -> &AccountLoader<'info, Store> {
138        &self.store
139    }
140}
141
142/// The accounts definition for [`close_empty_claimable_account`](crate::gmsol_store::close_empty_claimable_account).
143///
144/// *[See also the documentation for the instruction.](crate::gmsol_store::close_empty_claimable_account)*
145#[derive(Accounts)]
146#[instruction(timestamp: i64)]
147pub struct CloseEmptyClaimableAccount<'info> {
148    /// The caller.
149    #[account(mut)]
150    pub authority: Signer<'info>,
151    pub store: AccountLoader<'info, Store>,
152    pub mint: Account<'info, Mint>,
153    /// CHECK: only use to reference the owner.
154    pub owner: UncheckedAccount<'info>,
155    /// CHECK: will be checked during the execution.
156    #[account(
157        mut,
158        seeds = [
159            constants::CLAIMABLE_ACCOUNT_SEED,
160            store.key().as_ref(),
161            mint.key().as_ref(),
162            owner.key().as_ref(),
163            &store.load()?.claimable_time_key(timestamp)?,
164        ],
165        bump,
166    )]
167    pub account: UncheckedAccount<'info>,
168    pub system_program: Program<'info, System>,
169    pub token_program: Program<'info, Token>,
170}
171
172/// Close claimable account if it is empty.
173///
174/// ## CHECK
175/// - Only ORDER_KEEPER can close claimable account.
176pub(crate) fn unchecked_close_empty_claimable_account(
177    ctx: Context<CloseEmptyClaimableAccount>,
178    _timestamp: i64,
179) -> Result<()> {
180    if must_be_uninitialized(&ctx.accounts.account) {
181        return Ok(());
182    }
183    let account = ctx.accounts.account.to_account_info();
184    let amount = anchor_spl::token::accessor::amount(&account)?;
185    if amount == 0 {
186        anchor_spl::token::close_account(CpiContext::new_with_signer(
187            ctx.accounts.token_program.to_account_info(),
188            anchor_spl::token::CloseAccount {
189                account: ctx.accounts.account.to_account_info(),
190                destination: ctx.accounts.authority.to_account_info(),
191                authority: ctx.accounts.store.to_account_info(),
192            },
193            &[&ctx.accounts.store.load()?.signer_seeds()],
194        ))?;
195    }
196    Ok(())
197}
198
199impl<'info> internal::Authentication<'info> for CloseEmptyClaimableAccount<'info> {
200    fn authority(&self) -> &Signer<'info> {
201        &self.authority
202    }
203
204    fn store(&self) -> &AccountLoader<'info, Store> {
205        &self.store
206    }
207}
208
209/// The accounts definition for [`prepare_associated_token_account`](crate::gmsol_store::prepare_associated_token_account).
210///
211/// *[See also the documentation for the instruction.](crate::gmsol_store::prepare_associated_token_account)*
212#[derive(Accounts)]
213pub struct PrepareAssociatedTokenAccount<'info> {
214    /// The payer.
215    #[account(mut)]
216    pub payer: Signer<'info>,
217    /// CHECK: only use as the owner of the token account.
218    pub owner: UncheckedAccount<'info>,
219    /// The mint account for the token account.
220    pub mint: InterfaceAccount<'info, token_interface::Mint>,
221    /// The token account to prepare.
222    #[account(
223        init_if_needed,
224        payer = payer,
225        associated_token::mint = mint,
226        associated_token::authority = owner,
227    )]
228    pub account: InterfaceAccount<'info, token_interface::TokenAccount>,
229    /// The [`System`] program.
230    pub system_program: Program<'info, System>,
231    /// The [`Token`] program.
232    pub token_program: Interface<'info, token_interface::TokenInterface>,
233    /// The [`AssociatedToken`] program.
234    pub associated_token_program: Program<'info, AssociatedToken>,
235}
236
237pub(crate) fn prepare_associated_token_account(
238    _ctx: Context<PrepareAssociatedTokenAccount>,
239) -> Result<()> {
240    Ok(())
241}