use crate::client::LightconeClient;
#[cfg(feature = "solana-rpc")]
use crate::error::SdkError;
use solana_pubkey::Pubkey;
#[cfg(feature = "solana-rpc")]
use solana_client::nonblocking::rpc_client::RpcClient as SolanaRpcClient;
#[cfg(feature = "solana-rpc")]
use crate::rpc_failover::{is_infrastructure_error_solana, with_failover, ActiveRpc};
#[cfg(feature = "solana-rpc")]
use std::pin::Pin;
#[cfg(feature = "solana-rpc")]
pub(crate) async fn resolve_solana_rpc(
client: &LightconeClient,
) -> Result<&SolanaRpcClient, SdkError> {
let state = client.rpc_failover_state.read().await;
let active = state.active();
drop(state);
match active {
ActiveRpc::Primary => client.primary_solana_rpc_client.as_ref(),
ActiveRpc::Backup => client
.backup_solana_rpc_client
.as_ref()
.or(client.primary_solana_rpc_client.as_ref()),
}
.ok_or_else(|| {
SdkError::Other("RPC client not configured — use .rpc_url() on the builder".to_string())
})
}
#[cfg(feature = "solana-rpc")]
async fn solana_rpc_with_failover<F, T>(
client: &LightconeClient,
operation: F,
) -> Result<T, solana_client::client_error::ClientError>
where
for<'r> F: Fn(
&'r SolanaRpcClient,
) -> Pin<
Box<
dyn std::future::Future<Output = Result<T, solana_client::client_error::ClientError>>
+ Send
+ 'r,
>,
>,
{
let primary = client.primary_solana_rpc_client.as_ref();
let backup = client.backup_solana_rpc_client.as_ref();
with_failover(
&client.rpc_failover_state,
|target| {
let rpc = match target {
ActiveRpc::Primary => primary.or(backup),
ActiveRpc::Backup => backup.or(primary),
}
.expect("RPC client not configured");
operation(rpc)
},
backup.is_some(),
is_infrastructure_error_solana,
)
.await
}
pub struct Rpc<'a> {
pub(crate) client: &'a LightconeClient,
}
impl<'a> Rpc<'a> {
#[cfg(feature = "solana-rpc")]
pub async fn inner(&self) -> Result<&SolanaRpcClient, SdkError> {
resolve_solana_rpc(self.client).await
}
pub fn get_exchange_pda(&self) -> Pubkey {
crate::program::pda::get_exchange_pda(&self.client.program_id).0
}
pub fn get_global_deposit_token_pda(&self, mint: &Pubkey) -> Pubkey {
crate::program::pda::get_global_deposit_token_pda(mint, &self.client.program_id).0
}
pub fn get_user_global_deposit_pda(&self, user: &Pubkey, mint: &Pubkey) -> Pubkey {
crate::program::pda::get_user_global_deposit_pda(user, mint, &self.client.program_id).0
}
}
#[cfg(feature = "solana-rpc")]
impl<'a> Rpc<'a> {
pub async fn get_latest_blockhash(&self) -> Result<solana_hash::Hash, SdkError> {
solana_rpc_with_failover(self.client, |rpc| {
Box::pin(async move { rpc.get_latest_blockhash().await })
})
.await
.map_err(|error| SdkError::Program(crate::program::error::SdkError::Rpc(error)))
}
pub async fn get_exchange(&self) -> Result<crate::program::accounts::Exchange, SdkError> {
let (pda, _) = crate::program::pda::get_exchange_pda(&self.client.program_id);
let account = solana_rpc_with_failover(self.client, move |rpc| {
Box::pin(async move { rpc.get_account(&pda).await })
})
.await
.map_err(|error| {
SdkError::Program(crate::program::error::SdkError::AccountNotFound(format!(
"Exchange: {}",
error
)))
})?;
Ok(crate::program::accounts::Exchange::deserialize(
&account.data,
)?)
}
pub async fn get_global_deposit_token(
&self,
mint: &Pubkey,
) -> Result<crate::program::accounts::GlobalDepositToken, SdkError> {
let (pda, _) =
crate::program::pda::get_global_deposit_token_pda(mint, &self.client.program_id);
let account = solana_rpc_with_failover(self.client, move |rpc| {
Box::pin(async move { rpc.get_account(&pda).await })
})
.await
.map_err(|error| {
SdkError::Program(crate::program::error::SdkError::AccountNotFound(format!(
"GlobalDepositToken: {}",
error
)))
})?;
Ok(crate::program::accounts::GlobalDepositToken::deserialize(
&account.data,
)?)
}
}