shadow_drive_user_staking/instructions/
delete_account.rs1use crate::constants::*;
2use crate::errors::ErrorCodes;
3use crate::instructions::{
4 crank::crank,
5 initialize_account::{ShadowDriveStorageAccount, StorageAccount, StorageAccountV2, UserInfo},
6 initialize_config::StorageConfig,
7};
8use anchor_lang::prelude::*;
9use anchor_spl::token::{Mint, Token, TokenAccount};
10use std::convert::TryFrom;
11
12pub fn handler(mut ctx: impl DeleteAccount, storage_used: u64) -> Result<()> {
14 require!(
16 !ctx.check_immutable(),
17 ErrorCodes::StorageAccountMarkedImmutable
18 );
19
20 require!(
29 ctx.check_delete_flag(),
30 ErrorCodes::AccountNotMarkedToBeDeleted
31 );
32
33 require!(
35 ctx.check_grace_period(),
36 ErrorCodes::AccountStillInGracePeriod
37 );
38
39 msg!("Deleting StorageAccount account: {}", ctx.get_identifier(),);
40 msg!("Returning stake to user");
43 ctx.return_stake(storage_used)?;
44
45 msg!("Updating global storage on StorageConfig account");
46 ctx.update_global_storage()?;
47
48 msg!("Update User Info");
49 ctx.increment_del_counter()?;
50
51 Ok(())
52}
53
54#[derive(Accounts)]
55pub struct DeleteAccountV1<'info> {
58 #[account(
60 mut,
61 seeds = [
62 "storage-config".as_bytes()
63 ],
64 bump,
65 )]
66 pub storage_config: Box<Account<'info, StorageConfig>>,
67
68 #[account(
70 mut,
71 seeds = [
72 "user-info".as_bytes(),
73 &storage_account.owner_1.key().to_bytes(),
74 ],
75 bump,
76 )]
77 pub user_info: Box<Account<'info, UserInfo>>,
78
79 #[account(
81 mut,
82 close = owner,
83 seeds = [
84 "storage-account".as_bytes(),
85 &storage_account.owner_1.key().to_bytes(),
86 &storage_account.account_counter_seed.to_le_bytes()
87 ],
88 bump,
89 )]
90 pub storage_account: Box<Account<'info, StorageAccount>>,
91
92 #[account(
94 mut,
95 seeds = [
96 "stake-account".as_bytes(),
97 &storage_account.key().to_bytes(),
98 ],
99 bump,
100 )]
101 pub stake_account: Box<Account<'info, TokenAccount>>,
102
103 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
108 pub owner: AccountInfo<'info>,
109
110 #[account(
112 mut,
113 constraint = {
114 storage_account.is_owner(shdw_payer.owner)
115 && shdw_payer.mint == token_mint.key()
116 },
117 )]
118 pub shdw_payer: Account<'info, TokenAccount>,
119
120 #[account(constraint = uploader.key() == storage_config.uploader)]
122 pub uploader: Signer<'info>,
123
124 #[account(mut, address=shdw::emissions_wallet::ID)]
126 pub emissions_wallet: Box<Account<'info, TokenAccount>>,
127
128 #[account(mut, address = shdw::ID)]
130 pub token_mint: Account<'info, Mint>,
131
132 pub system_program: Program<'info, System>,
134
135 pub token_program: Program<'info, Token>,
137}
138
139#[derive(Accounts)]
140pub struct DeleteAccountV2<'info> {
143 #[account(
145 mut,
146 seeds = [
147 "storage-config".as_bytes()
148 ],
149 bump,
150 )]
151 pub storage_config: Box<Account<'info, StorageConfig>>,
152
153 #[account(
155 mut,
156 seeds = [
157 "user-info".as_bytes(),
158 &storage_account.owner_1.key().to_bytes(),
159 ],
160 bump,
161 )]
162 pub user_info: Box<Account<'info, UserInfo>>,
163
164 #[account(
166 mut,
167 close = owner,
168 seeds = [
169 "storage-account".as_bytes(),
170 &storage_account.owner_1.key().to_bytes(),
171 &storage_account.account_counter_seed.to_le_bytes()
172 ],
173 bump,
174 )]
175 pub storage_account: Box<Account<'info, StorageAccountV2>>,
176
177 #[account(
179 mut,
180 close = owner,
181 seeds = [
182 "stake-account".as_bytes(),
183 &storage_account.key().to_bytes(),
184 ],
185 bump,
186 )]
187 pub stake_account: Box<Account<'info, TokenAccount>>,
188
189 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
194 pub owner: AccountInfo<'info>,
195
196 #[account(
198 mut,
199 constraint = {
200 storage_account.is_owner(shdw_payer.owner)
201 && shdw_payer.mint == token_mint.key()
202 },
203 )]
204 pub shdw_payer: Account<'info, TokenAccount>,
205
206 #[account(constraint = uploader.key() == storage_config.uploader)]
208 pub uploader: Signer<'info>,
209
210 #[account(mut, address=shdw::emissions_wallet::ID)]
212 pub emissions_wallet: Box<Account<'info, TokenAccount>>,
213
214 #[account(mut, address = shdw::ID)]
216 pub token_mint: Account<'info, Mint>,
217
218 pub system_program: Program<'info, System>,
220
221 pub token_program: Program<'info, Token>,
223}
224
225pub trait DeleteAccount {
226 fn check_immutable(&self) -> bool;
227 fn check_delete_flag(&self) -> bool;
228 fn check_grace_period(&self) -> bool;
229 fn get_identifier(&self) -> String;
230 fn return_stake(&mut self, storage_used: u64) -> Result<()>;
231 fn update_global_storage(&mut self) -> Result<()>;
232 fn increment_del_counter(&mut self) -> Result<()>;
233}
234
235impl DeleteAccount for Context<'_, '_, '_, '_, DeleteAccountV1<'_>> {
236 fn check_immutable(&self) -> bool {
237 self.accounts.storage_account.check_immutable()
238 }
239 fn check_delete_flag(&self) -> bool {
240 self.accounts.storage_account.check_delete_flag()
241 }
242 fn check_grace_period(&self) -> bool {
243 u32::try_from(Clock::get().unwrap().epoch).unwrap()
244 >= self
245 .accounts
246 .storage_account
247 .delete_request_epoch
248 .checked_add(DELETION_GRACE_PERIOD as u32)
249 .unwrap()
250 }
251 fn get_identifier(&self) -> String {
252 self.accounts.storage_account.get_identifier()
253 }
254 fn return_stake(&mut self, storage_used: u64) -> Result<()> {
255 let storage_config_seeds = [
257 "storage-config".as_bytes(),
258 &[*self.bumps.get("storage_config").unwrap()],
259 ];
260 let signer_seeds: &[&[&[u8]]] = &[&storage_config_seeds];
261
262 let mut return_amount = self.accounts.stake_account.amount;
264
265 let account_info = self.accounts.storage_account.to_account_info();
268 if let Some((emission_fee, crank_fee)) = crank(
269 &self.accounts.storage_config,
270 &mut self.accounts.storage_account,
271 account_info,
272 &self.accounts.emissions_wallet,
273 &self.accounts.stake_account,
274 &self.accounts.token_program,
275 &self.accounts.emissions_wallet,
276 &self.accounts.token_mint,
277 *self.bumps.get("storage_config").unwrap(),
278 storage_used,
279 )? {
280 let fee = emission_fee.checked_add(crank_fee).unwrap();
282 return_amount = return_amount.saturating_sub(fee);
283 }
284
285 anchor_spl::token::transfer(
287 CpiContext::new_with_signer(
288 self.accounts.token_program.to_account_info(),
289 anchor_spl::token::Transfer {
290 from: self.accounts.stake_account.to_account_info(),
291 to: self.accounts.shdw_payer.to_account_info(),
292 authority: self.accounts.storage_config.to_account_info(),
293 },
294 signer_seeds,
295 ),
296 return_amount,
297 )?;
298
299 Ok(())
300 }
301 fn update_global_storage(&mut self) -> Result<()> {
302 let storage_config = &mut self.accounts.storage_config;
303
304 storage_config.storage_available = storage_config
306 .storage_available
307 .checked_add(self.accounts.storage_account.storage as u128)
308 .unwrap();
309
310 Ok(())
311 }
312 fn increment_del_counter(&mut self) -> Result<()> {
313 self.accounts.user_info.del_counter =
315 self.accounts.user_info.del_counter.checked_add(1).unwrap();
316
317 Ok(())
318 }
319}
320
321impl DeleteAccount for Context<'_, '_, '_, '_, DeleteAccountV2<'_>> {
322 fn check_immutable(&self) -> bool {
323 self.accounts.storage_account.check_immutable()
324 }
325 fn check_delete_flag(&self) -> bool {
326 self.accounts.storage_account.check_delete_flag()
327 }
328 fn check_grace_period(&self) -> bool {
329 u32::try_from(Clock::get().unwrap().epoch).unwrap()
330 >= self
331 .accounts
332 .storage_account
333 .delete_request_epoch
334 .checked_add(DELETION_GRACE_PERIOD as u32)
335 .unwrap()
336 }
337 fn get_identifier(&self) -> String {
338 self.accounts.storage_account.get_identifier()
339 }
340 fn return_stake(&mut self, storage_used: u64) -> Result<()> {
341 let storage_config_seeds = [
343 "storage-config".as_bytes(),
344 &[*self.bumps.get("storage_config").unwrap()],
345 ];
346 let signer_seeds: &[&[&[u8]]] = &[&storage_config_seeds];
347
348 let mut return_amount = self.accounts.stake_account.amount;
350
351 let account_info = self.accounts.storage_account.to_account_info();
354 if let Some((emission_fee, crank_fee)) = crank(
355 &self.accounts.storage_config,
356 &mut self.accounts.storage_account,
357 account_info,
358 &self.accounts.emissions_wallet,
359 &self.accounts.stake_account,
360 &self.accounts.token_program,
361 &self.accounts.emissions_wallet,
362 &self.accounts.token_mint,
363 *self.bumps.get("storage_config").unwrap(),
364 storage_used,
365 )? {
366 let fee = emission_fee.checked_add(crank_fee).unwrap();
368 return_amount = return_amount.saturating_sub(fee);
369 }
370
371 anchor_spl::token::transfer(
373 CpiContext::new_with_signer(
374 self.accounts.token_program.to_account_info(),
375 anchor_spl::token::Transfer {
376 from: self.accounts.stake_account.to_account_info(),
377 to: self.accounts.shdw_payer.to_account_info(),
378 authority: self.accounts.storage_config.to_account_info(),
379 },
380 signer_seeds,
381 ),
382 return_amount,
383 )?;
384
385 Ok(())
386 }
387 fn update_global_storage(&mut self) -> Result<()> {
388 let storage_config = &mut self.accounts.storage_config;
389
390 storage_config.storage_available = storage_config
392 .storage_available
393 .checked_add(self.accounts.storage_account.storage as u128)
394 .unwrap();
395
396 Ok(())
397 }
398 fn increment_del_counter(&mut self) -> Result<()> {
399 self.accounts.user_info.del_counter =
401 self.accounts.user_info.del_counter.checked_add(1).unwrap();
402
403 Ok(())
404 }
405}