sui_sdk_types/transaction/
serialization.rs1use 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 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}