gmsol_store/instructions/
token.rs1use 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#[derive(Accounts)]
18pub struct InitializeMarketVault<'info> {
19 #[account(mut)]
21 pub authority: Signer<'info>,
22 pub store: AccountLoader<'info, Store>,
24 pub mint: Account<'info, Mint>,
26 #[account(
28 init_if_needed,
29 payer = authority,
30 token::mint = mint,
31 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 pub system_program: Program<'info, System>,
43 pub token_program: Program<'info, Token>,
45}
46
47#[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#[derive(Accounts)]
71#[instruction(timestamp: i64)]
72pub struct UseClaimableAccount<'info> {
73 #[account(mut)]
75 pub authority: Signer<'info>,
76 pub store: AccountLoader<'info, Store>,
78 pub mint: Account<'info, Mint>,
80 pub owner: UncheckedAccount<'info>,
83 #[account(
85 init_if_needed,
86 payer = authority,
87 token::mint = mint,
88 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 pub system_program: Program<'info, System>,
102 pub token_program: Program<'info, Token>,
104}
105
106pub(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#[derive(Accounts)]
146#[instruction(timestamp: i64)]
147pub struct CloseEmptyClaimableAccount<'info> {
148 #[account(mut)]
150 pub authority: Signer<'info>,
151 pub store: AccountLoader<'info, Store>,
152 pub mint: Account<'info, Mint>,
153 pub owner: UncheckedAccount<'info>,
155 #[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
172pub(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#[derive(Accounts)]
213pub struct PrepareAssociatedTokenAccount<'info> {
214 #[account(mut)]
216 pub payer: Signer<'info>,
217 pub owner: UncheckedAccount<'info>,
219 pub mint: InterfaceAccount<'info, token_interface::Mint>,
221 #[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 pub system_program: Program<'info, System>,
231 pub token_program: Interface<'info, token_interface::TokenInterface>,
233 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}
242
243#[derive(Accounts)]
245pub struct CreateTokenMetadata<'info> {
246 #[account(mut)]
248 pub authority: Signer<'info>,
249 pub store: AccountLoader<'info, Store>,
251 #[account(mut)]
253 pub mint: InterfaceAccount<'info, token_interface::Mint>,
254 #[account(mut)]
257 pub metadata: UncheckedAccount<'info>,
258 pub system_program: Program<'info, System>,
260 pub sysvar_instructions: UncheckedAccount<'info>,
263 pub metadata_program: Program<'info, anchor_spl::metadata::Metadata>,
265}
266
267impl CreateTokenMetadata<'_> {
268 pub(crate) fn invoke_unchecked(
273 ctx: Context<Self>,
274 name: String,
275 symbol: String,
276 uri: String,
277 ) -> Result<()> {
278 ctx.accounts.create_token_metadata(name, symbol, uri)?;
279 Ok(())
280 }
281
282 fn create_token_metadata(&self, name: String, symbol: String, uri: String) -> Result<()> {
283 use mpl_token_metadata::{instructions::CreateV1CpiBuilder, types::TokenStandard};
284
285 let store = self.store.load()?;
286 let signer_seeds = store.signer_seeds();
287
288 CreateV1CpiBuilder::new(&self.metadata_program)
289 .metadata(&self.metadata)
290 .mint(self.mint.as_ref(), false)
291 .authority(self.store.as_ref())
292 .payer(&self.authority)
293 .update_authority(self.store.as_ref(), true)
294 .system_program(&self.system_program)
295 .sysvar_instructions(&self.sysvar_instructions)
296 .name(name)
297 .symbol(symbol)
298 .uri(uri)
299 .seller_fee_basis_points(0)
300 .token_standard(TokenStandard::Fungible)
301 .invoke_signed(&[&signer_seeds])?;
302
303 Ok(())
304 }
305}
306
307impl<'info> internal::Authentication<'info> for CreateTokenMetadata<'info> {
308 fn authority(&self) -> &Signer<'info> {
309 &self.authority
310 }
311
312 fn store(&self) -> &AccountLoader<'info, Store> {
313 &self.store
314 }
315}
316
317#[derive(Accounts)]
319pub struct UpdateTokenMetadata<'info> {
320 pub authority: Signer<'info>,
322 pub store: AccountLoader<'info, Store>,
324 #[account(mut)]
327 pub metadata: UncheckedAccount<'info>,
328 pub metadata_program: Program<'info, anchor_spl::metadata::Metadata>,
330}
331
332impl UpdateTokenMetadata<'_> {
333 pub(crate) fn invoke_unchecked(
338 ctx: Context<Self>,
339 name: String,
340 symbol: String,
341 uri: String,
342 ) -> Result<()> {
343 use anchor_spl::metadata::mpl_token_metadata::types::DataV2;
344
345 ctx.accounts.update_token_metadata(DataV2 {
346 name,
347 symbol,
348 uri,
349 seller_fee_basis_points: 0,
350 creators: None,
351 collection: None,
352 uses: None,
353 })?;
354 Ok(())
355 }
356
357 fn update_token_metadata(
358 &self,
359 data: anchor_spl::metadata::mpl_token_metadata::types::DataV2,
360 ) -> Result<()> {
361 use anchor_spl::metadata;
362
363 let store = self.store.load()?;
364 let signer_seeds = store.signer_seeds();
365
366 let ctx = CpiContext::new(
367 self.metadata_program.to_account_info(),
368 metadata::UpdateMetadataAccountsV2 {
369 metadata: self.metadata.to_account_info(),
370 update_authority: self.store.to_account_info(),
371 },
372 );
373
374 metadata::update_metadata_accounts_v2(
375 ctx.with_signer(&[&signer_seeds]),
376 None,
377 Some(data),
378 None,
379 None,
380 )?;
381 Ok(())
382 }
383}
384
385impl<'info> internal::Authentication<'info> for UpdateTokenMetadata<'info> {
386 fn authority(&self) -> &Signer<'info> {
387 &self.authority
388 }
389
390 fn store(&self) -> &AccountLoader<'info, Store> {
391 &self.store
392 }
393}