1use serde_json::{Map, Value};
2
3use crate::error::{Error, Result};
4use crate::model::{
5 BurnTransaction, ByteString, CreateAliasTransaction, DataTransaction, EthereumTransaction,
6 ExchangeTransaction, GenesisTransaction, InvokeScriptTransaction, IssueTransaction,
7 LeaseCancelTransaction, LeaseTransaction, MassTransferTransaction, PaymentTransaction,
8 ReissueTransaction, SetAssetScriptTransaction, SetScriptTransaction, SignedOrder,
9 SignedTransaction, SponsorFeeTransaction, Transaction, TransactionData, TransferTransaction,
10 UpdateAssetInfoTransaction,
11};
12
13pub struct JsonSerializer;
14
15impl JsonSerializer {
16 pub fn serialize_signed_tx(sign_tx: &SignedTransaction) -> Result<Value> {
17 let mut json_props: Map<String, Value> = Map::new();
18 let mut json_props_with_default_values = add_default_fields(sign_tx, &mut json_props)?;
19 let json_props_with_additional_fields =
20 add_additional_fields(sign_tx.tx().data(), &mut json_props_with_default_values)?;
21 Ok(json_props_with_additional_fields.into())
22 }
23
24 pub fn serialize_signed_order(signed_order: &SignedOrder) -> Result<Value> {
25 signed_order.try_into()
26 }
27}
28
29fn add_default_fields(
30 sign_tx: &SignedTransaction,
31 json_props: &mut Map<String, Value>,
32) -> Result<Map<String, Value>> {
33 let tx = sign_tx.tx();
34 json_props.insert("type".to_string(), tx_type(tx).into());
35 json_props.insert("version".to_string(), tx.version().into());
36 json_props.insert("chainId".to_string(), tx.chain_id().into());
37 json_props.insert(
38 "senderPublicKey".to_string(),
39 tx.public_key().encoded().into(),
40 );
41 json_props.insert(
42 "sender".to_string(),
43 tx.public_key()
44 .address(sign_tx.tx().chain_id())?
45 .encoded()
46 .into(),
47 );
48 json_props.insert("fee".to_string(), tx.fee().value().into());
49 json_props.insert("feeAssetId".to_string(), tx.fee().asset_id().into());
50 json_props.insert("timestamp".to_string(), tx.timestamp().into());
51 json_props.insert("proofs".to_string(), proofs(sign_tx).into());
52 Ok(json_props.clone())
53}
54
55fn add_additional_fields(
56 tx_data: &TransactionData,
57 json_props: &mut Map<String, Value>,
58) -> Result<Map<String, Value>> {
59 match tx_data {
60 TransactionData::Genesis(_) => Err(Error::UnsupportedOperation(
61 "broadcasting genesis transaction".to_owned(),
62 ))?,
63 TransactionData::Payment(_) => Err(Error::UnsupportedOperation(
64 "broadcasting payment transaction".to_owned(),
65 ))?,
66 TransactionData::Transfer(transfer_tx) => {
67 json_props.append(&mut transfer_tx.try_into()?);
68 }
69 TransactionData::Data(data_tx) => {
70 json_props.append(&mut data_tx.try_into()?);
71 }
72 TransactionData::Issue(issue_tx) => {
73 json_props.append(&mut issue_tx.try_into()?);
74 }
75 TransactionData::InvokeScript(invoke_tx) => {
76 json_props.append(&mut invoke_tx.try_into()?);
77 }
78 TransactionData::Exchange(exchange_tx) => {
79 json_props.append(&mut exchange_tx.try_into()?);
80 }
81 TransactionData::Reissue(reissue_tx) => {
82 json_props.append(&mut reissue_tx.try_into()?);
83 }
84 TransactionData::Burn(burn_tx) => {
85 json_props.append(&mut burn_tx.try_into()?);
86 }
87 TransactionData::Lease(lease_tx) => {
88 json_props.append(&mut lease_tx.try_into()?);
89 }
90 TransactionData::LeaseCancel(lease_cancel_tx) => {
91 json_props.append(&mut lease_cancel_tx.try_into()?);
92 }
93 TransactionData::CreateAlias(create_alias_tx) => {
94 json_props.append(&mut create_alias_tx.try_into()?);
95 }
96 TransactionData::MassTransfer(mass_transfer_tx) => {
97 json_props.append(&mut mass_transfer_tx.try_into()?);
98 }
99 TransactionData::SetScript(set_script_tx) => {
100 json_props.append(&mut set_script_tx.try_into()?);
101 }
102 TransactionData::SetAssetScript(set_asset_script_tx) => {
103 json_props.append(&mut set_asset_script_tx.try_into()?);
104 }
105 TransactionData::SponsorFee(sponsor_fee_tx) => {
106 json_props.append(&mut sponsor_fee_tx.try_into()?);
107 }
108 TransactionData::UpdateAssetInfo(update_asset_info_tx) => {
109 json_props.append(&mut update_asset_info_tx.try_into()?);
110 }
111 TransactionData::Ethereum(_) => Err(Error::UnsupportedOperation(
112 "broadcasting ethereum transaction".to_owned(),
113 ))?,
114 };
115 Ok(json_props.clone())
116}
117
118fn tx_type(tx: &Transaction) -> u8 {
119 match tx.data() {
120 TransactionData::Genesis(_) => GenesisTransaction::tx_type(),
121 TransactionData::Payment(_) => PaymentTransaction::tx_type(),
122 TransactionData::Transfer(_) => TransferTransaction::tx_type(),
123 TransactionData::Data(_) => DataTransaction::tx_type(),
124 TransactionData::Issue(_) => IssueTransaction::tx_type(),
125 TransactionData::InvokeScript(_) => InvokeScriptTransaction::tx_type(),
126 TransactionData::Exchange(_) => ExchangeTransaction::tx_type(),
127 TransactionData::Reissue(_) => ReissueTransaction::tx_type(),
128 TransactionData::Burn(_) => BurnTransaction::tx_type(),
129 TransactionData::Lease(_) => LeaseTransaction::tx_type(),
130 TransactionData::LeaseCancel(_) => LeaseCancelTransaction::tx_type(),
131 TransactionData::CreateAlias(_) => CreateAliasTransaction::tx_type(),
132 TransactionData::MassTransfer(_) => MassTransferTransaction::tx_type(),
133 TransactionData::SetScript(_) => SetScriptTransaction::tx_type(),
134 TransactionData::SetAssetScript(_) => SetAssetScriptTransaction::tx_type(),
135 TransactionData::SponsorFee(_) => SponsorFeeTransaction::tx_type(),
136 TransactionData::UpdateAssetInfo(_) => UpdateAssetInfoTransaction::tx_type(),
137 TransactionData::Ethereum(_) => EthereumTransaction::tx_type(),
138 }
139}
140
141fn proofs(sign_tx: &SignedTransaction) -> Vec<String> {
142 sign_tx
143 .proofs()
144 .iter()
145 .map(|proof| proof.encoded())
146 .collect()
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::error::Result;
152 use crate::model::account::PublicKey;
153 use crate::model::data_entry::DataEntry;
154 use crate::model::{
155 Amount, ChainId, DataTransaction, Proof, SignedTransaction, Transaction, TransactionData,
156 };
157 use crate::util::{Base58, JsonSerializer};
158
159 #[test]
160 fn test_data_transaction_to_json() -> Result<()> {
161 let binary_value: [u8; 12] = [0; 12];
162
163 let transaction_data = TransactionData::Data(DataTransaction::new(vec![
164 DataEntry::IntegerEntry {
165 key: "int".to_string(),
166 value: 12,
167 },
168 DataEntry::BooleanEntry {
169 key: "bool".to_string(),
170 value: false,
171 },
172 DataEntry::BinaryEntry {
173 key: "binary".to_string(),
174 value: binary_value.to_vec(),
175 },
176 DataEntry::StringEntry {
177 key: "str".to_string(),
178 value: "value".to_string(),
179 },
180 ]));
181
182 let public_key = PublicKey::from_string("8jDzNuHZwuTTo6WvZMdSoNc8ydY6a7UnxvwHZ8kooMuS")?;
183 let signed_transaction = SignedTransaction::new(
184 Transaction::new(
185 transaction_data,
186 Amount::new(100000, None),
187 1661456063029,
188 public_key,
189 2,
190 ChainId::TESTNET.byte(),
191 ),
192 vec![
193 Proof::new(Base58::decode(
194 "4nDUCnVw9j9D5bTBSLfFCHR9CtvS32mSdxctccChRAohfLwz3ng3ps5ffUiy4NtRmXG7vDHRMW57ABxzkMW64tzC"
195 )?)
196 ],
197 );
198
199 let json = &JsonSerializer::serialize_signed_tx(&signed_transaction)?;
200 let signed_tx_from_json: SignedTransaction = json.try_into()?;
201 assert_eq!(signed_transaction, signed_tx_from_json);
202 Ok(())
203 }
204}