Skip to content

Commit 972e896

Browse files
committed
feat: add recover m admin instruction
1 parent 2ab334b commit 972e896

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

programs/earn/src/instructions/admin/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use anchor_lang::prelude::*;
22

33
pub mod initialize;
44
pub use initialize::*;
5+
pub mod recover;
6+
pub use recover::*;
57

68
use crate::{
79
errors::EarnError,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use anchor_lang::prelude::*;
2+
use anchor_spl::{
3+
token_2022::spl_token_2022::state::AccountState,
4+
token_interface::{Mint, Token2022, TokenAccount},
5+
};
6+
use crate::{
7+
errors::EarnError,
8+
state::{EarnGlobal, GLOBAL_SEED},
9+
utils::token::{freeze_token_account, thaw_token_account, transfer_tokens_from_program},
10+
};
11+
12+
// This instruction allows the admin to recover M from a frozen token account and transfer it to a new token account.
13+
14+
#[derive(Accounts)]
15+
pub struct RecoverM<'info> {
16+
#[account(mut)]
17+
pub admin: Signer<'info>,
18+
19+
#[account(
20+
seeds = [GLOBAL_SEED],
21+
bump = global_account.bump,
22+
has_one = m_mint @ EarnError::InvalidAccount,
23+
has_one = admin @ EarnError::NotAuthorized,
24+
)]
25+
pub global_account: Account<'info, EarnGlobal>,
26+
27+
pub m_mint: InterfaceAccount<'info, Mint>,
28+
29+
#[account(
30+
mut,
31+
token::mint = global_account.m_mint,
32+
constraint = source_token_account.state == AccountState::Frozen @ EarnError::InvalidAccount,
33+
)]
34+
pub source_token_account: InterfaceAccount<'info, TokenAccount>,
35+
36+
#[account(
37+
mut,
38+
token::mint = global_account.m_mint,
39+
)]
40+
pub destination_token_account: InterfaceAccount<'info, TokenAccount>,
41+
42+
pub token_program: Program<'info, Token2022>,
43+
}
44+
45+
impl RecoverM<'_> {
46+
pub fn handler(ctx: Context<Self>, amount: Option<u64>) -> Result<()> {
47+
// Transfer the amount specified or, if not specified, the entire balance
48+
// Amounts greater than the balance will revert during the transfer
49+
let recover_amount = amount.unwrap_or(ctx.accounts.source_token_account.amount);
50+
51+
let authority_seeds: &[&[&[u8]]] = &[&[GLOBAL_SEED, &[ctx.accounts.global_account.bump]]];
52+
53+
// Thaw the source token account to allow the transfer
54+
thaw_token_account(
55+
&ctx.accounts.source_token_account,
56+
&ctx.accounts.m_mint,
57+
&ctx.accounts.global_account.to_account_info(),
58+
authority_seeds,
59+
&ctx.accounts.token_program,
60+
)?;
61+
62+
// Check the state of the destination token account
63+
// If it is frozen, thaw it as well
64+
if ctx.accounts.destination_token_account.state == AccountState::Frozen {
65+
thaw_token_account(
66+
&ctx.accounts.destination_token_account,
67+
&ctx.accounts.m_mint,
68+
&ctx.accounts.global_account.to_account_info(),
69+
authority_seeds,
70+
&ctx.accounts.token_program,
71+
)?;
72+
}
73+
74+
// Transfer the tokens from the source account to the destination account
75+
// This only works because the global_account is a permanent delegate
76+
// on the M mint, allowing it to transfer tokens from any account
77+
transfer_tokens_from_program(
78+
&ctx.accounts.source_token_account,
79+
&ctx.accounts.destination_token_account,
80+
recover_amount,
81+
&ctx.accounts.m_mint,
82+
&ctx.accounts.global_account.to_account_info(),
83+
authority_seeds,
84+
&ctx.accounts.token_program,
85+
)?;
86+
87+
// Re-freeze the source token account
88+
freeze_token_account(
89+
&ctx.accounts.source_token_account,
90+
&ctx.accounts.m_mint,
91+
&ctx.accounts.global_account.to_account_info(),
92+
authority_seeds,
93+
&ctx.accounts.token_program,
94+
)?;
95+
96+
Ok(())
97+
}
98+
}
99+

0 commit comments

Comments
 (0)