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#[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#[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#[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#[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), 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#[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, 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#[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}