1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use anchor_lang::{AccountDeserialize, Discriminator};
use shadow_drive_user_staking::instructions::initialize_account::StorageAccount;
use solana_account_decoder::UiAccountEncoding;
use solana_client::{
    rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
    rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
};
use solana_sdk::{bs58, pubkey::Pubkey, signer::Signer};

use super::ShadowDriveClient;
use crate::{constants::PROGRAM_ADDRESS, error::Error, models::*};

impl<T> ShadowDriveClient<T>
where
    T: Signer + Send + Sync,
{
    /// Returns the [`StorageAccount`](crate::models::StorageAccount) associated with the pubkey provided by a user.
    /// * `key` - The public key of the [`StorageAccount`](crate::models::StorageAccount).
    ///
    /// # Example
    ///
    /// ```
    /// # use shadow_drive_rust::{ShadowDriveClient, derived_addresses::storage_account};
    /// # use solana_client::rpc_client::RpcClient;
    /// # use solana_sdk::{
    /// # pubkey::Pubkey,
    /// # signature::Keypair,
    /// # signer::{keypair::read_keypair_file, Signer},
    /// # };
    /// #
    /// # let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    /// # let user_pubkey = keypair.pubkey();
    /// # let rpc_client = RpcClient::new("https://ssc-dao.genesysgo.net");
    /// # let shdw_drive_client = ShadowDriveClient::new(keypair, rpc_client);
    /// # let (storage_account_key, _) = storage_account(&user_pubkey, 0);
    /// #
    /// let storage_account = shdw_drive_client
    ///     .get_storage_account(&storage_account_key)
    ///     .await
    ///     .expect("failed to get storage account");
    /// ```
    pub async fn get_storage_account(&self, key: &Pubkey) -> ShadowDriveResult<StorageAccount> {
        let account_info = self.rpc_client.get_account(&key)?;
        StorageAccount::try_deserialize(&mut account_info.data.as_slice())
            .map_err(Error::AnchorError)
    }

    /// Returns all [`StorageAccount`]s associated with the public key provided by a user.
    /// * `owner` - The public key that is the owner of all the returned [`StorageAccount`]s.
    ///
    /// # Example
    ///
    /// ```
    /// # use shadow_drive_rust::ShadowDriveClient;
    /// # use solana_client::rpc_client::RpcClient;
    /// # use solana_sdk::{
    /// # pubkey::Pubkey,
    /// # signature::Keypair,
    /// # signer::{keypair::read_keypair_file, Signer},
    /// # };
    /// #
    /// # let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    /// # let user_pubkey = keypair.pubkey();
    /// # let rpc_client = RpcClient::new("https://ssc-dao.genesysgo.net");
    /// # let shdw_drive_client = ShadowDriveClient::new(keypair, rpc_client);
    /// #
    /// let storage_accounts = shdw_drive_client
    ///     .get_storage_accounts(&user_pubkey)
    ///     .await
    ///     .expect("failed to get storage account");
    /// ```
    pub async fn get_storage_accounts(
        &self,
        owner: &Pubkey,
    ) -> ShadowDriveResult<Vec<StorageAccount>> {
        let account_type_filter = RpcFilterType::Memcmp(Memcmp {
            offset: 0,
            bytes: MemcmpEncodedBytes::Base58(
                bs58::encode(StorageAccount::discriminator()).into_string(),
            ),
            encoding: None,
        });

        let owner_filter = RpcFilterType::Memcmp(Memcmp {
            offset: 39,
            bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
            encoding: None,
        });

        let get_accounts_config = RpcProgramAccountsConfig {
            filters: Some(vec![account_type_filter, owner_filter]),
            account_config: RpcAccountInfoConfig {
                encoding: Some(UiAccountEncoding::Base64),
                ..RpcAccountInfoConfig::default()
            },
            ..RpcProgramAccountsConfig::default()
        };

        let accounts = self
            .rpc_client
            .get_program_accounts_with_config(&PROGRAM_ADDRESS, get_accounts_config)?;

        let accounts = accounts
            .into_iter()
            .map(|(_, account)| {
                StorageAccount::try_deserialize(&mut account.data.as_slice())
                    .map_err(Error::AnchorError)
            })
            .collect::<Result<Vec<_>, _>>()?;

        Ok(accounts)
    }
}