diff --git a/rpc-client-api/src/response.rs b/rpc-client-api/src/response.rs index dfe9272a5ca83d..25f62dc0f3276a 100644 --- a/rpc-client-api/src/response.rs +++ b/rpc-client-api/src/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-client/src/nonblocking/rpc_client.rs b/rpc-client/src/nonblocking/rpc_client.rs index 4ab7cf1d66fce1..a1b4f008677b46 100644 --- a/rpc-client/src/nonblocking/rpc_client.rs +++ b/rpc-client/src/nonblocking/rpc_client.rs @@ -4420,12 +4420,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/rpc-client/src/rpc_client.rs b/rpc-client/src/rpc_client.rs index b4a4eca7c807c6..b559f47dd294c5 100644 --- a/rpc-client/src/rpc_client.rs +++ b/rpc-client/src/rpc_client.rs @@ -4292,4 +4292,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/rpc/src/rpc.rs b/rpc/src/rpc.rs index 11838cf1bc5f47..f4ef58b339a9d4 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, @@ -122,16 +121,6 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { } } -/// 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(RpcResponse), - NoContext(T), -} - fn is_finalized( block_commitment_cache: &BlockCommitmentCache, bank: &Bank,