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