Skip to main content

shadow_drive_user_staking/instructions/
update_account.rs

1use anchor_lang::prelude::*;
2use anchor_lang::solana_program::program::invoke;
3use anchor_lang::solana_program::system_instruction::transfer;
4use anchor_spl::token::Mint;
5
6use crate::constants::*;
7use crate::errors::ErrorCodes;
8use crate::instructions::{
9    initialize_account::{ShadowDriveStorageAccount, StorageAccount, StorageAccountV2}, initialize_config::StorageConfig,
10};
11
12/// This is the function that handles the `update_account` ix
13pub fn handler(
14    mut ctx: impl UpdateAccount,
15    identifier: Option<String>,
16    owner_2: Option<Pubkey>,
17    // owner_3: Option<Pubkey>,
18    // owner_4: Option<Pubkey>,
19) -> Result<()> {
20    // Check if account is immutable
21    require!(
22        !ctx.check_immutable(),
23        ErrorCodes::StorageAccountMarkedImmutable
24    );
25
26    msg!(
27        "Updating StorageAccount: {}",
28        ctx.get_identifier()
29    );
30
31    // Change identifier
32    ctx.update_identifier(identifier)?;
33
34    // Update owners
35    if let Some(owner_2) = owner_2 {
36        ctx.update_owner2(owner_2)?;
37    }
38
39    Ok(())
40}
41
42#[derive(Accounts)]
43/// This `UpdateAccount` context is used in the instruction that allows users to
44/// update storage account metadata, such as the identifier and owner 2-4 pubkeys.
45pub struct UpdateAccountV1<'info> {
46    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
47    #[account(
48        seeds = [
49            "storage-config".as_bytes()
50        ],
51        bump,
52    )]
53    pub storage_config: Box<Account<'info, StorageConfig>>,
54
55    /// Parent storage account.
56    #[account(
57        mut,
58        seeds = [
59            "storage-account".as_bytes(),
60            &storage_account.owner_1.key().to_bytes(),
61            &storage_account.account_counter_seed.to_le_bytes()
62        ],
63        bump,
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    /// Token mint account
73    #[account(address = shdw::ID)]
74    pub token_mint: Account<'info, Mint>,
75
76    /// System Program
77    pub system_program: Program<'info, System>,
78}
79
80
81#[derive(Accounts)]
82/// This `UpdateAccount` context is used in the instruction that allows users to
83/// update storage account metadata, such as the identifier and owner 2-4 pubkeys.
84pub struct UpdateAccountV2<'info> {
85    /// This is the `StorageConfig` accounts that holds all of the admin, uploader keys.
86    #[account(
87        seeds = [
88            "storage-config".as_bytes()
89        ],
90        bump,
91    )]
92    pub storage_config: Box<Account<'info, StorageConfig>>,
93
94    /// Parent storage account.
95    #[account(
96        mut,
97        seeds = [
98            "storage-account".as_bytes(),
99            &storage_account.owner_1.key().to_bytes(),
100            &storage_account.account_counter_seed.to_le_bytes()
101        ],
102        bump,
103    )]
104    pub storage_account: Box<Account<'info, StorageAccountV2>>,
105
106    /// File owner, user, fee-payer
107    /// Requires mutability since owner/user is fee payer.
108    #[account(mut, constraint=storage_account.is_owner(owner.key()))]
109    pub owner: Signer<'info>,
110
111    /// Token mint account
112    #[account(address = shdw::ID)]
113    pub token_mint: Account<'info, Mint>,
114
115    /// System Program
116    pub system_program: Program<'info, System>,
117}
118
119pub trait UpdateAccount {
120    fn check_immutable(&self) -> bool;
121    fn get_identifier(&self) -> String;
122    fn update_identifier(&mut self, identifier: Option<String>) -> Result<()>;
123    fn update_owner2(&mut self, owner_2: Pubkey) -> Result<()>;
124}
125
126impl UpdateAccount for Context<'_,'_,'_,'_, UpdateAccountV1<'_>> {
127    fn check_immutable(&self) -> bool {
128        self.accounts.storage_account.check_immutable()
129    }
130    fn get_identifier(&self) -> String {
131        self.accounts.storage_account.get_identifier()
132    }
133    fn update_identifier(&mut self, identifier: Option<String>) -> Result<()> {
134
135        if let Some(identifier) = identifier {
136
137            let account_info = self.accounts.storage_account.to_account_info();
138
139            // Get new pda size
140            let new_size = crate::instructions::initialize_account::calc_v1_storage(&identifier);
141            let current_size =  crate::instructions::initialize_account::calc_v1_storage(&self.accounts.storage_account.identifier);
142
143            // Update identifier
144            msg!("Renaming account from {} to {}", self.accounts.storage_account.identifier, identifier);
145            self.accounts.storage_account.identifier = identifier;
146
147            // calculate rent diff
148            let rent = Rent::default();
149            let min_balance: u64 = rent.minimum_balance(new_size);
150            let current_balance: u64 = account_info.lamports();
151
152        
153            // Transfer from account to user if rent cost is decreasing
154            if current_balance > min_balance {
155
156
157                let mut user_balance = self.accounts.owner.try_borrow_mut_lamports()?;
158                let mut account_balance = account_info.try_borrow_mut_lamports()?;
159
160                let diff = current_balance.checked_sub(min_balance).unwrap();
161                **user_balance = user_balance.checked_add(diff).unwrap();
162                **account_balance = account_balance.checked_sub(diff).unwrap();
163
164            // Otherwise charge user
165            } else if current_balance < min_balance {
166
167                // Construct transfer `Instruction`
168                let ix = transfer(
169                    &self.accounts.owner.key(),
170                    &self.accounts.storage_account.key(),
171                    min_balance.checked_sub(current_balance).unwrap(),
172                );
173
174                // Invoke
175                invoke(
176                    &ix,
177                    &[
178                        self.accounts.owner.to_account_info(),
179                        self.accounts.storage_account.to_account_info(),
180                    ],
181                )?;
182            }
183
184            if new_size != current_size {
185                account_info.realloc(new_size, false)?;
186            }
187        }
188
189
190        Ok(())
191    }
192    fn update_owner2(&mut self, owner_2: Pubkey) -> Result<()> {
193        self.accounts.storage_account.owner_2 = owner_2;
194        Ok(())
195    }
196}
197
198impl UpdateAccount for Context<'_,'_,'_,'_, UpdateAccountV2<'_>> {
199    fn check_immutable(&self) -> bool {
200        self.accounts.storage_account.check_immutable()
201    }
202    fn get_identifier(&self) -> String {
203        self.accounts.storage_account.get_identifier()
204    }
205    fn update_identifier(&mut self, identifier: Option<String>) -> Result<()> {
206
207        if let Some(identifier) = identifier {
208
209            let account_info = self.accounts.storage_account.to_account_info();
210
211            // Get new pda size
212            let new_size = crate::instructions::initialize_account::calc_v2_storage(&identifier);
213            let current_size =  crate::instructions::initialize_account::calc_v2_storage(&self.accounts.storage_account.identifier);
214
215            // Update identifier
216            msg!("Renaming account from {} to {}", self.accounts.storage_account.identifier, identifier);
217            self.accounts.storage_account.identifier = identifier;
218
219            // calculate rent diff
220            let rent = Rent::default();
221            let min_balance: u64 = rent.minimum_balance(new_size);
222            let current_balance: u64 = account_info.lamports();
223
224        
225            // Transfer from account to user if rent cost is decreasing
226            if current_balance > min_balance {
227
228
229                let mut user_balance = self.accounts.owner.try_borrow_mut_lamports()?;
230                let mut account_balance = account_info.try_borrow_mut_lamports()?;
231
232                let diff = current_balance.checked_sub(min_balance).unwrap();
233                **user_balance = user_balance.checked_add(diff).unwrap();
234                **account_balance = account_balance.checked_sub(diff).unwrap();
235
236            // Otherwise charge user
237            } else if current_balance < min_balance {
238
239                // Construct transfer `Instruction`
240                let ix = transfer(
241                    &self.accounts.owner.key(),
242                    &self.accounts.storage_account.key(),
243                    min_balance.checked_sub(current_balance).unwrap(),
244                );
245
246                // Invoke
247                invoke(
248                    &ix,
249                    &[
250                        self.accounts.owner.to_account_info(),
251                        self.accounts.storage_account.to_account_info(),
252                    ],
253                )?;
254            }
255
256            if new_size != current_size {
257                account_info.realloc(new_size, false)?;
258            }
259        }
260        
261        Ok(())
262    }
263    fn update_owner2(&mut self, _owner_2: Pubkey) -> Result<()> {
264        err!(ErrorCodes::OnlyOneOwnerAllowedInV1_5)
265    }
266}
267