triad_protocol/instructions/stake/
withdraw_stake.rs1use crate::constants::{ ADMIN, TTRIAD_MINT };
2use crate::constraints::{ is_authority_for_stake, is_mint_for_stake };
3use crate::{ errors::TriadProtocolError, StakeVault };
4use crate::{ StakeV2, User };
5use anchor_lang::prelude::*;
6use anchor_spl::token_2022::{ close_account, CloseAccount, Token2022 };
7use anchor_spl::{
8 associated_token::AssociatedToken,
9 token_interface::{ transfer_checked, Mint, TokenAccount, TransferChecked },
10};
11use std::str::FromStr;
12
13#[derive(Accounts)]
14pub struct WithdrawStake<'info> {
15 #[account(mut)]
16 pub signer: Signer<'info>,
17
18 #[account(mut)]
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(mut, close = signer, constraint = is_authority_for_stake(&stake, &signer)?)]
25 pub stake: Box<Account<'info, StakeV2>>,
26
27 #[account(mut, constraint = admin.key.to_string() == ADMIN)]
29 pub admin: AccountInfo<'info>,
30
31 #[account(mut, constraint = is_mint_for_stake(&stake, &mint.key())?)]
32 pub mint: Box<InterfaceAccount<'info, Mint>>,
33
34 #[account(mut)]
35 pub from_ata: Box<InterfaceAccount<'info, TokenAccount>>,
36
37 #[account(
38 init_if_needed,
39 payer = signer,
40 constraint = to_ata.owner == *signer.key && to_ata.mint == mint.key(),
41 associated_token::mint = mint,
42 associated_token::authority = signer
43 )]
44 pub to_ata: Box<InterfaceAccount<'info, TokenAccount>>,
45
46 pub token_program: Program<'info, Token2022>,
47 pub associated_token_program: Program<'info, AssociatedToken>,
48 pub system_program: Program<'info, System>,
49}
50
51pub fn withdraw_stake(ctx: Context<WithdrawStake>) -> Result<()> {
52 let stake = &mut ctx.accounts.stake;
53 let stake_vault = &mut ctx.accounts.stake_vault;
54 let user = &mut ctx.accounts.user;
55
56 if stake.withdraw_ts > Clock::get()?.unix_timestamp {
57 return Err(TriadProtocolError::StakeLocked.into());
58 }
59
60 let signer: &[&[&[u8]]] = &[
61 &[b"stake_vault", stake_vault.name.as_bytes(), &[stake_vault.bump]],
62 ];
63
64 let is_token_stake = stake.mint.eq(&Pubkey::from_str(TTRIAD_MINT).unwrap());
65
66 let mut amount = 1;
67
68 if is_token_stake {
69 amount = stake.amount;
70 }
71
72 transfer_checked(
73 CpiContext::new_with_signer(
74 ctx.accounts.token_program.to_account_info(),
75 TransferChecked {
76 from: ctx.accounts.from_ata.to_account_info().clone(),
77 mint: ctx.accounts.mint.to_account_info().clone(),
78 to: ctx.accounts.to_ata.to_account_info().clone(),
79 authority: stake_vault.to_account_info(),
80 },
81 signer
82 ),
83 amount,
84 ctx.accounts.mint.decimals
85 )?;
86
87 if is_token_stake {
88 stake_vault.token_staked -= stake.amount;
89 user.staked -= stake.amount;
90 } else {
91 close_account(
92 CpiContext::new_with_signer(
93 ctx.accounts.token_program.to_account_info(),
94 CloseAccount {
95 account: ctx.accounts.from_ata.to_account_info(),
96 destination: ctx.accounts.admin.to_account_info(),
97 authority: stake_vault.to_account_info(),
98 },
99 signer
100 )
101 )?;
102
103 stake_vault.nft_staked -= 1;
104 }
105
106 Ok(())
107}