shadow_drive_user_staking/instructions/
increase_storage.rs

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
10/// This is the function that handles the `increase_storage` ix
11pub fn handler(
12    mut ctx: impl IncreaseStorage,
13    additional_storage: u64
14) -> Result<()> {
15
16    // // Check if account is immutable
17    // require!(
18    //     !ctx.check_immutable(),
19    //     ErrorCodes::StorageAccountMarkedImmutable
20    // );
21
22    // Require nonzero change
23    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)]
42/// This `IncreaseStorage` context is used in the instruction which allow users to
43/// expand the storage available in a storage account.
44pub struct IncreaseStorageV1<'info> {
45    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
46    #[account(
47        seeds = [
48            "storage-config".as_bytes()
49        ],
50        bump,
51    )]
52    pub storage_config: Box<Account<'info, StorageConfig>>,
53
54    /// Parent storage account.
55    #[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    /// File owner, user, fee-payer
68    /// Requires mutability since owner/user is fee payer.
69    #[account(mut, constraint=storage_account.is_owner(owner.key()))]
70    pub owner: Signer<'info>,
71
72    /// This is the user's token account with which they are staking
73    #[account(mut, constraint = owner_ata.mint == shdw::ID)]
74    pub owner_ata: Box<Account<'info, TokenAccount>>,
75
76    /// This token account serves as the account which holds user's stake for file storage.
77    #[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    /// Uploader needs to sign off on increase storage
88    #[account(constraint = uploader.key() == storage_config.uploader)]
89    pub uploader: Signer<'info>,
90
91    /// Token mint account
92    #[account(address = shdw::ID)]
93    pub token_mint: Account<'info, Mint>,
94
95    /// System Program
96    pub system_program: Program<'info, System>,
97
98    /// Token Program
99    pub token_program: Program<'info, Token>,
100}
101
102#[derive(Accounts)]
103/// This `IncreaseStorage` context is used in the instruction which allow users to
104/// expand the storage available in a storage account.
105pub struct IncreaseStorageV2<'info> {
106    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
107    #[account(
108        seeds = [
109            "storage-config".as_bytes()
110        ],
111        bump,
112    )]
113    pub storage_config: Box<Account<'info, StorageConfig>>,
114
115    /// Parent storage account.
116    #[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    /// File owner, user, fee-payer
129    /// Requires mutability since owner/user is fee payer.
130    #[account(mut, constraint=storage_account.is_owner(owner.key()))]
131    pub owner: Signer<'info>,
132
133    /// This is the user's token account with which they are staking
134    #[account(mut, constraint = owner_ata.mint == shdw::ID)]
135    pub owner_ata: Box<Account<'info, TokenAccount>>,
136
137    /// This token account serves as the account which holds user's stake for file storage.
138    #[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    /// Uploader needs to sign off on increase storage
149    #[account(constraint = uploader.key() == storage_config.uploader)]
150    pub uploader: Signer<'info>,
151
152    /// Token mint account
153    #[account(address = shdw::ID)]
154    pub token_mint: Account<'info, Mint>,
155
156    /// System Program
157    pub system_program: Program<'info, System>,
158
159    /// Token Program
160    pub token_program: Program<'info, Token>,
161}
162
163
164#[derive(Accounts)]
165/// This `IncreaseStorage` context is used in the instruction which allow users to
166/// expand the storage available in a storage account.
167pub struct IncreaseImmutableStorageV1<'info> {
168    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
169    #[account(
170        seeds = [
171            "storage-config".as_bytes()
172        ],
173        bump,
174    )]
175    pub storage_config: Box<Account<'info, StorageConfig>>,
176
177    /// Parent storage account.
178    #[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    /// Wallet that receives storage fees
191    #[account(mut, address=shdw::emissions_wallet::ID)]
192    pub emissions_wallet: Box<Account<'info, TokenAccount>>,
193
194    /// File owner, user, fee-payer
195    /// Requires mutability since owner/user is fee payer.
196    #[account(mut, constraint=storage_account.is_owner(owner.key()))]
197    pub owner: Signer<'info>,
198
199    /// This is the user's token account with which they are staking
200    #[account(mut, constraint = owner_ata.mint == shdw::ID)]
201    pub owner_ata: Box<Account<'info, TokenAccount>>,
202
203    /// Uploader needs to sign off on increase storage
204    #[account(constraint = uploader.key() == storage_config.uploader)]
205    pub uploader: Signer<'info>,
206
207    /// Token mint account
208    #[account(address = shdw::ID)]
209    pub token_mint: Account<'info, Mint>,
210
211    /// System Program
212    pub system_program: Program<'info, System>,
213
214    /// Token Program
215    pub token_program: Program<'info, Token>,
216}
217
218#[derive(Accounts)]
219/// This `IncreaseStorage` context is used in the instruction which allow users to
220/// expand the storage available in a storage account.
221pub struct IncreaseImmutableStorageV2<'info> {
222    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
223    #[account(
224        seeds = [
225            "storage-config".as_bytes()
226        ],
227        bump,
228    )]
229    pub storage_config: Box<Account<'info, StorageConfig>>,
230
231    /// Parent storage account.
232    #[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    /// Wallet that receives storage fees
245    #[account(mut, address=shdw::emissions_wallet::ID)]
246    pub emissions_wallet: Box<Account<'info, TokenAccount>>,
247
248    /// File owner, user, fee-payer
249    /// Requires mutability since owner/user is fee payer.
250    #[account(mut, constraint=storage_account.is_owner(owner.key()))]
251    pub owner: Signer<'info>,
252
253    /// This is the user's token account with which they are staking
254    #[account(mut, constraint = owner_ata.mint == shdw::ID)]
255    pub owner_ata: Box<Account<'info, TokenAccount>>,
256
257    /// Uploader needs to sign off on increase storage
258    #[account(constraint = uploader.key() == storage_config.uploader)]
259    pub uploader: Signer<'info>,
260
261    /// Token mint account
262    #[account(address = shdw::ID)]
263    pub token_mint: Account<'info, Mint>,
264
265    /// System Program
266    pub system_program: Program<'info, System>,
267
268    /// Token Program
269    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        // Update total storage and storage_available
302        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        // Compute cost (at least one shade)
320        let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
321
322        // Transfer SHDW
323        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        // Update total storage and storage_available
346        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        // Compute cost (at least one shade)
364        let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
365
366        // Transfer SHDW
367        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        // Update total storage and storage_available
391        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        // Compute cost (at least one shade)
409        let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
410
411        // Transfer SHDW
412        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        // Update total storage and storage_available
435        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        // Compute cost (at least one shade)
453        let cost = safe_amount(additional_storage, self.accounts.storage_config.shades_per_gib)?.max(1);
454
455        // Transfer SHDW
456        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