2022-07-16 05:37:15 -07:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
2022-07-25 07:07:53 -07:00
|
|
|
use anyhow::Context;
|
2022-07-16 05:37:15 -07:00
|
|
|
|
2022-07-25 07:07:53 -07:00
|
|
|
use anchor_client::ClientError;
|
2022-07-16 05:37:15 -07:00
|
|
|
use anchor_lang::AccountDeserialize;
|
|
|
|
|
|
|
|
use solana_client::rpc_client::RpcClient;
|
|
|
|
use solana_sdk::account::Account;
|
|
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
|
2022-07-25 07:07:53 -07:00
|
|
|
use mango_v4::state::MangoAccountValue;
|
|
|
|
|
2022-07-16 05:37:15 -07:00
|
|
|
pub trait AccountFetcher: Sync + Send {
|
|
|
|
fn fetch_raw_account(&self, address: Pubkey) -> anyhow::Result<Account>;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't be in the trait, since then it would no longer be object-safe...
|
|
|
|
pub fn account_fetcher_fetch_anchor_account<T: AccountDeserialize>(
|
|
|
|
fetcher: &dyn AccountFetcher,
|
|
|
|
address: Pubkey,
|
|
|
|
) -> anyhow::Result<T> {
|
|
|
|
let account = fetcher.fetch_raw_account(address)?;
|
|
|
|
let mut data: &[u8] = &account.data;
|
2022-07-25 07:07:53 -07:00
|
|
|
T::try_deserialize(&mut data)
|
|
|
|
.with_context(|| format!("deserializing anchor account {}", address))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can't be in the trait, since then it would no longer be object-safe...
|
|
|
|
pub fn account_fetcher_fetch_mango_account(
|
|
|
|
fetcher: &dyn AccountFetcher,
|
|
|
|
address: Pubkey,
|
|
|
|
) -> anyhow::Result<MangoAccountValue> {
|
|
|
|
let account = fetcher.fetch_raw_account(address)?;
|
|
|
|
let data: &[u8] = &account.data;
|
|
|
|
MangoAccountValue::from_bytes(&data[8..])
|
|
|
|
.with_context(|| format!("deserializing mango account {}", address))
|
2022-07-16 05:37:15 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RpcAccountFetcher {
|
|
|
|
pub rpc: RpcClient,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AccountFetcher for RpcAccountFetcher {
|
|
|
|
fn fetch_raw_account(&self, address: Pubkey) -> anyhow::Result<Account> {
|
|
|
|
self.rpc
|
|
|
|
.get_account_with_commitment(&address, self.rpc.commitment())
|
|
|
|
.with_context(|| format!("fetch account {}", address))?
|
|
|
|
.value
|
|
|
|
.ok_or(ClientError::AccountNotFound)
|
|
|
|
.with_context(|| format!("fetch account {}", address))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct CachedAccountFetcher<T: AccountFetcher> {
|
|
|
|
fetcher: T,
|
|
|
|
cache: Mutex<HashMap<Pubkey, Account>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: AccountFetcher> CachedAccountFetcher<T> {
|
|
|
|
pub fn new(fetcher: T) -> Self {
|
|
|
|
Self {
|
|
|
|
fetcher,
|
|
|
|
cache: Mutex::new(HashMap::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear_cache(&self) {
|
|
|
|
let mut cache = self.cache.lock().unwrap();
|
|
|
|
cache.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: AccountFetcher> AccountFetcher for CachedAccountFetcher<T> {
|
|
|
|
fn fetch_raw_account(&self, address: Pubkey) -> anyhow::Result<Account> {
|
|
|
|
let mut cache = self.cache.lock().unwrap();
|
|
|
|
if let Some(account) = cache.get(&address) {
|
|
|
|
return Ok(account.clone());
|
|
|
|
}
|
|
|
|
let account = self.fetcher.fetch_raw_account(address)?;
|
|
|
|
cache.insert(address, account.clone());
|
|
|
|
Ok(account)
|
|
|
|
}
|
|
|
|
}
|