gmsol/utils/
mod.rs

1use std::ops::Deref;
2
3use anchor_client::{
4    anchor_lang::prelude::borsh::BorshDeserialize,
5    solana_client::{nonblocking::rpc_client::RpcClient, rpc_client::SerializableTransaction},
6    solana_sdk::{pubkey::Pubkey, signer::Signer},
7};
8
9use anchor_spl::associated_token::get_associated_token_address;
10use base64::{prelude::BASE64_STANDARD, Engine};
11
12/// Workarounds
13pub mod workarounds;
14
15/// Instruction utils.
16pub mod instruction;
17
18/// Solana RPC utils.
19pub mod rpc;
20
21/// Signer.
22pub mod signer;
23
24/// Utils for fixed-point numbers.
25pub mod fixed;
26
27/// Utils for token calculation.
28pub mod token;
29
30/// Utils for action builders.
31pub mod builder;
32
33pub use self::{
34    fixed::{
35        signed_amount_to_decimal, signed_fixed_to_decimal, signed_value_to_decimal,
36        unsigned_amount_to_decimal, unsigned_fixed_to_decimal, unsigned_value_to_decimal,
37    },
38    instruction::serialize_instruction,
39    rpc::{
40        accounts::{account_with_context, accounts_lazy_with_context, ProgramAccountsConfig},
41        context::{WithContext, WithSlot},
42        pubsub::{PubsubClient, SubscriptionConfig},
43        transaction_history::fetch_transaction_history_with_config,
44    },
45    signer::{local_signer, shared_signer, LocalSignerRef, SignerRef},
46    token::price_to_min_output_amount,
47    workarounds::{
48        optional::fix_optional_account_metas,
49        zero_copy::{try_deserailize_zero_copy_account, ZeroCopy},
50    },
51};
52
53#[cfg(feature = "decode")]
54pub use rpc::transaction_history::extract_cpi_events;
55
56/// View the return data by simulating the transaction.
57pub async fn view<T: BorshDeserialize>(
58    client: &RpcClient,
59    transaction: &impl SerializableTransaction,
60) -> crate::Result<T> {
61    let res = client
62        .simulate_transaction(transaction)
63        .await
64        .map_err(anchor_client::ClientError::from)?;
65    if let Some(error) = res.value.err {
66        return Err(crate::Error::unknown(format!(
67            "error={error}, logs={:#?}",
68            res.value.logs,
69        )));
70    }
71    let (data, _encoding) = res
72        .value
73        .return_data
74        .ok_or(crate::Error::MissingReturnData)?
75        .data;
76    let decoded = BASE64_STANDARD.decode(data)?;
77    let output = T::deserialize_reader(&mut decoded.as_slice())?;
78    Ok(output)
79}
80
81/// Token Account Params.
82#[derive(Debug, Clone, Copy, Default)]
83pub struct TokenAccountParams {
84    token: Option<Pubkey>,
85    token_account: Option<Pubkey>,
86}
87
88impl TokenAccountParams {
89    /// Set token account.
90    pub fn set_token_account(&mut self, account: Pubkey) -> &mut Self {
91        self.token_account = Some(account);
92        self
93    }
94
95    /// Set token.
96    pub fn set_token(&mut self, mint: Pubkey) -> &mut Self {
97        self.token = Some(mint);
98        self
99    }
100
101    /// Get token.
102    pub fn token(&self) -> Option<&Pubkey> {
103        self.token.as_ref()
104    }
105
106    /// Get or find associated token account.
107    pub fn get_or_find_associated_token_account(&self, owner: Option<&Pubkey>) -> Option<Pubkey> {
108        match self.token_account {
109            Some(account) => Some(account),
110            None => {
111                let token = self.token.as_ref()?;
112                let owner = owner?;
113                Some(get_associated_token_address(owner, token))
114            }
115        }
116    }
117
118    /// Get of fetch token and token account.
119    ///
120    /// Returns `(token, token_account)` if success.
121    pub async fn get_or_fetch_token_and_token_account<S, C>(
122        &self,
123        client: &crate::Client<C>,
124        owner: Option<&Pubkey>,
125    ) -> crate::Result<Option<(Pubkey, Pubkey)>>
126    where
127        C: Deref<Target = S> + Clone,
128        S: Signer,
129    {
130        use anchor_spl::token::TokenAccount;
131        match (self.token, self.token_account) {
132            (Some(token), Some(account)) => Ok(Some((token, account))),
133            (None, Some(account)) => {
134                let mint = client
135                    .account::<TokenAccount>(&account)
136                    .await?
137                    .ok_or(crate::Error::NotFound)?
138                    .mint;
139                Ok(Some((mint, account)))
140            }
141            (Some(token), None) => {
142                let Some(account) = self.get_or_find_associated_token_account(owner) else {
143                    return Err(crate::Error::invalid_argument(
144                        "cannot find associated token account: `owner` is not provided",
145                    ));
146                };
147                Ok(Some((token, account)))
148            }
149            (None, None) => Ok(None),
150        }
151    }
152
153    /// Returns whether the params is empty.
154    pub fn is_empty(&self) -> bool {
155        self.token.is_none() && self.token.is_none()
156    }
157}
158
159pub use gmsol_store::constants::EVENT_AUTHORITY_SEED;