Skip to main content

terra_rust_api/client/
tx.rs

1use reqwest::StatusCode;
2//use crate::client::core_types::Msg;
3#[allow(deprecated)]
4use crate::client::tx_types::TXResultBlock;
5use crate::client::tx_types::{
6    TXResultAsync, TXResultSync, TxEstimate, TxFeeResult, V1TXResult, V1TXSResult,
7};
8
9use crate::core_types::{Coin, StdSignMsg, StdSignature, StdTx};
10use crate::errors::TerraRustAPIError;
11use crate::errors::TerraRustAPIError::TXNotFound;
12use crate::messages::Message;
13use crate::{LCDResult, Terra};
14
15#[allow(clippy::upper_case_acronyms)]
16pub struct TX<'a> {
17    terra: &'a Terra,
18}
19impl<'a> TX<'a> {
20    pub fn create(terra: &'a Terra) -> TX<'a> {
21        TX { terra }
22    }
23    /// perform an Async submission to the blockchain. This returns the TXHash
24    /// This is not guaranteed to successfully create a transaction record, due to numerous factors
25    pub async fn broadcast_async(
26        &self,
27        std_sign_msg: &StdSignMsg,
28        sigs: &[StdSignature],
29    ) -> Result<TXResultAsync, TerraRustAPIError> {
30        let std_tx: StdTx = StdTx::from_StdSignMsg(std_sign_msg, sigs, "async");
31
32        //  let js_sig = serde_json::to_string(&std_tx)?;
33        let response = self
34            .terra
35            .post_cmd::<StdTx, TXResultAsync>("/txs", &std_tx)
36            .await?;
37        Ok(response)
38    }
39    /// perform a sync submission to the blockchain. This will return more validation logic than async
40    /// but you wait. It is still not guaranteed to create a blockchain transaction
41    #[allow(deprecated)]
42    pub async fn broadcast_sync(
43        &self,
44        std_sign_msg: &StdSignMsg,
45        sigs: &[StdSignature],
46    ) -> Result<TXResultSync, TerraRustAPIError> {
47        let std_tx: StdTx = StdTx::from_StdSignMsg(std_sign_msg, sigs, "sync");
48        //    let js_sig = serde_json::to_string(&std_tx)?;
49        log::debug!("TX broadcast #messages ={}", &std_tx.tx.msg.len());
50        if self.terra.debug {
51            log::info!("TX broadcast #messages ={}", &std_tx.tx.msg.len());
52            log::debug!("{}", serde_json::to_string(&std_tx)?);
53        }
54        let response = self
55            .terra
56            .post_cmd::<StdTx, TXResultSync>("/txs", &std_tx)
57            .await?;
58        Ok(response)
59    }
60    /// perform a 'blocking' submission to the blockchain. This will only return once the transaction
61    /// is executed on the blockchain. This is great for debugging, but not recommended to be used otherwise
62    #[allow(deprecated)]
63    pub async fn broadcast_block(
64        &self,
65        std_sign_msg: &StdSignMsg,
66        sigs: &[StdSignature],
67    ) -> Result<TXResultBlock, TerraRustAPIError> {
68        log::warn!("Broadcast_block is not recommended to be used in production situations");
69        let std_tx: StdTx = StdTx::from_StdSignMsg(std_sign_msg, sigs, "block");
70        //    let js_sig = serde_json::to_string(&std_tx)?;
71        let response = self
72            .terra
73            .post_cmd::<StdTx, TXResultBlock>("/txs", &std_tx)
74            .await?;
75        Ok(response)
76    }
77    #[deprecated(
78        since = "1.2.12",
79        note = "terra has deprecated this API endpoint. use get_v1"
80    )]
81    /// get TX result
82    #[allow(deprecated)]
83    pub async fn get(&self, hash: &str) -> Result<TXResultBlock, TerraRustAPIError> {
84        let resp = self
85            .terra
86            .send_cmd::<TXResultBlock>(&format!("/txs/{}", hash), None)
87            .await?;
88        Ok(resp)
89    }
90    /// use v1 API
91    pub async fn get_v1(&self, hash: &str) -> Result<V1TXResult, TerraRustAPIError> {
92        let resp = self
93            .terra
94            .send_cmd::<V1TXResult>(&format!("/cosmos/tx/v1beta1/txs/{}", hash), None)
95            .await?;
96        Ok(resp)
97    }
98    #[deprecated(
99        since = "1.2.12",
100        note = "terra has deprecated this API endpoint. Use get_and_wait_v1"
101    )]
102    /// get TX result, retrying a few times.
103    #[allow(deprecated)]
104    pub async fn get_and_wait(
105        &self,
106        hash: &str,
107        max_times: usize,
108        sleep_amount: tokio::time::Duration,
109    ) -> Result<TXResultBlock, TerraRustAPIError> {
110        let mut times = 0;
111        while times < max_times {
112            #[allow(deprecated)]
113            let tx = self.get(hash).await;
114
115            match tx {
116                Ok(tx_response) => return Ok(tx_response),
117                Err(e) => {
118                    times += 1;
119                    match &e {
120                        TerraRustAPIError::TerraLCDResponse(statuscode, out) => {
121                            if statuscode == &StatusCode::NOT_FOUND {
122                                log::debug!(
123                                    "Transaction not applied .. retry #{} sleeping {} seconds",
124                                    times,
125                                    sleep_amount.as_secs()
126                                );
127                                tokio::time::sleep(sleep_amount).await;
128                            } else {
129                                log::error!("Invalid Response TX: {} {}", statuscode, out);
130                                break;
131                            }
132                        }
133                        _ => {
134                            log::error!("Invalid Response TX: {:?}", e);
135                            break;
136                        }
137                    }
138                }
139            }
140        }
141        Err(TXNotFound(hash.to_string(), max_times))
142    }
143    /// get TX result, retrying a few times.
144    pub async fn get_and_wait_v1(
145        &self,
146        hash: &str,
147        max_times: usize,
148        sleep_amount: tokio::time::Duration,
149    ) -> Result<V1TXResult, TerraRustAPIError> {
150        let mut times = 0;
151        while times < max_times {
152            let tx = self.get_v1(hash).await;
153
154            match tx {
155                Ok(tx_response) => return Ok(tx_response),
156                Err(e) => {
157                    times += 1;
158                    match &e {
159                        TerraRustAPIError::TerraLCDResponse(statuscode, out) => {
160                            if statuscode == &StatusCode::BAD_REQUEST {
161                                log::debug!(
162                                    "Transaction not applied .. retry #{} sleeping {} seconds",
163                                    times,
164                                    sleep_amount.as_secs()
165                                );
166                                tokio::time::sleep(sleep_amount).await;
167                            } else {
168                                log::error!("Invalid Response TX: {} {}", statuscode, out);
169                                break;
170                            }
171                        }
172                        _ => {
173                            log::error!("Invalid Response TX: {:?}", e);
174                            break;
175                        }
176                    }
177                }
178            }
179        }
180        Err(TXNotFound(hash.to_string(), max_times))
181    }
182
183    /// Estimate the StdFee structure based on the gas used
184    pub async fn estimate_fee(
185        &self,
186        sender: &str,
187        msgs: &[Message],
188        gas_adjustment: f64,
189        gas_prices: &[&Coin],
190    ) -> Result<LCDResult<TxFeeResult>, TerraRustAPIError> {
191        let tx_est = TxEstimate::create(
192            &self.terra.chain_id,
193            sender,
194            msgs,
195            gas_adjustment,
196            gas_prices,
197        );
198
199        if self.terra.debug {
200            log::info!("Estimate Transaction = {}", serde_json::to_string(&tx_est)?);
201        } else {
202            log::debug!(
203                "Estimate Transaction = {:#?} #messages={}",
204                tx_est.base_req,
205                tx_est.msgs.len()
206            )
207        }
208        let resp = self
209            .terra
210            .post_cmd::<TxEstimate, LCDResult<TxFeeResult>>("/txs/estimate_fee", &tx_est)
211            .await?;
212        Ok(resp)
213    }
214    /// simulate transaction for estimating gas usage
215    pub async fn simulate_v1(
216        &self,
217        sender: &str,
218        msgs: &[Message],
219        gas_adjustment: f64,
220        gas_prices: &[&Coin],
221    ) -> Result<LCDResult<TxFeeResult>, TerraRustAPIError> {
222        let tx_est = TxEstimate::create(
223            &self.terra.chain_id,
224            sender,
225            msgs,
226            gas_adjustment,
227            gas_prices,
228        );
229
230        log::debug!(
231            "simulate Transaction = {:#?} #messages={}",
232            tx_est.base_req,
233            tx_est.msgs.len()
234        );
235
236        let resp = self
237            .terra
238            .post_cmd::<TxEstimate, LCDResult<TxFeeResult>>("/cosmos/tx/v1beta1/simulate", &tx_est)
239            .await?;
240        Ok(resp)
241    }
242    /// Get a list of transactions in a given block
243    pub async fn get_txs_in_block(
244        &self,
245        height: u64,
246        offset: Option<u64>,
247        limit: Option<u64>,
248    ) -> Result<V1TXSResult, TerraRustAPIError> {
249        let resp = self
250            .terra
251            .send_cmd::<V1TXSResult>(
252                &format!(
253                    "/cosmos/tx/v1beta1/txs?events=tx.height={}&order_by=ORDER_BY_ASC&pagination.limit={}&pagination.offset={}",
254                    height,
255                    limit.unwrap_or(100),
256                    offset.unwrap_or_default()
257                ),
258                None,
259            )
260            .await?;
261        Ok(resp)
262    }
263}