1pub use solana_address::Address;
2use {
3 crate::response::RpcSimulateTransactionResult,
4 serde::{Deserialize, Serialize},
5 serde_json::{json, Value},
6 solana_clock::Slot,
7 std::fmt,
8 thiserror::Error,
9};
10
11#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
12pub enum RpcRequest {
13 Custom { method: &'static str },
14 DeregisterNode,
15 GetAccountInfo,
16 GetBalance,
17 GetBlock,
18 GetBlockHeight,
19 GetBlockProduction,
20 GetBlocks,
21 GetBlocksWithLimit,
22 GetBlockTime,
23 GetClusterNodes,
24 GetEpochInfo,
25 GetEpochSchedule,
26 GetFeeForMessage,
27 GetFirstAvailableBlock,
28 GetGenesisHash,
29 GetHealth,
30 GetIdentity,
31 GetInflationGovernor,
32 GetInflationRate,
33 GetInflationReward,
34 GetLargestAccounts,
35 GetLatestBlockhash,
36 GetLeaderSchedule,
37 GetMaxRetransmitSlot,
38 GetMaxShredInsertSlot,
39 GetMinimumBalanceForRentExemption,
40 GetMultipleAccounts,
41 GetProgramAccounts,
42 GetRecentPerformanceSamples,
43 GetRecentPrioritizationFees,
44 GetHighestSnapshotSlot,
45 GetSignaturesForAddress,
46 GetSignatureStatuses,
47 GetSlot,
48 GetSlotLeader,
49 GetSlotLeaders,
50 GetStorageTurn,
51 GetStorageTurnRate,
52 GetSlotsPerSegment,
53 GetStakeMinimumDelegation,
54 GetStoragePubkeysForSlot,
55 GetSupply,
56 GetTokenAccountBalance,
57 GetTokenAccountsByDelegate,
58 GetTokenAccountsByOwner,
59 GetTokenLargestAccounts,
60 GetTokenSupply,
61 GetTransaction,
62 GetTransactionCount,
63 GetVersion,
64 GetVoteAccounts,
65 IsBlockhashValid,
66 MinimumLedgerSlot,
67 RegisterNode,
68 RequestAirdrop,
69 SendTransaction,
70 SimulateTransaction,
71 SignVote,
72}
73
74#[allow(deprecated)]
75impl fmt::Display for RpcRequest {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 let method = match self {
78 RpcRequest::Custom { method } => method,
79 RpcRequest::DeregisterNode => "deregisterNode",
80 RpcRequest::GetAccountInfo => "getAccountInfo",
81 RpcRequest::GetBalance => "getBalance",
82 RpcRequest::GetBlock => "getBlock",
83 RpcRequest::GetBlockHeight => "getBlockHeight",
84 RpcRequest::GetBlockProduction => "getBlockProduction",
85 RpcRequest::GetBlocks => "getBlocks",
86 RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
87 RpcRequest::GetBlockTime => "getBlockTime",
88 RpcRequest::GetClusterNodes => "getClusterNodes",
89 RpcRequest::GetEpochInfo => "getEpochInfo",
90 RpcRequest::GetEpochSchedule => "getEpochSchedule",
91 RpcRequest::GetFeeForMessage => "getFeeForMessage",
92 RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
93 RpcRequest::GetGenesisHash => "getGenesisHash",
94 RpcRequest::GetHealth => "getHealth",
95 RpcRequest::GetIdentity => "getIdentity",
96 RpcRequest::GetInflationGovernor => "getInflationGovernor",
97 RpcRequest::GetInflationRate => "getInflationRate",
98 RpcRequest::GetInflationReward => "getInflationReward",
99 RpcRequest::GetLargestAccounts => "getLargestAccounts",
100 RpcRequest::GetLatestBlockhash => "getLatestBlockhash",
101 RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
102 RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
103 RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
104 RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
105 RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
106 RpcRequest::GetProgramAccounts => "getProgramAccounts",
107 RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
108 RpcRequest::GetRecentPrioritizationFees => "getRecentPrioritizationFees",
109 RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot",
110 RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
111 RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
112 RpcRequest::GetSlot => "getSlot",
113 RpcRequest::GetSlotLeader => "getSlotLeader",
114 RpcRequest::GetSlotLeaders => "getSlotLeaders",
115 RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation",
116 RpcRequest::GetStorageTurn => "getStorageTurn",
117 RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
118 RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
119 RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
120 RpcRequest::GetSupply => "getSupply",
121 RpcRequest::GetTokenAccountBalance => "getTokenAccountBalance",
122 RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
123 RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
124 RpcRequest::GetTokenSupply => "getTokenSupply",
125 RpcRequest::GetTokenLargestAccounts => "getTokenLargestAccounts",
126 RpcRequest::GetTransaction => "getTransaction",
127 RpcRequest::GetTransactionCount => "getTransactionCount",
128 RpcRequest::GetVersion => "getVersion",
129 RpcRequest::GetVoteAccounts => "getVoteAccounts",
130 RpcRequest::IsBlockhashValid => "isBlockhashValid",
131 RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
132 RpcRequest::RegisterNode => "registerNode",
133 RpcRequest::RequestAirdrop => "requestAirdrop",
134 RpcRequest::SendTransaction => "sendTransaction",
135 RpcRequest::SimulateTransaction => "simulateTransaction",
136 RpcRequest::SignVote => "signVote",
137 };
138
139 write!(f, "{method}")
140 }
141}
142
143pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
145pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
146pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
147pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
148pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
149pub const NUM_LARGEST_ACCOUNTS: usize = 20;
150pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4;
151pub const MAX_GET_SLOT_LEADERS: usize = 5000;
152
153pub const MAX_RPC_VOTE_ACCOUNT_INFO_EPOCH_CREDITS_HISTORY: usize = 5;
156
157pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
159
160impl RpcRequest {
161 pub fn build_request_json(self, id: u64, params: Value) -> Value {
162 let jsonrpc = "2.0";
163 json!({
164 "jsonrpc": jsonrpc,
165 "id": id,
166 "method": format!("{self}"),
167 "params": params,
168 })
169 }
170}
171
172#[derive(Debug)]
173#[allow(clippy::large_enum_variant)]
174pub enum RpcResponseErrorData {
175 Empty,
176 SendTransactionPreflightFailure(RpcSimulateTransactionResult),
177 NodeUnhealthy { num_slots_behind: Option<Slot> },
178}
179
180impl fmt::Display for RpcResponseErrorData {
181 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182 match self {
183 RpcResponseErrorData::SendTransactionPreflightFailure(
184 RpcSimulateTransactionResult {
185 logs: Some(logs), ..
186 },
187 ) => {
188 if logs.is_empty() {
189 Ok(())
190 } else {
191 writeln!(f, "{} log messages:", logs.len())?;
192 for log in logs {
193 writeln!(f, " {log}")?;
194 }
195 Ok(())
196 }
197 }
198 _ => Ok(()),
199 }
200 }
201}
202
203#[derive(Debug, Error)]
204#[allow(clippy::large_enum_variant)]
205pub enum RpcError {
206 #[error("RPC request error: {0}")]
207 RpcRequestError(String),
208 #[error("RPC response error {code}: {message}; {data}")]
209 RpcResponseError {
210 code: i64,
211 message: String,
212 data: RpcResponseErrorData,
213 },
214 #[error("parse error: expected {0}")]
215 ParseError(String), #[error("{0}")]
219 ForUser(String), }
221
222#[derive(Serialize, Deserialize)]
223pub enum TokenAccountsFilter {
224 Mint(Address),
225 ProgramId(Address),
226}
227
228#[cfg(test)]
229mod tests {
230 use {
231 super::*,
232 crate::config::RpcTokenAccountsFilter,
233 solana_commitment_config::{CommitmentConfig, CommitmentLevel},
234 };
235
236 #[test]
237 fn test_build_request_json() {
238 let test_request = RpcRequest::GetAccountInfo;
239 let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
240 let request = test_request.build_request_json(1, json!([addr]));
241 assert_eq!(request["method"], "getAccountInfo");
242 assert_eq!(request["params"], json!([addr]));
243
244 let test_request = RpcRequest::GetBalance;
245 let request = test_request.build_request_json(1, json!([addr]));
246 assert_eq!(request["method"], "getBalance");
247
248 let test_request = RpcRequest::GetEpochInfo;
249 let request = test_request.build_request_json(1, Value::Null);
250 assert_eq!(request["method"], "getEpochInfo");
251
252 let test_request = RpcRequest::GetLatestBlockhash;
253 let request = test_request.build_request_json(1, Value::Null);
254 assert_eq!(request["method"], "getLatestBlockhash");
255
256 let test_request = RpcRequest::GetSlot;
257 let request = test_request.build_request_json(1, Value::Null);
258 assert_eq!(request["method"], "getSlot");
259
260 let test_request = RpcRequest::GetTransactionCount;
261 let request = test_request.build_request_json(1, Value::Null);
262 assert_eq!(request["method"], "getTransactionCount");
263
264 let test_request = RpcRequest::RequestAirdrop;
265 let request = test_request.build_request_json(1, Value::Null);
266 assert_eq!(request["method"], "requestAirdrop");
267
268 let test_request = RpcRequest::SendTransaction;
269 let request = test_request.build_request_json(1, Value::Null);
270 assert_eq!(request["method"], "sendTransaction");
271
272 let test_request = RpcRequest::GetTokenLargestAccounts;
273 let request = test_request.build_request_json(1, Value::Null);
274 assert_eq!(request["method"], "getTokenLargestAccounts");
275 }
276
277 #[test]
278 fn test_build_request_json_config_options() {
279 let commitment_config = CommitmentConfig {
280 commitment: CommitmentLevel::Finalized,
281 };
282 let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
283
284 let test_request = RpcRequest::GetLatestBlockhash;
286 let request = test_request.build_request_json(1, json!([commitment_config]));
287 assert_eq!(request["params"], json!([commitment_config.clone()]));
288
289 let test_request = RpcRequest::GetBalance;
291 let request = test_request.build_request_json(1, json!([addr, commitment_config]));
292 assert_eq!(request["params"], json!([addr, commitment_config]));
293
294 let test_request = RpcRequest::GetTokenAccountsByOwner;
296 let mint = solana_pubkey::new_rand();
297 let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
298 let request = test_request
299 .build_request_json(1, json!([addr, token_account_filter, commitment_config]));
300 assert_eq!(
301 request["params"],
302 json!([addr, token_account_filter, commitment_config])
303 );
304 }
305}