1use anchor_lang::prelude::*;
2use anchor_spl::token::{Mint, Token, TokenAccount};
3
4use crate::constants::*;
5use crate::errors::ErrorCodes;
6use crate::instructions::{
7 initialize_account::{StorageAccount, StorageAccountV2, ShadowDriveStorageAccount}, initialize_config::StorageConfig,
8};
9
10pub fn handler(
12 mut ctx: impl IncreaseStorage,
13 additional_storage: u64
14) -> Result<()> {
15
16 require!(additional_storage > 0, ErrorCodes::NoStorageIncrease);
24
25 msg!(
26 "Increasing storage on StorageAccount: {}",
27 ctx.get_identifier()
28 );
29 {
30 ctx.add_storage(additional_storage)?
31 }
32
33 msg!("Charging user for storage");
34 {
35 ctx.charge_user(additional_storage)?
36 }
37
38 Ok(())
39}
40
41#[derive(Accounts)]
42pub struct IncreaseStorageV1<'info> {
45 #[account(
47 seeds = [
48 "storage-config".as_bytes()
49 ],
50 bump,
51 )]
52 pub storage_config: Box<Account<'info, StorageConfig>>,
53
54 #[account(
56 mut,
57 seeds = [
58 "storage-account".as_bytes(),
59 &storage_account.owner_1.key().to_bytes(),
60 &storage_account.account_counter_seed.to_le_bytes()
61 ],
62 bump,
63 constraint = !storage_account.immutable,
64 )]
65 pub storage_account: Box<Account<'info, StorageAccount>>,
66
67 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
70 pub owner: Signer<'info>,
71
72 #[account(mut, constraint = owner_ata.mint == shdw::ID)]
74 pub owner_ata: Box<Account<'info, TokenAccount>>,
75
76 #[account(
78 mut,
79 seeds = [
80 "stake-account".as_bytes(),
81 &storage_account.key().to_bytes(),
82 ],
83 bump,
84 )]
85 pub stake_account: Box<Account<'info, TokenAccount>>,
86
87 #[account(constraint = uploader.key() == storage_config.uploader)]
89 pub uploader: Signer<'info>,
90
91 #[account(address = shdw::ID)]
93 pub token_mint: Account<'info, Mint>,
94
95 pub system_program: Program<'info, System>,
97
98 pub token_program: Program<'info, Token>,
100}
101
102#[derive(Accounts)]
103pub struct IncreaseStorageV2<'info> {
106 #[account(
108 seeds = [
109 "storage-config".as_bytes()
110 ],
111 bump,
112 )]
113 pub storage_config: Box<Account<'info, StorageConfig>>,
114
115 #[account(
117 mut,
118 seeds = [
119 "storage-account".as_bytes(),
120 &storage_account.owner_1.key().to_bytes(),
121 &storage_account.account_counter_seed.to_le_bytes()
122 ],
123 bump,
124 constraint = !storage_account.immutable,
125 )]
126 pub storage_account: Box<Account<'info, StorageAccountV2>>,
127
128 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
131 pub owner: Signer<'info>,
132
133 #[account(mut, constraint = owner_ata.mint == shdw::ID)]
135 pub owner_ata: Box<Account<'info, TokenAccount>>,
136
137 #[account(
139 mut,
140 seeds = [
141 "stake-account".as_bytes(),
142 &storage_account.key().to_bytes(),
143 ],
144 bump,
145 )]
146 pub stake_account: Box<Account<'info, TokenAccount>>,
147
148 #[account(constraint = uploader.key() == storage_config.uploader)]
150 pub uploader: Signer<'info>,
151
152 #[account(address = shdw::ID)]
154 pub token_mint: Account<'info, Mint>,
155
156 pub system_program: Program<'info, System>,
158
159 pub token_program: Program<'info, Token>,
161}
162
163
164#[derive(Accounts)]
165pub struct IncreaseImmutableStorageV1<'info> {
168 #[account(
170 seeds = [
171 "storage-config".as_bytes()
172 ],
173 bump,
174 )]
175 pub storage_config: Box<Account<'info, StorageConfig>>,
176
177 #[account(
179 mut,
180 seeds = [
181 "storage-account".as_bytes(),
182 &storage_account.owner_1.key().to_bytes(),
183 &storage_account.account_counter_seed.to_le_bytes()
184 ],
185 bump,
186 constraint = storage_account.immutable,
187 )]
188 pub storage_account: Box<Account<'info, StorageAccount>>,
189
190 #[account(mut, address=shdw::emissions_wallet::ID)]
192 pub emissions_wallet: Box<Account<'info, TokenAccount>>,
193
194 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
197 pub owner: Signer<'info>,
198
199 #[account(mut, constraint = owner_ata.mint == shdw::ID)]
201 pub owner_ata: Box<Account<'info, TokenAccount>>,
202
203 #[account(constraint = uploader.key() == storage_config.uploader)]
205 pub uploader: Signer<'info>,
206
207 #[account(address = shdw::ID)]
209 pub token_mint: Account<'info, Mint>,
210
211 pub system_program: Program<'info, System>,
213
214 pub token_program: Program<'info, Token>,
216}
217
218#[derive(Accounts)]
219pub struct IncreaseImmutableStorageV2<'info> {
222 #[account(
224 seeds = [
225 "storage-config".as_bytes()
226 ],
227 bump,
228 )]
229 pub storage_config: Box<Account<'info, StorageConfig>>,
230
231 #[account(
233 mut,
234 seeds = [
235 "storage-account".as_bytes(),
236 &storage_account.owner_1.key().to_bytes(),
237 &storage_account.account_counter_seed.to_le_bytes()
238 ],
239 bump,
240 constraint = storage_account.immutable,
241 )]
242 pub storage_account: Box<Account<'info, StorageAccountV2>>,
243
244 #[account(mut, address=shdw::emissions_wallet::ID)]
246 pub emissions_wallet: Box<Account<'info, TokenAccount>>,
247
248 #[account(mut, constraint=storage_account.is_owner(owner.key()))]
251 pub owner: Signer<'info>,
252
253 #[account(mut, constraint = owner_ata.mint == shdw::ID)]
255 pub owner_ata: Box<Account<'info, TokenAccount>>,
256
257 #[account(constraint = uploader.key() == storage_config.uploader)]
259 pub uploader: Signer<'info>,
260
261 #[account(address = shdw::ID)]
263 pub token_mint: Account<'info, Mint>,
264
265 pub system_program: Program<'info, System>,
267
268 pub token_program: Program<'info, Token>,
270}
271
272
273fn safe_amount(additional_storage: u64, rate_per_gib: u64) -> Result<u64> {
274 let result = (additional_storage as u128)
275 .checked_mul(rate_per_gib as u128)
276 .unwrap()
277 .checked_div(BYTES_PER_GIB as u128)
278 .unwrap();
279 if (result as u64) as u128 == result {
280 Ok(result as u64)
281 } else {
282 err!(ErrorCodes::UnsignedIntegerCastFailed)
283 }
284}
285
286pub trait IncreaseStorage {
287 fn check_immutable(&self) -> bool;
288 fn get_identifier(&self) -> String;
289 fn add_storage(&mut self, additional_storage: u64) -> Result<()>;
290 fn charge_user(&mut self, additional_storage: u64) -> Result<()>;
291}
292
293impl IncreaseStorage for Context<'_,'_,'_,'_, IncreaseStorageV1<'_>> {
294 fn check_immutable(&self) -> bool {
295 self.accounts.storage_account.immutable
296 }
297 fn get_identifier(&self) -> String {
298 self.accounts.storage_account.identifier.clone()
299 }
300 fn add_storage(&mut self, additional_storage: u64) -> Result<()> {
301 msg!(
303 "Initial storage: {}",
304 self.accounts.storage_account.storage,
305 );
306 self.accounts.storage_account.storage = self.accounts.storage_account
307 .storage
308 .checked_add(additional_storage)
309 .unwrap();
310 msg!(
311 "New storage: {}",
312 self.accounts.storage_account.storage,
313 );
314
315 Ok(())
316 }
317 fn charge_user(&mut self, additional_storage: u64) -> Result<()> {
318
319 let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
321
322 anchor_spl::token::transfer(
324 CpiContext::new(
325 self.accounts.token_program.to_account_info(),
326 anchor_spl::token::Transfer {
327 from: self.accounts.owner_ata.to_account_info(),
328 to: self.accounts.stake_account.to_account_info(),
329 authority: self.accounts.owner.to_account_info(),
330 },
331 ),
332 cost,
333 )
334 }
335}
336
337impl IncreaseStorage for Context<'_,'_,'_,'_, IncreaseStorageV2<'_>> {
338 fn check_immutable(&self) -> bool {
339 self.accounts.storage_account.immutable
340 }
341 fn get_identifier(&self) -> String {
342 self.accounts.storage_account.identifier.clone()
343 }
344 fn add_storage(&mut self, additional_storage: u64) -> Result<()> {
345 msg!(
347 "Initial storage: {}",
348 self.accounts.storage_account.storage,
349 );
350 self.accounts.storage_account.storage = self.accounts.storage_account
351 .storage
352 .checked_add(additional_storage)
353 .unwrap();
354 msg!(
355 "New storage: {}",
356 self.accounts.storage_account.storage,
357 );
358
359 Ok(())
360 }
361 fn charge_user(&mut self, additional_storage: u64) -> Result<()> {
362
363 let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
365
366 anchor_spl::token::transfer(
368 CpiContext::new(
369 self.accounts.token_program.to_account_info(),
370 anchor_spl::token::Transfer {
371 from: self.accounts.owner_ata.to_account_info(),
372 to: self.accounts.stake_account.to_account_info(),
373 authority: self.accounts.owner.to_account_info(),
374 },
375 ),
376 cost,
377 )
378 }
379}
380
381
382impl IncreaseStorage for Context<'_,'_,'_,'_, IncreaseImmutableStorageV1<'_>> {
383 fn check_immutable(&self) -> bool {
384 self.accounts.storage_account.immutable
385 }
386 fn get_identifier(&self) -> String {
387 self.accounts.storage_account.identifier.clone()
388 }
389 fn add_storage(&mut self, additional_storage: u64) -> Result<()> {
390 msg!(
392 "Initial storage: {}",
393 self.accounts.storage_account.storage,
394 );
395 self.accounts.storage_account.storage = self.accounts.storage_account
396 .storage
397 .checked_add(additional_storage)
398 .unwrap();
399 msg!(
400 "New storage: {}",
401 self.accounts.storage_account.storage,
402 );
403
404 Ok(())
405 }
406 fn charge_user(&mut self, additional_storage: u64) -> Result<()> {
407
408 let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
410
411 anchor_spl::token::transfer(
413 CpiContext::new(
414 self.accounts.token_program.to_account_info(),
415 anchor_spl::token::Transfer {
416 from: self.accounts.owner_ata.to_account_info(),
417 to: self.accounts.emissions_wallet.to_account_info(),
418 authority: self.accounts.owner.to_account_info(),
419 },
420 ),
421 cost,
422 )
423 }
424}
425
426impl IncreaseStorage for Context<'_,'_,'_,'_, IncreaseImmutableStorageV2<'_>> {
427 fn check_immutable(&self) -> bool {
428 self.accounts.storage_account.immutable
429 }
430 fn get_identifier(&self) -> String {
431 self.accounts.storage_account.identifier.clone()
432 }
433 fn add_storage(&mut self, additional_storage: u64) -> Result<()> {
434 msg!(
436 "Initial storage: {}",
437 self.accounts.storage_account.storage,
438 );
439 self.accounts.storage_account.storage = self.accounts.storage_account
440 .storage
441 .checked_add(additional_storage)
442 .unwrap();
443 msg!(
444 "New storage: {}",
445 self.accounts.storage_account.storage,
446 );
447
448 Ok(())
449 }
450 fn charge_user(&mut self, additional_storage: u64) -> Result<()> {
451
452 let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
454
455 anchor_spl::token::transfer(
457 CpiContext::new(
458 self.accounts.token_program.to_account_info(),
459 anchor_spl::token::Transfer {
460 from: self.accounts.owner_ata.to_account_info(),
461 to: self.accounts.emissions_wallet.to_account_info(),
462 authority: self.accounts.owner.to_account_info(),
463 },
464 ),
465 cost,
466 )
467 }
468}
469
470