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