waves_rust/model/transaction/
transfer_transaction.rs1use crate::error::{Error, Result};
2use crate::model::{Address, Amount, AssetId, Base58String, ByteString};
3use crate::util::{Base58, JsonDeserializer};
4use crate::waves_proto::{recipient, Amount as ProtoAmount, Recipient, TransferTransactionData};
5use serde_json::{Map, Value};
6
7const TYPE: u8 = 4;
8
9#[derive(Clone, Eq, PartialEq, Debug)]
10pub struct TransferTransactionInfo {
11 recipient: Address,
12 amount: Amount,
13 attachment: Base58String,
14}
15
16impl TransferTransactionInfo {
17 pub fn attachment(&self) -> Base58String {
18 self.attachment.clone()
19 }
20
21 pub fn amount(&self) -> Amount {
22 self.amount.clone()
23 }
24
25 pub fn recipient(&self) -> Address {
26 self.recipient.clone()
27 }
28}
29
30impl TryFrom<&Value> for TransferTransactionInfo {
31 type Error = Error;
32
33 fn try_from(value: &Value) -> Result<Self> {
34 let transfer_transaction: TransferTransaction = value.try_into()?;
35 Ok(TransferTransactionInfo {
36 recipient: transfer_transaction.recipient(),
37 amount: transfer_transaction.amount(),
38 attachment: transfer_transaction.attachment(),
39 })
40 }
41}
42
43#[derive(Clone, Eq, PartialEq, Debug)]
44pub struct TransferTransaction {
45 recipient: Address,
46 amount: Amount,
47 attachment: Base58String,
48}
49
50impl TransferTransaction {
51 pub fn new(
52 recipient: Address,
53 amount: Amount,
54 attachment: Base58String,
55 ) -> TransferTransaction {
56 TransferTransaction {
57 recipient,
58 amount,
59 attachment,
60 }
61 }
62
63 pub fn recipient(&self) -> Address {
64 self.recipient.clone()
65 }
66
67 pub fn amount(&self) -> Amount {
68 self.amount.clone()
69 }
70
71 pub fn attachment(&self) -> Base58String {
72 self.attachment.clone()
73 }
74
75 pub fn tx_type() -> u8 {
76 TYPE
77 }
78}
79
80impl TryFrom<&TransferTransaction> for TransferTransactionData {
81 type Error = Error;
82
83 fn try_from(transfer_tx: &TransferTransaction) -> Result<Self> {
84 let recipient = Some(Recipient {
85 recipient: Some(recipient::Recipient::PublicKeyHash(
86 transfer_tx.recipient().public_key_hash(),
87 )),
88 });
89 let asset_id = match transfer_tx.amount().asset_id() {
90 Some(value) => value.bytes(),
91 None => vec![],
92 };
93 let amount = Some(ProtoAmount {
94 asset_id,
95 amount: transfer_tx.amount().value() as i64,
96 });
97 Ok(TransferTransactionData {
98 recipient,
99 amount,
100 attachment: transfer_tx.attachment().bytes(),
101 })
102 }
103}
104
105impl TryFrom<&Value> for TransferTransaction {
106 type Error = Error;
107
108 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
109 let recipient = JsonDeserializer::safe_to_string_from_field(value, "recipient")?;
110 let asset: Option<AssetId> = match value["assetId"].as_str() {
111 Some(value) => {
112 let vec = Base58::decode(value)?;
113 Some(AssetId::from_bytes(vec))
114 }
115 None => None,
116 };
117 let amount = JsonDeserializer::safe_to_int_from_field(value, "amount")? as u64;
118 let attachment = match value["attachment"].as_str() {
119 Some(value) => Base58String::from_string(value)?,
120 None => Base58String::empty(),
121 };
122
123 Ok(TransferTransaction {
124 recipient: Address::from_string(&recipient)?,
125 amount: Amount::new(amount, asset),
126 attachment,
127 })
128 }
129}
130
131impl TryFrom<&TransferTransaction> for Map<String, Value> {
132 type Error = Error;
133
134 fn try_from(transfer_tx: &TransferTransaction) -> Result<Self> {
135 let mut json = Map::new();
136 json.insert(
137 "recipient".to_owned(),
138 transfer_tx.recipient().encoded().into(),
139 );
140 json.insert("amount".to_owned(), transfer_tx.amount().value().into());
141 json.insert("assetId".to_owned(), transfer_tx.amount().asset_id().into());
142 json.insert(
143 "attachment".to_owned(),
144 transfer_tx.attachment().encoded().into(),
145 );
146 Ok(json)
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use crate::error::Result;
153 use crate::model::{Address, Amount, AssetId, Base58String, ByteString, TransferTransaction};
154 use crate::waves_proto::recipient::Recipient;
155 use crate::waves_proto::TransferTransactionData;
156 use serde_json::{json, Map, Value};
157 use std::borrow::Borrow;
158 use std::fs;
159
160 #[test]
161 fn test_json_to_transfer_transaction() {
162 let data =
163 fs::read_to_string("./tests/resources/transfer_rs.json").expect("Unable to read file");
164 let json: Value = serde_json::from_str(&data).expect("failed to generate json from str");
165
166 let transfer_tx_from_json: TransferTransaction = json.borrow().try_into().unwrap();
167
168 assert_eq!(
169 "3Mq3pueXcAgLcuWvJzJ4ndRHfqYgjUZvL7q",
170 transfer_tx_from_json.recipient().encoded()
171 );
172 let amount = transfer_tx_from_json.amount();
173 assert_eq!(None, amount.asset_id());
174 assert_eq!(200000000, amount.value());
175 assert_eq!("", transfer_tx_from_json.attachment().encoded())
176 }
177
178 #[test]
179 fn test_transfer_to_proto() -> Result<()> {
180 let transfer_tx = &TransferTransaction::new(
181 Address::from_string("3Mq3pueXcAgLcuWvJzJ4ndRHfqYgjUZvL7q")?,
182 Amount::new(
183 32,
184 Some(AssetId::from_string(
185 "8bt2MZjuUCJPmfucPfaZPTXqrxmoCHCC8gVnbjZ7bhH6",
186 )?),
187 ),
188 Base58String::from_bytes(vec![1, 2, 3]),
189 );
190 let proto: TransferTransactionData = transfer_tx.try_into()?;
191
192 let proto_recipient = if let Recipient::PublicKeyHash(bytes) =
193 proto.clone().recipient.unwrap().recipient.unwrap()
194 {
195 bytes
196 } else {
197 panic!("expected dapp public key hash")
198 };
199 assert_eq!(proto_recipient, transfer_tx.recipient().public_key_hash());
200 let amount = proto.amount.unwrap();
201 assert_eq!(amount.amount as u64, transfer_tx.amount().value());
202 assert_eq!(
203 Some(amount.asset_id),
204 transfer_tx.amount().asset_id().map(|it| it.bytes())
205 );
206
207 Ok(())
208 }
209
210 #[test]
211 fn test_transfer_to_json() -> Result<()> {
212 let transfer_tx = &TransferTransaction::new(
213 Address::from_string("3Mq3pueXcAgLcuWvJzJ4ndRHfqYgjUZvL7q")?,
214 Amount::new(
215 32,
216 Some(AssetId::from_string(
217 "8bt2MZjuUCJPmfucPfaZPTXqrxmoCHCC8gVnbjZ7bhH6",
218 )?),
219 ),
220 Base58String::from_bytes(vec![1, 2, 3]),
221 );
222 let map: Map<String, Value> = transfer_tx.try_into()?;
223 let json: Value = map.into();
224 let expected_json = json!({
225 "recipient": "3Mq3pueXcAgLcuWvJzJ4ndRHfqYgjUZvL7q",
226 "assetId": "8bt2MZjuUCJPmfucPfaZPTXqrxmoCHCC8gVnbjZ7bhH6",
227 "amount": 32,
228 "attachment": "Ldp",
229 });
230 assert_eq!(expected_json, json);
231 Ok(())
232 }
233}