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
12pub mod workarounds;
14
15pub mod instruction;
17
18pub mod rpc;
20
21pub mod signer;
23
24pub mod fixed;
26
27pub mod token;
29
30pub 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
56pub 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#[derive(Debug, Clone, Copy, Default)]
83pub struct TokenAccountParams {
84 token: Option<Pubkey>,
85 token_account: Option<Pubkey>,
86}
87
88impl TokenAccountParams {
89 pub fn set_token_account(&mut self, account: Pubkey) -> &mut Self {
91 self.token_account = Some(account);
92 self
93 }
94
95 pub fn set_token(&mut self, mint: Pubkey) -> &mut Self {
97 self.token = Some(mint);
98 self
99 }
100
101 pub fn token(&self) -> Option<&Pubkey> {
103 self.token.as_ref()
104 }
105
106 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 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 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;