1use {
2 crate::client_error,
3 serde::{Deserialize, Deserializer, Serialize, Serializer},
4 solana_account_decoder_client_types::{token::UiTokenAmount, UiAccount},
5 solana_sdk::{
6 clock::{Epoch, Slot, UnixTimestamp},
7 fee_calculator::{FeeCalculator, FeeRateGovernor},
8 inflation::Inflation,
9 transaction::{Result, TransactionError},
10 },
11 solana_transaction_status_client_types::{
12 ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
13 UiInnerInstructions, UiTransactionReturnData,
14 },
15 std::{collections::HashMap, fmt, net::SocketAddr, str::FromStr},
16 thiserror::Error,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(untagged)]
24pub enum OptionalContext<T> {
25 Context(Response<T>),
26 NoContext(T),
27}
28
29impl<T> OptionalContext<T> {
30 pub fn parse_value(self) -> T {
31 match self {
32 Self::Context(response) => response.value,
33 Self::NoContext(value) => value,
34 }
35 }
36}
37
38pub type RpcResult<T> = client_error::Result<Response<T>>;
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct RpcResponseContext {
43 pub slot: Slot,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub api_version: Option<RpcApiVersion>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct RpcApiVersion(semver::Version);
50
51impl std::ops::Deref for RpcApiVersion {
52 type Target = semver::Version;
53 fn deref(&self) -> &Self::Target {
54 &self.0
55 }
56}
57
58impl Default for RpcApiVersion {
59 fn default() -> Self {
60 Self(solana_version::Version::default().as_semver_version())
61 }
62}
63
64impl Serialize for RpcApiVersion {
65 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
66 where
67 S: Serializer,
68 {
69 serializer.serialize_str(&self.to_string())
70 }
71}
72
73impl<'de> Deserialize<'de> for RpcApiVersion {
74 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
75 where
76 D: Deserializer<'de>,
77 {
78 let s: String = Deserialize::deserialize(deserializer)?;
79 Ok(RpcApiVersion(
80 semver::Version::from_str(&s).map_err(serde::de::Error::custom)?,
81 ))
82 }
83}
84
85impl RpcResponseContext {
86 pub fn new(slot: Slot) -> Self {
87 Self {
88 slot,
89 api_version: Some(RpcApiVersion::default()),
90 }
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
95pub struct Response<T> {
96 pub context: RpcResponseContext,
97 pub value: T,
98}
99
100#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
101#[serde(rename_all = "camelCase")]
102pub struct RpcBlockCommitment<T> {
103 pub commitment: Option<T>,
104 pub total_stake: u64,
105}
106
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
108#[serde(rename_all = "camelCase")]
109pub struct RpcBlockhashFeeCalculator {
110 pub blockhash: String,
111 pub fee_calculator: FeeCalculator,
112}
113
114#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
115#[serde(rename_all = "camelCase")]
116pub struct RpcBlockhash {
117 pub blockhash: String,
118 pub last_valid_block_height: u64,
119}
120
121#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
122#[serde(rename_all = "camelCase")]
123pub struct RpcFeeCalculator {
124 pub fee_calculator: FeeCalculator,
125}
126
127#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
128#[serde(rename_all = "camelCase")]
129pub struct RpcFeeRateGovernor {
130 pub fee_rate_governor: FeeRateGovernor,
131}
132
133#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
134#[serde(rename_all = "camelCase")]
135pub struct RpcInflationGovernor {
136 pub initial: f64,
137 pub terminal: f64,
138 pub taper: f64,
139 pub foundation: f64,
140 pub foundation_term: f64,
141}
142
143impl From<Inflation> for RpcInflationGovernor {
144 fn from(inflation: Inflation) -> Self {
145 Self {
146 initial: inflation.initial,
147 terminal: inflation.terminal,
148 taper: inflation.taper,
149 foundation: inflation.foundation,
150 foundation_term: inflation.foundation_term,
151 }
152 }
153}
154
155#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
156#[serde(rename_all = "camelCase")]
157pub struct RpcInflationRate {
158 pub total: f64,
159 pub validator: f64,
160 pub foundation: f64,
161 pub epoch: Epoch,
162}
163
164#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
165#[serde(rename_all = "camelCase")]
166pub struct RpcKeyedAccount {
167 pub pubkey: String,
168 pub account: UiAccount,
169}
170
171#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
172pub struct SlotInfo {
173 pub slot: Slot,
174 pub parent: Slot,
175 pub root: Slot,
176}
177
178#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
179#[serde(rename_all = "camelCase")]
180pub struct SlotTransactionStats {
181 pub num_transaction_entries: u64,
182 pub num_successful_transactions: u64,
183 pub num_failed_transactions: u64,
184 pub max_transactions_per_entry: u64,
185}
186
187#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
188#[serde(rename_all = "camelCase", tag = "type")]
189pub enum SlotUpdate {
190 FirstShredReceived {
191 slot: Slot,
192 timestamp: u64,
193 },
194 Completed {
195 slot: Slot,
196 timestamp: u64,
197 },
198 CreatedBank {
199 slot: Slot,
200 parent: Slot,
201 timestamp: u64,
202 },
203 Frozen {
204 slot: Slot,
205 timestamp: u64,
206 stats: SlotTransactionStats,
207 },
208 Dead {
209 slot: Slot,
210 timestamp: u64,
211 err: String,
212 },
213 OptimisticConfirmation {
214 slot: Slot,
215 timestamp: u64,
216 },
217 Root {
218 slot: Slot,
219 timestamp: u64,
220 },
221}
222
223impl SlotUpdate {
224 pub fn slot(&self) -> Slot {
225 match self {
226 Self::FirstShredReceived { slot, .. } => *slot,
227 Self::Completed { slot, .. } => *slot,
228 Self::CreatedBank { slot, .. } => *slot,
229 Self::Frozen { slot, .. } => *slot,
230 Self::Dead { slot, .. } => *slot,
231 Self::OptimisticConfirmation { slot, .. } => *slot,
232 Self::Root { slot, .. } => *slot,
233 }
234 }
235}
236
237#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
238#[serde(rename_all = "camelCase", untagged)]
239pub enum RpcSignatureResult {
240 ProcessedSignature(ProcessedSignatureResult),
241 ReceivedSignature(ReceivedSignatureResult),
242}
243
244#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
245#[serde(rename_all = "camelCase")]
246pub struct RpcLogsResponse {
247 pub signature: String, pub err: Option<TransactionError>,
249 pub logs: Vec<String>,
250}
251
252#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
253#[serde(rename_all = "camelCase")]
254pub struct ProcessedSignatureResult {
255 pub err: Option<TransactionError>,
256}
257
258#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
259#[serde(rename_all = "camelCase")]
260pub enum ReceivedSignatureResult {
261 ReceivedSignature,
262}
263
264#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
265#[serde(rename_all = "camelCase")]
266pub struct RpcContactInfo {
267 pub pubkey: String,
269 pub gossip: Option<SocketAddr>,
271 pub tvu: Option<SocketAddr>,
273 pub tpu: Option<SocketAddr>,
275 pub tpu_quic: Option<SocketAddr>,
277 pub tpu_forwards: Option<SocketAddr>,
279 pub tpu_forwards_quic: Option<SocketAddr>,
281 pub tpu_vote: Option<SocketAddr>,
283 pub serve_repair: Option<SocketAddr>,
285 pub rpc: Option<SocketAddr>,
287 pub pubsub: Option<SocketAddr>,
289 pub version: Option<String>,
291 pub feature_set: Option<u32>,
293 pub shred_version: Option<u16>,
295}
296
297pub type RpcLeaderSchedule = HashMap<String, Vec<usize>>;
299
300#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
301#[serde(rename_all = "camelCase")]
302pub struct RpcBlockProductionRange {
303 pub first_slot: Slot,
304 pub last_slot: Slot,
305}
306
307#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
308#[serde(rename_all = "camelCase")]
309pub struct RpcBlockProduction {
310 pub by_identity: HashMap<String, (usize, usize)>,
312 pub range: RpcBlockProductionRange,
313}
314
315#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
316#[serde(rename_all = "kebab-case")]
317pub struct RpcVersionInfo {
318 pub solana_core: String,
320 pub feature_set: Option<u32>,
322}
323
324impl fmt::Debug for RpcVersionInfo {
325 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
326 write!(f, "{}", self.solana_core)
327 }
328}
329
330impl fmt::Display for RpcVersionInfo {
331 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332 if let Some(version) = self.solana_core.split_whitespace().next() {
333 write!(f, "{version}")
335 } else {
336 write!(f, "{}", self.solana_core)
337 }
338 }
339}
340
341#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
342#[serde(rename_all = "kebab-case")]
343pub struct RpcIdentity {
344 pub identity: String,
346}
347
348#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
349#[serde(rename_all = "camelCase")]
350pub struct RpcVote {
351 pub vote_pubkey: String,
353 pub slots: Vec<Slot>,
354 pub hash: String,
355 pub timestamp: Option<UnixTimestamp>,
356 pub signature: String,
357}
358
359#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
360#[serde(rename_all = "camelCase")]
361pub struct RpcVoteAccountStatus {
362 pub current: Vec<RpcVoteAccountInfo>,
363 pub delinquent: Vec<RpcVoteAccountInfo>,
364}
365
366#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
367#[serde(rename_all = "camelCase")]
368pub struct RpcVoteAccountInfo {
369 pub vote_pubkey: String,
371
372 pub node_pubkey: String,
374
375 pub activated_stake: u64,
377
378 pub commission: u8,
380
381 pub epoch_vote_account: bool,
383
384 pub epoch_credits: Vec<(Epoch, u64, u64)>,
387
388 pub last_vote: u64,
390
391 pub root_slot: Slot,
393}
394
395#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
396#[serde(rename_all = "camelCase")]
397pub struct RpcSignatureConfirmation {
398 pub confirmations: usize,
399 pub status: Result<()>,
400}
401
402#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
403#[serde(rename_all = "camelCase")]
404pub struct RpcSimulateTransactionResult {
405 pub err: Option<TransactionError>,
406 pub logs: Option<Vec<String>>,
407 pub accounts: Option<Vec<Option<UiAccount>>>,
408 pub units_consumed: Option<u64>,
409 pub return_data: Option<UiTransactionReturnData>,
410 pub inner_instructions: Option<Vec<UiInnerInstructions>>,
411 pub replacement_blockhash: Option<RpcBlockhash>,
412}
413
414#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
415#[serde(rename_all = "camelCase")]
416pub struct RpcStorageTurn {
417 pub blockhash: String,
418 pub slot: Slot,
419}
420
421#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
422#[serde(rename_all = "camelCase")]
423pub struct RpcAccountBalance {
424 pub address: String,
425 pub lamports: u64,
426}
427
428#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
429#[serde(rename_all = "camelCase")]
430pub struct RpcSupply {
431 pub total: u64,
432 pub circulating: u64,
433 pub non_circulating: u64,
434 pub non_circulating_accounts: Vec<String>,
435}
436
437#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
438#[serde(rename_all = "camelCase")]
439pub enum StakeActivationState {
440 Activating,
441 Active,
442 Deactivating,
443 Inactive,
444}
445
446#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
447#[serde(rename_all = "camelCase")]
448pub struct RpcTokenAccountBalance {
449 pub address: String,
450 #[serde(flatten)]
451 pub amount: UiTokenAmount,
452}
453
454#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
455#[serde(rename_all = "camelCase")]
456pub struct RpcConfirmedTransactionStatusWithSignature {
457 pub signature: String,
458 pub slot: Slot,
459 pub err: Option<TransactionError>,
460 pub memo: Option<String>,
461 pub block_time: Option<UnixTimestamp>,
462 pub confirmation_status: Option<TransactionConfirmationStatus>,
463}
464
465#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
466#[serde(rename_all = "camelCase")]
467pub struct RpcPerfSample {
468 pub slot: Slot,
469 pub num_transactions: u64,
470 pub num_non_vote_transactions: Option<u64>,
471 pub num_slots: u64,
472 pub sample_period_secs: u16,
473}
474
475#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
476#[serde(rename_all = "camelCase")]
477pub struct RpcInflationReward {
478 pub epoch: Epoch,
479 pub effective_slot: Slot,
480 pub amount: u64, pub post_balance: u64, pub commission: Option<u8>, }
484
485#[derive(Clone, Deserialize, Serialize, Debug, Error, Eq, PartialEq)]
486pub enum RpcBlockUpdateError {
487 #[error("block store error")]
488 BlockStoreError,
489
490 #[error("unsupported transaction version ({0})")]
491 UnsupportedTransactionVersion(u8),
492}
493
494#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
495#[serde(rename_all = "camelCase")]
496pub struct RpcBlockUpdate {
497 pub slot: Slot,
498 pub block: Option<UiConfirmedBlock>,
499 pub err: Option<RpcBlockUpdateError>,
500}
501
502impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionStatusWithSignature {
503 fn from(value: ConfirmedTransactionStatusWithSignature) -> Self {
504 let ConfirmedTransactionStatusWithSignature {
505 signature,
506 slot,
507 err,
508 memo,
509 block_time,
510 } = value;
511 Self {
512 signature: signature.to_string(),
513 slot,
514 err,
515 memo,
516 block_time,
517 confirmation_status: None,
518 }
519 }
520}
521
522#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
523pub struct RpcSnapshotSlotInfo {
524 pub full: Slot,
525 pub incremental: Option<Slot>,
526}
527
528#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
529#[serde(rename_all = "camelCase")]
530pub struct RpcPrioritizationFee {
531 pub slot: Slot,
532 pub prioritization_fee: u64,
533}
534
535#[cfg(test)]
536pub mod tests {
537
538 use {super::*, serde_json::json};
539
540 #[test]
543 fn rpc_perf_sample_deserialize_old() {
544 let slot = 424;
545 let num_transactions = 2597;
546 let num_slots = 2783;
547 let sample_period_secs = 398;
548
549 let input = json!({
550 "slot": slot,
551 "numTransactions": num_transactions,
552 "numSlots": num_slots,
553 "samplePeriodSecs": sample_period_secs,
554 })
555 .to_string();
556
557 let actual: RpcPerfSample =
558 serde_json::from_str(&input).expect("Can parse RpcPerfSample from string as JSON");
559 let expected = RpcPerfSample {
560 slot,
561 num_transactions,
562 num_non_vote_transactions: None,
563 num_slots,
564 sample_period_secs,
565 };
566
567 assert_eq!(actual, expected);
568 }
569
570 #[test]
572 fn rpc_perf_sample_serializes_num_non_vote_transactions() {
573 let slot = 1286;
574 let num_transactions = 1732;
575 let num_non_vote_transactions = Some(757);
576 let num_slots = 393;
577 let sample_period_secs = 197;
578
579 let input = RpcPerfSample {
580 slot,
581 num_transactions,
582 num_non_vote_transactions,
583 num_slots,
584 sample_period_secs,
585 };
586 let actual =
587 serde_json::to_value(input).expect("Can convert RpcPerfSample into a JSON value");
588 let expected = json!({
589 "slot": slot,
590 "numTransactions": num_transactions,
591 "numNonVoteTransactions": num_non_vote_transactions,
592 "numSlots": num_slots,
593 "samplePeriodSecs": sample_period_secs,
594 });
595
596 assert_eq!(actual, expected);
597 }
598}