diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index a63289ee8a1504..e48b49db7b2c21 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -4518,12 +4518,14 @@ impl RpcClient { if let Some(filters) = config.filters { config.filters = Some(self.maybe_map_filters(filters).await?); } - let accounts: Vec = self - .send( + + let accounts = self + .send::>>( RpcRequest::GetProgramAccounts, json!([pubkey.to_string(), config]), ) - .await?; + .await? + .parse_value(); parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) } diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 53400fbce61777..4c1e805a9ea8b4 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -4431,4 +4431,83 @@ mod tests { assert_eq!(expected_minimum_delegation, actual_minimum_delegation); } } + + #[test] + fn test_get_program_accounts_with_config() { + let program_id = Pubkey::new_unique(); + let pubkey = Pubkey::new_unique(); + let account = Account { + lamports: 1_000_000, + data: vec![], + owner: program_id, + executable: false, + rent_epoch: 0, + }; + let keyed_account = RpcKeyedAccount { + pubkey: pubkey.to_string(), + account: UiAccount::encode(&pubkey, &account, UiAccountEncoding::Base64, None, None), + }; + let expected_result = vec![(pubkey, account)]; + // Test: without context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::NoContext(vec![keyed_account.clone()])) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + + // Test: with context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::Context(Response { + context: RpcResponseContext { + slot: 1, + api_version: None, + }, + value: vec![keyed_account], + })) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: Some(true), + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + } } diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index f7158ff88ae04a..79a3429a478fb9 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -17,6 +17,25 @@ use { thiserror::Error, }; +/// Wrapper for rpc return types of methods that provide responses both with and without context. +/// Main purpose of this is to fix methods that lack context information in their return type, +/// without breaking backwards compatibility. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum OptionalContext { + Context(Response), + NoContext(T), +} + +impl OptionalContext { + pub fn parse_value(self) -> T { + match self { + Self::Context(response) => response.value, + Self::NoContext(value) => value, + } + } +} + pub type RpcResult = client_error::Result>; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 43d7078914f1e6..f66db6581c6aeb 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -9,7 +9,6 @@ use { crossbeam_channel::{unbounded, Receiver, Sender}, jsonrpc_core::{futures::future, types::error, BoxFuture, Error, Metadata, Result}, jsonrpc_derive::rpc, - serde::{Deserialize, Serialize}, solana_account_decoder::{ parse_token::{is_known_spl_token_id, token_amount_to_ui_amount, UiTokenAmount}, UiAccount, UiAccountEncoding, UiDataSliceConfig, MAX_BASE58_BYTES, @@ -126,6 +125,7 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { } } +<<<<<<< HEAD /// Wrapper for rpc return types of methods that provide responses both with and without context. /// Main purpose of this is to fix methods that lack context information in their return type, /// without breaking backwards compatibility. @@ -136,6 +136,8 @@ pub enum OptionalContext { NoContext(T), } +======= +>>>>>>> b18ef88c4 (Fix client get_program_accounts_with_config calls with context (#28772)) fn is_finalized( block_commitment_cache: &BlockCommitmentCache, bank: &Bank,