client: add typed getProgramAccounts support (#1297)
This commit is contained in:
parent
9f46b30bc7
commit
a8f820c391
|
@ -183,6 +183,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"regex",
|
||||
"serde",
|
||||
"solana-account-decoder",
|
||||
"solana-client",
|
||||
"solana-sdk",
|
||||
"thiserror",
|
||||
|
|
|
@ -16,5 +16,6 @@ regex = "1.4.5"
|
|||
serde = { version = "1.0.122", features = ["derive"] }
|
||||
solana-client = "1.7.2"
|
||||
solana-sdk = "1.7.2"
|
||||
solana-account-decoder = "1.7.2"
|
||||
thiserror = "1.0.20"
|
||||
url = "2.2.2"
|
||||
|
|
|
@ -5,18 +5,27 @@ use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
|
|||
use anchor_lang::solana_program::program_error::ProgramError;
|
||||
use anchor_lang::solana_program::pubkey::Pubkey;
|
||||
use anchor_lang::solana_program::system_program;
|
||||
use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas};
|
||||
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
|
||||
use regex::Regex;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
use solana_client::client_error::ClientError as SolanaClientError;
|
||||
use solana_client::pubsub_client::{PubsubClient, PubsubClientError, PubsubClientSubscription};
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_client::rpc_config::{RpcTransactionLogsConfig, RpcTransactionLogsFilter};
|
||||
use solana_client::rpc_config::{
|
||||
RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcTransactionLogsConfig,
|
||||
RpcTransactionLogsFilter,
|
||||
};
|
||||
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
|
||||
use solana_client::rpc_response::{Response as RpcResponse, RpcLogsResponse};
|
||||
use solana_sdk::account::Account;
|
||||
use solana_sdk::bs58;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::{Signature, Signer};
|
||||
use solana_sdk::transaction::Transaction;
|
||||
use std::convert::Into;
|
||||
use std::iter::Map;
|
||||
use std::rc::Rc;
|
||||
use std::vec::IntoIter;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use anchor_lang;
|
||||
|
@ -129,6 +138,45 @@ impl Program {
|
|||
T::try_deserialize(&mut data).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Returns all program accounts of the given type matching the given filters
|
||||
pub fn accounts<T: AccountDeserialize + Discriminator>(
|
||||
&self,
|
||||
filters: Vec<RpcFilterType>,
|
||||
) -> Result<Vec<(Pubkey, T)>, ClientError> {
|
||||
self.accounts_lazy(filters)?.collect()
|
||||
}
|
||||
|
||||
/// Returns all program accounts of the given type matching the given filters as an iterator
|
||||
/// Deserialization is executed lazily
|
||||
pub fn accounts_lazy<T: AccountDeserialize + Discriminator>(
|
||||
&self,
|
||||
filters: Vec<RpcFilterType>,
|
||||
) -> Result<ProgramAccountsIterator<T>, ClientError> {
|
||||
let account_type_filter = RpcFilterType::Memcmp(Memcmp {
|
||||
offset: 0,
|
||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(T::discriminator()).into_string()),
|
||||
encoding: None,
|
||||
});
|
||||
let config = RpcProgramAccountsConfig {
|
||||
filters: Some([vec![account_type_filter], filters].concat()),
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
data_slice: None,
|
||||
commitment: None,
|
||||
},
|
||||
with_context: None,
|
||||
};
|
||||
Ok(ProgramAccountsIterator {
|
||||
inner: self
|
||||
.rpc()
|
||||
.get_program_accounts_with_config(&self.id(), config)?
|
||||
.into_iter()
|
||||
.map(|(key, account)| {
|
||||
Ok((key, T::try_deserialize(&mut (&account.data as &[u8]))?))
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
|
||||
self.account(anchor_lang::__private::state::address(&self.program_id))
|
||||
}
|
||||
|
@ -209,6 +257,23 @@ impl Program {
|
|||
}
|
||||
}
|
||||
|
||||
/// Iterator with items of type (Pubkey, T). Used to lazily deserialize account structs.
|
||||
/// Wrapper type hides the inner type from usages so the implementation can be changed.
|
||||
pub struct ProgramAccountsIterator<T> {
|
||||
inner: Map<IntoIter<(Pubkey, Account)>, AccountConverterFunction<T>>,
|
||||
}
|
||||
|
||||
/// Function type that accepts solana accounts and returns deserialized anchor accounts
|
||||
type AccountConverterFunction<T> = fn((Pubkey, Account)) -> Result<(Pubkey, T), ClientError>;
|
||||
|
||||
impl<T> Iterator for ProgramAccountsIterator<T> {
|
||||
type Item = Result<(Pubkey, T), ClientError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
|
||||
self_program_str: &str,
|
||||
l: &str,
|
||||
|
|
Loading…
Reference in New Issue