use anyhow::{Context, Result};
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use serde::de::DeserializeOwned;
use serde_json::Value;
use std::time::Duration;
#[derive(Clone)]
pub struct LcdClient {
client: reqwest::Client,
pub(crate) base_url: String,
}
impl LcdClient {
pub fn new(base_url: &str) -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(30))
.build()
.expect("failed to build HTTP client");
Self {
client,
base_url: base_url.trim_end_matches('/').to_string(),
}
}
pub async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
let url = format!("{}{}", self.base_url, path);
let resp = self
.client
.get(&url)
.send()
.await
.with_context(|| format!("LCD GET {url}"))?;
let status = resp.status();
if !status.is_success() {
let body = resp.text().await.unwrap_or_default();
anyhow::bail!("LCD GET {url} returned {status}: {body}");
}
resp.json::<T>()
.await
.with_context(|| format!("LCD GET {url} json parse"))
}
pub async fn get_json(&self, path: &str) -> Result<Value> {
self.get::<Value>(path).await
}
pub async fn smart_query(&self, contract: &str, query: &Value) -> Result<Value> {
let encoded = BASE64.encode(serde_json::to_string(query)?);
let path = format!("/cosmwasm/wasm/v1/contract/{contract}/smart/{encoded}");
let resp: Value = self.get(&path).await?;
Ok(resp.get("data").cloned().unwrap_or(resp))
}
}