pub mod accepted;
pub mod call;
pub mod error;
pub use accepted::Accepted;
pub use call::Call;
pub use error::RelayerError;
use df_hyperliquid_core::client::Http;
use df_hyperliquid_core::utils::{Json, hex};
use crate::client::deployment::ORIGIN;
use crate::trade::intent::{Intent, Signed};
use crate::utils::wire::BadWire;
#[derive(Debug, Clone)]
pub struct Relayer<H> {
http: H,
base: String,
}
impl<H> Relayer<H> {
pub fn new(http: H) -> Self {
Relayer { http, base: ORIGIN.to_owned() }
}
pub fn at(http: H, base: impl Into<String>) -> Self {
Relayer { http, base: base.into() }
}
#[must_use]
pub fn base(&self) -> &str {
&self.base
}
#[must_use]
pub const fn http(&self) -> &H {
&self.http
}
}
impl<H: Http> Relayer<H> {
pub async fn send(&self, call: &Call) -> Result<String, RelayerError> {
let response = self.http.send(call.request(&self.base)).await?;
if !(200..300).contains(&response.status) {
return Err(error::refusal(response.status, &response.body));
}
let text = String::from_utf8(response.body)
.map_err(|_| BadWire { what: "body".to_owned(), found: "not UTF-8".to_owned() })?;
if let Ok(json) = Json::parse(&text)
&& (json.get("error").is_some_and(|error| *error != Json::Null)
|| matches!(json.get("ok"), Some(Json::Bool(false))))
{
return Err(error::refusal(response.status, text.as_bytes()));
}
Ok(text)
}
pub async fn submit(&self, signed: &Signed) -> Result<Accepted, RelayerError> {
let accepted = Accepted::parse(&self.send(&Call::submit(signed)).await?)?;
if matches!(signed.intent(), Intent::CancelIntent(_)) {
return Ok(accepted);
}
let sent = signed.intent_id();
if accepted.intent_id != sent {
return Err(RelayerError::OtherIntent {
sent: hex::encode_prefixed(&sent),
found: hex::encode_prefixed(&accepted.intent_id),
});
}
Ok(accepted)
}
}