From 5337657cbac3f14821f0859b26180a5e14b9f316 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Tue, 23 Sep 2025 11:50:49 +0200 Subject: [PATCH 1/4] fix: do not ignore `response_size_estimate` for `getBlock` --- canister/src/rpc_client/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/canister/src/rpc_client/mod.rs b/canister/src/rpc_client/mod.rs index b9aeb843..d36021a8 100644 --- a/canister/src/rpc_client/mod.rs +++ b/canister/src/rpc_client/mod.rs @@ -155,7 +155,9 @@ impl GetBlockRequest { let params = params.into(); let consensus_strategy = config.response_consensus.unwrap_or_default(); let providers = Providers::new(rpc_sources, consensus_strategy.clone(), now)?; - let max_response_bytes = Self::response_size_estimate(¶ms); + let max_response_bytes = config + .response_size_estimate + .unwrap_or(Self::response_size_estimate(¶ms)); Ok(MultiRpcRequest::new( providers, From d93ac112f1d5259044f6f84f759cbe8e71a43df3 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Wed, 24 Sep 2025 15:37:30 +0200 Subject: [PATCH 2/4] test: integration tests for RPC config --- integration_tests/tests/tests.rs | 2113 ++++++++++++++++++------------ libs/client/src/lib.rs | 2 +- libs/client/src/request/mod.rs | 85 +- libs/types/src/rpc_client/mod.rs | 10 + 4 files changed, 1371 insertions(+), 839 deletions(-) diff --git a/integration_tests/tests/tests.rs b/integration_tests/tests/tests.rs index a776dd2a..1c41aadd 100644 --- a/integration_tests/tests/tests.rs +++ b/integration_tests/tests/tests.rs @@ -6,26 +6,31 @@ use const_format::formatcp; use ic_cdk::api::{call::RejectionCode, management_canister::http_request::HttpHeader}; use pocket_ic::common::rest::CanisterHttpMethod; use serde::de::DeserializeOwned; -use serde_json::json; +use serde_json::{json, Value}; use sol_rpc_canister::constants::*; -use sol_rpc_client::{RequestBuilder, SolRpcEndpoint}; +use sol_rpc_client::{RequestBuilder, SolRpcClient, SolRpcConfig, SolRpcEndpoint}; use sol_rpc_int_tests::{ mock::MockOutcallBuilder, PocketIcRuntime, Setup, SolRpcTestClient, DEFAULT_CALLER_TEST_ID, }; use sol_rpc_types::{ CommitmentLevel, ConfirmedTransactionStatusWithSignature, ConsensusStrategy, - GetSignaturesForAddressLimit, GetSlotParams, InstallArgs, InstructionError, Mode, - MultiRpcResult, ProviderError, RpcAccess, RpcAuth, RpcEndpoint, RpcError, RpcResult, RpcSource, - RpcSources, Slot, SolanaCluster, SupportedRpcProvider, SupportedRpcProviderId, - TransactionDetails, TransactionError, + GetSignaturesForAddressLimit, GetSlotParams, GetTransactionEncoding, HttpOutcallError, + InstallArgs, InstructionError, Mode, MultiRpcResult, PrioritizationFee, ProviderError, + RpcAccess, RpcAuth, RpcEndpoint, RpcError, RpcResult, RpcSource, RpcSources, Slot, + SolanaCluster, SupportedRpcProvider, SupportedRpcProviderId, TransactionDetails, + TransactionError, }; use solana_account_decoder_client_types::{ token::UiTokenAmount, UiAccount, UiAccountData, UiAccountEncoding, }; use solana_pubkey::pubkey; use solana_signer::Signer; -use solana_transaction_status_client_types::{TransactionConfirmationStatus, TransactionStatus}; -use std::{fmt::Debug, iter::zip, str::FromStr}; +use solana_transaction_status_client_types::{ + option_serializer::OptionSerializer, EncodedConfirmedTransactionWithStatusMeta, + EncodedTransaction, EncodedTransactionWithStatusMeta, TransactionBinaryEncoding, + TransactionConfirmationStatus, TransactionStatus, UiLoadedAddresses, UiTransactionStatusMeta, +}; +use std::{fmt::Debug, iter::zip, num::NonZeroU8, str::FromStr}; use strum::IntoEnumIterator; const MOCK_REQUEST_URL: &str = "https://api.devnet.solana.com/"; @@ -59,7 +64,7 @@ mod mock_request_tests { value: "Value".to_string(), }]), })])); - let expected_result: serde_json::Value = serde_json::from_str(MOCK_RESPONSE).unwrap(); + let expected_result: Value = serde_json::from_str(MOCK_RESPONSE).unwrap(); assert_matches!( client .mock_http(builder_fn(MockOutcallBuilder::new(200, MOCK_RESPONSE))).build() @@ -67,7 +72,7 @@ mod mock_request_tests { .with_cycles(0) .send() .await, - sol_rpc_types::MultiRpcResult::Consistent(Ok(msg)) if msg == serde_json::Value::to_string(&expected_result["result"]) + MultiRpcResult::Consistent(Ok(msg)) if msg == Value::to_string(&expected_result["result"]) ); } @@ -170,24 +175,7 @@ mod get_account_info_tests { solana_pubkey::Pubkey::from_str("11111111111111111111111111111111").unwrap(); let results = client - .mock_sequential_json_rpc_responses::<3>( - 200, - json!({ - "id": Id::from(ConstantSizeId::from(first_id)), - "jsonrpc": "2.0", - "result": { - "context": { "apiVersion": "2.0.15", "slot": 341197053 }, - "value": { - "data": ["1234", "base58"], - "executable": false, - "lamports": 88849814690250u64, - "owner": "11111111111111111111111111111111", - "rentEpoch": 18446744073709551615u64, - "space": 0 - } - }, - }), - ) + .mock_sequential_json_rpc_responses::<3>(200, get_account_info_response(first_id)) .build() .get_account_info(pubkey) .send() @@ -256,19 +244,7 @@ mod get_block_tests { let slot: Slot = 123; let results = client - .mock_sequential_json_rpc_responses::<3>( - 200, - json!({ - "id": Id::from(ConstantSizeId::from(first_id)), - "jsonrpc": "2.0", - "result":{ - "blockHeight": 360854634, - "blockTime": 1744122369, - "parentSlot": 372877611, - "blockhash": "8QeCusqSTKeC23NwjTKRBDcPuEfVLtszkxbpL6mXQEp4", - "previousBlockhash": "4Pcj2yJkCYyhnWe8Ze3uK2D2EtesBxhAevweDoTcxXf3"} - }), - ) + .mock_sequential_json_rpc_responses::<3>(200, get_block_response(first_id)) .build() .get_block(slot) .send() @@ -332,24 +308,22 @@ mod get_slot_tests { #[tokio::test] async fn should_get_slot_with_full_params() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { let id = ConstantSizeId::from(id).to_string(); json!({ "jsonrpc": "2.0", "id": id, "method": "getSlot", "params": [{"commitment": "processed", "minContextSlot": 100}] }) } - fn response_body(id: u8) -> serde_json::Value { - let id = ConstantSizeId::from(id).to_string(); - json!({ "id": id, "jsonrpc": "2.0", "result": 1234, }) - } - let setup = Setup::new().await.with_mock_api_keys().await; let client = setup.client(); let slot = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(0)).with_request_body(request_body(0)), - MockOutcallBuilder::new(200, response_body(1)).with_request_body(request_body(1)), - MockOutcallBuilder::new(200, response_body(2)).with_request_body(request_body(2)), + MockOutcallBuilder::new(200, get_slot_response(0)) + .with_request_body(request_body(0)), + MockOutcallBuilder::new(200, get_slot_response(1)) + .with_request_body(request_body(1)), + MockOutcallBuilder::new(200, get_slot_response(2)) + .with_request_body(request_body(2)), ]) .build() .get_slot() @@ -375,14 +349,7 @@ mod get_slot_tests { let client = setup.client().with_rpc_sources(sources); let results = client - .mock_sequential_json_rpc_responses::<3>( - 200, - json!({ - "id": Id::from(ConstantSizeId::from(first_id)), - "jsonrpc": "2.0", - "result": 1234, - }), - ) + .mock_sequential_json_rpc_responses::<3>(200, get_slot_response(first_id)) .build() .get_slot() .with_rounding_error(0) @@ -401,17 +368,13 @@ mod get_slot_tests { let setup = Setup::new().await.with_mock_api_keys().await; for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { - let responses = [1234, 1229, 1237] - .iter() + let responses = [1234_u64, 1229, 1237] + .into_iter() .enumerate() .map(|(id, slot)| { MockOutcallBuilder::new( 200, - json!({ - "id": Id::from(ConstantSizeId::from(id as u64 + first_id as u64)), - "jsonrpc": "2.0", - "result": slot, - }), + get_slot_response_with_slot(id as u8 + first_id, slot), ) }) .collect(); @@ -436,17 +399,13 @@ mod get_slot_tests { let setup = Setup::new().await.with_mock_api_keys().await; for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { - let responses = [1234, 1229, 1237] - .iter() + let responses = [1234_u64, 1229, 1237] + .into_iter() .enumerate() .map(|(id, slot)| { MockOutcallBuilder::new( 200, - json!({ - "id": Id::from(ConstantSizeId::from(id as u64 + first_id as u64)), - "jsonrpc": "2.0", - "result": slot, - }), + get_slot_response_with_slot(id as u8 + first_id, slot), ) }) .collect(); @@ -472,638 +431,25 @@ mod get_slot_tests { } mod get_recent_prioritization_fees_tests { - use crate::USDC_PUBLIC_KEY; - use canhttp::http::json::ConstantSizeId; - use serde_json::json; - use sol_rpc_int_tests::{mock::MockOutcallBuilder, Setup, SolRpcTestClient}; - use sol_rpc_types::PrioritizationFee; - use std::num::NonZeroU8; + use super::*; #[tokio::test] async fn should_get_fees_with_rounding() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { let id = ConstantSizeId::from(id).to_string(); json!( { "jsonrpc": "2.0", "id": id, "method": "getRecentPrioritizationFees", "params": [ [ USDC_PUBLIC_KEY.to_string() ] ] } ) } - fn response_body(id: u8) -> serde_json::Value { - let id = ConstantSizeId::from(id).to_string(); - json!({ - "jsonrpc": "2.0", - "result": [ - { - "prioritizationFee": 0, - "slot": 338225766 - }, - { - "prioritizationFee": 203228, - "slot": 338225767 - }, - { - "prioritizationFee": 110788, - "slot": 338225768 - }, - { - "prioritizationFee": 395962, - "slot": 338225769 - }, - { - "prioritizationFee": 0, - "slot": 338225770 - }, - { - "prioritizationFee": 395477, - "slot": 338225771 - }, - { - "prioritizationFee": 202136, - "slot": 338225772 - }, - { - "prioritizationFee": 0, - "slot": 338225773 - }, - { - "prioritizationFee": 0, - "slot": 338225774 - }, - { - "prioritizationFee": 0, - "slot": 338225775 - }, - { - "prioritizationFee": 2894338, - "slot": 338225776 - }, - { - "prioritizationFee": 0, - "slot": 338225777 - }, - { - "prioritizationFee": 162918, - "slot": 338225778 - }, - { - "prioritizationFee": 238785, - "slot": 338225779 - }, - { - "prioritizationFee": 10714, - "slot": 338225780 - }, - { - "prioritizationFee": 81000, - "slot": 338225781 - }, - { - "prioritizationFee": 0, - "slot": 338225782 - }, - { - "prioritizationFee": 0, - "slot": 338225783 - }, - { - "prioritizationFee": 202136, - "slot": 338225784 - }, - { - "prioritizationFee": 166667, - "slot": 338225785 - }, - { - "prioritizationFee": 166667, - "slot": 338225786 - }, - { - "prioritizationFee": 0, - "slot": 338225787 - }, - { - "prioritizationFee": 0, - "slot": 338225788 - }, - { - "prioritizationFee": 0, - "slot": 338225789 - }, - { - "prioritizationFee": 0, - "slot": 338225790 - }, - { - "prioritizationFee": 0, - "slot": 338225791 - }, - { - "prioritizationFee": 0, - "slot": 338225792 - }, - { - "prioritizationFee": 0, - "slot": 338225793 - }, - { - "prioritizationFee": 494120, - "slot": 338225794 - }, - { - "prioritizationFee": 0, - "slot": 338225795 - }, - { - "prioritizationFee": 0, - "slot": 338225796 - }, - { - "prioritizationFee": 202136, - "slot": 338225797 - }, - { - "prioritizationFee": 0, - "slot": 338225798 - }, - { - "prioritizationFee": 0, - "slot": 338225799 - }, - { - "prioritizationFee": 202136, - "slot": 338225800 - }, - { - "prioritizationFee": 0, - "slot": 338225801 - }, - { - "prioritizationFee": 0, - "slot": 338225802 - }, - { - "prioritizationFee": 10001, - "slot": 338225803 - }, - { - "prioritizationFee": 0, - "slot": 338225804 - }, - { - "prioritizationFee": 0, - "slot": 338225805 - }, - { - "prioritizationFee": 0, - "slot": 338225806 - }, - { - "prioritizationFee": 0, - "slot": 338225807 - }, - { - "prioritizationFee": 202136, - "slot": 338225808 - }, - { - "prioritizationFee": 0, - "slot": 338225809 - }, - { - "prioritizationFee": 202136, - "slot": 338225810 - }, - { - "prioritizationFee": 0, - "slot": 338225811 - }, - { - "prioritizationFee": 0, - "slot": 338225812 - }, - { - "prioritizationFee": 0, - "slot": 338225813 - }, - { - "prioritizationFee": 0, - "slot": 338225814 - }, - { - "prioritizationFee": 6064097, - "slot": 338225815 - }, - { - "prioritizationFee": 0, - "slot": 338225816 - }, - { - "prioritizationFee": 0, - "slot": 338225817 - }, - { - "prioritizationFee": 0, - "slot": 338225818 - }, - { - "prioritizationFee": 517927, - "slot": 338225819 - }, - { - "prioritizationFee": 0, - "slot": 338225820 - }, - { - "prioritizationFee": 0, - "slot": 338225821 - }, - { - "prioritizationFee": 0, - "slot": 338225822 - }, - { - "prioritizationFee": 602011, - "slot": 338225823 - }, - { - "prioritizationFee": 187015, - "slot": 338225824 - }, - { - "prioritizationFee": 50000, - "slot": 338225825 - }, - { - "prioritizationFee": 0, - "slot": 338225826 - }, - { - "prioritizationFee": 0, - "slot": 338225827 - }, - { - "prioritizationFee": 0, - "slot": 338225828 - }, - { - "prioritizationFee": 0, - "slot": 338225829 - }, - { - "prioritizationFee": 0, - "slot": 338225830 - }, - { - "prioritizationFee": 0, - "slot": 338225831 - }, - { - "prioritizationFee": 0, - "slot": 338225832 - }, - { - "prioritizationFee": 0, - "slot": 338225833 - }, - { - "prioritizationFee": 0, - "slot": 338225834 - }, - { - "prioritizationFee": 0, - "slot": 338225835 - }, - { - "prioritizationFee": 0, - "slot": 338225836 - }, - { - "prioritizationFee": 0, - "slot": 338225837 - }, - { - "prioritizationFee": 0, - "slot": 338225838 - }, - { - "prioritizationFee": 487330, - "slot": 338225839 - }, - { - "prioritizationFee": 149432, - "slot": 338225840 - }, - { - "prioritizationFee": 0, - "slot": 338225841 - }, - { - "prioritizationFee": 0, - "slot": 338225842 - }, - { - "prioritizationFee": 68526, - "slot": 338225843 - }, - { - "prioritizationFee": 0, - "slot": 338225844 - }, - { - "prioritizationFee": 310090, - "slot": 338225845 - }, - { - "prioritizationFee": 0, - "slot": 338225846 - }, - { - "prioritizationFee": 2173913, - "slot": 338225847 - }, - { - "prioritizationFee": 99725, - "slot": 338225848 - }, - { - "prioritizationFee": 0, - "slot": 338225849 - }, - { - "prioritizationFee": 88441, - "slot": 338225850 - }, - { - "prioritizationFee": 0, - "slot": 338225851 - }, - { - "prioritizationFee": 400000, - "slot": 338225852 - }, - { - "prioritizationFee": 0, - "slot": 338225853 - }, - { - "prioritizationFee": 0, - "slot": 338225854 - }, - { - "prioritizationFee": 164507, - "slot": 338225855 - }, - { - "prioritizationFee": 0, - "slot": 338225856 - }, - { - "prioritizationFee": 4898, - "slot": 338225857 - }, - { - "prioritizationFee": 0, - "slot": 338225858 - }, - { - "prioritizationFee": 0, - "slot": 338225859 - }, - { - "prioritizationFee": 142369, - "slot": 338225860 - }, - { - "prioritizationFee": 84566, - "slot": 338225861 - }, - { - "prioritizationFee": 0, - "slot": 338225862 - }, - { - "prioritizationFee": 10001, - "slot": 338225863 - }, - { - "prioritizationFee": 187015, - "slot": 338225864 - }, - { - "prioritizationFee": 8902, - "slot": 338225865 - }, - { - "prioritizationFee": 0, - "slot": 338225866 - }, - { - "prioritizationFee": 75000, - "slot": 338225867 - }, - { - "prioritizationFee": 0, - "slot": 338225868 - }, - { - "prioritizationFee": 0, - "slot": 338225869 - }, - { - "prioritizationFee": 1771477, - "slot": 338225870 - }, - { - "prioritizationFee": 1110536, - "slot": 338225871 - }, - { - "prioritizationFee": 215920, - "slot": 338225872 - }, - { - "prioritizationFee": 68408, - "slot": 338225873 - }, - { - "prioritizationFee": 0, - "slot": 338225874 - }, - { - "prioritizationFee": 260520, - "slot": 338225875 - }, - { - "prioritizationFee": 2143332, - "slot": 338225876 - }, - { - "prioritizationFee": 0, - "slot": 338225877 - }, - { - "prioritizationFee": 84168, - "slot": 338225878 - }, - { - "prioritizationFee": 0, - "slot": 338225879 - }, - { - "prioritizationFee": 0, - "slot": 338225880 - }, - { - "prioritizationFee": 501111, - "slot": 338225881 - }, - { - "prioritizationFee": 88060, - "slot": 338225882 - }, - { - "prioritizationFee": 10001, - "slot": 338225883 - }, - { - "prioritizationFee": 171521, - "slot": 338225884 - }, - { - "prioritizationFee": 0, - "slot": 338225885 - }, - { - "prioritizationFee": 6064097, - "slot": 338225886 - }, - { - "prioritizationFee": 6064097, - "slot": 338225887 - }, - { - "prioritizationFee": 0, - "slot": 338225888 - }, - { - "prioritizationFee": 7578, - "slot": 338225889 - }, - { - "prioritizationFee": 0, - "slot": 338225890 - }, - { - "prioritizationFee": 0, - "slot": 338225891 - }, - { - "prioritizationFee": 202136, - "slot": 338225892 - }, - { - "prioritizationFee": 106090, - "slot": 338225893 - }, - { - "prioritizationFee": 80776, - "slot": 338225894 - }, - { - "prioritizationFee": 111939, - "slot": 338225895 - }, - { - "prioritizationFee": 75000, - "slot": 338225896 - }, - { - "prioritizationFee": 0, - "slot": 338225897 - }, - { - "prioritizationFee": 0, - "slot": 338225898 - }, - { - "prioritizationFee": 0, - "slot": 338225899 - }, - { - "prioritizationFee": 0, - "slot": 338225900 - }, - { - "prioritizationFee": 0, - "slot": 338225901 - }, - { - "prioritizationFee": 183582, - "slot": 338225902 - }, - { - "prioritizationFee": 0, - "slot": 338225903 - }, - { - "prioritizationFee": 0, - "slot": 338225904 - }, - { - "prioritizationFee": 0, - "slot": 338225905 - }, - { - "prioritizationFee": 535775, - "slot": 338225906 - }, - { - "prioritizationFee": 65038, - "slot": 338225907 - }, - { - "prioritizationFee": 0, - "slot": 338225908 - }, - { - "prioritizationFee": 0, - "slot": 338225909 - }, - { - "prioritizationFee": 0, - "slot": 338225910 - }, - { - "prioritizationFee": 0, - "slot": 338225911 - }, - { - "prioritizationFee": 0, - "slot": 338225912 - }, - { - "prioritizationFee": 0, - "slot": 338225913 - }, - { - "prioritizationFee": 0, - "slot": 338225914 - }, - { - "prioritizationFee": 0, - "slot": 338225915 - } - ], - "id": id - } - ) - } - let setup = Setup::new().await.with_mock_api_keys().await; let client = setup.client(); let fees = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(0)).with_request_body(request_body(0)), - MockOutcallBuilder::new(200, response_body(1)).with_request_body(request_body(1)), - MockOutcallBuilder::new(200, response_body(2)).with_request_body(request_body(2)), + MockOutcallBuilder::new(200, get_recent_prioritization_fees_response(0)) + .with_request_body(request_body(0)), + MockOutcallBuilder::new(200, get_recent_prioritization_fees_response(1)) + .with_request_body(request_body(1)), + MockOutcallBuilder::new(200, get_recent_prioritization_fees_response(2)) + .with_request_body(request_body(2)), ]) .build() .get_recent_prioritization_fees(&[USDC_PUBLIC_KEY]) @@ -1150,29 +496,93 @@ mod send_transaction_tests { #[tokio::test] async fn should_send_transaction() { let setup = Setup::new().await.with_mock_api_keys().await; - let signature = "2vC221MDR312jrFzh5TRnMfUCHrCiG4cBuzHmagdgrQSsdLHaq65uJVLCWmubw4FkBDUxhRpQma785MpMwRS6ob7"; + for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { + let client = setup.client().with_rpc_sources(sources); + + let results = client + .mock_sequential_json_rpc_responses::<3>( + 200, + send_transaction_response(first_id, some_signature()), + ) + .build() + .send_transaction(some_transaction()) + .send() + .await + .expect_consistent(); + + assert_eq!(results, Ok(some_signature())); + } + setup.drop().await; + } +} + +mod get_transaction_tests { + use super::*; + + #[tokio::test] + async fn should_get_transaction() { + let setup = Setup::new().await.with_mock_api_keys().await; for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { let client = setup.client().with_rpc_sources(sources); let results = client - .mock_sequential_json_rpc_responses::<3>( - 200, - json!({ - "id": Id::from(ConstantSizeId::from(first_id)), - "jsonrpc": "2.0", - "result": signature - }), - ) + .mock_sequential_json_rpc_responses::<3>(200, get_transaction_response(first_id)) .build() - .send_transaction(some_transaction()) + .get_transaction(some_signature()) + .with_encoding(GetTransactionEncoding::Base64) .send() .await .expect_consistent(); assert_eq!( results, - Ok(solana_signature::Signature::from_str(signature).unwrap()) + Ok(Some(EncodedConfirmedTransactionWithStatusMeta { + slot: 369_139_986, + transaction: EncodedTransactionWithStatusMeta { + transaction: EncodedTransaction::Binary("ARAJPXmph5xbnfO74gv8tBIwTA0yw0BuRZvqrr113O9BTj0T4kXejUz3jh1RCasjsZkr2do/ZjMIOg56TTvRlQgBAAMGDEiA3o3u6XvTb57cHKZkhrHuNhISrOgMMafRPe48Q4QgJhAewgMolkoyq6sTbFQFuR86447k9ky2veh5uGg40kK5Pth9DxkikievxiovoyrY6lRfLhWKUZINPu2s+AlMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAMGkhusDr3enQhfGliLPnjUOYbtCSz9fET+Twnd+37hJkr+3Zt+dBsrfJ0eCM1bDr9NITRuvFbzpE4a9q1ZEXggDBAAFAqQBAAAFAgACqAELVaozzA/wZnC9ckuJIt1EqfSq6QAzzGYyZzOAmQEAAHF0Ee4i3YhEjwv/FswzZpkBBxEiM0RVZneImaq7zN3u/wCqVTPMZpkSNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8AxIgN6N7ul702+e3BymZIYDAgABDAIAAADoAwAAAAAAAA==".to_string(), TransactionBinaryEncoding::Base64), + meta: Some(UiTransactionStatusMeta { + err: None, + status: Ok(()), + fee: 5000_u64, + pre_balances: vec![ + 463360320850, + 6608068, + 2060160, + 1, + 1, + 1141440 + ], + post_balances: vec![ + 463360314850, + 6609068, + 2060160, + 1, + 1, + 1141440 + ], + inner_instructions: Some(vec![]).into(), + log_messages: Some(vec![ + "Program ComputeBudget111111111111111111111111111111 invoke [1]".to_string(), + "Program ComputeBudget111111111111111111111111111111 success".to_string(), + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS invoke [1]".to_string(), + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS consumed 110 of 270 compute units".to_string(), + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS success".to_string(), + "Program 11111111111111111111111111111111 invoke [1]".to_string(), + "Program 11111111111111111111111111111111 success".to_string() + ]).into(), + pre_token_balances: Some(vec![]).into(), + post_token_balances: Some(vec![]).into(), + rewards: Some(vec![]).into(), + loaded_addresses: Some(UiLoadedAddresses::default()).into(), + return_data: OptionSerializer::Skip, + compute_units_consumed: Some(410_u64).into(), + cost_units: Some(2084_u64).into(), + }), + version: None, + }, + block_time: Some(1_758_792_475), + })) ); } @@ -1328,7 +738,7 @@ mod generic_request_tests { json!({ "id": Id::from(ConstantSizeId::ZERO), "jsonrpc": "2.0", - "result": serde_json::Value::from_str(MOCK_RESPONSE_RESULT).unwrap() + "result": Value::from_str(MOCK_RESPONSE_RESULT).unwrap() }), ) .build() @@ -1539,8 +949,23 @@ mod canister_upgrade_tests { } } -fn get_version_request() -> serde_json::Value { - json!({"jsonrpc": "2.0", "id": Id::from(ConstantSizeId::ZERO), "method": "getVersion"}) +fn get_version_request() -> Value { + get_version_request_with_id(0) +} + +fn get_version_request_with_id(id: u8) -> Value { + json!({"jsonrpc": "2.0", "id": Id::from(ConstantSizeId::from(id)), "method": "getVersion"}) +} + +fn get_version_response(id: u8) -> Value { + json!({ + "jsonrpc": "2.0", + "result": { + "feature-set": 3640012085_u64, + "solana-core": "2.3.6" + }, + "id": Id::from(ConstantSizeId::from(id)) + }) } fn rpc_sources() -> Vec { @@ -1686,8 +1111,8 @@ mod cycles_cost_tests { PocketIcRuntime<'_>, Config, Params, - sol_rpc_types::MultiRpcResult, - sol_rpc_types::MultiRpcResult, + MultiRpcResult, + MultiRpcResult, >, expected_cycles_cost: u128, ) where @@ -1695,8 +1120,7 @@ mod cycles_cost_tests { Params: CandidType + Clone + Send, CandidOutput: CandidType + DeserializeOwned, Output: Debug, - sol_rpc_types::MultiRpcResult: - Into>, + MultiRpcResult: Into>, { let five_percents = 5_u8; @@ -1848,6 +1272,309 @@ mod cycles_cost_tests { } } +mod rpc_config_tests { + use super::*; + use sol_rpc_int_tests::json_rpc_sequential_id; + + #[tokio::test] + async fn should_respect_response_size_estimate() { + async fn check(setup: &Setup, request: F) + where + F: Fn( + SolRpcClient>, + ) -> RequestBuilder< + PocketIcRuntime<'_>, + Config, + Params, + MultiRpcResult, + MultiRpcResult, + >, + Config: CandidType + Clone + Send + SolRpcConfig, + Params: CandidType + Clone + Send, + CandidOutput: CandidType + DeserializeOwned, + Output: Debug + PartialEq, + MultiRpcResult: Into>, + { + // Expect multiple retries with increasingly larger values of `max_response_bytes` + let client = setup + .client() + .with_rpc_sources(RpcSources::Custom(vec![RpcSource::Supported( + SupportedRpcProviderId::AlchemyMainnet, + )])) + .mock_http_sequence(vec![ + MockOutcallBuilder::new_error( + RejectionCode::SysFatal, + "Http body exceeds size limit of 2000000 bytes.", + ); + 12 + ]) + .build(); + let result = request(client) + .with_response_size_estimate(1) + .with_cycles(1_000_000_000_000) + .send() + .await; + assert_eq!( + result, + MultiRpcResult::Consistent(Err(RpcError::HttpOutcallError( + HttpOutcallError::IcError { + code: RejectionCode::SysFatal, + message: "Http body exceeds size limit of 2000000 bytes.".to_string() + } + ))) + ); + + // Expect no retries because `max_response_size` is already 2_000_000 + let client = setup + .client() + .with_rpc_sources(RpcSources::Custom(vec![RpcSource::Supported( + SupportedRpcProviderId::AlchemyMainnet, + )])) + .mock_http_once(MockOutcallBuilder::new_error( + RejectionCode::SysFatal, + "Http body exceeds size limit of 2000000 bytes.", + )) + .build(); + let result = request(client) + .with_response_size_estimate(2_000_000) + .with_cycles(1_000_000_000_000) + .send() + .await; + assert_eq!( + result, + MultiRpcResult::Consistent(Err(RpcError::HttpOutcallError( + HttpOutcallError::IcError { + code: RejectionCode::SysFatal, + message: "Http body exceeds size limit of 2000000 bytes.".to_string() + } + ))) + ); + } + + let setup = Setup::new().await.with_mock_api_keys().await; + for endpoint in SolRpcEndpoint::iter() { + match endpoint { + SolRpcEndpoint::GetAccountInfo => { + check(&setup, |client| client.get_account_info(USDC_PUBLIC_KEY)).await; + } + SolRpcEndpoint::GetBalance => { + check(&setup, |client| client.get_balance(USDC_PUBLIC_KEY)).await; + } + SolRpcEndpoint::GetBlock => check(&setup, |client| client.get_block(577996)).await, + SolRpcEndpoint::GetRecentPrioritizationFees => { + check(&setup, |client| { + client.get_recent_prioritization_fees(&[]).unwrap() + }) + .await; + } + SolRpcEndpoint::GetSignaturesForAddress => { + check(&setup, |client| { + client.get_signatures_for_address(USDC_PUBLIC_KEY) + }) + .await; + } + SolRpcEndpoint::GetSignatureStatuses => { + check(&setup, |client| { + client.get_signature_statuses(&[some_signature()]).unwrap() + }) + .await; + } + SolRpcEndpoint::GetSlot => { + check(&setup, |client| client.get_slot()).await; + } + SolRpcEndpoint::GetTokenAccountBalance => { + check(&setup, |client| { + client.get_token_account_balance(USDC_PUBLIC_KEY) + }) + .await; + } + SolRpcEndpoint::GetTransaction => { + check(&setup, |client| client.get_transaction(some_signature())).await; + } + SolRpcEndpoint::JsonRequest => { + check(&setup, |client| client.json_request(get_version_request())).await; + } + SolRpcEndpoint::SendTransaction => { + check(&setup, |client| client.send_transaction(some_transaction())).await + } + } + } + + setup.drop().await; + } + + #[tokio::test] + async fn should_respect_response_strategy() { + async fn check( + setup: &Setup, + request: F, + ok_result: Value, + ) where + F: Fn( + SolRpcClient>, + ) -> RequestBuilder< + PocketIcRuntime<'_>, + Config, + Params, + MultiRpcResult, + MultiRpcResult, + >, + Config: CandidType + Clone + Send + SolRpcConfig, + Params: CandidType + Clone + Send, + CandidOutput: CandidType + DeserializeOwned, + Output: Debug + PartialEq, + MultiRpcResult: Into>, + { + let [ok_result_0, ok_result_1, _, ok_result_3, ok_result_4] = + json_rpc_sequential_id(ok_result); + + let client = setup + .client() + .with_rpc_sources(RpcSources::Custom(vec![ + RpcSource::Supported(SupportedRpcProviderId::AlchemyMainnet), + RpcSource::Supported(SupportedRpcProviderId::AnkrMainnet), + RpcSource::Supported(SupportedRpcProviderId::PublicNodeMainnet), + ])) + .mock_http_sequence(vec![ + MockOutcallBuilder::new(200, ok_result_0), + MockOutcallBuilder::new(200, ok_result_1), + MockOutcallBuilder::new_error(RejectionCode::SysFatal, "Some error!"), + ]) + .build(); + + let result = request(client.clone()) + .with_response_consensus(ConsensusStrategy::Equality) + .send() + .await; + assert_matches!(result, MultiRpcResult::Inconsistent(_)); + + let client = setup + .client() + .with_rpc_sources(RpcSources::Custom(vec![ + RpcSource::Supported(SupportedRpcProviderId::AlchemyMainnet), + RpcSource::Supported(SupportedRpcProviderId::AnkrMainnet), + RpcSource::Supported(SupportedRpcProviderId::PublicNodeMainnet), + ])) + .mock_http_sequence(vec![ + MockOutcallBuilder::new(200, ok_result_3), + MockOutcallBuilder::new(200, ok_result_4), + MockOutcallBuilder::new_error(RejectionCode::SysFatal, "Some error!"), + ]) + .build(); + + let result = request(client) + .with_response_consensus(ConsensusStrategy::Threshold { + total: Some(3), + min: 2, + }) + .send() + .await; + assert_matches!(result, MultiRpcResult::Consistent(_)); + } + + let setup = Setup::new().await.with_mock_api_keys().await; + for endpoint in SolRpcEndpoint::iter() { + match endpoint { + SolRpcEndpoint::GetAccountInfo => { + check( + &setup, + |client| client.get_account_info(USDC_PUBLIC_KEY), + get_account_info_response(0), + ) + .await; + } + SolRpcEndpoint::GetBalance => { + check( + &setup, + |client| client.get_balance(USDC_PUBLIC_KEY), + get_balance_response(6), + ) + .await; + } + SolRpcEndpoint::GetBlock => { + check( + &setup, + |client| client.get_block(577996), + get_block_response(12), + ) + .await + } + SolRpcEndpoint::GetRecentPrioritizationFees => { + check( + &setup, + |client| { + client + .get_recent_prioritization_fees(&[]) + .unwrap() + .with_max_slot_rounding_error(10) + .with_max_length(NonZeroU8::new(5).unwrap()) + }, + get_recent_prioritization_fees_response(18), + ) + .await; + } + SolRpcEndpoint::GetSignaturesForAddress => { + check( + &setup, + |client| { + client + .get_signatures_for_address(USDC_PUBLIC_KEY) + .with_limit(GetSignaturesForAddressLimit::try_from(5).unwrap()) + }, + get_signatures_for_address_response(24), + ) + .await; + } + SolRpcEndpoint::GetSignatureStatuses => { + check( + &setup, + |client| client.get_signature_statuses(&[some_signature()]).unwrap(), + get_signature_statuses_response(30), + ) + .await; + } + SolRpcEndpoint::GetSlot => { + check(&setup, |client| client.get_slot(), get_slot_response(36)).await; + } + SolRpcEndpoint::GetTokenAccountBalance => { + check( + &setup, + |client| client.get_token_account_balance(USDC_PUBLIC_KEY), + get_token_account_balance_response(42), + ) + .await; + } + SolRpcEndpoint::GetTransaction => { + check( + &setup, + |client| client.get_transaction(some_signature()), + get_transaction_response(48), + ) + .await; + } + SolRpcEndpoint::JsonRequest => { + check( + &setup, + |client| client.json_request(get_version_request_with_id(54)), + get_version_response(54), + ) + .await; + } + SolRpcEndpoint::SendTransaction => { + check( + &setup, + |client| client.send_transaction(some_transaction()), + send_transaction_response(60, some_signature()), + ) + .await + } + } + } + + setup.drop().await; + } +} + mod get_balance_tests { use super::*; @@ -1855,8 +1582,8 @@ mod get_balance_tests { async fn should_get_balance() { fn request_body(id: u8) -> serde_json::Value { json!({ - "jsonrpc": "2.0", "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", "method": "getBalance", "params": [ USDC_PUBLIC_KEY.to_string(), @@ -1868,29 +1595,17 @@ mod get_balance_tests { }) } - fn response_body(id: u8) -> serde_json::Value { - json!({ - "id": Id::from(ConstantSizeId::from(id)), - "jsonrpc": "2.0", - "result": { - // context should be filtered out by transform - "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, - "value": 389086612571_u64 - }, - }) - } let setup = Setup::new().await.with_mock_api_keys().await; - for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { let client = setup.client().with_rpc_sources(sources); let results = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(first_id)) + MockOutcallBuilder::new(200, get_balance_response(first_id)) .with_request_body(request_body(first_id)), - MockOutcallBuilder::new(200, response_body(first_id + 1)) + MockOutcallBuilder::new(200, get_balance_response(first_id + 1)) .with_request_body(request_body(first_id + 1)), - MockOutcallBuilder::new(200, response_body(first_id + 2)) + MockOutcallBuilder::new(200, get_balance_response(first_id + 2)) .with_request_body(request_body(first_id + 2)), ]) .build() @@ -1913,7 +1628,7 @@ mod get_token_account_balance_tests { #[tokio::test] async fn should_get_token_account_balance() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { json!({ "jsonrpc": "2.0", "id": Id::from(ConstantSizeId::from(id)), @@ -1927,34 +1642,17 @@ mod get_token_account_balance_tests { }) } - fn response_body(id: u8) -> serde_json::Value { - json!({ - "id": Id::from(ConstantSizeId::from(id)), - "jsonrpc": "2.0", - "result": { - // context should be filtered out by transform - "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, - "value": { - "amount": "9864", - "decimals": 2, - "uiAmount": 98.64, - "uiAmountString": "98.64", - } - }, - }) - } let setup = Setup::new().await.with_mock_api_keys().await; - for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { let client = setup.client().with_rpc_sources(sources); let results = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(first_id)) + MockOutcallBuilder::new(200, get_token_account_balance_response(first_id)) .with_request_body(request_body(first_id)), - MockOutcallBuilder::new(200, response_body(first_id + 1)) + MockOutcallBuilder::new(200, get_token_account_balance_response(first_id + 1)) .with_request_body(request_body(first_id + 1)), - MockOutcallBuilder::new(200, response_body(first_id + 2)) + MockOutcallBuilder::new(200, get_token_account_balance_response(first_id + 2)) .with_request_body(request_body(first_id + 2)), ]) .build() @@ -1984,7 +1682,7 @@ mod get_signature_statuses_tests { #[tokio::test] async fn should_get_signature_statuses() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { json!({ "jsonrpc": "2.0", "id": Id::from(ConstantSizeId::from(id)), @@ -1998,40 +1696,17 @@ mod get_signature_statuses_tests { }) } - fn response_body(id: u8) -> serde_json::Value { - json!({ - "id": Id::from(ConstantSizeId::from(id)), - "jsonrpc": "2.0", - "result": { - // context should be filtered out by transform - "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, - "value": [ - { - "slot": 48, - // confirmations should be filtered out by transform - "confirmations": id, - "err": null, - "status": { "Ok": null }, - "confirmationStatus": "finalized" - }, - null - ] - }, - }) - } - let setup = Setup::new().await.with_mock_api_keys().await; - for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { let client = setup.client().with_rpc_sources(sources); let results = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(first_id)) + MockOutcallBuilder::new(200, get_signature_statuses_response(first_id)) .with_request_body(request_body(first_id)), - MockOutcallBuilder::new(200, response_body(first_id + 1)) + MockOutcallBuilder::new(200, get_signature_statuses_response(first_id + 1)) .with_request_body(request_body(first_id + 1)), - MockOutcallBuilder::new(200, response_body(first_id + 2)) + MockOutcallBuilder::new(200, get_signature_statuses_response(first_id + 2)) .with_request_body(request_body(first_id + 2)), ]) .build() @@ -2066,7 +1741,7 @@ mod get_signatures_for_address_tests { #[tokio::test] async fn should_get_signatures_for_address() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { json!({ "jsonrpc": "2.0", "id": Id::from(ConstantSizeId::from(id)), @@ -2080,69 +1755,17 @@ mod get_signatures_for_address_tests { }) } - fn response_body(id: u8) -> serde_json::Value { - json!({ - "id": Id::from(ConstantSizeId::from(id)), - "jsonrpc": "2.0", - "result": [ - { - "signature": "3jPA8CnZb9sfs4zVAypa9KB7VAGwrTdXB6mg9H1H9XpATN6Y8iek4Y21Nb9LjbrpYACbF9USV8RBWvXFFhVoQUAs", - "confirmationStatus": "finalized", - "memo": null, - "slot": 340_372_399, - "err": null, - "blockTime": 1_747_389_084, - }, - { - "signature": "3WM42nYDQAHgBWFd6SbJ3pj1AGgiTJfxXJ2d5dHu49GgqSUui5qdh64S5yLCN1cMKcLMFVKKo776GrtVhfatLqP6", - "confirmationStatus": "finalized", - "memo": null, - "slot": 340_372_399, - "err": null, - "blockTime": 1_747_389_084, - }, - { - "signature": "5iByUT1gTNXDY24hRx25YmQeebvUMD6jsNpGcu2jh1yjKmYwdo5GtRrYozyhdtdcn8SurwHq6EMp4YTpHgdansjc", - "confirmationStatus": "finalized", - "memo": null, - "slot": 340_372_399, - "err": null, - "blockTime": 1_747_389_084, - }, - { - "signature": "2Zuhxr6qMGwBrpV611Ema7pZAy1WGSkQyurTcbfyoXwFMNuziUJbM6FCyoL8WxTRG6G3fEik2wSFeN76miUeUnmJ", - "confirmationStatus": "finalized", - "memo": null, - "slot": 340_372_399, - "err": null, - "blockTime": 1_747_389_084, - }, - { - "signature": "4V1j8jZvXjcUdRoWQBRzxFVigfr61bJdHGsCFAkTm5h4z28FkrDczuTpcvwTRamiwiGm7E77EB5DKRBwG1mUEC8f", - "confirmationStatus": "finalized", - "memo": null, - "slot": 340_372_399, - "err": { - "InstructionError" : [ 3, { "Custom" : 6_001 } ], - }, - "blockTime": 1_747_389_084, - }, - ] - }) - } - let setup = Setup::new().await.with_mock_api_keys().await; - for (sources, first_id) in zip(rpc_sources(), vec![0_u8, 3, 6]) { let client = setup.client().with_rpc_sources(sources); let results = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, response_body(first_id)) + MockOutcallBuilder::new(200, get_signatures_for_address_response(first_id)) .with_request_body(request_body(first_id)), - MockOutcallBuilder::new(200, response_body(first_id + 1)) + MockOutcallBuilder::new(200, get_signatures_for_address_response(first_id + 1)) .with_request_body(request_body(first_id + 1)), - MockOutcallBuilder::new(200, response_body(first_id + 2)) + MockOutcallBuilder::new(200, get_signatures_for_address_response(first_id + 2)) .with_request_body(request_body(first_id + 2)), ]) .build() @@ -2413,17 +2036,17 @@ async fn should_log_request_and_response() { #[tokio::test] async fn should_change_default_providers_when_one_keeps_failing() { - fn request_body(id: u8) -> serde_json::Value { + fn request_body(id: u8) -> Value { let id = ConstantSizeId::from(id).to_string(); json!({ "jsonrpc": "2.0", "id": id, "method": "getSlot", "params": [null] }) } - fn response_body(id: u8) -> serde_json::Value { + fn response_body(id: u8) -> Value { let id = ConstantSizeId::from(id).to_string(); json!({ "id": id, "jsonrpc": "2.0", "result": 1200, }) } - let setup = Setup::new().await.with_mock_api_keys().await; + let setup = Setup::new().await.with_mock_api_keys().await; let client = setup.client(); let slot = client .with_consensus_strategy(ConsensusStrategy::Threshold { @@ -2517,7 +2140,7 @@ fn some_transaction() -> solana_transaction::Transaction { fn some_signature() -> solana_signature::Signature { solana_signature::Signature::from_str( - "MMNPdhf4gW6pPkAtNdJKAroAC7HjaxXLE2CWNeeDtLzYEaBYrvbNzD2TSdYMsoakyD8w88YjwypAgSUYKsU4tVb", + "KbYRTmvx4uz3xuRRGNdKyt1jBngz2TjLp9nPebT4h3LQzAG7BfYrd5pSU2xDT7dVg3EXXbZugH8XbKwiGU7Jqzw", ) .unwrap() } @@ -2528,3 +2151,823 @@ fn another_signature() -> solana_signature::Signature { ) .unwrap() } + +fn get_account_info_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": { + "context": { "apiVersion": "2.0.15", "slot": 341197053 }, + "value": { + "data": ["1234", "base58"], + "executable": false, + "lamports": 88849814690250u64, + "owner": "11111111111111111111111111111111", + "rentEpoch": 18446744073709551615u64, + "space": 0 + } + }, + }) +} + +fn get_balance_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": { + // context should be filtered out by transform + "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, + "value": 389086612571_u64 + }, + }) +} + +fn get_block_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result":{ + "blockHeight": 360854634, + "blockTime": 1744122369, + "parentSlot": 372877611, + "blockhash": "8QeCusqSTKeC23NwjTKRBDcPuEfVLtszkxbpL6mXQEp4", + "previousBlockhash": "4Pcj2yJkCYyhnWe8Ze3uK2D2EtesBxhAevweDoTcxXf3"} + }) +} + +fn get_recent_prioritization_fees_response(id: u8) -> Value { + json!({ + "jsonrpc": "2.0", + "result": [ + { + "prioritizationFee": 0, + "slot": 338225766 + }, + { + "prioritizationFee": 203228, + "slot": 338225767 + }, + { + "prioritizationFee": 110788, + "slot": 338225768 + }, + { + "prioritizationFee": 395962, + "slot": 338225769 + }, + { + "prioritizationFee": 0, + "slot": 338225770 + }, + { + "prioritizationFee": 395477, + "slot": 338225771 + }, + { + "prioritizationFee": 202136, + "slot": 338225772 + }, + { + "prioritizationFee": 0, + "slot": 338225773 + }, + { + "prioritizationFee": 0, + "slot": 338225774 + }, + { + "prioritizationFee": 0, + "slot": 338225775 + }, + { + "prioritizationFee": 2894338, + "slot": 338225776 + }, + { + "prioritizationFee": 0, + "slot": 338225777 + }, + { + "prioritizationFee": 162918, + "slot": 338225778 + }, + { + "prioritizationFee": 238785, + "slot": 338225779 + }, + { + "prioritizationFee": 10714, + "slot": 338225780 + }, + { + "prioritizationFee": 81000, + "slot": 338225781 + }, + { + "prioritizationFee": 0, + "slot": 338225782 + }, + { + "prioritizationFee": 0, + "slot": 338225783 + }, + { + "prioritizationFee": 202136, + "slot": 338225784 + }, + { + "prioritizationFee": 166667, + "slot": 338225785 + }, + { + "prioritizationFee": 166667, + "slot": 338225786 + }, + { + "prioritizationFee": 0, + "slot": 338225787 + }, + { + "prioritizationFee": 0, + "slot": 338225788 + }, + { + "prioritizationFee": 0, + "slot": 338225789 + }, + { + "prioritizationFee": 0, + "slot": 338225790 + }, + { + "prioritizationFee": 0, + "slot": 338225791 + }, + { + "prioritizationFee": 0, + "slot": 338225792 + }, + { + "prioritizationFee": 0, + "slot": 338225793 + }, + { + "prioritizationFee": 494120, + "slot": 338225794 + }, + { + "prioritizationFee": 0, + "slot": 338225795 + }, + { + "prioritizationFee": 0, + "slot": 338225796 + }, + { + "prioritizationFee": 202136, + "slot": 338225797 + }, + { + "prioritizationFee": 0, + "slot": 338225798 + }, + { + "prioritizationFee": 0, + "slot": 338225799 + }, + { + "prioritizationFee": 202136, + "slot": 338225800 + }, + { + "prioritizationFee": 0, + "slot": 338225801 + }, + { + "prioritizationFee": 0, + "slot": 338225802 + }, + { + "prioritizationFee": 10001, + "slot": 338225803 + }, + { + "prioritizationFee": 0, + "slot": 338225804 + }, + { + "prioritizationFee": 0, + "slot": 338225805 + }, + { + "prioritizationFee": 0, + "slot": 338225806 + }, + { + "prioritizationFee": 0, + "slot": 338225807 + }, + { + "prioritizationFee": 202136, + "slot": 338225808 + }, + { + "prioritizationFee": 0, + "slot": 338225809 + }, + { + "prioritizationFee": 202136, + "slot": 338225810 + }, + { + "prioritizationFee": 0, + "slot": 338225811 + }, + { + "prioritizationFee": 0, + "slot": 338225812 + }, + { + "prioritizationFee": 0, + "slot": 338225813 + }, + { + "prioritizationFee": 0, + "slot": 338225814 + }, + { + "prioritizationFee": 6064097, + "slot": 338225815 + }, + { + "prioritizationFee": 0, + "slot": 338225816 + }, + { + "prioritizationFee": 0, + "slot": 338225817 + }, + { + "prioritizationFee": 0, + "slot": 338225818 + }, + { + "prioritizationFee": 517927, + "slot": 338225819 + }, + { + "prioritizationFee": 0, + "slot": 338225820 + }, + { + "prioritizationFee": 0, + "slot": 338225821 + }, + { + "prioritizationFee": 0, + "slot": 338225822 + }, + { + "prioritizationFee": 602011, + "slot": 338225823 + }, + { + "prioritizationFee": 187015, + "slot": 338225824 + }, + { + "prioritizationFee": 50000, + "slot": 338225825 + }, + { + "prioritizationFee": 0, + "slot": 338225826 + }, + { + "prioritizationFee": 0, + "slot": 338225827 + }, + { + "prioritizationFee": 0, + "slot": 338225828 + }, + { + "prioritizationFee": 0, + "slot": 338225829 + }, + { + "prioritizationFee": 0, + "slot": 338225830 + }, + { + "prioritizationFee": 0, + "slot": 338225831 + }, + { + "prioritizationFee": 0, + "slot": 338225832 + }, + { + "prioritizationFee": 0, + "slot": 338225833 + }, + { + "prioritizationFee": 0, + "slot": 338225834 + }, + { + "prioritizationFee": 0, + "slot": 338225835 + }, + { + "prioritizationFee": 0, + "slot": 338225836 + }, + { + "prioritizationFee": 0, + "slot": 338225837 + }, + { + "prioritizationFee": 0, + "slot": 338225838 + }, + { + "prioritizationFee": 487330, + "slot": 338225839 + }, + { + "prioritizationFee": 149432, + "slot": 338225840 + }, + { + "prioritizationFee": 0, + "slot": 338225841 + }, + { + "prioritizationFee": 0, + "slot": 338225842 + }, + { + "prioritizationFee": 68526, + "slot": 338225843 + }, + { + "prioritizationFee": 0, + "slot": 338225844 + }, + { + "prioritizationFee": 310090, + "slot": 338225845 + }, + { + "prioritizationFee": 0, + "slot": 338225846 + }, + { + "prioritizationFee": 2173913, + "slot": 338225847 + }, + { + "prioritizationFee": 99725, + "slot": 338225848 + }, + { + "prioritizationFee": 0, + "slot": 338225849 + }, + { + "prioritizationFee": 88441, + "slot": 338225850 + }, + { + "prioritizationFee": 0, + "slot": 338225851 + }, + { + "prioritizationFee": 400000, + "slot": 338225852 + }, + { + "prioritizationFee": 0, + "slot": 338225853 + }, + { + "prioritizationFee": 0, + "slot": 338225854 + }, + { + "prioritizationFee": 164507, + "slot": 338225855 + }, + { + "prioritizationFee": 0, + "slot": 338225856 + }, + { + "prioritizationFee": 4898, + "slot": 338225857 + }, + { + "prioritizationFee": 0, + "slot": 338225858 + }, + { + "prioritizationFee": 0, + "slot": 338225859 + }, + { + "prioritizationFee": 142369, + "slot": 338225860 + }, + { + "prioritizationFee": 84566, + "slot": 338225861 + }, + { + "prioritizationFee": 0, + "slot": 338225862 + }, + { + "prioritizationFee": 10001, + "slot": 338225863 + }, + { + "prioritizationFee": 187015, + "slot": 338225864 + }, + { + "prioritizationFee": 8902, + "slot": 338225865 + }, + { + "prioritizationFee": 0, + "slot": 338225866 + }, + { + "prioritizationFee": 75000, + "slot": 338225867 + }, + { + "prioritizationFee": 0, + "slot": 338225868 + }, + { + "prioritizationFee": 0, + "slot": 338225869 + }, + { + "prioritizationFee": 1771477, + "slot": 338225870 + }, + { + "prioritizationFee": 1110536, + "slot": 338225871 + }, + { + "prioritizationFee": 215920, + "slot": 338225872 + }, + { + "prioritizationFee": 68408, + "slot": 338225873 + }, + { + "prioritizationFee": 0, + "slot": 338225874 + }, + { + "prioritizationFee": 260520, + "slot": 338225875 + }, + { + "prioritizationFee": 2143332, + "slot": 338225876 + }, + { + "prioritizationFee": 0, + "slot": 338225877 + }, + { + "prioritizationFee": 84168, + "slot": 338225878 + }, + { + "prioritizationFee": 0, + "slot": 338225879 + }, + { + "prioritizationFee": 0, + "slot": 338225880 + }, + { + "prioritizationFee": 501111, + "slot": 338225881 + }, + { + "prioritizationFee": 88060, + "slot": 338225882 + }, + { + "prioritizationFee": 10001, + "slot": 338225883 + }, + { + "prioritizationFee": 171521, + "slot": 338225884 + }, + { + "prioritizationFee": 0, + "slot": 338225885 + }, + { + "prioritizationFee": 6064097, + "slot": 338225886 + }, + { + "prioritizationFee": 6064097, + "slot": 338225887 + }, + { + "prioritizationFee": 0, + "slot": 338225888 + }, + { + "prioritizationFee": 7578, + "slot": 338225889 + }, + { + "prioritizationFee": 0, + "slot": 338225890 + }, + { + "prioritizationFee": 0, + "slot": 338225891 + }, + { + "prioritizationFee": 202136, + "slot": 338225892 + }, + { + "prioritizationFee": 106090, + "slot": 338225893 + }, + { + "prioritizationFee": 80776, + "slot": 338225894 + }, + { + "prioritizationFee": 111939, + "slot": 338225895 + }, + { + "prioritizationFee": 75000, + "slot": 338225896 + }, + { + "prioritizationFee": 0, + "slot": 338225897 + }, + { + "prioritizationFee": 0, + "slot": 338225898 + }, + { + "prioritizationFee": 0, + "slot": 338225899 + }, + { + "prioritizationFee": 0, + "slot": 338225900 + }, + { + "prioritizationFee": 0, + "slot": 338225901 + }, + { + "prioritizationFee": 183582, + "slot": 338225902 + }, + { + "prioritizationFee": 0, + "slot": 338225903 + }, + { + "prioritizationFee": 0, + "slot": 338225904 + }, + { + "prioritizationFee": 0, + "slot": 338225905 + }, + { + "prioritizationFee": 535775, + "slot": 338225906 + }, + { + "prioritizationFee": 65038, + "slot": 338225907 + }, + { + "prioritizationFee": 0, + "slot": 338225908 + }, + { + "prioritizationFee": 0, + "slot": 338225909 + }, + { + "prioritizationFee": 0, + "slot": 338225910 + }, + { + "prioritizationFee": 0, + "slot": 338225911 + }, + { + "prioritizationFee": 0, + "slot": 338225912 + }, + { + "prioritizationFee": 0, + "slot": 338225913 + }, + { + "prioritizationFee": 0, + "slot": 338225914 + }, + { + "prioritizationFee": 0, + "slot": 338225915 + } + ], + "id": Id::from(ConstantSizeId::from(id)) + } + ) +} + +fn get_signatures_for_address_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": [ + { + "signature": "3jPA8CnZb9sfs4zVAypa9KB7VAGwrTdXB6mg9H1H9XpATN6Y8iek4Y21Nb9LjbrpYACbF9USV8RBWvXFFhVoQUAs", + "confirmationStatus": "finalized", + "memo": null, + "slot": 340_372_399, + "err": null, + "blockTime": 1_747_389_084, + }, + { + "signature": "3WM42nYDQAHgBWFd6SbJ3pj1AGgiTJfxXJ2d5dHu49GgqSUui5qdh64S5yLCN1cMKcLMFVKKo776GrtVhfatLqP6", + "confirmationStatus": "finalized", + "memo": null, + "slot": 340_372_399, + "err": null, + "blockTime": 1_747_389_084, + }, + { + "signature": "5iByUT1gTNXDY24hRx25YmQeebvUMD6jsNpGcu2jh1yjKmYwdo5GtRrYozyhdtdcn8SurwHq6EMp4YTpHgdansjc", + "confirmationStatus": "finalized", + "memo": null, + "slot": 340_372_399, + "err": null, + "blockTime": 1_747_389_084, + }, + { + "signature": "2Zuhxr6qMGwBrpV611Ema7pZAy1WGSkQyurTcbfyoXwFMNuziUJbM6FCyoL8WxTRG6G3fEik2wSFeN76miUeUnmJ", + "confirmationStatus": "finalized", + "memo": null, + "slot": 340_372_399, + "err": null, + "blockTime": 1_747_389_084, + }, + { + "signature": "4V1j8jZvXjcUdRoWQBRzxFVigfr61bJdHGsCFAkTm5h4z28FkrDczuTpcvwTRamiwiGm7E77EB5DKRBwG1mUEC8f", + "confirmationStatus": "finalized", + "memo": null, + "slot": 340_372_399, + "err": { + "InstructionError" : [ 3, { "Custom" : 6_001 } ], + }, + "blockTime": 1_747_389_084, + }, + ] + }) +} + +fn get_signature_statuses_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": { + // context should be filtered out by transform + "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, + "value": [ + { + "slot": 48, + // confirmations should be filtered out by transform + "confirmations": id, + "err": null, + "status": { "Ok": null }, + "confirmationStatus": "finalized" + }, + null + ] + }, + }) +} + +fn get_slot_response(id: u8) -> Value { + get_slot_response_with_slot(id, 1234) +} + +fn get_slot_response_with_slot(id: u8, slot: u64) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": slot, + }) +} + +fn get_token_account_balance_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": { + // context should be filtered out by transform + "context": { "slot": 334048531 + id as u64, "apiVersion": "2.1.9" }, + "value": { + "amount": "9864", + "decimals": 2, + "uiAmount": 98.64, + "uiAmountString": "98.64", + } + }, + }) +} + +fn get_transaction_response(id: u8) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": { + "blockTime": 1758792475, + "meta": { + "computeUnitsConsumed": 410, + "costUnits": 2084, + "err": null, + "fee": 5000, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS invoke [1]", + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS consumed 110 of 270 compute units", + "Program E2uCGJ4TtYyKPGaK57UMfbs9sgaumwDEZF1aAY6fF3mS success", + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ], + "postBalances": [ + 463360314850_u64, + 6609068, + 2060160, + 1, + 1, + 1141440 + ], + "postTokenBalances": [], + "preBalances": [ + 463360320850_u64, + 6608068, + 2060160, + 1, + 1, + 1141440 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 369139986, + "transaction": [ + "ARAJPXmph5xbnfO74gv8tBIwTA0yw0BuRZvqrr113O9BTj0T4kXejUz3jh1RCasjsZkr2do/ZjMIOg56TTvRlQgBAAMGDEiA3o3u6XvTb57cHKZkhrHuNhISrOgMMafRPe48Q4QgJhAewgMolkoyq6sTbFQFuR86447k9ky2veh5uGg40kK5Pth9DxkikievxiovoyrY6lRfLhWKUZINPu2s+AlMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAMGkhusDr3enQhfGliLPnjUOYbtCSz9fET+Twnd+37hJkr+3Zt+dBsrfJ0eCM1bDr9NITRuvFbzpE4a9q1ZEXggDBAAFAqQBAAAFAgACqAELVaozzA/wZnC9ckuJIt1EqfSq6QAzzGYyZzOAmQEAAHF0Ee4i3YhEjwv/FswzZpkBBxEiM0RVZneImaq7zN3u/wCqVTPMZpkSNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8AxIgN6N7ul702+e3BymZIYDAgABDAIAAADoAwAAAAAAAA==", + "base64" + ] + }, + }) +} + +fn send_transaction_response(id: u8, transaction_signature: solana_signature::Signature) -> Value { + json!({ + "id": Id::from(ConstantSizeId::from(id)), + "jsonrpc": "2.0", + "result": transaction_signature.to_string(), + }) +} diff --git a/libs/client/src/lib.rs b/libs/client/src/lib.rs index fb3fad2a..eb23b960 100644 --- a/libs/client/src/lib.rs +++ b/libs/client/src/lib.rs @@ -146,7 +146,7 @@ use ic_cdk::api::call::RejectionCode as IcCdkRejectionCode; use ic_error_types::RejectCode; pub use request::{ EstimateBlockhashRequestBuilder, EstimateRecentBlockhashError, Request, RequestBuilder, - SolRpcEndpoint, SolRpcRequest, + SolRpcConfig, SolRpcEndpoint, SolRpcRequest, }; use serde::de::DeserializeOwned; use sol_rpc_types::{ diff --git a/libs/client/src/request/mod.rs b/libs/client/src/request/mod.rs index 749c3927..3c3c6d62 100644 --- a/libs/client/src/request/mod.rs +++ b/libs/client/src/request/mod.rs @@ -8,9 +8,9 @@ use ic_error_types::RejectCode; use serde::de::DeserializeOwned; use sol_rpc_types::{ AccountInfo, CommitmentLevel, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, - DataSlice, EncodedConfirmedTransactionWithStatusMeta, GetAccountInfoEncoding, - GetAccountInfoParams, GetBalanceParams, GetBlockCommitmentLevel, GetBlockParams, - GetRecentPrioritizationFeesParams, GetRecentPrioritizationFeesRpcConfig, + ConsensusStrategy, DataSlice, EncodedConfirmedTransactionWithStatusMeta, + GetAccountInfoEncoding, GetAccountInfoParams, GetBalanceParams, GetBlockCommitmentLevel, + GetBlockParams, GetRecentPrioritizationFeesParams, GetRecentPrioritizationFeesRpcConfig, GetSignatureStatusesParams, GetSignaturesForAddressLimit, GetSignaturesForAddressParams, GetSlotParams, GetSlotRpcConfig, GetTokenAccountBalanceParams, GetTransactionEncoding, GetTransactionParams, Lamport, MultiRpcResult, NonZeroU8, PrioritizationFee, RoundingError, @@ -783,6 +783,85 @@ impl } } +/// Common behavior for the RPC config for SOL RPC canister endpoints. +pub trait SolRpcConfig: Default { + /// Return a new RPC config with the given response size estimate. + fn with_response_size_estimate(self, response_size_estimate: u64) -> Self; + + /// Return a new RPC config with the given response consensys. + fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self; +} + +impl SolRpcConfig for RpcConfig { + fn with_response_size_estimate(self, response_size_estimate: u64) -> Self { + Self { + response_size_estimate: Some(response_size_estimate), + ..self + } + } + + fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self { + Self { + response_consensus: Some(response_consensus), + ..self + } + } +} + +impl SolRpcConfig for GetSlotRpcConfig { + fn with_response_size_estimate(self, response_size_estimate: u64) -> Self { + Self { + response_size_estimate: Some(response_size_estimate), + ..self + } + } + + fn with_response_consensus(self, response_consensus: ConsensusStrategy) -> Self { + Self { + response_consensus: Some(response_consensus), + ..self + } + } +} + +impl SolRpcConfig for GetRecentPrioritizationFeesRpcConfig { + fn with_response_size_estimate(mut self, response_size_estimate: u64) -> Self { + self.set_response_size_estimate(response_size_estimate); + self + } + + fn with_response_consensus(mut self, response_consensus: ConsensusStrategy) -> Self { + self.set_response_consensus(response_consensus); + self + } +} + +impl + RequestBuilder +{ + /// Change the response size estimate to use for that request. + pub fn with_response_size_estimate(mut self, response_size_estimate: u64) -> Self { + self.request.rpc_config = Some( + self.request + .rpc_config + .unwrap_or_default() + .with_response_size_estimate(response_size_estimate), + ); + self + } + + /// Change the consensus strategy to use for that request. + pub fn with_response_consensus(mut self, response_consensus: ConsensusStrategy) -> Self { + self.request.rpc_config = Some( + self.request + .rpc_config + .unwrap_or_default() + .with_response_consensus(response_consensus), + ); + self + } +} + impl RequestBuilder { diff --git a/libs/types/src/rpc_client/mod.rs b/libs/types/src/rpc_client/mod.rs index 11577bc8..b8767955 100644 --- a/libs/types/src/rpc_client/mod.rs +++ b/libs/types/src/rpc_client/mod.rs @@ -234,6 +234,16 @@ impl GetRecentPrioritizationFeesRpcConfig { pub fn set_max_length(&mut self, len: NonZeroU8) { self.max_length = Some(len) } + + /// Change the `response_consensus` value. + pub fn set_response_consensus(&mut self, response_consensus: ConsensusStrategy) { + self.response_consensus = Some(response_consensus); + } + + /// Change the `response_size_estimate` value. + pub fn set_response_size_estimate(&mut self, response_size_estimate: u64) { + self.response_size_estimate = Some(response_size_estimate); + } } impl From for GetRecentPrioritizationFeesRpcConfig { From 941a183fec9209af457e5fc515d1d20052f286b3 Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Wed, 8 Oct 2025 14:33:59 +0200 Subject: [PATCH 3/4] XC-502: Remove `Default` bound from `SolRpcConfig` --- integration_tests/tests/tests.rs | 35 ++++++++++++++------------------ libs/client/src/request/mod.rs | 4 ++-- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/integration_tests/tests/tests.rs b/integration_tests/tests/tests.rs index 1c41aadd..83b59635 100644 --- a/integration_tests/tests/tests.rs +++ b/integration_tests/tests/tests.rs @@ -318,11 +318,11 @@ mod get_slot_tests { let slot = client .mock_http_sequence(vec![ - MockOutcallBuilder::new(200, get_slot_response(0)) + MockOutcallBuilder::new(200, get_slot_response(0, 1234)) .with_request_body(request_body(0)), - MockOutcallBuilder::new(200, get_slot_response(1)) + MockOutcallBuilder::new(200, get_slot_response(1, 1234)) .with_request_body(request_body(1)), - MockOutcallBuilder::new(200, get_slot_response(2)) + MockOutcallBuilder::new(200, get_slot_response(2, 1234)) .with_request_body(request_body(2)), ]) .build() @@ -349,7 +349,7 @@ mod get_slot_tests { let client = setup.client().with_rpc_sources(sources); let results = client - .mock_sequential_json_rpc_responses::<3>(200, get_slot_response(first_id)) + .mock_sequential_json_rpc_responses::<3>(200, get_slot_response(first_id, 1234)) .build() .get_slot() .with_rounding_error(0) @@ -372,10 +372,7 @@ mod get_slot_tests { .into_iter() .enumerate() .map(|(id, slot)| { - MockOutcallBuilder::new( - 200, - get_slot_response_with_slot(id as u8 + first_id, slot), - ) + MockOutcallBuilder::new(200, get_slot_response(id as u8 + first_id, slot)) }) .collect(); let client = setup.client().with_rpc_sources(sources); @@ -403,10 +400,7 @@ mod get_slot_tests { .into_iter() .enumerate() .map(|(id, slot)| { - MockOutcallBuilder::new( - 200, - get_slot_response_with_slot(id as u8 + first_id, slot), - ) + MockOutcallBuilder::new(200, get_slot_response(id as u8 + first_id, slot)) }) .collect(); let client = setup.client().with_rpc_sources(sources); @@ -1289,7 +1283,7 @@ mod rpc_config_tests { MultiRpcResult, MultiRpcResult, >, - Config: CandidType + Clone + Send + SolRpcConfig, + Config: CandidType + Clone + Send + SolRpcConfig + Default, Params: CandidType + Clone + Send, CandidOutput: CandidType + DeserializeOwned, Output: Debug + PartialEq, @@ -1419,7 +1413,7 @@ mod rpc_config_tests { MultiRpcResult, MultiRpcResult, >, - Config: CandidType + Clone + Send + SolRpcConfig, + Config: CandidType + Clone + Send + SolRpcConfig + Default, Params: CandidType + Clone + Send, CandidOutput: CandidType + DeserializeOwned, Output: Debug + PartialEq, @@ -1534,7 +1528,12 @@ mod rpc_config_tests { .await; } SolRpcEndpoint::GetSlot => { - check(&setup, |client| client.get_slot(), get_slot_response(36)).await; + check( + &setup, + |client| client.get_slot(), + get_slot_response(36, 1234), + ) + .await; } SolRpcEndpoint::GetTokenAccountBalance => { check( @@ -2878,11 +2877,7 @@ fn get_signature_statuses_response(id: u8) -> Value { }) } -fn get_slot_response(id: u8) -> Value { - get_slot_response_with_slot(id, 1234) -} - -fn get_slot_response_with_slot(id: u8, slot: u64) -> Value { +fn get_slot_response(id: u8, slot: u64) -> Value { json!({ "id": Id::from(ConstantSizeId::from(id)), "jsonrpc": "2.0", diff --git a/libs/client/src/request/mod.rs b/libs/client/src/request/mod.rs index 3c3c6d62..490a3ab1 100644 --- a/libs/client/src/request/mod.rs +++ b/libs/client/src/request/mod.rs @@ -784,7 +784,7 @@ impl } /// Common behavior for the RPC config for SOL RPC canister endpoints. -pub trait SolRpcConfig: Default { +pub trait SolRpcConfig { /// Return a new RPC config with the given response size estimate. fn with_response_size_estimate(self, response_size_estimate: u64) -> Self; @@ -836,7 +836,7 @@ impl SolRpcConfig for GetRecentPrioritizationFeesRpcConfig { } } -impl +impl RequestBuilder { /// Change the response size estimate to use for that request. From 7f1f6bbbd7e93d9a475200ddc2b050f6eb7db91d Mon Sep 17 00:00:00 2001 From: Louis Pahlavi Date: Wed, 8 Oct 2025 14:46:45 +0200 Subject: [PATCH 4/4] Simplify test for `max_response_bytes` --- integration_tests/tests/tests.rs | 46 ++++++-------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/integration_tests/tests/tests.rs b/integration_tests/tests/tests.rs index 83b59635..58e8530b 100644 --- a/integration_tests/tests/tests.rs +++ b/integration_tests/tests/tests.rs @@ -10,7 +10,8 @@ use serde_json::{json, Value}; use sol_rpc_canister::constants::*; use sol_rpc_client::{RequestBuilder, SolRpcClient, SolRpcConfig, SolRpcEndpoint}; use sol_rpc_int_tests::{ - mock::MockOutcallBuilder, PocketIcRuntime, Setup, SolRpcTestClient, DEFAULT_CALLER_TEST_ID, + json_rpc_sequential_id, mock::MockOutcallBuilder, PocketIcRuntime, Setup, SolRpcTestClient, + DEFAULT_CALLER_TEST_ID, }; use sol_rpc_types::{ CommitmentLevel, ConfirmedTransactionStatusWithSignature, ConsensusStrategy, @@ -1268,7 +1269,6 @@ mod cycles_cost_tests { mod rpc_config_tests { use super::*; - use sol_rpc_int_tests::json_rpc_sequential_id; #[tokio::test] async fn should_respect_response_size_estimate() { @@ -1289,48 +1289,18 @@ mod rpc_config_tests { Output: Debug + PartialEq, MultiRpcResult: Into>, { - // Expect multiple retries with increasingly larger values of `max_response_bytes` let client = setup .client() .with_rpc_sources(RpcSources::Custom(vec![RpcSource::Supported( SupportedRpcProviderId::AlchemyMainnet, )])) - .mock_http_sequence(vec![ - MockOutcallBuilder::new_error( - RejectionCode::SysFatal, - "Http body exceeds size limit of 2000000 bytes.", - ); - 12 - ]) - .build(); - let result = request(client) - .with_response_size_estimate(1) - .with_cycles(1_000_000_000_000) - .send() - .await; - assert_eq!( - result, - MultiRpcResult::Consistent(Err(RpcError::HttpOutcallError( - HttpOutcallError::IcError { - code: RejectionCode::SysFatal, - message: "Http body exceeds size limit of 2000000 bytes.".to_string() - } - ))) - ); - - // Expect no retries because `max_response_size` is already 2_000_000 - let client = setup - .client() - .with_rpc_sources(RpcSources::Custom(vec![RpcSource::Supported( - SupportedRpcProviderId::AlchemyMainnet, - )])) - .mock_http_once(MockOutcallBuilder::new_error( - RejectionCode::SysFatal, - "Http body exceeds size limit of 2000000 bytes.", - )) + .mock_http_once( + MockOutcallBuilder::new_error(RejectionCode::SysFatal, "Unrecoverable error!") + .with_max_response_bytes(1_999_999), + ) .build(); let result = request(client) - .with_response_size_estimate(2_000_000) + .with_response_size_estimate(1_999_999) .with_cycles(1_000_000_000_000) .send() .await; @@ -1339,7 +1309,7 @@ mod rpc_config_tests { MultiRpcResult::Consistent(Err(RpcError::HttpOutcallError( HttpOutcallError::IcError { code: RejectionCode::SysFatal, - message: "Http body exceeds size limit of 2000000 bytes.".to_string() + message: "Unrecoverable error!".to_string() } ))) );