waves_rust/util/json/
json_serializer.rs

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}