1use std::collections::HashMap;
2
3use anchor_lang::{system_program, InstructionData, ToAccountMetas};
4use serde_json::Value;
5use shadow_drive_user_staking::accounts as shdw_drive_accounts;
6use shadow_drive_user_staking::instruction as shdw_drive_instructions;
7use solana_sdk::sysvar::rent;
8use solana_sdk::{
9 instruction::Instruction, pubkey::Pubkey, signer::Signer, transaction::Transaction,
10};
11use spl_associated_token_account::get_associated_token_address;
12use spl_token::ID as TokenProgramID;
13
14use super::ShadowDriveClient;
15use crate::constants::SHDW_DRIVE_ENDPOINT;
16use crate::models::GetBucketSizeResponse;
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 ShadowDriveResult, StorageResponse,
24 },
25 serialize_and_encode,
26};
27
28impl<T> ShadowDriveClient<T>
29where
30 T: Signer,
31{
32 pub async fn make_storage_immutable(
59 &self,
60 storage_account_key: &Pubkey,
61 ) -> ShadowDriveResult<StorageResponse> {
62 let selected_storage_acct = self.get_storage_account(storage_account_key).await?;
63
64 let mut bucket_query = HashMap::new();
65 bucket_query.insert("storageAccount", storage_account_key.to_string());
66
67 let response = self
68 .http_client
69 .get(format!("{}/storage-account-size", SHDW_DRIVE_ENDPOINT))
70 .query(&bucket_query)
71 .header("Content-Type", "application/json")
72 .send()
73 .await?;
74
75 if !response.status().is_success() {
76 return Err(Error::ShadowDriveServerError {
77 status: response.status().as_u16(),
78 message: response.json::<Value>().await?,
79 });
80 }
81
82 let response = response.json::<GetBucketSizeResponse>().await?;
83 let txn_encoded = match selected_storage_acct {
84 StorageAcct::V1(storage_account) => {
85 self.make_storage_immutable_v1(
86 storage_account_key,
87 storage_account,
88 response.storage_used,
89 )
90 .await?
91 }
92 StorageAcct::V2(storage_account) => {
93 self.make_storage_immutable_v2(
94 storage_account_key,
95 storage_account,
96 response.storage_used,
97 )
98 .await?
99 }
100 };
101
102 self.send_shdw_txn("make-immutable", txn_encoded, Some(response.storage_used))
103 .await
104 }
105
106 async fn make_storage_immutable_v1(
107 &self,
108 storage_account_key: &Pubkey,
109 storage_account: StorageAccount,
110 storage_used: u64,
111 ) -> ShadowDriveResult<String> {
112 let wallet_pubkey = self.wallet.pubkey();
113 let owner_ata = get_associated_token_address(&wallet_pubkey, &TOKEN_MINT);
114 let emissions_ata = get_associated_token_address(&EMISSIONS, &TOKEN_MINT);
115 let (stake_account, _) = derived_addresses::stake_account(storage_account_key);
116
117 let accounts = shdw_drive_accounts::MakeAccountImmutableV1 {
118 storage_config: *STORAGE_CONFIG_PDA,
119 storage_account: *storage_account_key,
120 owner: storage_account.owner_1,
121 uploader: UPLOADER,
122 owner_ata,
123 stake_account,
124 emissions_wallet: emissions_ata,
125 token_mint: TOKEN_MINT,
126 system_program: system_program::ID,
127 token_program: TokenProgramID,
128 associated_token_program: spl_associated_token_account::ID,
129 rent: rent::ID,
130 };
131
132 let args = shdw_drive_instructions::MakeAccountImmutable {
133 storage_used: storage_used,
134 };
135
136 let instruction = Instruction {
137 program_id: PROGRAM_ADDRESS,
138 accounts: accounts.to_account_metas(None),
139 data: args.data(),
140 };
141
142 let mut txn = Transaction::new_with_payer(&[instruction], Some(&wallet_pubkey));
143
144 txn.try_partial_sign(
145 &[&self.wallet],
146 self.rpc_client.get_latest_blockhash().await?,
147 )?;
148
149 let txn_encoded = serialize_and_encode(&txn)?;
150
151 Ok(txn_encoded)
152 }
153
154 async fn make_storage_immutable_v2(
155 &self,
156 storage_account_key: &Pubkey,
157 storage_account: StorageAccountV2,
158 storage_used: u64,
159 ) -> ShadowDriveResult<String> {
160 let wallet_pubkey = self.wallet.pubkey();
161 let owner_ata = get_associated_token_address(&wallet_pubkey, &TOKEN_MINT);
162 let emissions_ata = get_associated_token_address(&EMISSIONS, &TOKEN_MINT);
163 let (stake_account, _) = derived_addresses::stake_account(storage_account_key);
164
165 let accounts = shdw_drive_accounts::MakeAccountImmutableV2 {
166 storage_config: *STORAGE_CONFIG_PDA,
167 storage_account: *storage_account_key,
168 owner: storage_account.owner_1,
169 uploader: UPLOADER,
170 owner_ata,
171 stake_account,
172 emissions_wallet: emissions_ata,
173 token_mint: TOKEN_MINT,
174 system_program: system_program::ID,
175 token_program: TokenProgramID,
176 associated_token_program: spl_associated_token_account::ID,
177 rent: rent::ID,
178 };
179 let args = shdw_drive_instructions::MakeAccountImmutable2 {
180 storage_used: storage_used,
181 };
182
183 let instruction = Instruction {
184 program_id: PROGRAM_ADDRESS,
185 accounts: accounts.to_account_metas(None),
186 data: args.data(),
187 };
188
189 let mut txn = Transaction::new_with_payer(&[instruction], Some(&wallet_pubkey));
190
191 txn.try_partial_sign(
192 &[&self.wallet],
193 self.rpc_client.get_latest_blockhash().await?,
194 )?;
195
196 let txn_encoded = serialize_and_encode(&txn)?;
197
198 Ok(txn_encoded)
199 }
200}