shadow_drive_sdk/client/
reduce_storage.rs

1use std::collections::HashMap;
2
3use anchor_lang::{system_program, InstructionData, ToAccountMetas};
4use byte_unit::Byte;
5use serde_json::Value;
6use shadow_drive_user_staking::accounts as shdw_drive_accounts;
7use shadow_drive_user_staking::instruction as shdw_drive_instructions;
8use solana_sdk::sysvar::rent;
9use solana_sdk::{
10    instruction::Instruction, pubkey::Pubkey, signer::Signer, transaction::Transaction,
11};
12use spl_associated_token_account::get_associated_token_address;
13use spl_token::ID as TokenProgramID;
14
15use super::ShadowDriveClient;
16use crate::constants::SHDW_DRIVE_ENDPOINT;
17use crate::{
18    constants::{EMISSIONS, PROGRAM_ADDRESS, STORAGE_CONFIG_PDA, TOKEN_MINT, UPLOADER},
19    derived_addresses,
20    error::Error,
21    models::{
22        storage_acct::{StorageAccount, StorageAccountV2, StorageAcct},
23        *,
24    },
25    serialize_and_encode,
26};
27
28impl<T> ShadowDriveClient<T>
29where
30    T: Signer,
31{
32    /// Reduces the amount of total storage available for the given storage account.
33    /// * `storage_account_key` - The public key of the [`StorageAccount`](crate::models::StorageAccount) whose storage will be reduced.
34    /// * `size` - The amount of storage you want to remove.
35    /// E.g if you have an existing [`StorageAccount`](crate::models::StorageAccount) with 3MB of storage
36    /// but you want 2MB total, `size` should equal 1MB.
37    /// When specifying size, only KB, MB, and GB storage units are currently supported.
38    ///
39    /// # Example
40    ///
41    /// ```
42    /// # use byte_unit::Byte;
43    /// # use shadow_drive_rust::{ShadowDriveClient, derived_addresses::storage_account};
44    /// # use solana_client::rpc_client::RpcClient;
45    /// # use solana_sdk::{
46    /// # pubkey::Pubkey,
47    /// # signature::Keypair,
48    /// # signer::{keypair::read_keypair_file, Signer},
49    /// # };
50    /// #
51    /// # let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
52    /// # let user_pubkey = keypair.pubkey();
53    /// # let rpc_client = RpcClient::new("https://ssc-dao.genesysgo.net");
54    /// # let shdw_drive_client = ShadowDriveClient::new(keypair, rpc_client);
55    /// # let (storage_account_key, _) = storage_account(&user_pubkey, 0);
56    /// # let reduced_bytes = Byte::from_str("1MB").expect("invalid byte string");
57    /// #
58    /// let reduce_storage_response = shdw_drive_client
59    ///     .reduce_storage(&storage_account_key, reduced_bytes)
60    ///     .await?;
61    /// ```
62    pub async fn reduce_storage(
63        &self,
64        storage_account_key: &Pubkey,
65        size: Byte,
66    ) -> ShadowDriveResult<StorageResponse> {
67        let size_as_bytes: u64 = size
68            .get_bytes()
69            .try_into()
70            .map_err(|_| Error::InvalidStorage)?;
71
72        let selected_storage_acct = self.get_storage_account(storage_account_key).await?;
73        let mut bucket_query = HashMap::new();
74        bucket_query.insert("storageAccount", storage_account_key.to_string());
75        let response = self
76            .http_client
77            .get(format!("{}/storage-account-size", SHDW_DRIVE_ENDPOINT))
78            .query(&bucket_query)
79            .header("Content-Type", "application/json")
80            .send()
81            .await?;
82
83        if !response.status().is_success() {
84            return Err(Error::ShadowDriveServerError {
85                status: response.status().as_u16(),
86                message: response.json::<Value>().await?,
87            });
88        }
89
90        let response = response.json::<GetBucketSizeResponse>().await?;
91
92        let txn_encoded = match selected_storage_acct {
93            StorageAcct::V1(storage_account) => {
94                self.reduce_storage_v1(
95                    storage_account_key,
96                    storage_account,
97                    size_as_bytes,
98                    response.storage_used,
99                )
100                .await?
101            }
102            StorageAcct::V2(storage_account) => {
103                self.reduce_storage_v2(
104                    storage_account_key,
105                    storage_account,
106                    size_as_bytes,
107                    response.storage_used,
108                )
109                .await?
110            }
111        };
112
113        self.send_shdw_txn("reduce-storage", txn_encoded, Some(response.storage_used))
114            .await
115    }
116
117    async fn reduce_storage_v1(
118        &self,
119        storage_account_key: &Pubkey,
120        storage_account: StorageAccount,
121        size_as_bytes: u64,
122        storage_used: u64,
123    ) -> ShadowDriveResult<String> {
124        let wallet_pubkey = self.wallet.pubkey();
125        let (unstake_account, _) = derived_addresses::unstake_account(storage_account_key);
126        let (unstake_info, _) = derived_addresses::unstake_info(storage_account_key);
127
128        let owner_ata = get_associated_token_address(&wallet_pubkey, &TOKEN_MINT);
129        let (stake_account, _) = derived_addresses::stake_account(storage_account_key);
130
131        let emeissions_ata = get_associated_token_address(&EMISSIONS, &TOKEN_MINT);
132
133        let accounts = shdw_drive_accounts::DecreaseStorageV1 {
134            storage_config: *STORAGE_CONFIG_PDA,
135            storage_account: *storage_account_key,
136            unstake_info,
137            unstake_account,
138            owner: storage_account.owner_1,
139            owner_ata,
140            stake_account,
141            uploader: UPLOADER,
142            emissions_wallet: emeissions_ata,
143            token_mint: TOKEN_MINT,
144            system_program: system_program::ID,
145            token_program: TokenProgramID,
146            rent: rent::ID,
147        };
148        let args = shdw_drive_instructions::DecreaseStorage {
149            remove_storage: size_as_bytes,
150            storage_used: storage_used,
151        };
152
153        let instruction = Instruction {
154            program_id: PROGRAM_ADDRESS,
155            accounts: accounts.to_account_metas(None),
156            data: args.data(),
157        };
158
159        let mut txn = Transaction::new_with_payer(&[instruction], Some(&wallet_pubkey));
160        txn.try_partial_sign(
161            &[&self.wallet],
162            self.rpc_client.get_latest_blockhash().await?,
163        )?;
164
165        let txn_encoded = serialize_and_encode(&txn)?;
166
167        Ok(txn_encoded)
168    }
169
170    async fn reduce_storage_v2(
171        &self,
172        storage_account_key: &Pubkey,
173        storage_account: StorageAccountV2,
174        size_as_bytes: u64,
175        storage_used: u64,
176    ) -> ShadowDriveResult<String> {
177        let wallet_pubkey = self.wallet.pubkey();
178        let (unstake_account, _) = derived_addresses::unstake_account(storage_account_key);
179        let (unstake_info, _) = derived_addresses::unstake_info(storage_account_key);
180
181        let owner_ata = get_associated_token_address(&wallet_pubkey, &TOKEN_MINT);
182        let (stake_account, _) = derived_addresses::stake_account(storage_account_key);
183
184        let emeissions_ata = get_associated_token_address(&EMISSIONS, &TOKEN_MINT);
185
186        let accounts = shdw_drive_accounts::DecreaseStorageV2 {
187            storage_config: *STORAGE_CONFIG_PDA,
188            storage_account: *storage_account_key,
189            unstake_info,
190            unstake_account,
191            owner: storage_account.owner_1,
192            owner_ata,
193            stake_account,
194            uploader: UPLOADER,
195            emissions_wallet: emeissions_ata,
196            token_mint: TOKEN_MINT,
197            system_program: system_program::ID,
198            token_program: TokenProgramID,
199            rent: rent::ID,
200        };
201        let args = shdw_drive_instructions::DecreaseStorage2 {
202            remove_storage: size_as_bytes,
203            storage_used: storage_used,
204        };
205
206        let instruction = Instruction {
207            program_id: PROGRAM_ADDRESS,
208            accounts: accounts.to_account_metas(None),
209            data: args.data(),
210        };
211
212        let mut txn = Transaction::new_with_payer(&[instruction], Some(&wallet_pubkey));
213        txn.try_partial_sign(
214            &[&self.wallet],
215            self.rpc_client.get_latest_blockhash().await?,
216        )?;
217
218        let txn_encoded = serialize_and_encode(&txn)?;
219
220        Ok(txn_encoded)
221    }
222}