1use reqwest::StatusCode;
2#[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 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 response = self
34 .terra
35 .post_cmd::<StdTx, TXResultAsync>("/txs", &std_tx)
36 .await?;
37 Ok(response)
38 }
39 #[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 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 #[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 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 #[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 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 #[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 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 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 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 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}