solders_transaction_status/
lib.rs

1#![allow(clippy::too_many_arguments)]
2use derive_more::{From, Into};
3extern crate base64;
4use pythonize::{depythonize, pythonize};
5use solders_account_decoder::UiTokenAmount;
6use solders_hash::Hash as SolderHash;
7use solders_message::MessageHeader;
8use solders_pubkey::Pubkey;
9use solders_signature::Signature;
10use solders_traits_core::{
11    common_methods_default, handle_py_value_err, py_from_bytes_general_via_bincode,
12    pybytes_general_via_bincode, richcmp_type_error, transaction_status_boilerplate,
13    RichcmpEqualityOnly,
14};
15use solders_transaction_confirmation_status::TransactionConfirmationStatus;
16use solders_transaction_error::{
17    InstructionErrorBorshIO, InstructionErrorCustom, InstructionErrorFieldless,
18    TransactionErrorDuplicateInstruction, TransactionErrorFieldless,
19    TransactionErrorInstructionError, TransactionErrorInsufficientFundsForRent,
20    TransactionErrorType,
21};
22use solders_transaction_return_data::TransactionReturnData;
23use solders_transaction_status_enums::{TransactionDetails, UiTransactionEncoding};
24use solders_transaction_status_struct::TransactionStatus;
25
26use std::str::FromStr;
27
28use pyo3::{
29    prelude::*,
30    pyclass::CompareOp,
31    types::{PyBytes, PyTuple},
32    PyTypeInfo,
33};
34use serde::{Deserialize, Serialize};
35use serde_json::Value;
36use solana_sdk::{clock::UnixTimestamp, slot_history::Slot};
37use solana_transaction_status::{
38    parse_accounts::{
39        ParsedAccount as ParsedAccountOriginal, ParsedAccountSource as ParsedAccountSourceOriginal,
40    },
41    parse_instruction::ParsedInstruction as ParsedInstructionOriginal,
42    EncodedTransaction as EncodedTransactionOriginal,
43    EncodedTransactionWithStatusMeta as EncodedTransactionWithStatusMetaOriginal,
44    Reward as RewardOriginal, RewardType as RewardTypeOriginal,
45    TransactionBinaryEncoding as TransactionBinaryEncodingOriginal,
46    UiAccountsList as UiAccountsListOriginal, UiAddressTableLookup as UiAddressTableLookupOriginal,
47    UiCompiledInstruction as UiCompiledInstructionOriginal,
48    UiConfirmedBlock as UiConfirmedBlockOriginal,
49    UiInnerInstructions as UiInnerInstructionsOriginal, UiInstruction as UiInstructionOriginal,
50    UiLoadedAddresses as UiLoadedAddressesOriginal, UiMessage as UiMessageOriginal,
51    UiParsedInstruction as UiParsedInstructionOriginal, UiParsedMessage as UiParsedMessageOriginal,
52    UiPartiallyDecodedInstruction as UiPartiallyDecodedInstructionOriginal,
53    UiRawMessage as UiRawMessageOriginal, UiTransaction as UiTransactionOriginal,
54    UiTransactionReturnData, UiTransactionStatusMeta as UiTransactionStatusMetaOriginal,
55    UiTransactionTokenBalance as UiTransactionTokenBalanceOriginal,
56};
57use solders_macros::{common_methods, enum_original_mapping, richcmp_eq_only, EnumIntoPy};
58use solders_transaction::{TransactionVersion, VersionedTransaction};
59
60#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
61#[serde(rename_all = "camelCase")]
62#[enum_original_mapping(TransactionBinaryEncodingOriginal)]
63#[pyclass(module = "solders.transaction_status")]
64pub enum TransactionBinaryEncoding {
65    Base58,
66    Base64,
67}
68
69#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
70#[pyclass(module = "solders.transaction_status", subclass)]
71pub struct UiCompiledInstruction(UiCompiledInstructionOriginal);
72
73transaction_status_boilerplate!(UiCompiledInstruction);
74
75#[richcmp_eq_only]
76#[common_methods]
77#[pymethods]
78impl UiCompiledInstruction {
79    #[new]
80    fn new(
81        program_id_index: u8,
82        accounts: Vec<u8>,
83        data: String,
84        stack_height: Option<u32>,
85    ) -> Self {
86        UiCompiledInstructionOriginal {
87            program_id_index,
88            accounts,
89            data,
90            stack_height,
91        }
92        .into()
93    }
94
95    #[getter]
96    pub fn program_id_index(&self) -> u8 {
97        self.0.program_id_index
98    }
99
100    #[getter]
101    pub fn accounts<'a>(&self, py: Python<'a>) -> &'a PyBytes {
102        PyBytes::new(py, &self.0.accounts)
103    }
104
105    #[getter]
106    pub fn data(&self) -> String {
107        self.0.data.clone()
108    }
109}
110
111#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
112#[pyclass(module = "solders.transaction_status", subclass)]
113pub struct UiAddressTableLookup(UiAddressTableLookupOriginal);
114
115transaction_status_boilerplate!(UiAddressTableLookup);
116
117#[richcmp_eq_only]
118#[common_methods]
119#[pymethods]
120impl UiAddressTableLookup {
121    #[new]
122    fn new(account_key: Pubkey, writable_indexes: Vec<u8>, readonly_indexes: Vec<u8>) -> Self {
123        UiAddressTableLookupOriginal {
124            account_key: account_key.to_string(),
125            writable_indexes,
126            readonly_indexes,
127        }
128        .into()
129    }
130
131    #[getter]
132    pub fn account_key(&self) -> Pubkey {
133        Pubkey::from_str(&self.0.account_key).unwrap()
134    }
135
136    #[getter]
137    pub fn writable_indexes<'a>(&self, py: Python<'a>) -> &'a PyBytes {
138        PyBytes::new(py, &self.0.writable_indexes)
139    }
140
141    #[getter]
142    pub fn readonly_indexes<'a>(&self, py: Python<'a>) -> &'a PyBytes {
143        PyBytes::new(py, &self.0.readonly_indexes)
144    }
145}
146
147/// A duplicate representation of a Message, in raw format, for pretty JSON serialization
148#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
149#[pyclass(module = "solders.transaction_status", subclass)]
150pub struct UiRawMessage(UiRawMessageOriginal);
151
152transaction_status_boilerplate!(UiRawMessage);
153
154#[richcmp_eq_only]
155#[common_methods]
156#[pymethods]
157impl UiRawMessage {
158    #[new]
159    fn new(
160        header: MessageHeader,
161        account_keys: Vec<Pubkey>,
162        recent_blockhash: SolderHash,
163        instructions: Vec<UiCompiledInstruction>,
164        address_table_lookups: Option<Vec<UiAddressTableLookup>>,
165    ) -> Self {
166        UiRawMessageOriginal {
167            header: header.into(),
168            account_keys: account_keys.into_iter().map(|p| p.to_string()).collect(),
169            recent_blockhash: recent_blockhash.to_string(),
170            instructions: instructions.into_iter().map(|ix| ix.into()).collect(),
171            address_table_lookups: address_table_lookups
172                .map(|v| v.into_iter().map(|a| a.into()).collect()),
173        }
174        .into()
175    }
176
177    #[getter]
178    pub fn header(&self) -> MessageHeader {
179        self.0.header.into()
180    }
181
182    #[getter]
183    pub fn account_keys(&self) -> Vec<Pubkey> {
184        self.0
185            .account_keys
186            .iter()
187            .map(|s| Pubkey::from_str(s).unwrap())
188            .collect()
189    }
190
191    #[getter]
192    pub fn recent_blockhash(&self) -> SolderHash {
193        SolderHash::from_str(&self.0.recent_blockhash).unwrap()
194    }
195
196    #[getter]
197    pub fn instructions(&self) -> Vec<UiCompiledInstruction> {
198        self.0
199            .instructions
200            .clone()
201            .into_iter()
202            .map(|ix| ix.into())
203            .collect()
204    }
205
206    #[getter]
207    pub fn address_table_lookups(&self) -> Option<Vec<UiAddressTableLookup>> {
208        self.0
209            .address_table_lookups
210            .clone()
211            .map(|v| v.into_iter().map(UiAddressTableLookup::from).collect())
212    }
213}
214
215#[pyclass(module = "solders.transaction_status")]
216#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)]
217#[serde(rename_all = "camelCase")]
218#[enum_original_mapping(ParsedAccountSourceOriginal)]
219pub enum ParsedAccountSource {
220    Transaction,
221    LookupTable,
222}
223/// A duplicate representation of a Message, in raw format, for pretty JSON serialization
224#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
225#[pyclass(module = "solders.transaction_status", subclass)]
226pub struct ParsedAccount(ParsedAccountOriginal);
227
228transaction_status_boilerplate!(ParsedAccount);
229
230#[richcmp_eq_only]
231#[common_methods]
232#[pymethods]
233impl ParsedAccount {
234    #[new]
235    fn new(
236        pubkey: Pubkey,
237        writable: bool,
238        signer: bool,
239        source: Option<ParsedAccountSource>,
240    ) -> Self {
241        ParsedAccountOriginal {
242            pubkey: pubkey.to_string(),
243            writable,
244            signer,
245            source: source.map(Into::into),
246        }
247        .into()
248    }
249
250    #[getter]
251    pub fn pubkey(&self) -> Pubkey {
252        Pubkey::from_str(&self.0.pubkey).unwrap()
253    }
254
255    #[getter]
256    pub fn writable(&self) -> bool {
257        self.0.writable
258    }
259
260    #[getter]
261    pub fn signer(&self) -> bool {
262        self.0.signer
263    }
264
265    #[getter]
266    pub fn source(&self) -> Option<ParsedAccountSource> {
267        self.0.source.clone().map(Into::into)
268    }
269}
270
271#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
272#[pyclass(module = "solders.transaction_status", subclass)]
273pub struct ParsedInstruction(ParsedInstructionOriginal);
274
275transaction_status_boilerplate!(ParsedInstruction);
276
277#[richcmp_eq_only]
278#[common_methods]
279#[pymethods]
280impl ParsedInstruction {
281    #[new]
282    fn new(
283        program: String,
284        program_id: Pubkey,
285        parsed: &PyAny,
286        stack_height: Option<u32>,
287    ) -> PyResult<Self> {
288        let value = handle_py_value_err(depythonize::<Value>(parsed))?;
289        Ok(ParsedInstructionOriginal {
290            program,
291            program_id: program_id.to_string(),
292            parsed: value,
293            stack_height,
294        }
295        .into())
296    }
297
298    #[getter]
299    pub fn program(&self) -> String {
300        self.0.program.clone()
301    }
302
303    #[getter]
304    pub fn program_id(&self) -> Pubkey {
305        Pubkey::from_str(&self.0.program_id).unwrap()
306    }
307
308    #[getter]
309    pub fn parsed(&self, py: Python<'_>) -> PyResult<PyObject> {
310        handle_py_value_err(pythonize(py, &self.0.parsed))
311    }
312
313    #[getter]
314    pub fn stack_height(&self) -> Option<u32> {
315        self.0.stack_height
316    }
317}
318
319#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
320#[pyclass(module = "solders.transaction_status", subclass)]
321pub struct UiPartiallyDecodedInstruction(UiPartiallyDecodedInstructionOriginal);
322
323transaction_status_boilerplate!(UiPartiallyDecodedInstruction);
324
325#[richcmp_eq_only]
326#[common_methods]
327#[pymethods]
328impl UiPartiallyDecodedInstruction {
329    #[new]
330    fn new(
331        program_id: Pubkey,
332        accounts: Vec<Pubkey>,
333        data: String,
334        stack_height: Option<u32>,
335    ) -> Self {
336        UiPartiallyDecodedInstructionOriginal {
337            program_id: program_id.to_string(),
338            accounts: accounts.into_iter().map(|a| a.to_string()).collect(),
339            data,
340            stack_height,
341        }
342        .into()
343    }
344
345    #[getter]
346    pub fn program_id(&self) -> Pubkey {
347        Pubkey::from_str(&self.0.program_id).unwrap()
348    }
349
350    #[getter]
351    pub fn accounts(&self) -> Vec<Pubkey> {
352        self.0
353            .accounts
354            .clone()
355            .into_iter()
356            .map(|a| Pubkey::from_str(&a).unwrap())
357            .collect()
358    }
359
360    #[getter]
361    pub fn data(&self) -> String {
362        self.0.data.clone()
363    }
364
365    #[getter]
366    pub fn stack_height(&self) -> Option<u32> {
367        self.0.stack_height
368    }
369}
370
371#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromPyObject, EnumIntoPy)]
372#[serde(rename_all = "camelCase", untagged)]
373pub enum UiParsedInstruction {
374    Parsed(ParsedInstruction),
375    PartiallyDecoded(UiPartiallyDecodedInstruction),
376}
377
378impl From<UiParsedInstruction> for UiParsedInstructionOriginal {
379    fn from(ix: UiParsedInstruction) -> Self {
380        match ix {
381            UiParsedInstruction::Parsed(p) => Self::Parsed(p.into()),
382            UiParsedInstruction::PartiallyDecoded(p) => Self::PartiallyDecoded(p.into()),
383        }
384    }
385}
386
387impl From<UiParsedInstructionOriginal> for UiParsedInstruction {
388    fn from(ix: UiParsedInstructionOriginal) -> Self {
389        match ix {
390            UiParsedInstructionOriginal::Parsed(p) => Self::Parsed(p.into()),
391            UiParsedInstructionOriginal::PartiallyDecoded(p) => Self::PartiallyDecoded(p.into()),
392        }
393    }
394}
395
396/// A duplicate representation of an Instruction for pretty JSON serialization
397#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromPyObject, EnumIntoPy)]
398#[serde(rename_all = "camelCase", untagged)]
399pub enum UiInstruction {
400    Compiled(UiCompiledInstruction),
401    Parsed(UiParsedInstruction),
402}
403
404impl From<UiInstruction> for UiInstructionOriginal {
405    fn from(ix: UiInstruction) -> Self {
406        match ix {
407            UiInstruction::Compiled(c) => Self::Compiled(c.into()),
408            UiInstruction::Parsed(p) => Self::Parsed(p.into()),
409        }
410    }
411}
412
413impl From<UiInstructionOriginal> for UiInstruction {
414    fn from(ix: UiInstructionOriginal) -> Self {
415        match ix {
416            UiInstructionOriginal::Compiled(c) => Self::Compiled(c.into()),
417            UiInstructionOriginal::Parsed(p) => Self::Parsed(p.into()),
418        }
419    }
420}
421
422/// A duplicate representation of a Message, in raw format, for pretty JSON serialization
423#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
424#[pyclass(module = "solders.transaction_status", subclass)]
425pub struct UiParsedMessage(UiParsedMessageOriginal);
426
427transaction_status_boilerplate!(UiParsedMessage);
428
429#[richcmp_eq_only]
430#[common_methods]
431#[pymethods]
432impl UiParsedMessage {
433    #[new]
434    fn new(
435        account_keys: Vec<ParsedAccount>,
436        recent_blockhash: SolderHash,
437        instructions: Vec<UiInstruction>,
438        address_table_lookups: Option<Vec<UiAddressTableLookup>>,
439    ) -> Self {
440        UiParsedMessageOriginal {
441            account_keys: account_keys.into_iter().map(|p| p.into()).collect(),
442            recent_blockhash: recent_blockhash.to_string(),
443            instructions: instructions.into_iter().map(|ix| ix.into()).collect(),
444            address_table_lookups: address_table_lookups
445                .map(|v| v.into_iter().map(|a| a.into()).collect()),
446        }
447        .into()
448    }
449
450    #[getter]
451    pub fn account_keys(&self) -> Vec<ParsedAccount> {
452        self.0
453            .account_keys
454            .clone()
455            .into_iter()
456            .map(|p| p.into())
457            .collect()
458    }
459
460    #[getter]
461    pub fn recent_blockhash(&self) -> SolderHash {
462        SolderHash::from_str(&self.0.recent_blockhash).unwrap()
463    }
464
465    #[getter]
466    pub fn instructions(&self) -> Vec<UiInstruction> {
467        self.0
468            .instructions
469            .clone()
470            .into_iter()
471            .map(|ix| ix.into())
472            .collect()
473    }
474
475    #[getter]
476    pub fn address_table_lookups(&self) -> Option<Vec<UiAddressTableLookup>> {
477        self.0
478            .address_table_lookups
479            .clone()
480            .map(|v| v.into_iter().map(|a| a.into()).collect())
481    }
482}
483
484#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromPyObject, EnumIntoPy)]
485#[serde(rename_all = "camelCase", untagged)]
486pub enum UiMessage {
487    Parsed(UiParsedMessage),
488    Raw(UiRawMessage),
489}
490
491impl From<UiMessageOriginal> for UiMessage {
492    fn from(m: UiMessageOriginal) -> Self {
493        match m {
494            UiMessageOriginal::Parsed(msg) => Self::Parsed(msg.into()),
495            UiMessageOriginal::Raw(msg) => Self::Raw(msg.into()),
496        }
497    }
498}
499
500impl From<UiMessage> for UiMessageOriginal {
501    fn from(m: UiMessage) -> Self {
502        match m {
503            UiMessage::Parsed(msg) => Self::Parsed(msg.into()),
504            UiMessage::Raw(msg) => Self::Raw(msg.into()),
505        }
506    }
507}
508
509#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
510#[pyclass(module = "solders.transaction_status", subclass)]
511pub struct UiTransaction(UiTransactionOriginal);
512
513transaction_status_boilerplate!(UiTransaction);
514
515#[richcmp_eq_only]
516#[common_methods]
517#[pymethods]
518impl UiTransaction {
519    #[new]
520    fn new(signatures: Vec<Signature>, message: UiMessage) -> Self {
521        UiTransactionOriginal {
522            signatures: signatures.into_iter().map(|s| s.to_string()).collect(),
523            message: message.into(),
524        }
525        .into()
526    }
527
528    #[getter]
529    pub fn signatures(&self) -> Vec<Signature> {
530        self.0
531            .signatures
532            .iter()
533            .map(|s| Signature::from_str(s).unwrap())
534            .collect()
535    }
536
537    #[getter]
538    pub fn message(&self) -> UiMessage {
539        self.0.message.clone().into()
540    }
541}
542
543#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromPyObject, EnumIntoPy)]
544#[serde(rename_all = "camelCase", untagged)]
545pub enum EncodedVersionedTransaction {
546    Binary(VersionedTransaction),
547    Json(UiTransaction),
548    Accounts(UiAccountsList),
549}
550
551impl From<EncodedTransaction> for EncodedVersionedTransaction {
552    fn from(e: EncodedTransaction) -> Self {
553        match e {
554            EncodedTransaction::LegacyBinary(..) | EncodedTransaction::Binary(..) => Self::Binary(
555                VersionedTransaction::from(EncodedTransactionOriginal::from(e).decode().unwrap()),
556            ),
557            EncodedTransaction::Json(u) => Self::Json(u),
558            EncodedTransaction::Accounts(u) => Self::Accounts(u),
559        }
560    }
561}
562
563impl From<EncodedVersionedTransaction> for EncodedTransaction {
564    fn from(e: EncodedVersionedTransaction) -> Self {
565        match e {
566            EncodedVersionedTransaction::Binary(v) => Self::Binary(
567                base64::encode(bincode::serialize(&v).unwrap()),
568                TransactionBinaryEncoding::Base64,
569            ),
570            EncodedVersionedTransaction::Json(u) => Self::Json(u),
571            EncodedVersionedTransaction::Accounts(u) => Self::Accounts(u),
572        }
573    }
574}
575
576impl From<EncodedVersionedTransaction> for EncodedTransactionOriginal {
577    fn from(e: EncodedVersionedTransaction) -> Self {
578        EncodedTransaction::from(e).into()
579    }
580}
581
582impl From<EncodedTransactionOriginal> for EncodedVersionedTransaction {
583    fn from(e: EncodedTransactionOriginal) -> Self {
584        EncodedTransaction::from(e).into()
585    }
586}
587
588#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
589#[pyclass(module = "solders.transaction_status", subclass)]
590pub struct UiAccountsList(UiAccountsListOriginal);
591
592transaction_status_boilerplate!(UiAccountsList);
593
594#[richcmp_eq_only]
595#[common_methods]
596#[pymethods]
597impl UiAccountsList {
598    #[new]
599    pub fn new(signatures: Vec<Signature>, account_keys: Vec<ParsedAccount>) -> Self {
600        UiAccountsListOriginal {
601            signatures: signatures.into_iter().map(|s| s.to_string()).collect(),
602            account_keys: account_keys.into_iter().map(Into::into).collect(),
603        }
604        .into()
605    }
606
607    #[getter]
608    pub fn signatures(&self) -> Vec<Signature> {
609        self.0
610            .signatures
611            .clone()
612            .into_iter()
613            .map(|s| s.parse().unwrap())
614            .collect()
615    }
616
617    #[getter]
618    pub fn account_keys(&self) -> Vec<ParsedAccount> {
619        self.0
620            .account_keys
621            .clone()
622            .into_iter()
623            .map(Into::into)
624            .collect()
625    }
626}
627
628#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromPyObject)]
629#[serde(rename_all = "camelCase", untagged)]
630pub enum EncodedTransaction {
631    LegacyBinary(String), // Old way of expressing base-58, retained for RPC backwards compatibility
632    Binary(String, TransactionBinaryEncoding),
633    Json(UiTransaction),
634    Accounts(UiAccountsList),
635}
636
637impl From<EncodedTransactionOriginal> for EncodedTransaction {
638    fn from(e: EncodedTransactionOriginal) -> Self {
639        match e {
640            EncodedTransactionOriginal::LegacyBinary(s) => Self::LegacyBinary(s),
641            EncodedTransactionOriginal::Binary(s, b) => Self::Binary(s, b.into()),
642            EncodedTransactionOriginal::Json(t) => Self::Json(t.into()),
643            EncodedTransactionOriginal::Accounts(a) => Self::Accounts(a.into()),
644        }
645    }
646}
647
648impl From<EncodedTransaction> for EncodedTransactionOriginal {
649    fn from(e: EncodedTransaction) -> Self {
650        match e {
651            EncodedTransaction::LegacyBinary(s) => Self::LegacyBinary(s),
652            EncodedTransaction::Binary(s, b) => Self::Binary(s, b.into()),
653            EncodedTransaction::Json(t) => Self::Json(t.into()),
654            EncodedTransaction::Accounts(t) => Self::Accounts(t.into()),
655        }
656    }
657}
658
659#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
660#[pyclass(module = "solders.transaction_status", subclass)]
661pub struct UiInnerInstructions(UiInnerInstructionsOriginal);
662
663transaction_status_boilerplate!(UiInnerInstructions);
664
665#[richcmp_eq_only]
666#[common_methods]
667#[pymethods]
668impl UiInnerInstructions {
669    #[new]
670    pub fn new(index: u8, instructions: Vec<UiInstruction>) -> Self {
671        UiInnerInstructionsOriginal {
672            index,
673            instructions: instructions.into_iter().map(|ix| ix.into()).collect(),
674        }
675        .into()
676    }
677
678    #[getter]
679    pub fn index(&self) -> u8 {
680        self.0.index
681    }
682
683    #[getter]
684    pub fn instructions(&self) -> Vec<UiInstruction> {
685        self.0
686            .instructions
687            .clone()
688            .into_iter()
689            .map(|ix| ix.into())
690            .collect()
691    }
692}
693
694#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
695#[pyclass(module = "solders.transaction_status", subclass)]
696pub struct UiLoadedAddresses(UiLoadedAddressesOriginal);
697
698transaction_status_boilerplate!(UiLoadedAddresses);
699
700#[richcmp_eq_only]
701#[common_methods]
702#[pymethods]
703impl UiLoadedAddresses {
704    #[new]
705    pub fn new(writable: Vec<Pubkey>, readonly: Vec<Pubkey>) -> Self {
706        UiLoadedAddressesOriginal {
707            writable: writable.iter().map(|x| x.to_string()).collect(),
708            readonly: readonly.iter().map(|x| x.to_string()).collect(),
709        }
710        .into()
711    }
712
713    #[getter]
714    pub fn writable(&self) -> Vec<Pubkey> {
715        self.0
716            .writable
717            .iter()
718            .map(|x| Pubkey::from_str(x).unwrap())
719            .collect()
720    }
721
722    #[getter]
723    pub fn readonly(&self) -> Vec<Pubkey> {
724        self.0
725            .readonly
726            .iter()
727            .map(|x| Pubkey::from_str(x).unwrap())
728            .collect()
729    }
730}
731
732#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, From, Into)]
733#[pyclass(module = "solders.transaction_status", subclass)]
734pub struct UiTransactionTokenBalance(UiTransactionTokenBalanceOriginal);
735
736transaction_status_boilerplate!(UiTransactionTokenBalance);
737
738#[richcmp_eq_only]
739#[common_methods]
740#[pymethods]
741impl UiTransactionTokenBalance {
742    #[new]
743    pub fn new(
744        account_index: u8,
745        mint: Pubkey,
746        ui_token_amount: UiTokenAmount,
747        owner: Option<Pubkey>,
748        program_id: Option<Pubkey>,
749    ) -> Self {
750        UiTransactionTokenBalanceOriginal {
751            account_index,
752            mint: mint.to_string(),
753            ui_token_amount: ui_token_amount.into(),
754            owner: owner.map(|x| x.to_string()).into(),
755            program_id: program_id.map(|x| x.to_string()).into(),
756        }
757        .into()
758    }
759
760    #[getter]
761    pub fn account_index(&self) -> u8 {
762        self.0.account_index
763    }
764
765    #[getter]
766    pub fn mint(&self) -> Pubkey {
767        Pubkey::from_str(&self.0.mint).unwrap()
768    }
769
770    #[getter]
771    pub fn ui_token_amount(&self) -> UiTokenAmount {
772        self.0.ui_token_amount.clone().into()
773    }
774
775    #[getter]
776    pub fn owner(&self) -> Option<Pubkey> {
777        let maybe_key: Option<String> = self.0.owner.clone().into();
778        maybe_key.map(|x| Pubkey::from_str(&x).unwrap())
779    }
780
781    #[getter]
782    pub fn program_id(&self) -> Option<Pubkey> {
783        let maybe_id: Option<String> = self.0.clone().program_id.into();
784        maybe_id.map(|x| Pubkey::from_str(&x).unwrap())
785    }
786}
787
788#[pyclass(module = "solders.transaction_status")]
789#[enum_original_mapping(RewardTypeOriginal)]
790#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)]
791#[serde(rename_all = "camelCase")]
792pub enum RewardType {
793    Fee,
794    Rent,
795    Staking,
796    Voting,
797}
798/// A duplicate representation of TransactionStatusMeta with `err` field
799#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, From, Into)]
800#[pyclass(module = "solders.transaction_status", subclass)]
801pub struct UiTransactionStatusMeta(UiTransactionStatusMetaOriginal);
802impl RichcmpEqualityOnly for UiTransactionStatusMeta {
803    fn richcmp(&self, other: &Self, op: pyo3::pyclass::CompareOp) -> PyResult<bool> {
804        match op {
805            CompareOp::Eq => Ok(self.compare(other)),
806            CompareOp::Ne => Ok(!self.compare(other)),
807            CompareOp::Lt => Err(richcmp_type_error("<")),
808            CompareOp::Gt => Err(richcmp_type_error(">")),
809            CompareOp::Le => Err(richcmp_type_error("<=")),
810            CompareOp::Ge => Err(richcmp_type_error(">=")),
811        }
812    }
813}
814
815impl UiTransactionStatusMeta {
816    fn compare(&self, other: &Self) -> bool {
817        self.err() == other.err()
818            && self.fee() == other.fee()
819            && self.pre_balances() == other.pre_balances()
820            && self.post_balances() == other.post_balances()
821            && self.inner_instructions() == other.inner_instructions()
822            && self.log_messages() == other.log_messages()
823            && self.pre_token_balances() == other.pre_token_balances()
824            && self.post_token_balances() == other.post_token_balances()
825            && self.rewards() == other.rewards()
826            && self.loaded_addresses() == other.loaded_addresses()
827            && self.return_data() == other.return_data()
828            && self.compute_units_consumed() == other.compute_units_consumed()
829    }
830}
831
832impl std::fmt::Display for UiTransactionStatusMeta {
833    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
834        write!(f, "{:?}", self)
835    }
836}
837pybytes_general_via_bincode!(UiTransactionStatusMeta);
838py_from_bytes_general_via_bincode!(UiTransactionStatusMeta);
839common_methods_default!(UiTransactionStatusMeta);
840
841#[richcmp_eq_only]
842#[common_methods]
843#[pymethods]
844impl UiTransactionStatusMeta {
845    #[pyo3(
846        signature = (err, fee, pre_balances, post_balances, inner_instructions=None, log_messages=None, pre_token_balances=None, post_token_balances=None, rewards=None, loaded_addresses=None, return_data=None, compute_units_consumed=None)
847    )]
848    #[new]
849    pub fn new(
850        err: Option<TransactionErrorType>,
851        fee: u64,
852        pre_balances: Vec<u64>,
853        post_balances: Vec<u64>,
854        inner_instructions: Option<Vec<UiInnerInstructions>>,
855        log_messages: Option<Vec<String>>,
856        pre_token_balances: Option<Vec<UiTransactionTokenBalance>>,
857        post_token_balances: Option<Vec<UiTransactionTokenBalance>>,
858        rewards: Option<Rewards>,
859        loaded_addresses: Option<UiLoadedAddresses>,
860        return_data: Option<TransactionReturnData>,
861        compute_units_consumed: Option<u64>,
862    ) -> Self {
863        UiTransactionStatusMetaOriginal {
864            err: err.map(|e| e.into()),
865            status: Ok(()),
866            fee,
867            pre_balances,
868            post_balances,
869            inner_instructions: inner_instructions
870                .map(|v| v.into_iter().map(|ix| ix.into()).collect())
871                .into(),
872            log_messages: log_messages.into(),
873            pre_token_balances: pre_token_balances
874                .map(|v| v.into_iter().map(|bal| bal.into()).collect())
875                .into(),
876            post_token_balances: post_token_balances
877                .map(|v| v.into_iter().map(|bal| bal.into()).collect())
878                .into(),
879            rewards: rewards
880                .map(|v| v.into_iter().map(|r| r.into()).collect())
881                .into(),
882            loaded_addresses: loaded_addresses.map(|a| a.into()).into(),
883            return_data: return_data.map(|r| r.into()).into(),
884            compute_units_consumed: compute_units_consumed.into(),
885        }
886        .into()
887    }
888
889    #[getter]
890    pub fn err(&self) -> Option<TransactionErrorType> {
891        self.0.err.clone().map(|e| e.into())
892    }
893    #[getter]
894    pub fn fee(&self) -> u64 {
895        self.0.fee
896    }
897    #[getter]
898    pub fn pre_balances(&self) -> Vec<u64> {
899        self.0.pre_balances.clone()
900    }
901    #[getter]
902    pub fn post_balances(&self) -> Vec<u64> {
903        self.0.post_balances.clone()
904    }
905    #[getter]
906    pub fn inner_instructions(&self) -> Option<Vec<UiInnerInstructions>> {
907        let maybe_instructions: Option<Vec<UiInnerInstructionsOriginal>> =
908            self.0.inner_instructions.clone().into();
909        maybe_instructions.map(|v| v.into_iter().map(|ix| ix.into()).collect())
910    }
911    #[getter]
912    pub fn log_messages(&self) -> Option<Vec<String>> {
913        self.0.log_messages.clone().into()
914    }
915    #[getter]
916    pub fn pre_token_balances(&self) -> Option<Vec<UiTransactionTokenBalance>> {
917        let maybe_balances: Option<Vec<UiTransactionTokenBalanceOriginal>> =
918            self.0.pre_token_balances.clone().into();
919        maybe_balances.map(|v| v.into_iter().map(|bal| bal.into()).collect())
920    }
921    #[getter]
922    pub fn post_token_balances(&self) -> Option<Vec<UiTransactionTokenBalance>> {
923        let maybe_balances: Option<Vec<UiTransactionTokenBalanceOriginal>> =
924            self.0.post_token_balances.clone().into();
925        maybe_balances.map(|v| v.into_iter().map(|bal| bal.into()).collect())
926    }
927    #[getter]
928    pub fn rewards(&self) -> Option<Rewards> {
929        let maybe_rewards: Option<Vec<RewardOriginal>> = self.0.rewards.clone().into();
930        maybe_rewards.map(|v| v.into_iter().map(|r| r.into()).collect())
931    }
932    #[getter]
933    pub fn loaded_addresses(&self) -> Option<UiLoadedAddresses> {
934        let maybe_addresses: Option<UiLoadedAddressesOriginal> =
935            self.0.loaded_addresses.clone().into();
936        maybe_addresses.map(UiLoadedAddresses::from)
937    }
938    #[getter]
939    pub fn return_data(&self) -> Option<TransactionReturnData> {
940        let maybe_underlying: Option<UiTransactionReturnData> = self.0.return_data.clone().into();
941        maybe_underlying.map(|r| r.into())
942    }
943    #[getter]
944    pub fn compute_units_consumed(&self) -> Option<u64> {
945        self.0.compute_units_consumed.clone().into()
946    }
947}
948
949#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, From, Into)]
950#[pyclass(module = "solders.transaction_status", subclass)]
951pub struct EncodedTransactionWithStatusMeta(EncodedTransactionWithStatusMetaOriginal);
952
953transaction_status_boilerplate!(EncodedTransactionWithStatusMeta);
954
955#[richcmp_eq_only]
956#[common_methods]
957#[pymethods]
958impl EncodedTransactionWithStatusMeta {
959    #[new]
960    pub fn new(
961        transaction: EncodedVersionedTransaction,
962        meta: Option<UiTransactionStatusMeta>,
963        version: Option<TransactionVersion>,
964    ) -> Self {
965        EncodedTransactionWithStatusMetaOriginal {
966            transaction: transaction.into(),
967            meta: meta.map(|m| m.into()),
968            version: version.map(|v| v.into()),
969        }
970        .into()
971    }
972
973    #[getter]
974    pub fn transaction(&self) -> EncodedVersionedTransaction {
975        self.0.transaction.clone().into()
976    }
977
978    #[getter]
979    pub fn meta(&self) -> Option<UiTransactionStatusMeta> {
980        self.0.meta.clone().map(|t| t.into())
981    }
982
983    #[getter]
984    pub fn version(&self) -> Option<TransactionVersion> {
985        self.0.version.clone().map(|v| v.into())
986    }
987}
988
989#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into)]
990#[pyclass(module = "solders.transaction_status", subclass)]
991pub struct Reward(RewardOriginal);
992
993transaction_status_boilerplate!(Reward);
994
995#[richcmp_eq_only]
996#[common_methods]
997#[pymethods]
998impl Reward {
999    #[new]
1000    pub fn new(
1001        pubkey: Pubkey,
1002        lamports: i64,
1003        post_balance: u64, // Account balance in lamports after `lamports` was applied
1004        reward_type: Option<RewardType>,
1005        commission: Option<u8>,
1006    ) -> Self {
1007        RewardOriginal {
1008            pubkey: pubkey.to_string(),
1009            lamports,
1010            post_balance,
1011            reward_type: reward_type.map(|r| r.into()),
1012            commission,
1013        }
1014        .into()
1015    }
1016
1017    #[getter]
1018    pub fn pubkey(&self) -> Pubkey {
1019        Pubkey::from_str(&self.0.pubkey).unwrap()
1020    }
1021
1022    #[getter]
1023    pub fn lamports(&self) -> i64 {
1024        self.0.lamports
1025    }
1026
1027    #[getter]
1028    pub fn post_balance(&self) -> u64 {
1029        self.0.post_balance
1030    }
1031
1032    #[getter]
1033    pub fn reward_type(&self) -> Option<RewardType> {
1034        self.0.reward_type.map(|r| r.into())
1035    }
1036
1037    #[getter]
1038    pub fn commission(&self) -> Option<u8> {
1039        self.0.commission
1040    }
1041}
1042
1043pub type Rewards = Vec<Reward>;
1044
1045// the one in transaction_status is missing Clone
1046#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
1047#[serde(rename_all = "camelCase")]
1048#[pyclass(module = "solders.transaction_status", subclass)]
1049pub struct EncodedConfirmedTransactionWithStatusMeta {
1050    #[pyo3(get)]
1051    pub slot: Slot,
1052    #[serde(flatten)]
1053    #[pyo3(get)]
1054    pub transaction: EncodedTransactionWithStatusMeta,
1055    #[pyo3(get)]
1056    pub block_time: Option<UnixTimestamp>,
1057}
1058
1059transaction_status_boilerplate!(EncodedConfirmedTransactionWithStatusMeta);
1060
1061#[richcmp_eq_only]
1062#[common_methods]
1063#[pymethods]
1064impl EncodedConfirmedTransactionWithStatusMeta {
1065    #[new]
1066    pub fn new(
1067        slot: Slot,
1068        transaction: EncodedTransactionWithStatusMeta,
1069        block_time: Option<UnixTimestamp>,
1070    ) -> Self {
1071        Self {
1072            slot,
1073            transaction,
1074            block_time,
1075        }
1076    }
1077}
1078
1079#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, From, Into)]
1080#[pyclass(module = "solders.transaction_status", subclass)]
1081pub struct UiConfirmedBlock(UiConfirmedBlockOriginal);
1082
1083transaction_status_boilerplate!(UiConfirmedBlock);
1084
1085#[richcmp_eq_only]
1086#[common_methods]
1087#[pymethods]
1088impl UiConfirmedBlock {
1089    #[new]
1090    pub fn new(
1091        previous_blockhash: SolderHash,
1092        blockhash: SolderHash,
1093        parent_slot: Slot,
1094        transactions: Option<Vec<EncodedTransactionWithStatusMeta>>,
1095        signatures: Option<Vec<Signature>>,
1096        rewards: Option<Rewards>,
1097        block_time: Option<UnixTimestamp>,
1098        block_height: Option<u64>,
1099    ) -> Self {
1100        UiConfirmedBlockOriginal {
1101            previous_blockhash: previous_blockhash.to_string(),
1102            blockhash: blockhash.to_string(),
1103            parent_slot,
1104            transactions: transactions.map(|txs| txs.into_iter().map(|tx| tx.into()).collect()),
1105            signatures: signatures.map(|sigs| sigs.iter().map(|sig| sig.to_string()).collect()),
1106            rewards: rewards.map(|v| v.into_iter().map(|r| r.into()).collect()),
1107            block_time,
1108            block_height,
1109        }
1110        .into()
1111    }
1112
1113    #[getter]
1114    pub fn previous_blockhash(&self) -> SolderHash {
1115        self.0.previous_blockhash.parse().unwrap()
1116    }
1117
1118    #[getter]
1119    pub fn blockhash(&self) -> SolderHash {
1120        self.0.blockhash.parse().unwrap()
1121    }
1122
1123    #[getter]
1124    pub fn parent_slot(&self) -> Slot {
1125        self.0.parent_slot
1126    }
1127
1128    #[getter]
1129    pub fn transactions(&self) -> Option<Vec<EncodedTransactionWithStatusMeta>> {
1130        self.0
1131            .transactions
1132            .clone()
1133            .map(|txs| txs.into_iter().map(|tx| tx.into()).collect())
1134    }
1135    #[getter]
1136    pub fn signatures(&self) -> Option<Vec<Signature>> {
1137        self.0
1138            .signatures
1139            .clone()
1140            .map(|sigs| sigs.iter().map(|sig| sig.parse().unwrap()).collect())
1141    }
1142    #[getter]
1143    pub fn rewards(&self) -> Option<Rewards> {
1144        self.0
1145            .rewards
1146            .clone()
1147            .map(|v| v.into_iter().map(|r| r.into()).collect())
1148    }
1149    #[getter]
1150    pub fn block_time(&self) -> Option<UnixTimestamp> {
1151        self.0.block_time
1152    }
1153    #[getter]
1154    pub fn block_height(&self) -> Option<u64> {
1155        self.0.block_height
1156    }
1157}
1158
1159pub fn create_transaction_status_mod(py: Python<'_>) -> PyResult<&PyModule> {
1160    let m = PyModule::new(py, "transaction_status")?;
1161    m.add_class::<TransactionDetails>()?;
1162    m.add_class::<UiTransactionEncoding>()?;
1163    m.add_class::<TransactionBinaryEncoding>()?;
1164    m.add_class::<UiCompiledInstruction>()?;
1165    m.add_class::<UiAddressTableLookup>()?;
1166    m.add_class::<UiRawMessage>()?;
1167    m.add_class::<ParsedAccountSource>()?;
1168    m.add_class::<ParsedAccount>()?;
1169    m.add_class::<ParsedInstruction>()?;
1170    m.add_class::<UiPartiallyDecodedInstruction>()?;
1171    m.add_class::<UiParsedMessage>()?;
1172    m.add_class::<UiTransaction>()?;
1173    m.add_class::<UiInnerInstructions>()?;
1174    m.add_class::<UiLoadedAddresses>()?;
1175    m.add_class::<UiAccountsList>()?;
1176    m.add_class::<UiTransactionTokenBalance>()?;
1177    m.add_class::<RewardType>()?;
1178    m.add_class::<TransactionReturnData>()?;
1179    m.add_class::<UiTransactionStatusMeta>()?;
1180    m.add_class::<EncodedTransactionWithStatusMeta>()?;
1181    m.add_class::<InstructionErrorCustom>()?;
1182    m.add_class::<InstructionErrorBorshIO>()?;
1183    m.add_class::<InstructionErrorFieldless>()?;
1184    m.add_class::<TransactionErrorInstructionError>()?;
1185    m.add_class::<TransactionErrorDuplicateInstruction>()?;
1186    m.add_class::<TransactionErrorInsufficientFundsForRent>()?;
1187    m.add_class::<TransactionErrorFieldless>()?;
1188    m.add_class::<Reward>()?;
1189    m.add_class::<TransactionConfirmationStatus>()?;
1190    m.add_class::<TransactionStatus>()?;
1191    m.add_class::<EncodedConfirmedTransactionWithStatusMeta>()?;
1192    m.add_class::<UiConfirmedBlock>()?;
1193    let typing = py.import("typing")?;
1194    let union = typing.getattr("Union")?;
1195    let ui_parsed_instruction_members = vec![
1196        ParsedInstruction::type_object(py),
1197        UiPartiallyDecodedInstruction::type_object(py),
1198    ];
1199    m.add(
1200        "UiParsedInstruction",
1201        union.get_item(PyTuple::new(py, ui_parsed_instruction_members.clone()))?,
1202    )?;
1203    let mut ui_instruction_members = vec![UiCompiledInstruction::type_object(py)];
1204    ui_instruction_members.extend(ui_parsed_instruction_members);
1205    m.add(
1206        "UiInstruction",
1207        union.get_item(PyTuple::new(py, ui_instruction_members))?,
1208    )?;
1209    m.add(
1210        "UiMessage",
1211        union.get_item(PyTuple::new(
1212            py,
1213            vec![
1214                UiParsedMessage::type_object(py),
1215                UiRawMessage::type_object(py),
1216            ],
1217        ))?,
1218    )?;
1219    m.add(
1220        "EncodedVersionedTransaction",
1221        union.get_item(PyTuple::new(
1222            py,
1223            vec![
1224                VersionedTransaction::type_object(py),
1225                UiTransaction::type_object(py),
1226                UiAccountsList::type_object(py),
1227            ],
1228        ))?,
1229    )?;
1230    m.add(
1231        "InstructionErrorType",
1232        union.get_item(PyTuple::new(
1233            py,
1234            vec![
1235                InstructionErrorFieldless::type_object(py),
1236                InstructionErrorCustom::type_object(py),
1237                InstructionErrorBorshIO::type_object(py),
1238            ],
1239        ))?,
1240    )?;
1241    m.add(
1242        "TransactionErrorType",
1243        union.get_item(PyTuple::new(
1244            py,
1245            vec![
1246                TransactionErrorFieldless::type_object(py),
1247                TransactionErrorInstructionError::type_object(py),
1248                TransactionErrorDuplicateInstruction::type_object(py),
1249                TransactionErrorInsufficientFundsForRent::type_object(py),
1250            ],
1251        ))?,
1252    )?;
1253    Ok(m)
1254}