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, diff --git a/integration_tests/tests/tests.rs b/integration_tests/tests/tests.rs index a776dd2a..58e8530b 100644 --- a/integration_tests/tests/tests.rs +++ b/integration_tests/tests/tests.rs @@ -6,26 +6,32 @@ 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, + json_rpc_sequential_id, 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 +65,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 +73,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 +176,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 +245,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 +309,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, 1234)) + .with_request_body(request_body(0)), + MockOutcallBuilder::new(200, get_slot_response(1, 1234)) + .with_request_body(request_body(1)), + MockOutcallBuilder::new(200, get_slot_response(2, 1234)) + .with_request_body(request_body(2)), ]) .build() .get_slot() @@ -375,14 +350,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, 1234)) .build() .get_slot() .with_rounding_error(0) @@ -401,18 +369,11 @@ 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, - }), - ) + MockOutcallBuilder::new(200, get_slot_response(id as u8 + first_id, slot)) }) .collect(); let client = setup.client().with_rpc_sources(sources); @@ -436,18 +397,11 @@ 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, - }), - ) + MockOutcallBuilder::new(200, get_slot_response(id as u8 + first_id, slot)) }) .collect(); let client = setup.client().with_rpc_sources(sources); @@ -472,638 +426,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]) @@ -1144,35 +485,99 @@ mod get_recent_prioritization_fees_tests { } } -mod send_transaction_tests { +mod send_transaction_tests { + use super::*; + + #[tokio::test] + async fn should_send_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, + 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_send_transaction() { + async fn should_get_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, - 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 +733,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 +944,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 +1106,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 +1115,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 +1267,283 @@ mod cycles_cost_tests { } } +mod rpc_config_tests { + use super::*; + + #[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 + Default, + Params: CandidType + Clone + Send, + CandidOutput: CandidType + DeserializeOwned, + Output: Debug + PartialEq, + MultiRpcResult: Into>, + { + let client = setup + .client() + .with_rpc_sources(RpcSources::Custom(vec![RpcSource::Supported( + SupportedRpcProviderId::AlchemyMainnet, + )])) + .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(1_999_999) + .with_cycles(1_000_000_000_000) + .send() + .await; + assert_eq!( + result, + MultiRpcResult::Consistent(Err(RpcError::HttpOutcallError( + HttpOutcallError::IcError { + code: RejectionCode::SysFatal, + message: "Unrecoverable error!".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 + Default, + 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, 1234), + ) + .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 +1551,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 +1564,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 +1597,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 +1611,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 +1651,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 +1665,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 +1710,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 +1724,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 +2005,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 +2109,7 @@ fn some_transaction() -> solana_transaction::Transaction { fn some_signature() -> solana_signature::Signature { solana_signature::Signature::from_str( - "MMNPdhf4gW6pPkAtNdJKAroAC7HjaxXLE2CWNeeDtLzYEaBYrvbNzD2TSdYMsoakyD8w88YjwypAgSUYKsU4tVb", + "KbYRTmvx4uz3xuRRGNdKyt1jBngz2TjLp9nPebT4h3LQzAG7BfYrd5pSU2xDT7dVg3EXXbZugH8XbKwiGU7Jqzw", ) .unwrap() } @@ -2528,3 +2120,819 @@ 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, 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..490a3ab1 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 { + /// 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 {