use anchor_lang::{AccountDeserialize, Discriminator}; use futures::{stream, StreamExt}; use mango_v4::state::{Bank, MangoAccount, MangoAccountValue, MintInfo, PerpMarket, Serum3Market}; use solana_account_decoder::UiAccountEncoding; use solana_client::nonblocking::rpc_client::RpcClient as RpcClientAsync; use solana_client::rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}; use solana_client::rpc_filter::{Memcmp, RpcFilterType}; use solana_sdk::account::{Account, AccountSharedData}; use solana_sdk::pubkey::Pubkey; pub async fn fetch_mango_accounts( rpc: &RpcClientAsync, program: Pubkey, group: Pubkey, owner: Pubkey, ) -> anyhow::Result> { let config = RpcProgramAccountsConfig { filters: Some(vec![ RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 0, MangoAccount::discriminator().to_vec(), )), RpcFilterType::Memcmp(Memcmp::new_raw_bytes(8, group.to_bytes().to_vec())), RpcFilterType::Memcmp(Memcmp::new_raw_bytes(40, owner.to_bytes().to_vec())), ]), account_config: RpcAccountInfoConfig { encoding: Some(UiAccountEncoding::Base64), ..RpcAccountInfoConfig::default() }, ..RpcProgramAccountsConfig::default() }; rpc.get_program_accounts_with_config(&program, config) .await? .into_iter() .map(|(key, account)| Ok((key, MangoAccountValue::from_bytes(&account.data[8..])?))) .collect::, _>>() } pub async fn fetch_anchor_account( rpc: &RpcClientAsync, address: &Pubkey, ) -> anyhow::Result { let account = rpc.get_account(address).await?; Ok(T::try_deserialize(&mut (&account.data as &[u8]))?) } async fn fetch_anchor_accounts( rpc: &RpcClientAsync, program: Pubkey, filters: Vec, ) -> anyhow::Result> { let account_type_filter = RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, T::discriminator().to_vec())); let config = RpcProgramAccountsConfig { filters: Some([vec![account_type_filter], filters].concat()), account_config: RpcAccountInfoConfig { encoding: Some(UiAccountEncoding::Base64), ..RpcAccountInfoConfig::default() }, ..RpcProgramAccountsConfig::default() }; rpc.get_program_accounts_with_config(&program, config) .await? .into_iter() .map(|(key, account)| Ok((key, T::try_deserialize(&mut (&account.data as &[u8]))?))) .collect() } pub async fn fetch_banks( rpc: &RpcClientAsync, program: Pubkey, group: Pubkey, ) -> anyhow::Result> { fetch_anchor_accounts::( rpc, program, vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8, group.to_bytes().to_vec(), ))], ) .await } pub async fn fetch_mint_infos( rpc: &RpcClientAsync, program: Pubkey, group: Pubkey, ) -> anyhow::Result> { fetch_anchor_accounts::( rpc, program, vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8, group.to_bytes().to_vec(), ))], ) .await } pub async fn fetch_serum3_markets( rpc: &RpcClientAsync, program: Pubkey, group: Pubkey, ) -> anyhow::Result> { fetch_anchor_accounts::( rpc, program, vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8, group.to_bytes().to_vec(), ))], ) .await } pub async fn fetch_perp_markets( rpc: &RpcClientAsync, program: Pubkey, group: Pubkey, ) -> anyhow::Result> { fetch_anchor_accounts::( rpc, program, vec![RpcFilterType::Memcmp(Memcmp::new_raw_bytes( 8, group.to_bytes().to_vec(), ))], ) .await } pub async fn fetch_multiple_accounts( rpc: &RpcClientAsync, keys: &[Pubkey], ) -> anyhow::Result> { let config = RpcAccountInfoConfig { encoding: Some(UiAccountEncoding::Base64), ..RpcAccountInfoConfig::default() }; Ok(rpc .get_multiple_accounts_with_config(keys, config) .await? .value .into_iter() .zip(keys.iter()) .filter(|(maybe_acc, _)| maybe_acc.is_some()) .map(|(acc, key)| (*key, acc.unwrap().into())) .collect()) } /// Fetch multiple account using one request per chunk of `max_chunk_size` accounts /// Can execute in parallel up to `parallel_rpc_requests` /// /// WARNING: some accounts requested may be missing from the result pub async fn fetch_multiple_accounts_in_chunks( rpc: &RpcClientAsync, keys: &[Pubkey], max_chunk_size: usize, parallel_rpc_requests: usize, ) -> anyhow::Result> { let config = RpcAccountInfoConfig { encoding: Some(UiAccountEncoding::Base64), ..RpcAccountInfoConfig::default() }; let raw_results = stream::iter(keys) .chunks(max_chunk_size) .map(|keys| { let account_info_config = config.clone(); async move { let keys = keys.iter().map(|x| **x).collect::>(); let req_res = rpc .get_multiple_accounts_with_config(&keys, account_info_config) .await; match req_res { Ok(v) => Ok(keys.into_iter().zip(v.value).collect::>()), Err(e) => Err(e), } } }) .buffer_unordered(parallel_rpc_requests) .collect::>() .await; let result = raw_results .into_iter() .collect::, _>>()? .into_iter() .flatten() .filter_map(|(pubkey, account_opt)| account_opt.map(|acc| (pubkey, acc))) .collect::>(); Ok(result) }