waves_rust/model/asset/
balance.rs1use crate::error::{Error, Result};
2use crate::model::{Address, AssetId, IssueTransactionInfo};
3use crate::util::JsonDeserializer;
4use serde_json::Value;
5
6#[derive(Clone, Eq, PartialEq, Debug)]
7pub struct AssetsBalanceResponse {
8 address: Address,
9 balances: Vec<AssetBalance>,
10}
11
12impl AssetsBalanceResponse {
13 pub fn new(address: Address, balances: Vec<AssetBalance>) -> AssetsBalanceResponse {
14 AssetsBalanceResponse { address, balances }
15 }
16
17 pub fn address(&self) -> Address {
18 self.address.clone()
19 }
20
21 pub fn balances(&self) -> Vec<AssetBalance> {
22 self.balances.clone()
23 }
24}
25
26impl TryFrom<&Value> for AssetsBalanceResponse {
27 type Error = Error;
28
29 fn try_from(value: &Value) -> Result<Self> {
30 let address = Address::from_string(&JsonDeserializer::safe_to_string_from_field(
31 value, "address",
32 )?)?;
33 let balances = JsonDeserializer::safe_to_array_from_field(value, "balances")?
34 .iter()
35 .map(|v| v.try_into())
36 .collect::<Result<Vec<AssetBalance>>>()?;
37 Ok(AssetsBalanceResponse::new(address, balances))
38 }
39}
40
41#[derive(Clone, Eq, PartialEq, Debug)]
42pub struct AssetBalance {
43 asset_id: AssetId,
44 balance: u64,
45 reissuable: bool,
46 min_sponsored_asset_fee: Option<u64>,
47 sponsor_balance: Option<u64>,
48 quantity: u64,
49 issue_transaction: Option<IssueTransactionInfo>,
50}
51
52impl AssetBalance {
53 pub fn new(
54 asset_id: AssetId,
55 balance: u64,
56 reissuable: bool,
57 min_sponsored_asset_fee: Option<u64>,
58 sponsor_balance: Option<u64>,
59 quantity: u64,
60 issue_transaction: Option<IssueTransactionInfo>,
61 ) -> AssetBalance {
62 AssetBalance {
63 asset_id,
64 balance,
65 reissuable,
66 min_sponsored_asset_fee,
67 sponsor_balance,
68 quantity,
69 issue_transaction,
70 }
71 }
72
73 pub fn asset_id(&self) -> AssetId {
74 self.asset_id.clone()
75 }
76
77 pub fn balance(&self) -> u64 {
78 self.balance
79 }
80
81 pub fn reissuable(&self) -> bool {
82 self.reissuable
83 }
84
85 pub fn min_sponsored_asset_fee(&self) -> Option<u64> {
86 self.min_sponsored_asset_fee
87 }
88
89 pub fn sponsor_balance(&self) -> Option<u64> {
90 self.sponsor_balance
91 }
92
93 pub fn quantity(&self) -> u64 {
94 self.quantity
95 }
96
97 pub fn issue_transaction(&self) -> Option<IssueTransactionInfo> {
98 self.issue_transaction.clone()
99 }
100}
101
102impl TryFrom<&Value> for AssetBalance {
103 type Error = Error;
104
105 fn try_from(value: &Value) -> Result<Self> {
106 let asset_id = AssetId::from_string(&JsonDeserializer::safe_to_string_from_field(
107 value, "assetId",
108 )?)?;
109 let balance = JsonDeserializer::safe_to_int_from_field(value, "balance")? as u64;
110 let reissuable = JsonDeserializer::safe_to_boolean_from_field(value, "reissuable")?;
111 let min_sponsored_asset_fee: Option<u64> = value["minSponsoredAssetFee"].as_u64();
112 let sponsor_balance: Option<u64> = value["sponsorBalance"].as_u64();
113 let quantity = JsonDeserializer::safe_to_int_from_field(value, "quantity")? as u64;
114 let issue_transaction = match value["issueTransaction"].as_object() {
115 Some(obj) => {
116 let issue_json: &Value = &obj.clone().into();
117 Some(issue_json.try_into()?)
118 }
119 None => None,
120 };
121 Ok(AssetBalance::new(
122 asset_id,
123 balance,
124 reissuable,
125 min_sponsored_asset_fee,
126 sponsor_balance,
127 quantity,
128 issue_transaction,
129 ))
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use crate::error::Result;
136 use crate::model::asset::balance::AssetsBalanceResponse;
137 use crate::model::ByteString;
138 use serde_json::Value;
139 use std::fs;
140
141 #[test]
142 fn test_json_to_assets_balance_response() -> Result<()> {
143 let data = fs::read_to_string("./tests/resources/assets/assets_balance_rs.json")
144 .expect("Unable to read file");
145 let json: &Value = &serde_json::from_str(&data).expect("failed to convert");
146 let assets_balance: AssetsBalanceResponse = json.try_into()?;
147 assert_eq!(
148 "3Mq3pueXcAgLcuWvJzJ4ndRHfqYgjUZvL7q",
149 assets_balance.address().encoded()
150 );
151
152 let balances = assets_balance.balances();
153 let asset_balance1 = &balances[0];
154 assert_eq!(
155 "85gPhjumNgwaMUpGfx9jEQqJMorbEjTQ4EUAHwfoYKjd",
156 asset_balance1.asset_id().encoded()
157 );
158 assert_eq!(false, asset_balance1.reissuable());
159 assert_eq!(None, asset_balance1.min_sponsored_asset_fee());
160 assert_eq!(None, asset_balance1.sponsor_balance());
161 assert_eq!(32, asset_balance1.quantity());
162 assert_eq!(42, asset_balance1.balance());
163 let issue_tx = asset_balance1
164 .issue_transaction()
165 .expect("must not be empty");
166
167 assert_eq!(
168 "85gPhjumNgwaMUpGfx9jEQqJMorbEjTQ4EUAHwfoYKjd",
169 issue_tx.asset_id().encoded()
170 );
171 assert_eq!("test asset", issue_tx.name());
172 assert_eq!(32, issue_tx.quantity());
173 assert_eq!(false, issue_tx.is_reissuable());
174 assert_eq!(3, issue_tx.decimals());
175 assert_eq!("this is test asset", issue_tx.description());
176 assert_eq!(None, issue_tx.script());
177
178 let asset_balance2 = &assets_balance.balances()[1];
179 assert_eq!(
180 "GyH2wqKQcjHtz6KgkUNzUpDYYy1azqZdYHZ2awXHWqYx",
181 asset_balance2.asset_id().encoded()
182 );
183 assert_eq!(false, asset_balance2.reissuable());
184 assert_eq!(Some(1), asset_balance2.min_sponsored_asset_fee());
185 assert_eq!(Some(199900003), asset_balance2.sponsor_balance());
186 assert_eq!(2, asset_balance2.quantity());
187 assert_eq!(None, asset_balance2.issue_transaction());
188 assert_eq!(2, asset_balance2.balance());
189
190 Ok(())
191 }
192}