tron_api_client/
client.rs

1use crate::error::{Error, Result};
2use crate::params::*;
3use crate::response::{
4    Account, AccountNet, AssetIssueList, Block, BlockList, ChainParameters, Contract, NodeInfo,
5    NodeList, Transaction, TransactionInfo, WitnessList,
6};
7use reqwest::{Client as HttpClient, Method, RequestBuilder, Response};
8use serde::{de::DeserializeOwned, Serialize};
9use serde_json;
10use url::Url;
11
12#[derive(Debug)]
13pub struct Client {
14    base_url: Url,
15    // private_key: String,
16    http_client: HttpClient,
17}
18
19pub enum Address {
20    Base58(String),
21    Hex(String),
22}
23pub struct TxId(pub String);
24
25pub enum Network {
26    Main,
27    Shasta,
28}
29
30async fn decode_response<T>(res: Response) -> Result<T>
31where
32    T: DeserializeOwned,
33{
34    let data = res.text().await?;
35    // dbg!(&data);
36
37    let s: T =
38        serde_json::from_str(&data).map_err(|orig_err| match serde_json::from_str(&data) {
39            Err(_) => {
40                println!("{}", data);
41                // dbg!(&data);
42                orig_err.into()
43            }
44            Ok(r) => Error::ServerError(r),
45        })?;
46
47    Ok(s)
48}
49
50impl Client {
51    pub fn new(base_url: String) -> Self {
52        Client {
53            base_url: Url::parse(&base_url).expect("could not parse base_url"),
54            http_client: HttpClient::new(),
55        }
56    }
57
58    pub fn for_network(network: Network) -> Self {
59        let base_url = match network {
60            Network::Shasta => "https://api.shasta.trongrid.io".to_string(),
61            Network::Main => "https://api.trongrid.io".to_string(),
62            _ => unimplemented!(),
63        };
64        Self::new(base_url)
65    }
66
67    pub fn for_shasta() -> Self {
68        Self::for_network(Network::Shasta)
69    }
70
71    pub fn for_main() -> Self {
72        Self::for_network(Network::Main)
73    }
74
75    // todo: for_network(shasta) -> Client (uses trongrid.io api url for shasta
76
77    async fn prep_req(&self, method: Method, url: Url) -> Result<RequestBuilder> {
78        let req = self
79            .http_client
80            .request(method, url)
81            .header("Content-Type", "application/json");
82        Ok(req)
83    }
84
85    fn get_url(&self, path: &str) -> Url {
86        self.base_url.join(path).expect("could not parse url")
87    }
88
89    async fn req<T, U>(&self, path: &str, method: Method, body: U) -> Result<T>
90    where
91        T: DeserializeOwned,
92        U: Serialize,
93    {
94        let res = match method {
95            Method::GET => {
96                self.prep_req(method, self.get_url(path))
97                    .await?
98                    .send()
99                    .await?
100            }
101            Method::POST => {
102                self.prep_req(method, self.get_url(path))
103                    .await?
104                    .json(&body)
105                    .send()
106                    .await?
107            }
108            _ => unimplemented!(),
109        };
110        decode_response::<T>(res).await
111    }
112
113    async fn post<T, U>(&self, path: &str, param: U) -> Result<T>
114    where
115        T: DeserializeOwned,
116        U: Serialize,
117    {
118        self.req(path, Method::POST, param).await
119    }
120
121    async fn get<T>(&self, path: &str) -> Result<T>
122    where
123        T: DeserializeOwned,
124    {
125        self.req(path, Method::GET, EmptyBody::default()).await
126    }
127
128    pub async fn get_node_info(&self) -> Result<NodeInfo> {
129        self.get("/wallet/getnodeinfo").await
130    }
131
132    pub async fn list_nodes(&self) -> Result<NodeList> {
133        self.get("/wallet/listnodes").await
134    }
135
136    pub async fn list_witnesses(&self) -> Result<WitnessList> {
137        self.get("/walletsolidity/listwitnesses").await
138    }
139
140    pub async fn get_chain_parameters(&self) -> Result<ChainParameters> {
141        self.get("/wallet/getchainparameters").await
142    }
143
144    pub async fn get_block_by_num(&self, num: u64) -> Result<Block> {
145        self.post("/wallet/getblockbynum", GetBlockByNumParams::new(num))
146            .await
147    }
148
149    pub async fn get_block_by_id(&self, id: &str) -> Result<Block> {
150        self.post("/wallet/getblockbyid", GetBlockByIdParams::new(id.into()))
151            .await
152    }
153
154    pub async fn get_now_block(&self) -> Result<Block> {
155        self.post("/wallet/getnowblock", EmptyBody::default()).await
156    }
157
158    // num is the number of blocks to query (not the block height)
159    pub async fn get_block_by_latest_num(&self, num: u64) -> Result<BlockList> {
160        self.post("/wallet/getblockbylatestnum", GetBlockByNumParams::new(num))
161            .await
162    }
163
164    pub async fn get_block_by_limit_next(&self, start_num: u64, end_num: u64) -> Result<BlockList> {
165        self.post(
166            "/wallet/getblockbylimitnext",
167            GetBlockByRangeParams::new(start_num, end_num),
168        )
169        .await
170    }
171
172    // TODO:
173    // walletgetblockbylatestnum
174    // getblockbylimitnext
175    // createtransaction
176    // getnowblock
177    // listnodes
178    // gettransactioninfobyid
179    // gettransactionbyid
180    // getchainparameters
181    // etc...
182
183    // TODO
184
185    pub async fn get_account(&self, address: Address) -> Result<Account> {
186        self.post("/walletsolidity/getaccount", GetAccountParams::new(address))
187            .await
188    }
189
190    pub async fn get_account_net(&self, address: Address) -> Result<AccountNet> {
191        self.post("/wallet/getaccountnet", GetAccountParams::new(address))
192            .await
193    }
194
195    // TODO: retry if tron node returns an empty object `{}`?
196    // This happens when querying a TX in a recently mined block.
197    pub async fn get_transaction_by_id(&self, tx_id: TxId) -> Result<Transaction> {
198        self.post(
199            "/wallet/gettransactionbyid",
200            GetTransactionParams::new(tx_id),
201        )
202        .await
203    }
204
205    pub async fn get_transaction_info_by_id(&self, tx_id: TxId) -> Result<TransactionInfo> {
206        self.post(
207            "/wallet/gettransactioninfobyid",
208            GetTransactionParams::new(tx_id),
209        )
210        .await
211    }
212
213    pub async fn get_contract(&self, address: Address) -> Result<Contract> {
214        self.post("/wallet/getcontract", GetContractParams::new(address))
215            .await
216    }
217
218    // TRC10
219    //  https://api.trongrid.io/walletsolidity/getassetissuelist
220    pub async fn get_asset_issue_list(&self) -> Result<AssetIssueList> {
221        self.post("/walletsolidity/getassetissuelist", EmptyBody::default())
222            .await
223    }
224}