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