triad_protocol/instructions/stake/
stake_token.rs

1use 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}