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 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}