1use serde_json::{Map, Value};
4use super::{SignedTransaction, ElrondCurrencyAmount, ElrondAddress, Result, ElrondClientError};
5
6enum RequestType {
8 GET,
9 POST
10}
11
12pub struct Client {
14 endpoint: String
15}
16
17impl Client {
18 fn request(&self, path: &str, request_type: RequestType, data: Option<Value>) -> Result<Value> {
20 let full_path = format!("{}/{}", self.endpoint, path);
21 let resp = match (request_type, data) {
22 (RequestType::GET, Some(json)) => {
23 ureq::get(&full_path).send_json(json)
24 },
25 (RequestType::GET, None) => {
26 ureq::get(&full_path).call()
27 },
28 (RequestType::POST, Some(json)) => {
29 ureq::post(&full_path).send_json(json)
30 },
31 (RequestType::POST, None) => {
32 ureq::post(&full_path).call()
33 }
34 };
35 if resp.ok() {
36 let response = resp.into_json().map_err(|_| {
37 ElrondClientError::new("could not decode response to JSON")
38 })?;
39 Ok(response)
40 } else {
41 let status = &resp.status();
42 let error_response = resp.into_string().map_err(|_|{
43 ElrondClientError::new("could not decode error response to string")
44 })?;
45 Err(ElrondClientError::new(
46 &format!(
47 "error code {}, data='{}'",
48 status,
49 error_response
50 )
51 ))
52 }
53 }
54}
55
56fn parse_response_data(response: &Value) -> Result<&Map<String, Value>> {
58 response
59 .as_object()
60 .ok_or(
61 ElrondClientError::new("response is not a JSON object")
62 )?
63 .get("data")
64 .ok_or(
65 ElrondClientError::new("response does not contain 'data' field")
66 )?
67 .as_object()
68 .ok_or(
69 ElrondClientError::new("'data' is not a JSON object")
70 )
71}
72
73
74impl Client {
75 pub fn new() -> Self {
77 Self { endpoint: "https://api.elrond.com".to_string() }
78 }
79
80 pub fn get_address_nonce(&self, addr_str: &str) -> Result<u64> {
82 let address = ElrondAddress::new(addr_str)?;
83 let path = format!("address/{}/nonce", address.to_string());
84 let response = self.request(&path, RequestType::GET, None)?;
85 let nonce = parse_response_data(&response)?
86 .get("nonce")
87 .ok_or(
88 ElrondClientError::new("response does not contain 'nonce' field")
89 )?
90 .as_u64()
91 .ok_or(
92 ElrondClientError::new("'nonce' is not a number")
93 )?;
94 Ok(nonce)
95 }
96
97 pub fn post_signed_transaction(&self, signed_tx: SignedTransaction) -> Result<String>{
99 let path = "transaction/send";
100 let serialized_tx = signed_tx.serialize()?;
101 let json_tx = serde_json::from_str(&serialized_tx).unwrap();
103 let response = self.request(path, RequestType::POST, Some(json_tx))?;
104 let tx_hash = parse_response_data(&response)?
105 .get("txHash")
106 .ok_or(ElrondClientError::new("response does not contain 'txHash' field"))?
107 .as_str()
108 .ok_or(ElrondClientError::new("tx hash is not a string"))?;
109 Ok(tx_hash.to_string())
110 }
111
112 pub fn get_address_balance(&self, addr_str: &str) -> Result<ElrondCurrencyAmount> {
114 let address = ElrondAddress::new(addr_str)?;
115 let path = format!("address/{}/balance", address.to_string());
116 let response = self.request(&path, RequestType::GET, None)?;
117 let balance = parse_response_data(&response)?
118 .get("balance")
119 .ok_or(
120 ElrondClientError::new("response does not contain 'balance' field")
121 )?
122 .as_str()
123 .ok_or(
124 ElrondClientError::new("'balance' is not a number")
125 )?;
126 Ok(ElrondCurrencyAmount::from_blockchain_precision(balance)?)
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::Client;
133 use super::super::account::Account;
134
135 #[test]
136 pub fn get_address_nonce(){
137 let address = "erd16jats393r8rnut88yhvu5wvxxje57qzlj3tqk7n6jnf7f6cxs4uqfeh65k";
138 let client = Client::new();
139 let response = client.get_address_nonce(address).unwrap();
140 println!("{:?}", response);
141 }
142
143 #[test]
144 pub fn get_newly_generated_address_nonce() {
145 let account = Account::generate().unwrap();
146 let address = account.address.to_string();
147 println!("{}", address);
148 let client = Client::new();
149 let nonce = client.get_address_nonce(&address).unwrap();
150 println!("{:?}", nonce);
151 assert_eq!(nonce, 0);
152 }
153
154 #[test]
155 pub fn get_address_balance() {
156 let client = Client::new();
157 let address = "erd18gx50mf0xvz3c3xljm0s5pkz0zugsprltl86ux6f0sz94gqeua3q7l77wd";
158 let balance = client.get_address_balance(address).unwrap();
159 assert_eq!(balance.to_string(), "0.0001");
160 }
161
162}
163
164
165