use crate::errors::{ErrorKind, Result};
use crate::core_types::{Coin, Msg, StdFee};
use num_traits::cast::ToPrimitive;
use reqwest::header::{HeaderMap, CONTENT_TYPE, USER_AGENT};
use reqwest::{Client, RequestBuilder};
use serde::{Deserialize, Serialize};
mod auth;
pub mod auth_types;
pub mod client_types;
pub mod core_types;
mod market;
pub mod market_types;
mod oracle;
pub mod oracle_types;
mod staking;
pub mod staking_types;
mod tendermint;
pub mod tendermint_types;
mod tx;
pub mod tx_types;
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
pub struct GasOptions {
pub fees: Option<Coin>,
pub estimate_gas: bool,
pub gas: Option<u64>,
pub gas_price: Option<Coin>,
pub gas_adjustment: Option<f64>,
}
pub struct Terra<'a> {
client: Client,
url: &'a str,
pub chain_id: &'a str,
pub gas_options: Option<&'a GasOptions>,
}
impl<'a> Terra<'_> {
pub async fn lcd_client(
url: &'a str,
chain_id: &'a str,
gas_options: &'a GasOptions,
) -> Result<Terra<'a>> {
let client = reqwest::Client::new();
Ok(Terra {
client,
url,
chain_id,
gas_options: Some(gas_options),
})
}
pub async fn lcd_client_no_tx(url: &'a str, chain_id: &'a str) -> Result<Terra<'a>> {
let client = reqwest::Client::new();
Ok(Terra {
client,
url,
chain_id,
gas_options: None,
})
}
pub fn auth(&self) -> auth::Auth {
auth::Auth::create(&self)
}
pub fn staking(&self) -> staking::Staking {
staking::Staking::create(&self)
}
pub fn market(&self) -> market::Market {
market::Market::create(&self)
}
pub fn oracle(&self) -> oracle::Oracle {
oracle::Oracle::create(&self)
}
pub fn tendermint(&self) -> tendermint::Tendermint {
tendermint::Tendermint::create(&self)
}
pub fn tx(&self) -> tx::TX {
tx::TX::create(&self)
}
fn construct_headers() -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(
USER_AGENT,
format!("PFC-TerraRust/{}", VERSION.unwrap_or("-?-"))
.parse()
.unwrap(),
);
headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
headers
}
pub async fn send_cmd<T: for<'de> Deserialize<'de>>(
&self,
path: &str,
args: Option<&str>,
) -> Result<T> {
let request_url = match args {
Some(a) => format!("{}{}{}", self.url.to_owned(), path, a),
None => format!("{}{}", self.url.to_owned(), path),
};
log::debug!("URL={}", &request_url);
let req = self
.client
.get(&request_url)
.headers(Terra::construct_headers());
Terra::resp::<T>(&request_url, req).await
}
pub async fn post_cmd<R: for<'de> Serialize, T: for<'de> Deserialize<'de>>(
&self,
path: &str,
args: &R,
) -> Result<T> {
let request_url = format!("{}{}", self.url.to_owned(), path);
log::debug!("URL={}", &request_url);
let req = self
.client
.post(&request_url)
.headers(Terra::construct_headers())
.json::<R>(args);
Terra::resp::<T>(&request_url, req).await
}
async fn resp<T: for<'de> Deserialize<'de>>(
request_url: &str,
req: RequestBuilder,
) -> Result<T> {
let response = req.send().await?;
if !response.status().is_success() {
let status_text = response.text().await?;
log::error!("URL={} - {}", &request_url, &status_text);
Err(ErrorKind::Terra(status_text).into())
} else {
let struct_response: T = response.json::<T>().await?;
Ok(struct_response)
}
}
}