use reqwest::Client;
use super::types::*;
#[derive(Clone, Debug)]
pub struct RelayerClient {
base_url: String,
client: Client,
}
impl RelayerClient {
pub fn new(base_url: impl Into<String>) -> Self {
Self {
base_url: base_url.into(),
client: Client::new(),
}
}
pub fn mainnet() -> Self {
Self::new("https://relayer-v2.polymarket.com")
}
pub async fn get_relay_payload(
&self,
proxy_wallet: &str,
tx_type: &str,
) -> Result<RelayPayload, Box<dyn std::error::Error>> {
let url = format!("{}/relay-payload", self.base_url);
let response = self
.client
.get(&url)
.query(&[("address", proxy_wallet), ("type", tx_type)])
.send()
.await?;
let status = response.status();
let body_text = response.text().await?;
if !status.is_success() {
eprintln!("Relayer error (get_relay_payload): Status {}, Body: {}", status, body_text);
return Err(format!("Relayer returned status {}: {}", status, body_text).into());
}
serde_json::from_str(&body_text).map_err(|e| {
eprintln!("Failed to parse relay payload. Body: {}", body_text);
eprintln!("Parse error: {}", e);
e.into()
})
}
pub async fn submit(
&self,
request: RelayerSubmitRequest,
) -> Result<RelayerSubmitResponse, Box<dyn std::error::Error>> {
let url = format!("{}/submit", self.base_url);
let response = self
.client
.post(&url)
.json(&request)
.send()
.await?;
let status = response.status();
let body_text = response.text().await?;
if !status.is_success() {
eprintln!("Relayer error (submit): Status {}, Body: {}", status, body_text);
return Err(format!("Relayer returned status {}: {}", status, body_text).into());
}
serde_json::from_str(&body_text).map_err(|e| {
eprintln!("Failed to parse submit response. Body: {}", body_text);
eprintln!("Parse error: {}", e);
e.into()
})
}
pub async fn get_transaction(
&self,
transaction_id: &str,
) -> Result<Vec<RelayerTransaction>, reqwest::Error> {
let url = format!("{}/transaction", self.base_url);
let response = self
.client
.get(&url)
.query(&[("id", transaction_id)])
.send()
.await?;
response.json().await
}
pub async fn wait_for_execution(
&self,
transaction_id: &str,
max_attempts: Option<u32>,
poll_interval_ms: Option<u64>,
) -> Result<RelayerTransaction, Box<dyn std::error::Error>> {
let max_attempts = max_attempts.unwrap_or(60);
let poll_interval = std::time::Duration::from_millis(poll_interval_ms.unwrap_or(1000));
for attempt in 0..max_attempts {
let transactions = self.get_transaction(transaction_id).await?;
if let Some(tx) = transactions.first() {
match tx.state.as_str() {
"STATE_EXECUTED" => return Ok(tx.clone()),
"STATE_FAILED" => {
return Err(format!("Transaction failed: {}", tx.state).into());
}
_ => {
if attempt < max_attempts - 1 {
tokio::time::sleep(poll_interval).await;
}
}
}
} else {
return Err("Transaction not found".into());
}
}
Err("Transaction did not complete within timeout".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_relayer_client_creation() {
let client = RelayerClient::mainnet();
assert_eq!(client.base_url, "https://relayer-v2.polymarket.com");
}
#[test]
fn test_custom_relayer_url() {
let client = RelayerClient::new("https://custom-relayer.example.com");
assert_eq!(client.base_url, "https://custom-relayer.example.com");
}
}