use polyfill_rs::{ClobClient, PolyfillError, Result, Side};
use rust_decimal::Decimal;
use tokio::time::{sleep, Duration};
use tracing::{error, info, warn};
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
info!("Polyfill-rs Quick Demo");
info!("======================");
let client = ClobClient::new("https://clob.polymarket.com");
info!("\nTesting API Connectivity...");
match test_connectivity(&client).await {
Ok(_) => info!("API connectivity test passed"),
Err(e) => {
error!("API connectivity test failed: {}", e);
return Err(e);
},
}
info!("\nGetting Market Data...");
let token_id = match get_valid_token_id(&client).await {
Ok(id) => {
info!("Found valid token ID: {}", id);
id
},
Err(e) => {
error!("Failed to get valid token ID: {}", e);
return Err(e);
},
};
info!("\nTesting Market Data Endpoints...");
test_market_data_endpoints(&client, &token_id).await?;
info!("\nTesting Error Handling...");
test_error_handling(&client).await?;
info!("\nTesting Performance...");
test_performance(&client, &token_id).await?;
info!("\nAll tests completed successfully!");
info!("The polyfill-rs client is working correctly with the Polymarket API.");
Ok(())
}
async fn test_connectivity(client: &ClobClient) -> Result<()> {
let is_ok = client.get_ok().await;
if !is_ok {
return Err(PolyfillError::network(
"API not responding",
std::io::Error::other("API not responding"),
));
}
info!(" /ok endpoint responding");
let server_time = client.get_server_time().await?;
info!(" Server time: {}", server_time);
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let time_diff = server_time.abs_diff(current_time);
if time_diff > 86400 {
warn!(" Server time seems off (diff: {} seconds)", time_diff);
} else {
info!(" Server time is reasonable");
}
Ok(())
}
async fn get_valid_token_id(client: &ClobClient) -> Result<String> {
let markets = client.get_sampling_markets(None).await?;
if markets.data.is_empty() {
return Err(PolyfillError::api(404, "No markets found"));
}
for market in &markets.data {
if market.active && !market.closed {
for token in &market.tokens {
if !token.token_id.is_empty() {
info!(" Found active market: {}", market.question);
info!(" Market slug: {}", market.market_slug);
info!(" Token ID: {}", token.token_id);
info!(" Outcome: {}", token.outcome);
return Ok(token.token_id.clone());
}
}
}
}
Err(PolyfillError::api(
404,
"No active markets with valid tokens found",
))
}
async fn test_market_data_endpoints(client: &ClobClient, token_id: &str) -> Result<()> {
info!(" Testing order book endpoint...");
let order_book = client.get_order_book(token_id).await?;
info!(
" Order book: {} bids, {} asks",
order_book.bids.len(),
order_book.asks.len()
);
info!(" Testing midpoint endpoint...");
let midpoint = client.get_midpoint(token_id).await?;
info!(" Midpoint: {}", midpoint.mid);
info!(" Testing spread endpoint...");
let spread = client.get_spread(token_id).await?;
info!(" Spread: {}", spread.spread);
info!(" Testing buy price endpoint...");
let buy_price = client.get_price(token_id, Side::BUY).await?;
info!(" Buy price: {}", buy_price.price);
info!(" Testing sell price endpoint...");
let sell_price = client.get_price(token_id, Side::SELL).await?;
info!(" Sell price: {}", sell_price.price);
info!(" Testing tick size endpoint...");
let tick_size = client.get_tick_size(token_id).await?;
info!(" Tick size: {}", tick_size);
info!(" Testing neg risk endpoint...");
let neg_risk = client.get_neg_risk(token_id).await?;
info!(" Neg risk: {}", neg_risk);
info!(" Validating data consistency...");
validate_market_data(&order_book, &midpoint, &spread, &buy_price, &sell_price)?;
Ok(())
}
fn validate_market_data(
order_book: &polyfill_rs::client::OrderBookSummary,
midpoint: &polyfill_rs::client::MidpointResponse,
spread: &polyfill_rs::client::SpreadResponse,
buy_price: &polyfill_rs::client::PriceResponse,
sell_price: &polyfill_rs::client::PriceResponse,
) -> Result<()> {
if order_book.bids.is_empty() && order_book.asks.is_empty() {
warn!(" Order book is empty");
} else {
info!(" Order book has liquidity");
}
if buy_price.price <= Decimal::ZERO {
warn!(" Buy price is not positive: {}", buy_price.price);
} else {
info!(" Buy price is positive");
}
if sell_price.price <= Decimal::ZERO {
warn!(" Sell price is not positive: {}", sell_price.price);
} else {
info!(" Sell price is positive");
}
if spread.spread < Decimal::ZERO {
warn!(" Spread is negative: {}", spread.spread);
} else {
info!(" Spread is non-negative");
}
if buy_price.price > Decimal::ZERO && sell_price.price > Decimal::ZERO {
if midpoint.mid < buy_price.price || midpoint.mid > sell_price.price {
warn!(
" Midpoint {} is not between buy {} and sell {}",
midpoint.mid, buy_price.price, sell_price.price
);
} else {
info!(" Midpoint is between buy and sell prices");
}
}
Ok(())
}
async fn test_error_handling(client: &ClobClient) -> Result<()> {
info!(" Testing invalid token ID...");
let result = client.get_order_book("invalid_token_12345").await;
match result {
Ok(_) => {
warn!(" Invalid token ID returned data instead of error");
},
Err(e) => match e {
PolyfillError::Api { status, .. } => {
if status >= 400 {
info!(" Invalid token ID correctly returned error: {}", status);
} else {
warn!(" Unexpected status code for invalid token: {}", status);
}
},
_ => {
info!(" Invalid token ID returned error: {:?}", e);
},
},
}
info!(" Testing empty token ID...");
let result = client.get_order_book("").await;
match result {
Ok(_) => {
warn!(" Empty token ID returned data instead of error");
},
Err(e) => {
info!(" Empty token ID correctly returned error: {:?}", e);
},
}
Ok(())
}
async fn test_performance(client: &ClobClient, token_id: &str) -> Result<()> {
let mut total_time = Duration::from_secs(0);
let mut success_count = 0;
let test_count = 5;
info!(" Running {} performance tests...", test_count);
for i in 1..=test_count {
let start = std::time::Instant::now();
let results = tokio::join!(
client.get_server_time(),
client.get_midpoint(token_id),
client.get_spread(token_id),
);
let duration = start.elapsed();
total_time += duration;
match results {
(Ok(_), Ok(_), Ok(_)) => {
success_count += 1;
info!(
" Test {}: PASSED {:.2}ms",
i,
duration.as_secs_f64() * 1000.0
);
},
_ => {
warn!(
" Test {}: FAILED in {:.2}ms",
i,
duration.as_secs_f64() * 1000.0
);
},
}
sleep(Duration::from_millis(100)).await;
}
let avg_time = total_time / test_count as u32;
let success_rate = (success_count as f64 / test_count as f64) * 100.0;
info!(" Performance Summary:");
info!(" Success rate: {:.1}%", success_rate);
info!(
" Average response time: {:.2}ms",
avg_time.as_secs_f64() * 1000.0
);
info!(" Total time: {:.2}s", total_time.as_secs_f64());
if avg_time > Duration::from_secs(2) {
warn!(
" Average response time is slow: {:.2}ms",
avg_time.as_secs_f64() * 1000.0
);
} else {
info!(" Response times are acceptable");
}
if success_rate < 80.0 {
warn!(" Success rate is low: {:.1}%", success_rate);
} else {
info!(" Success rate is good");
}
Ok(())
}