shadow_drive_user_staking/instructions/
claim_stake.rs1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, Token, TokenAccount};
3
4use crate::constants::*;
5use crate::errors::ErrorCodes;
6use crate::instructions::{
7 decrease_storage::UnstakeInfo, initialize_account::{StorageAccount, StorageAccountV2},
8 initialize_config::StorageConfig,
9};
10
11pub fn handler(
13 ctx: impl ClaimStake
14) -> Result<()> {
15
16 let clock = Clock::get().unwrap();
18 let current_time = clock.unix_timestamp;
19 let current_epoch = clock.epoch;
20 let (unstake_time, unstake_epoch) = ctx.get_unstake_time();
21
22 require!(
23 current_time.checked_sub(unstake_time).unwrap() >= UNSTAKE_TIME_PERIOD,
24 ErrorCodes::ClaimingStakeTooSoon
25 );
26 require!(
27 current_epoch.checked_sub(unstake_epoch).unwrap() >= UNSTAKE_EPOCH_PERIOD,
28 ErrorCodes::ClaimingStakeTooSoon
29 );
30
31 msg!("Transferring all outstanding unstake funds to user");
32 {
33 ctx.transfer_shades_to_user()?;
34 }
35
36 Ok(())
37}
38
39type Time = i64;
40type Epoch = u64;
41pub trait ClaimStake {
42 fn get_unstake_time(&self) -> (Time, Epoch);
43 fn transfer_shades_to_user(&self) -> Result<()>;
44}
45
46
47#[derive(Accounts)]
48pub struct ClaimStakeV2<'info> {
51 #[account(
53 mut,
54 seeds = [
55 "storage-config".as_bytes()
56 ],
57 bump,
58 )]
59 pub storage_config: Box<Account<'info, StorageConfig>>,
60
61 #[account(
63 mut,
64 seeds = [
65 "storage-account".as_bytes(),
66 &storage_account.owner_1.key().to_bytes(),
67 &storage_account.account_counter_seed.to_le_bytes()
68 ],
69 bump,
70 )]
71 pub storage_account: Box<Account<'info, StorageAccountV2>>,
72
73 #[account(
75 mut,
76 close = owner,
77 seeds = [
78 "unstake-info".as_bytes(),
79 &storage_account.key().to_bytes(),
80 ],
81 bump,
82 )]
83 pub unstake_info: Box<Account<'info, UnstakeInfo>>,
84
85 #[account(
87 mut,
88 seeds = [
89 "unstake-account".as_bytes(),
90 &storage_account.key().to_bytes(),
91 ],
92 bump,
93 )]
94 pub unstake_account: Box<Account<'info, TokenAccount>>,
95
96 #[account(mut, address=unstake_info.unstaker)]
99 pub owner: Signer<'info>,
100
101 #[account(
104 mut,
105 constraint = {
106 owner_ata.owner == owner.key()
107 && owner_ata.mint == token_mint.key()
108 }
109 )]
110 pub owner_ata: Account<'info, TokenAccount>,
111
112 #[account(address = shdw::ID)]
114 pub token_mint: Account<'info, Mint>,
115
116 pub system_program: Program<'info, System>,
118
119 pub token_program: Program<'info, Token>,
121}
122
123
124#[derive(Accounts)]
125pub struct ClaimStakeV1<'info> {
128 #[account(
130 mut,
131 seeds = [
132 "storage-config".as_bytes()
133 ],
134 bump,
135 )]
136 pub storage_config: Box<Account<'info, StorageConfig>>,
137
138 #[account(
140 mut,
141 seeds = [
142 "storage-account".as_bytes(),
143 &storage_account.owner_1.key().to_bytes(),
144 &storage_account.account_counter_seed.to_le_bytes()
145 ],
146 bump,
147 )]
148 pub storage_account: Box<Account<'info, StorageAccount>>,
149
150 #[account(
152 mut,
153 close = owner,
154 seeds = [
155 "unstake-info".as_bytes(),
156 &storage_account.key().to_bytes(),
157 ],
158 bump,
159 )]
160 pub unstake_info: Box<Account<'info, UnstakeInfo>>,
161
162 #[account(
164 mut,
165 seeds = [
166 "unstake-account".as_bytes(),
167 &storage_account.key().to_bytes(),
168 ],
169 bump,
170 )]
171 pub unstake_account: Box<Account<'info, TokenAccount>>,
172
173 #[account(mut, address=unstake_info.unstaker)]
176 pub owner: Signer<'info>,
177
178 #[account(
181 mut,
182 constraint = {
183 owner_ata.owner == owner.key()
184 && owner_ata.mint == token_mint.key()
185 }
186 )]
187 pub owner_ata: Box<Account<'info, TokenAccount>>,
188
189 #[account(address = shdw::ID)]
191 pub token_mint: Account<'info, Mint>,
192
193 pub system_program: Program<'info, System>,
195
196 pub token_program: Program<'info, Token>,
198}
199
200
201impl ClaimStake for Context<'_, '_, '_, '_, ClaimStakeV1<'_>> {
202 fn get_unstake_time(&self) -> (Time, Epoch) {
203 (self.accounts.unstake_info.time_last_unstaked, self.accounts.unstake_info.epoch_last_unstaked)
204 }
205 fn transfer_shades_to_user(&self) -> Result<()> {
206
207 let storage_config_seeds = [
209 "storage-config".as_bytes(),
210 &[*self.bumps.get("storage_config").unwrap()],
211 ];
212 let signer_seeds: &[&[&[u8]]] = &[&storage_config_seeds];
213
214 anchor_spl::token::transfer(
216 CpiContext::new_with_signer(
217 self.accounts.token_program.to_account_info(),
218 anchor_spl::token::Transfer {
219 from: self.accounts.unstake_account.to_account_info(),
220 to: self.accounts.owner_ata.to_account_info(),
221 authority: self.accounts.storage_config.to_account_info(),
222 },
223 signer_seeds,
224 ),
225 self.accounts.unstake_account.amount,
226 )?;
227
228 anchor_spl::token::close_account(CpiContext::new_with_signer(
230 self.accounts.token_program.to_account_info(),
231 anchor_spl::token::CloseAccount {
232 account: self.accounts.unstake_account.to_account_info(),
233 destination: self.accounts.owner_ata.to_account_info(),
234 authority: self.accounts.storage_config.to_account_info(),
235 },
236 signer_seeds,
237 ))
238 }
239}
240
241impl ClaimStake for Context<'_, '_, '_, '_, ClaimStakeV2<'_>> {
242 fn get_unstake_time(&self) -> (Time, Epoch) {
243 (self.accounts.unstake_info.time_last_unstaked, self.accounts.unstake_info.epoch_last_unstaked)
244 }
245 fn transfer_shades_to_user(&self) -> Result<()> {
246
247 let storage_config_seeds = [
249 "storage-config".as_bytes(),
250 &[*self.bumps.get("storage_config").unwrap()],
251 ];
252 let signer_seeds: &[&[&[u8]]] = &[&storage_config_seeds];
253
254 anchor_spl::token::transfer(
256 CpiContext::new_with_signer(
257 self.accounts.token_program.to_account_info(),
258 anchor_spl::token::Transfer {
259 from: self.accounts.unstake_account.to_account_info(),
260 to: self.accounts.owner_ata.to_account_info(),
261 authority: self.accounts.storage_config.to_account_info(),
262 },
263 signer_seeds,
264 ),
265 self.accounts.unstake_account.amount,
266 )?;
267
268 anchor_spl::token::close_account(CpiContext::new_with_signer(
270 self.accounts.token_program.to_account_info(),
271 anchor_spl::token::CloseAccount {
272 account: self.accounts.unstake_account.to_account_info(),
273 destination: self.accounts.owner_ata.to_account_info(),
274 authority: self.accounts.storage_config.to_account_info(),
275 },
276 signer_seeds,
277 ))
278 }
279
280}