triad_protocol/instructions/stake/
stake_token.rs1use crate::{
2 constraints::is_mint_for_stake_vault,
3 errors::TriadProtocolError,
4 state::{ StakeTokenArgs, StakeVault },
5 StakeV2,
6 User,
7};
8use anchor_lang::prelude::*;
9use anchor_spl::token_2022::{ Token2022, transfer_checked, TransferChecked };
10use anchor_spl::{ associated_token::AssociatedToken, token_interface::{ Mint, TokenAccount } };
11
12#[derive(Accounts)]
13#[instruction(args: StakeTokenArgs)]
14pub struct StakeToken<'info> {
15 #[account(mut)]
16 pub signer: Signer<'info>,
17
18 #[account(mut, seeds = [StakeVault::PREFIX_SEED, args.stake_vault.as_bytes()], bump)]
19 pub stake_vault: Box<Account<'info, StakeVault>>,
20
21 #[account(mut, constraint = user.authority == *signer.key)]
22 pub user: Box<Account<'info, User>>,
23
24 #[account(
25 init,
26 payer = signer,
27 space = StakeV2::SPACE,
28 seeds = [
29 StakeV2::PREFIX_SEED,
30 signer.to_account_info().key().as_ref(),
31 args.name.as_bytes(),
32 ],
33 bump
34 )]
35 pub stake: Box<Account<'info, StakeV2>>,
36
37 #[account(mut, constraint = is_mint_for_stake_vault(&stake_vault, &mint.key())?)]
38 pub mint: Box<InterfaceAccount<'info, Mint>>,
39
40 #[account(
41 mut,
42 constraint = from_ata.amount >= args.amount && signer.key() == from_ata.owner && from_ata.mint == mint.key(),
43 )]
44 pub from_ata: Box<InterfaceAccount<'info, TokenAccount>>,
45
46 #[account(
47 init_if_needed,
48 payer = signer,
49 associated_token::mint = mint,
50 associated_token::authority = stake_vault
51 )]
52 pub to_ata: Box<InterfaceAccount<'info, TokenAccount>>,
53
54 pub token_program: Program<'info, Token2022>,
55 pub associated_token_program: Program<'info, AssociatedToken>,
56 pub system_program: Program<'info, System>,
57}
58
59pub fn stake_token(ctx: Context<StakeToken>, args: StakeTokenArgs) -> Result<()> {
60 let mint = &ctx.accounts.mint.to_account_info();
61 let stake = &mut ctx.accounts.stake;
62 let stake_vault = &mut ctx.accounts.stake_vault;
63 let user = &mut ctx.accounts.user;
64
65 if stake_vault.is_locked {
66 return Err(TriadProtocolError::StakeVaultLocked.into());
67 }
68
69 stake.bump = ctx.bumps.stake;
70 stake.authority = *ctx.accounts.signer.key;
71 stake.init_ts = Clock::get()?.unix_timestamp;
72 stake.withdraw_ts = 0;
73 stake.claimed_ts = 0;
74 stake.name = args.name;
75 stake.mint = *mint.key;
76 stake.boost = false;
77 stake.stake_vault = stake_vault.key();
78 stake.claimed = 0;
79 stake.available = 0;
80 stake.amount = args.amount;
81
82 stake_vault.token_staked += args.amount;
83
84 user.staked += args.amount;
85
86 let result = user.staked / (10u64).pow(stake_vault.token_decimals as u32) / 10000;
87
88 if result > (i16::MAX as u64) {
89 return Err(TriadProtocolError::StakeOverflow.into());
90 } else {
91 user.swaps = result as i16;
92 }
93
94 transfer_checked(
95 CpiContext::new(ctx.accounts.token_program.to_account_info(), TransferChecked {
96 from: ctx.accounts.from_ata.to_account_info(),
97 mint: ctx.accounts.mint.to_account_info(),
98 to: ctx.accounts.to_ata.to_account_info(),
99 authority: ctx.accounts.signer.to_account_info(),
100 }),
101 args.amount,
102 ctx.accounts.mint.decimals
103 )?;
104
105 Ok(())
106}