solana_extra_wasm/
transaction_status.rs

1use std::fmt;
2
3use serde_json::Value;
4use solana_sdk::{
5    clock::{Slot, UnixTimestamp},
6    instruction::CompiledInstruction,
7    message::{
8        v0::{self, LoadedAddresses, LoadedMessage, MessageAddressTableLookup},
9        AccountKeys, Message, MessageHeader, VersionedMessage,
10    },
11    transaction::{
12        Result as TransactionResult, TransactionError, TransactionVersion, VersionedTransaction,
13    },
14    transaction_context::TransactionReturnData,
15};
16use thiserror::Error;
17
18use crate::{account_decoder::parse_token::UiTokenAmount, runtime::RewardType};
19
20#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
21#[serde(rename_all = "camelCase")]
22pub enum TransactionBinaryEncoding {
23    Base58,
24    Base64,
25}
26
27#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
28#[serde(rename_all = "camelCase")]
29pub enum UiTransactionEncoding {
30    Binary, // Legacy. Retained for RPC backwards compatibility
31    Base64,
32    Base58,
33    Json,
34    JsonParsed,
35}
36
37impl UiTransactionEncoding {
38    pub fn into_binary_encoding(&self) -> Option<TransactionBinaryEncoding> {
39        match self {
40            Self::Binary | Self::Base58 => Some(TransactionBinaryEncoding::Base58),
41            Self::Base64 => Some(TransactionBinaryEncoding::Base64),
42            _ => None,
43        }
44    }
45}
46
47impl fmt::Display for UiTransactionEncoding {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        let v = serde_json::to_value(self).map_err(|_| fmt::Error)?;
50        let s = v.as_str().ok_or(fmt::Error)?;
51        write!(f, "{}", s)
52    }
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
56#[serde(rename_all = "camelCase")]
57pub struct ParsedAccount {
58    pub pubkey: String,
59    pub writable: bool,
60    pub signer: bool,
61}
62
63/// A duplicate representation of a Transaction for pretty JSON serialization
64#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct UiTransaction {
67    pub signatures: Vec<String>,
68    pub message: UiMessage,
69}
70
71/// A duplicate representation of a CompiledInstruction for pretty JSON serialization
72#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
73#[serde(rename_all = "camelCase")]
74pub struct UiCompiledInstruction {
75    pub program_id_index: u8,
76    pub accounts: Vec<u8>,
77    pub data: String,
78}
79
80impl From<&CompiledInstruction> for UiCompiledInstruction {
81    fn from(instruction: &CompiledInstruction) -> Self {
82        Self {
83            program_id_index: instruction.program_id_index,
84            accounts: instruction.accounts.clone(),
85            data: bs58::encode(instruction.data.clone()).into_string(),
86        }
87    }
88}
89
90#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
91#[serde(rename_all = "camelCase")]
92pub struct ParsedInstruction {
93    pub program: String,
94    pub program_id: String,
95    pub parsed: Value,
96}
97
98/// A partially decoded CompiledInstruction that includes explicit account addresses
99#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct UiPartiallyDecodedInstruction {
102    pub program_id: String,
103    pub accounts: Vec<String>,
104    pub data: String,
105}
106
107impl UiPartiallyDecodedInstruction {
108    fn from(instruction: &CompiledInstruction, account_keys: &AccountKeys) -> Self {
109        Self {
110            program_id: account_keys[instruction.program_id_index as usize].to_string(),
111            accounts: instruction
112                .accounts
113                .iter()
114                .map(|&i| account_keys[i as usize].to_string())
115                .collect(),
116            data: bs58::encode(instruction.data.clone()).into_string(),
117        }
118    }
119}
120
121#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase", untagged)]
123pub enum UiParsedInstruction {
124    Parsed(ParsedInstruction),
125    PartiallyDecoded(UiPartiallyDecodedInstruction),
126}
127
128#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
129#[serde(rename_all = "camelCase")]
130pub enum ParsableProgram {
131    SplAssociatedTokenAccount,
132    SplMemo,
133    SplToken,
134    BpfLoader,
135    BpfUpgradeableLoader,
136    Stake,
137    System,
138    Vote,
139}
140
141#[derive(Error, Debug)]
142pub enum ParseInstructionError {
143    #[error("{0:?} instruction not parsable")]
144    InstructionNotParsable(ParsableProgram),
145
146    #[error("{0:?} instruction key mismatch")]
147    InstructionKeyMismatch(ParsableProgram),
148
149    #[error("Program not parsable")]
150    ProgramNotParsable,
151
152    #[error("Internal error, please report")]
153    SerdeJsonError(#[from] serde_json::error::Error),
154}
155
156// TODO:
157// lazy_static! {
158//     static ref ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey = spl_associated_token_id();
159//     static ref BPF_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader::id();
160//     static ref BPF_UPGRADEABLE_LOADER_PROGRAM_ID: Pubkey = solana_sdk::bpf_loader_upgradeable::id();
161//     static ref MEMO_V1_PROGRAM_ID: Pubkey = spl_memo_id_v1();
162//     static ref MEMO_V3_PROGRAM_ID: Pubkey = spl_memo_id_v3();
163//     static ref STAKE_PROGRAM_ID: Pubkey = stake::program::id();
164//     static ref SYSTEM_PROGRAM_ID: Pubkey = system_program::id();
165//     static ref VOTE_PROGRAM_ID: Pubkey = solana_vote_program::id();
166//     static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableProgram> = {
167//         let mut m = HashMap::new();
168//         m.insert(
169//             *ASSOCIATED_TOKEN_PROGRAM_ID,
170//             ParsableProgram::SplAssociatedTokenAccount,
171//         );
172//         m.insert(*MEMO_V1_PROGRAM_ID, ParsableProgram::SplMemo);
173//         m.insert(*MEMO_V3_PROGRAM_ID, ParsableProgram::SplMemo);
174//         for spl_token_id in spl_token_ids() {
175//             m.insert(spl_token_id, ParsableProgram::SplToken);
176//         }
177//         m.insert(*BPF_LOADER_PROGRAM_ID, ParsableProgram::BpfLoader);
178//         m.insert(
179//             *BPF_UPGRADEABLE_LOADER_PROGRAM_ID,
180//             ParsableProgram::BpfUpgradeableLoader,
181//         );
182//         m.insert(*STAKE_PROGRAM_ID, ParsableProgram::Stake);
183//         m.insert(*SYSTEM_PROGRAM_ID, ParsableProgram::System);
184//         m.insert(*VOTE_PROGRAM_ID, ParsableProgram::Vote);
185//         m
186//     };
187// }
188
189// pub fn parse(
190//     program_id: &Pubkey,
191//     instruction: &CompiledInstruction,
192//     account_keys: &AccountKeys,
193// ) -> Result<ParsedInstruction, ParseInstructionError> {
194//     let program_name = PARSABLE_PROGRAM_IDS
195//         .get(program_id)
196//         .ok_or(ParseInstructionError::ProgramNotParsable)?;
197//     let parsed_json = match program_name {
198//         ParsableProgram::SplAssociatedTokenAccount => {
199//             serde_json::to_value(parse_associated_token(instruction, account_keys)?)?
200//         }
201//         ParsableProgram::SplMemo => parse_memo(instruction)?,
202//         ParsableProgram::SplToken => serde_json::to_value(parse_token(instruction, account_keys)?)?,
203//         ParsableProgram::BpfLoader => {
204//             serde_json::to_value(parse_bpf_loader(instruction, account_keys)?)?
205//         }
206//         ParsableProgram::BpfUpgradeableLoader => {
207//             serde_json::to_value(parse_bpf_upgradeable_loader(instruction, account_keys)?)?
208//         }
209//         ParsableProgram::Stake => serde_json::to_value(parse_stake(instruction, account_keys)?)?,
210//         ParsableProgram::System => serde_json::to_value(parse_system(instruction, account_keys)?)?,
211//         ParsableProgram::Vote => serde_json::to_value(parse_vote(instruction, account_keys)?)?,
212//     };
213//     Ok(ParsedInstruction {
214//         program: format!("{:?}", program_name).to_kebab_case(),
215//         program_id: program_id.to_string(),
216//         parsed: parsed_json,
217//     })
218// }
219
220/// A duplicate representation of an Instruction for pretty JSON serialization
221#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
222#[serde(rename_all = "camelCase", untagged)]
223pub enum UiInstruction {
224    Compiled(UiCompiledInstruction),
225    Parsed(UiParsedInstruction),
226}
227
228impl UiInstruction {
229    fn parse(instruction: &CompiledInstruction, account_keys: &AccountKeys) -> Self {
230        // TODO:
231        // let program_id = &account_keys[instruction.program_id_index as usize];
232        // if let Ok(parsed_instruction) = parse(program_id, instruction, account_keys) {
233        //     UiInstruction::Parsed(UiParsedInstruction::Parsed(parsed_instruction))
234        // } else {
235        //     UiInstruction::Parsed(UiParsedInstruction::PartiallyDecoded(
236        //         UiPartiallyDecodedInstruction::from(instruction, account_keys),
237        //     ))
238        // }
239
240        UiInstruction::Parsed(UiParsedInstruction::PartiallyDecoded(
241            UiPartiallyDecodedInstruction::from(instruction, account_keys),
242        ))
243    }
244}
245
246/// A duplicate representation of a MessageAddressTableLookup, in raw format, for pretty JSON serialization
247#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
248#[serde(rename_all = "camelCase")]
249pub struct UiAddressTableLookup {
250    pub account_key: String,
251    pub writable_indexes: Vec<u8>,
252    pub readonly_indexes: Vec<u8>,
253}
254
255impl From<&MessageAddressTableLookup> for UiAddressTableLookup {
256    fn from(lookup: &MessageAddressTableLookup) -> Self {
257        Self {
258            account_key: lookup.account_key.to_string(),
259            writable_indexes: lookup.writable_indexes.clone(),
260            readonly_indexes: lookup.readonly_indexes.clone(),
261        }
262    }
263}
264
265/// A duplicate representation of a Message, in parsed format, for pretty JSON serialization
266#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
267#[serde(rename_all = "camelCase")]
268pub struct UiParsedMessage {
269    pub account_keys: Vec<ParsedAccount>,
270    pub recent_blockhash: String,
271    pub instructions: Vec<UiInstruction>,
272    pub address_table_lookups: Option<Vec<UiAddressTableLookup>>,
273}
274
275/// A duplicate representation of a Message, in raw format, for pretty JSON serialization
276#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct UiRawMessage {
279    pub header: MessageHeader,
280    pub account_keys: Vec<String>,
281    pub recent_blockhash: String,
282    pub instructions: Vec<UiCompiledInstruction>,
283    #[serde(default, skip_serializing_if = "Option::is_none")]
284    pub address_table_lookups: Option<Vec<UiAddressTableLookup>>,
285}
286
287#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase", untagged)]
289pub enum UiMessage {
290    Parsed(UiParsedMessage),
291    Raw(UiRawMessage),
292}
293
294#[derive(Clone, Debug, PartialEq)]
295pub struct TransactionStatusMeta {
296    pub status: TransactionResult<()>,
297    pub fee: u64,
298    pub pre_balances: Vec<u64>,
299    pub post_balances: Vec<u64>,
300    pub inner_instructions: Option<Vec<InnerInstructions>>,
301    pub log_messages: Option<Vec<String>>,
302    pub pre_token_balances: Option<Vec<TransactionTokenBalance>>,
303    pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
304    pub rewards: Option<Rewards>,
305    pub loaded_addresses: LoadedAddresses,
306    pub return_data: Option<TransactionReturnData>,
307}
308
309impl Default for TransactionStatusMeta {
310    fn default() -> Self {
311        Self {
312            status: Ok(()),
313            fee: 0,
314            pre_balances: vec![],
315            post_balances: vec![],
316            inner_instructions: None,
317            log_messages: None,
318            pre_token_balances: None,
319            post_token_balances: None,
320            rewards: None,
321            loaded_addresses: LoadedAddresses::default(),
322            return_data: None,
323        }
324    }
325}
326
327pub fn parse_accounts(message: &Message) -> Vec<ParsedAccount> {
328    let mut accounts: Vec<ParsedAccount> = vec![];
329    for (i, account_key) in message.account_keys.iter().enumerate() {
330        accounts.push(ParsedAccount {
331            pubkey: account_key.to_string(),
332            writable: message.is_writable(i),
333            signer: message.is_signer(i),
334        });
335    }
336    accounts
337}
338
339pub fn parse_static_accounts(message: &LoadedMessage) -> Vec<ParsedAccount> {
340    let mut accounts: Vec<ParsedAccount> = vec![];
341    for (i, account_key) in message.static_account_keys().iter().enumerate() {
342        accounts.push(ParsedAccount {
343            pubkey: account_key.to_string(),
344            writable: message.is_writable(i),
345            signer: message.is_signer(i),
346        });
347    }
348    accounts
349}
350
351/// Represents types that can be encoded into one of several encoding formats
352pub trait Encodable {
353    type Encoded;
354    fn encode(&self, encoding: UiTransactionEncoding) -> Self::Encoded;
355}
356
357impl Encodable for Message {
358    type Encoded = UiMessage;
359    fn encode(&self, encoding: UiTransactionEncoding) -> Self::Encoded {
360        if encoding == UiTransactionEncoding::JsonParsed {
361            let account_keys = AccountKeys::new(&self.account_keys, None);
362            UiMessage::Parsed(UiParsedMessage {
363                account_keys: parse_accounts(self),
364                recent_blockhash: self.recent_blockhash.to_string(),
365                instructions: self
366                    .instructions
367                    .iter()
368                    .map(|instruction| UiInstruction::parse(instruction, &account_keys))
369                    .collect(),
370                address_table_lookups: None,
371            })
372        } else {
373            UiMessage::Raw(UiRawMessage {
374                header: self.header,
375                account_keys: self.account_keys.iter().map(ToString::to_string).collect(),
376                recent_blockhash: self.recent_blockhash.to_string(),
377                instructions: self.instructions.iter().map(Into::into).collect(),
378                address_table_lookups: None,
379            })
380        }
381    }
382}
383
384impl EncodableWithMeta for v0::Message {
385    type Encoded = UiMessage;
386    fn encode_with_meta(
387        &self,
388        encoding: UiTransactionEncoding,
389        meta: &TransactionStatusMeta,
390    ) -> Self::Encoded {
391        if encoding == UiTransactionEncoding::JsonParsed {
392            let account_keys = AccountKeys::new(&self.account_keys, Some(&meta.loaded_addresses));
393            let loaded_message = LoadedMessage::new_borrowed(self, &meta.loaded_addresses);
394            UiMessage::Parsed(UiParsedMessage {
395                account_keys: parse_static_accounts(&loaded_message),
396                recent_blockhash: self.recent_blockhash.to_string(),
397                instructions: self
398                    .instructions
399                    .iter()
400                    .map(|instruction| UiInstruction::parse(instruction, &account_keys))
401                    .collect(),
402                address_table_lookups: Some(
403                    self.address_table_lookups.iter().map(Into::into).collect(),
404                ),
405            })
406        } else {
407            self.json_encode()
408        }
409    }
410    fn json_encode(&self) -> Self::Encoded {
411        UiMessage::Raw(UiRawMessage {
412            header: self.header,
413            account_keys: self.account_keys.iter().map(ToString::to_string).collect(),
414            recent_blockhash: self.recent_blockhash.to_string(),
415            instructions: self.instructions.iter().map(Into::into).collect(),
416            address_table_lookups: Some(
417                self.address_table_lookups.iter().map(Into::into).collect(),
418            ),
419        })
420    }
421}
422
423/// Represents types that can be encoded into one of several encoding formats
424pub trait EncodableWithMeta {
425    type Encoded;
426    fn encode_with_meta(
427        &self,
428        encoding: UiTransactionEncoding,
429        meta: &TransactionStatusMeta,
430    ) -> Self::Encoded;
431    fn json_encode(&self) -> Self::Encoded;
432}
433
434impl EncodableWithMeta for VersionedTransaction {
435    type Encoded = EncodedTransaction;
436    fn encode_with_meta(
437        &self,
438        encoding: UiTransactionEncoding,
439        meta: &TransactionStatusMeta,
440    ) -> Self::Encoded {
441        match encoding {
442            UiTransactionEncoding::Binary => EncodedTransaction::LegacyBinary(
443                bs58::encode(bincode::serialize(self).unwrap()).into_string(),
444            ),
445            UiTransactionEncoding::Base58 => EncodedTransaction::Binary(
446                bs58::encode(bincode::serialize(self).unwrap()).into_string(),
447                TransactionBinaryEncoding::Base58,
448            ),
449            UiTransactionEncoding::Base64 => EncodedTransaction::Binary(
450                base64::encode(bincode::serialize(self).unwrap()),
451                TransactionBinaryEncoding::Base64,
452            ),
453            UiTransactionEncoding::Json => self.json_encode(),
454            UiTransactionEncoding::JsonParsed => EncodedTransaction::Json(UiTransaction {
455                signatures: self.signatures.iter().map(ToString::to_string).collect(),
456                message: match &self.message {
457                    VersionedMessage::Legacy(message) => {
458                        message.encode(UiTransactionEncoding::JsonParsed)
459                    }
460                    VersionedMessage::V0(message) => {
461                        message.encode_with_meta(UiTransactionEncoding::JsonParsed, meta)
462                    }
463                },
464            }),
465        }
466    }
467    fn json_encode(&self) -> Self::Encoded {
468        EncodedTransaction::Json(UiTransaction {
469            signatures: self.signatures.iter().map(ToString::to_string).collect(),
470            message: match &self.message {
471                VersionedMessage::Legacy(message) => message.encode(UiTransactionEncoding::Json),
472                VersionedMessage::V0(message) => message.json_encode(),
473            },
474        })
475    }
476}
477
478#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
479#[serde(rename_all = "camelCase", untagged)]
480pub enum EncodedTransaction {
481    LegacyBinary(String), // Old way of expressing base-58, retained for RPC backwards compatibility
482    Binary(String, TransactionBinaryEncoding),
483    Json(UiTransaction),
484}
485
486impl EncodedTransaction {
487    pub fn decode(&self) -> Option<VersionedTransaction> {
488        let (blob, encoding) = match self {
489            Self::Json(_) => return None,
490            Self::LegacyBinary(blob) => (blob, TransactionBinaryEncoding::Base58),
491            Self::Binary(blob, encoding) => (blob, *encoding),
492        };
493
494        let transaction: Option<VersionedTransaction> = match encoding {
495            TransactionBinaryEncoding::Base58 => bs58::decode(blob)
496                .into_vec()
497                .ok()
498                .and_then(|bytes| bincode::deserialize(&bytes).ok()),
499            TransactionBinaryEncoding::Base64 => base64::decode(blob)
500                .ok()
501                .and_then(|bytes| bincode::deserialize(&bytes).ok()),
502        };
503
504        transaction.filter(|transaction| transaction.sanitize().is_ok())
505    }
506}
507
508#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
509#[serde(rename_all = "camelCase")]
510pub struct Reward {
511    pub pubkey: String,
512    pub lamports: i64,
513    pub post_balance: u64, // Account balance in lamports after `lamports` was applied
514    pub reward_type: Option<RewardType>,
515    pub commission: Option<u8>, // Vote account commission when the reward was credited, only present for voting and staking rewards
516}
517
518pub type Rewards = Vec<Reward>;
519
520#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
521pub struct InnerInstructions {
522    /// Transaction instruction index
523    pub index: u8,
524    /// List of inner instructions
525    pub instructions: Vec<CompiledInstruction>,
526}
527
528#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
529#[serde(rename_all = "camelCase")]
530pub struct UiInnerInstructions {
531    /// Transaction instruction index
532    pub index: u8,
533    /// List of inner instructions
534    pub instructions: Vec<UiInstruction>,
535}
536
537impl UiInnerInstructions {
538    fn parse(inner_instructions: InnerInstructions, account_keys: &AccountKeys) -> Self {
539        Self {
540            index: inner_instructions.index,
541            instructions: inner_instructions
542                .instructions
543                .iter()
544                .map(|ix| UiInstruction::parse(ix, account_keys))
545                .collect(),
546        }
547    }
548}
549
550impl From<InnerInstructions> for UiInnerInstructions {
551    fn from(inner_instructions: InnerInstructions) -> Self {
552        Self {
553            index: inner_instructions.index,
554            instructions: inner_instructions
555                .instructions
556                .iter()
557                .map(|ix| UiInstruction::Compiled(ix.into()))
558                .collect(),
559        }
560    }
561}
562
563#[derive(Clone, Debug, PartialEq)]
564pub struct TransactionTokenBalance {
565    pub account_index: u8,
566    pub mint: String,
567    pub ui_token_amount: UiTokenAmount,
568    pub owner: String,
569    pub program_id: String,
570}
571
572#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
573#[serde(rename_all = "camelCase")]
574pub struct UiTransactionTokenBalance {
575    pub account_index: u8,
576    pub mint: String,
577    pub ui_token_amount: UiTokenAmount,
578    #[serde(default, skip_serializing_if = "Option::is_none")]
579    pub owner: Option<String>,
580    #[serde(default, skip_serializing_if = "Option::is_none")]
581    pub program_id: Option<String>,
582}
583
584impl From<TransactionTokenBalance> for UiTransactionTokenBalance {
585    fn from(token_balance: TransactionTokenBalance) -> Self {
586        Self {
587            account_index: token_balance.account_index,
588            mint: token_balance.mint,
589            ui_token_amount: token_balance.ui_token_amount,
590            owner: if !token_balance.owner.is_empty() {
591                Some(token_balance.owner)
592            } else {
593                None
594            },
595            program_id: if !token_balance.program_id.is_empty() {
596                Some(token_balance.program_id)
597            } else {
598                None
599            },
600        }
601    }
602}
603
604/// A duplicate representation of LoadedAddresses
605#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
606#[serde(rename_all = "camelCase")]
607pub struct UiLoadedAddresses {
608    pub writable: Vec<String>,
609    pub readonly: Vec<String>,
610}
611
612impl From<&LoadedAddresses> for UiLoadedAddresses {
613    fn from(loaded_addresses: &LoadedAddresses) -> Self {
614        Self {
615            writable: loaded_addresses
616                .writable
617                .iter()
618                .map(ToString::to_string)
619                .collect(),
620            readonly: loaded_addresses
621                .readonly
622                .iter()
623                .map(ToString::to_string)
624                .collect(),
625        }
626    }
627}
628
629/// A duplicate representation of TransactionStatusMeta with `err` field
630#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
631#[serde(rename_all = "camelCase")]
632pub struct UiTransactionStatusMeta {
633    pub err: Option<TransactionError>,
634    pub status: TransactionResult<()>, // This field is deprecated.  See https://github.com/solana-labs/solana/issues/9302
635    pub fee: u64,
636    pub pre_balances: Vec<u64>,
637    pub post_balances: Vec<u64>,
638    pub inner_instructions: Option<Vec<UiInnerInstructions>>,
639    pub log_messages: Option<Vec<String>>,
640    pub pre_token_balances: Option<Vec<UiTransactionTokenBalance>>,
641    pub post_token_balances: Option<Vec<UiTransactionTokenBalance>>,
642    pub rewards: Option<Rewards>,
643    #[serde(default, skip_serializing_if = "Option::is_none")]
644    pub loaded_addresses: Option<UiLoadedAddresses>,
645    pub return_data: Option<TransactionReturnData>,
646}
647
648#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
649#[serde(rename_all = "camelCase")]
650pub struct EncodedTransactionWithStatusMeta {
651    pub transaction: EncodedTransaction,
652    pub meta: Option<UiTransactionStatusMeta>,
653    #[serde(default, skip_serializing_if = "Option::is_none")]
654    pub version: Option<TransactionVersion>,
655}
656
657#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
658#[serde(rename_all = "camelCase")]
659pub struct EncodedConfirmedTransactionWithStatusMeta {
660    pub slot: Slot,
661    #[serde(flatten)]
662    pub transaction: EncodedTransactionWithStatusMeta,
663    pub block_time: Option<UnixTimestamp>,
664}
665
666#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
667#[serde(rename_all = "camelCase")]
668pub enum TransactionConfirmationStatus {
669    Processed,
670    Confirmed,
671    Finalized,
672}
673
674#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
675#[serde(rename_all = "camelCase")]
676pub enum TransactionDetails {
677    Full,
678    Signatures,
679    None,
680}
681
682impl Default for TransactionDetails {
683    fn default() -> Self {
684        Self::Full
685    }
686}
687
688#[derive(Debug, PartialEq, Serialize, Deserialize)]
689#[serde(rename_all = "camelCase")]
690pub struct EncodedConfirmedBlock {
691    pub previous_blockhash: String,
692    pub blockhash: String,
693    pub parent_slot: Slot,
694    pub transactions: Vec<EncodedTransactionWithStatusMeta>,
695    pub rewards: Rewards,
696    pub block_time: Option<UnixTimestamp>,
697    pub block_height: Option<u64>,
698}
699
700impl From<UiConfirmedBlock> for EncodedConfirmedBlock {
701    fn from(block: UiConfirmedBlock) -> Self {
702        Self {
703            previous_blockhash: block.previous_blockhash,
704            blockhash: block.blockhash,
705            parent_slot: block.parent_slot,
706            transactions: block.transactions.unwrap_or_default(),
707            rewards: block.rewards.unwrap_or_default(),
708            block_time: block.block_time,
709            block_height: block.block_height,
710        }
711    }
712}
713
714#[derive(Debug, PartialEq, Serialize, Deserialize)]
715#[serde(rename_all = "camelCase")]
716pub struct UiConfirmedBlock {
717    pub previous_blockhash: String,
718    pub blockhash: String,
719    pub parent_slot: Slot,
720    #[serde(default, skip_serializing_if = "Option::is_none")]
721    pub transactions: Option<Vec<EncodedTransactionWithStatusMeta>>,
722    #[serde(default, skip_serializing_if = "Option::is_none")]
723    pub signatures: Option<Vec<String>>,
724    #[serde(default, skip_serializing_if = "Option::is_none")]
725    pub rewards: Option<Rewards>,
726    pub block_time: Option<UnixTimestamp>,
727    pub block_height: Option<u64>,
728}