shadow_drive_sdk/client/
make_storage_immutable.rs

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    /// Permanently locks a [`StorageAccount`](crate::models::StorageAccount) and all contained files. After a [`StorageAccount`](crate::models::StorageAccount)
33    /// has been locked, a user will no longer be able to delete/edit files, add/reduce storage amount,
34    /// or delete the [`StorageAccount`](crate::models::StorageAccount).
35    /// * `storage_account_key` - The public key of the [`StorageAccount`](crate::models::StorageAccount) that will be made immutable.
36    ///
37    /// # Example
38    ///
39    /// ```
40    /// # use shadow_drive_rust::{ShadowDriveClient, derived_addresses::storage_account};
41    /// # use solana_client::rpc_client::RpcClient;
42    /// # use solana_sdk::{
43    /// # pubkey::Pubkey,
44    /// # signature::Keypair,
45    /// # signer::{keypair::read_keypair_file, Signer},
46    /// # };
47    /// #
48    /// # let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
49    /// # let user_pubkey = keypair.pubkey();
50    /// # let rpc_client = RpcClient::new("https://ssc-dao.genesysgo.net");
51    /// # let shdw_drive_client = ShadowDriveClient::new(keypair, rpc_client);
52    /// # let (storage_account_key, _) = storage_account(&user_pubkey, 0);
53    /// #
54    /// let make_immutable_response = shdw_drive_client
55    ///     .make_storage_immutable(&storage_account_key)
56    ///     .await?;
57    /// ```
58    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}