sui_sdk_types/transaction/
serialization.rs

1use serde::Deserialize;
2use serde::Deserializer;
3use serde::Serialize;
4use serde::Serializer;
5use serde_with::DeserializeAs;
6use serde_with::SerializeAs;
7
8use crate::ObjectId;
9use crate::ObjectReference;
10
11mod transaction {
12    use super::*;
13    use crate::transaction::GasPayment;
14    use crate::transaction::Transaction;
15    use crate::transaction::TransactionExpiration;
16    use crate::transaction::TransactionKind;
17    use crate::Address;
18
19    #[derive(serde_derive::Serialize)]
20    #[serde(rename = "Transaction")]
21    enum TransactionDataRef<'a> {
22        V1(TransactionV1Ref<'a>),
23    }
24
25    #[derive(serde_derive::Deserialize)]
26    #[serde(rename = "Transaction")]
27    enum TransactionData {
28        V1(TransactionV1),
29    }
30
31    #[derive(serde_derive::Serialize)]
32    #[serde(rename = "TransactionV1")]
33    struct TransactionV1Ref<'a> {
34        kind: &'a TransactionKind,
35        sender: &'a Address,
36        gas_payment: &'a GasPayment,
37        expiration: &'a TransactionExpiration,
38    }
39
40    #[derive(serde_derive::Deserialize)]
41    #[serde(rename = "TransactionV1")]
42    struct TransactionV1 {
43        kind: TransactionKind,
44        sender: Address,
45        gas_payment: GasPayment,
46        expiration: TransactionExpiration,
47    }
48
49    impl Serialize for Transaction {
50        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
51        where
52            S: Serializer,
53        {
54            let transaction = TransactionV1Ref {
55                kind: &self.kind,
56                sender: &self.sender,
57                gas_payment: &self.gas_payment,
58                expiration: &self.expiration,
59            };
60
61            TransactionDataRef::V1(transaction).serialize(serializer)
62        }
63    }
64
65    impl<'de> Deserialize<'de> for Transaction {
66        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67        where
68            D: Deserializer<'de>,
69        {
70            let TransactionData::V1(TransactionV1 {
71                kind,
72                sender,
73                gas_payment,
74                expiration,
75            }) = Deserialize::deserialize(deserializer)?;
76
77            Ok(Transaction {
78                kind,
79                sender,
80                gas_payment,
81                expiration,
82            })
83        }
84    }
85}
86
87mod input_argument {
88    use crate::transaction::Input;
89
90    use super::*;
91
92    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
93    enum CallArg {
94        Pure(#[serde(with = "::serde_with::As::<::serde_with::Bytes>")] Vec<u8>),
95        Object(ObjectArg),
96    }
97
98    #[derive(serde_derive::Serialize, serde_derive::Deserialize)]
99    enum ObjectArg {
100        ImmutableOrOwned(ObjectReference),
101        Shared {
102            object_id: ObjectId,
103            initial_shared_version: u64,
104            mutable: bool,
105        },
106        Receiving(ObjectReference),
107    }
108
109    impl Serialize for Input {
110        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111        where
112            S: Serializer,
113        {
114            let binary = match self.clone() {
115                Input::Pure { value } => CallArg::Pure(value),
116                Input::ImmutableOrOwned(object_ref) => {
117                    CallArg::Object(ObjectArg::ImmutableOrOwned(object_ref))
118                }
119                Input::Shared {
120                    object_id,
121                    initial_shared_version,
122                    mutable,
123                } => CallArg::Object(ObjectArg::Shared {
124                    object_id,
125                    initial_shared_version,
126                    mutable,
127                }),
128                Input::Receiving(object_ref) => CallArg::Object(ObjectArg::Receiving(object_ref)),
129            };
130            binary.serialize(serializer)
131        }
132    }
133
134    impl<'de> Deserialize<'de> for Input {
135        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
136        where
137            D: Deserializer<'de>,
138        {
139            CallArg::deserialize(deserializer).map(|binary| match binary {
140                CallArg::Pure(value) => Input::Pure { value },
141                CallArg::Object(ObjectArg::ImmutableOrOwned(object_ref)) => {
142                    Input::ImmutableOrOwned(object_ref)
143                }
144                CallArg::Object(ObjectArg::Shared {
145                    object_id,
146                    initial_shared_version,
147                    mutable,
148                }) => Input::Shared {
149                    object_id,
150                    initial_shared_version,
151                    mutable,
152                },
153                CallArg::Object(ObjectArg::Receiving(object_ref)) => Input::Receiving(object_ref),
154            })
155        }
156    }
157}
158
159pub(crate) use signed_transaction::SignedTransactionWithIntentMessage;
160
161mod signed_transaction {
162    use serde::ser::SerializeSeq;
163
164    use super::*;
165    use crate::transaction::SignedTransaction;
166    use crate::transaction::Transaction;
167    use crate::UserSignature;
168
169    /// serde implementation that serializes a transaction prefixed with the signing intent. See
170    /// [struct Intent] for more info.
171    ///
172    /// So we need to serialize Transaction as (0, 0, 0, Transaction)
173    struct IntentMessageWrappedTransaction;
174
175    impl SerializeAs<Transaction> for IntentMessageWrappedTransaction {
176        fn serialize_as<S>(transaction: &Transaction, serializer: S) -> Result<S::Ok, S::Error>
177        where
178            S: Serializer,
179        {
180            use serde::ser::SerializeTuple;
181
182            let mut s = serializer.serialize_tuple(4)?;
183            s.serialize_element(&0u8)?;
184            s.serialize_element(&0u8)?;
185            s.serialize_element(&0u8)?;
186            s.serialize_element(transaction)?;
187            s.end()
188        }
189    }
190
191    impl<'de> DeserializeAs<'de, Transaction> for IntentMessageWrappedTransaction {
192        fn deserialize_as<D>(deserializer: D) -> Result<Transaction, D::Error>
193        where
194            D: Deserializer<'de>,
195        {
196            let (scope, version, app, transaction): (u8, u8, u8, Transaction) =
197                Deserialize::deserialize(deserializer)?;
198            match (scope, version, app) {
199                (0, 0, 0) => {}
200                _ => {
201                    return Err(serde::de::Error::custom(format!(
202                        "invalid intent message ({scope}, {version}, {app})"
203                    )))
204                }
205            }
206
207            Ok(transaction)
208        }
209    }
210
211    pub(crate) struct SignedTransactionWithIntentMessage;
212
213    #[derive(serde_derive::Serialize)]
214    struct BinarySignedTransactionWithIntentMessageRef<'a> {
215        #[serde(with = "::serde_with::As::<IntentMessageWrappedTransaction>")]
216        transaction: &'a Transaction,
217        signatures: &'a Vec<UserSignature>,
218    }
219
220    #[derive(serde_derive::Deserialize)]
221    struct BinarySignedTransactionWithIntentMessage {
222        #[serde(with = "::serde_with::As::<IntentMessageWrappedTransaction>")]
223        transaction: Transaction,
224        signatures: Vec<UserSignature>,
225    }
226
227    impl SerializeAs<SignedTransaction> for SignedTransactionWithIntentMessage {
228        fn serialize_as<S>(
229            transaction: &SignedTransaction,
230            serializer: S,
231        ) -> Result<S::Ok, S::Error>
232        where
233            S: Serializer,
234        {
235            if serializer.is_human_readable() {
236                transaction.serialize(serializer)
237            } else {
238                let SignedTransaction {
239                    transaction,
240                    signatures,
241                } = transaction;
242                let binary = BinarySignedTransactionWithIntentMessageRef {
243                    transaction,
244                    signatures,
245                };
246
247                let mut s = serializer.serialize_seq(Some(1))?;
248                s.serialize_element(&binary)?;
249                s.end()
250            }
251        }
252    }
253
254    impl<'de> DeserializeAs<'de, SignedTransaction> for SignedTransactionWithIntentMessage {
255        fn deserialize_as<D>(deserializer: D) -> Result<SignedTransaction, D::Error>
256        where
257            D: Deserializer<'de>,
258        {
259            if deserializer.is_human_readable() {
260                SignedTransaction::deserialize(deserializer)
261            } else {
262                struct V;
263                impl<'de> serde::de::Visitor<'de> for V {
264                    type Value = SignedTransaction;
265
266                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
267                        formatter.write_str("expected a sequence with length 1")
268                    }
269
270                    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
271                    where
272                        A: serde::de::SeqAccess<'de>,
273                    {
274                        if seq.size_hint().is_some_and(|size| size != 1) {
275                            return Err(serde::de::Error::custom(
276                                "expected a sequence with length 1",
277                            ));
278                        }
279
280                        let BinarySignedTransactionWithIntentMessage {
281                            transaction,
282                            signatures,
283                        } = seq.next_element()?.ok_or_else(|| {
284                            serde::de::Error::custom("expected a sequence with length 1")
285                        })?;
286                        Ok(SignedTransaction {
287                            transaction,
288                            signatures,
289                        })
290                    }
291                }
292
293                deserializer.deserialize_seq(V)
294            }
295        }
296    }
297}
298
299#[cfg(test)]
300mod test {
301    use base64ct::Base64;
302    use base64ct::Encoding;
303
304    use crate::transaction::Transaction;
305
306    #[cfg(target_arch = "wasm32")]
307    use wasm_bindgen_test::wasm_bindgen_test as test;
308
309    #[test]
310    fn transaction_fixtures() {
311        const GENESIS_TRANSACTION: &str = include_str!("fixtures/genesis-transaction");
312        const CONSENSUS_PROLOGUE: &str = "AAMAAAAAAAAAAAIAAAAAAAAAtkjHeocBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA==";
313        const EPOCH_CHANGE: &str = "AAUCAmkBAAAAAAAAmSrgAQAAAAAAagEAAAAAAAApAAAAAAAAALAQCoNLLwAAnNn0sywGAABsVBEfSC0AAKQnlhd1AAAAzve+vo4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=";
314        const AUTHENTICATOR_STATE_UPDATE: &str =
315            include_str!("fixtures/authenticator_state_update");
316        const PTB: &str = "AAADAQFEBbUNeR/TNGdU6Bcaqra8LtJsLEbv3QM8FLMK5QesMyx96QEAAAAAAQAIVsakAAAAAAABALyyokbZ/8ynfWQer6UyP1DpeCnPU1NC7AyFNJSaTztnQF40BQAAAAAgffPXh5XuG6TWjHk6qC5w9k2a+41oTWfm0sC1FOYRqsEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN7pB2Nsb2JfdjIMY2FuY2VsX29yZGVyAgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgNzdWkDU1VJAAddSzAlBmRcN/8TO5jEtQpa4UhBZZc41tcz1Z0NIXqTvwRjb2luBENPSU4AAwEAAAEBAAECAPgh00g/x3Jeuvqlo9Ejc9SZAb384UhPIZ2qcGajDfd9ASXQjpFOD6mfycbzwD1wc+IOkCXQ8rHQo/Vi5SDOGMR/Jl40BQAAAAAgV7P1E0IMKon5uI82R/0arWLt+dc1ng/4VwKDqpTCxHT4IdNIP8dyXrr6paPRI3PUmQG9/OFITyGdqnBmow33fe4CAAAAAAAAAMqaOwAAAAAA";
317        const WORMHOLE_PYTH_TRANSACTION: &str = include_str!("fixtures/wormhole-pyth-transaction");
318
319        for fixture in [
320            GENESIS_TRANSACTION,
321            CONSENSUS_PROLOGUE,
322            EPOCH_CHANGE,
323            AUTHENTICATOR_STATE_UPDATE,
324            PTB,
325            WORMHOLE_PYTH_TRANSACTION,
326        ] {
327            let fixture = Base64::decode_vec(fixture.trim()).unwrap();
328            let tx: Transaction = bcs::from_bytes(&fixture).unwrap();
329            assert_eq!(bcs::to_bytes(&tx).unwrap(), fixture);
330
331            let json = serde_json::to_string_pretty(&tx).unwrap();
332            println!("{json}");
333            assert_eq!(tx, serde_json::from_str(&json).unwrap());
334        }
335    }
336}