solana_block_decoder/transaction/
versioned_transaction.rs

1use {
2    crate::{
3        errors::{
4            decode_error::DecodeError,
5        },
6        block::{
7            encoded_block::{
8                EncodedTransaction,
9                EncodedTransactionWithStatusMeta,
10            },
11        },
12        message::{
13            message::Message,
14            message_v0::Message as MessageV0,
15            versioned_message::VersionedMessage,
16        },
17        transaction::{
18            tx_status_meta::TransactionStatusMeta,
19        },
20        decodable::{
21            Decodable,
22            DecodableWithMeta,
23        },
24    },
25    serde::{
26        Deserialize, Serialize,
27    },
28    solana_short_vec as short_vec,
29    solana_signature::Signature,
30    solana_transaction::{
31        versioned::TransactionVersion
32    },
33    solana_transaction_status_client_types::{
34        UiMessage,
35        UiTransactionEncoding,
36    },
37    base64::{Engine, prelude::BASE64_STANDARD},
38};
39
40#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
41pub struct VersionedTransactionWithStatusMeta {
42    pub transaction: VersionedTransaction,
43    pub meta: TransactionStatusMeta,
44}
45
46impl VersionedTransactionWithStatusMeta {
47    pub fn decode(
48        encoded: EncodedTransactionWithStatusMeta,
49        encoding: UiTransactionEncoding,
50    ) -> Result<Self, DecodeError> {
51        // Decoding the transaction
52        let transaction = match VersionedTransaction::decode_with_meta(encoded.transaction, encoding, encoded.version /*, meta*/) {
53            Ok(decoded) => decoded,
54            Err(e) => return Err(e),
55        };
56
57        // Decoding the meta
58        let meta = match encoded.meta {
59            Some(ui_meta) => match TransactionStatusMeta::try_from(ui_meta) {
60                Ok(meta) => meta,
61                Err(_) => return Err(DecodeError::InvalidData),
62            },
63            None => return Err(DecodeError::InvalidData),
64        };
65
66        Ok(Self {
67            transaction,
68            meta,
69        })
70    }
71}
72
73#[derive(Debug, PartialEq, Default, Eq, Clone, Serialize, Deserialize)]
74pub struct VersionedTransaction {
75    /// List of signatures
76    #[serde(with = "short_vec")]
77    pub signatures: Vec<Signature>,
78    /// Message to sign.
79    pub message: VersionedMessage,
80}
81
82
83impl DecodableWithMeta for VersionedTransaction {
84    type Encoded = EncodedTransaction;
85    type Decoded = VersionedTransaction;
86
87    fn decode_with_meta(
88        encoded: Self::Encoded,
89        decoding: UiTransactionEncoding,
90        version: Option<TransactionVersion>
91    ) -> Result<Self::Decoded, DecodeError> {
92        match decoding {
93            UiTransactionEncoding::Binary | UiTransactionEncoding::Base58 => {
94                if let EncodedTransaction::LegacyBinary(encoded_string) = encoded {
95                    let decoded_bytes = bs58::decode(encoded_string).into_vec().unwrap();
96                    let decoded: Self::Decoded =
97                        bincode::deserialize(&decoded_bytes).map_err(|_| DecodeError::DeserializeFailed)?;
98                    Ok(decoded)
99                } else {
100                    Err(DecodeError::UnsupportedEncoding)
101                }
102            }
103            UiTransactionEncoding::Base64 => {
104                if let EncodedTransaction::Binary(encoded_string, _) = encoded {
105                    let decoded_bytes = BASE64_STANDARD.decode(encoded_string).unwrap();
106                    let decoded: Self::Decoded =
107                        bincode::deserialize(&decoded_bytes).map_err(|_| DecodeError::DeserializeFailed)?;
108                    Ok(decoded)
109                } else {
110                    Err(DecodeError::UnsupportedEncoding)
111                }
112            }
113            UiTransactionEncoding::Json => Self::json_decode(encoded, version),
114            UiTransactionEncoding::JsonParsed => Err(DecodeError::UnsupportedEncoding),
115        }
116    }
117
118    fn json_decode(encoded: Self::Encoded, version: Option<TransactionVersion>) -> Result<Self::Decoded, DecodeError> {
119        if let EncodedTransaction::Json(ui_transaction) = encoded {
120            let signatures = ui_transaction
121                .signatures
122                .iter()
123                .map(|s| s.parse::<Signature>())
124                .collect::<Result<Vec<_>, _>>()
125                .map_err(|err| DecodeError::ParseSignatureFailed(err))?;
126
127            let message = match ui_transaction.message {
128                UiMessage::Raw(_) => {
129                    match version {
130                        Some(TransactionVersion::Number(0)) => {
131                            // Handle Version 0 message decoding for raw messages
132                            let v0_message = MessageV0::json_decode(ui_transaction.message, version)?;
133                            VersionedMessage::V0(v0_message)
134                        }
135                        Some(TransactionVersion::Legacy(_)) | None => {
136                            // Default to legacy message decoding for raw messages
137                            let legacy_message = Message::decode(&ui_transaction.message)?;
138                            VersionedMessage::Legacy(legacy_message)
139                        }
140                        // Add additional cases here for other versions as needed
141                        _ => {
142                            // Handle other versions or return an error if not supported
143                            return Err(DecodeError::UnsupportedVersion);
144                        }
145                    }
146                }
147                UiMessage::Parsed(_) => {
148                    return Err(DecodeError::UnsupportedEncoding);
149                }
150            };
151
152            Ok(Self {
153                signatures,
154                message,
155            })
156        } else {
157            Err(DecodeError::UnsupportedEncoding)
158        }
159    }
160}
161
162impl From<VersionedTransaction> for solana_transaction::versioned::VersionedTransaction {
163    fn from(tx: VersionedTransaction) -> Self {
164        Self {
165            signatures: tx.signatures,
166            message: tx.message.into(),
167        }
168    }
169}
170
171impl From<VersionedTransactionWithStatusMeta> for solana_transaction_status::VersionedTransactionWithStatusMeta {
172    fn from(tx: VersionedTransactionWithStatusMeta) -> Self {
173        Self {
174            transaction: tx.transaction.into(),
175            meta: tx.meta.into(),
176        }
177    }
178}