polymarket-rs-client 0.1.1

An async client to interact with polymarket.
Documentation
use crate::Decimal;

use crate::SignedOrderRequest;
use alloy_primitives::U256;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::Display;
use std::str::FromStr;

const ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";

pub enum AssetType {
    COLLATERAL,
    CONDITIONAL,
}

#[allow(clippy::to_string_trait_impl)]
impl ToString for AssetType {
    fn to_string(&self) -> String {
        match self {
            AssetType::COLLATERAL => "COLLATERAL".to_string(),
            AssetType::CONDITIONAL => "CONDITIONAL".to_string(),
        }
    }
}

#[derive(Default)]
pub struct BalanceAllowanceParams {
    pub asset_type: Option<AssetType>,
    pub token_id: Option<String>,
    pub signature_type: Option<u8>,
}

impl BalanceAllowanceParams {
    pub fn to_query_params(&self) -> Vec<(&str, String)> {
        let mut params = Vec::with_capacity(3);

        if let Some(x) = &self.asset_type {
            params.push(("asset_type", x.to_string()));
        }

        if let Some(x) = &self.token_id {
            params.push(("token_id", x.to_string()));
        }

        if let Some(x) = &self.signature_type {
            params.push(("signature_type", x.to_string()));
        }
        params
    }
}

impl BalanceAllowanceParams {
    pub fn set_signature_type(&mut self, s: u8) {
        self.signature_type = Some(s);
    }
}

#[derive(Debug)]
pub struct TradeParams {
    pub id: Option<String>,
    pub maker_address: Option<String>,
    pub market: Option<String>,
    pub asset_id: Option<String>,
    pub before: Option<u64>,
    pub after: Option<u64>,
}

impl TradeParams {
    pub fn to_query_params(&self) -> Vec<(&str, String)> {
        let mut params = Vec::with_capacity(4);

        if let Some(x) = &self.id {
            params.push(("id", x.clone()));
        }

        if let Some(x) = &self.asset_id {
            params.push(("asset_id", x.clone()));
        }

        if let Some(x) = &self.market {
            params.push(("market", x.clone()));
        }
        if let Some(x) = &self.before {
            params.push(("before", x.to_string()));
        }
        if let Some(x) = &self.after {
            params.push(("after", x.to_string()));
        }
        params
    }
}

#[derive(Debug, Deserialize)]
pub struct OpenOrder {
    pub associate_trades: Vec<String>,
    pub id: String,
    pub status: String,
    pub market: String,

    #[serde(with = "rust_decimal::serde::str")]
    pub original_size: Decimal,
    pub outcome: String,
    pub maker_address: String,
    pub owner: String,

    #[serde(with = "rust_decimal::serde::str")]
    pub price: Decimal,
    pub side: Side,

    #[serde(with = "rust_decimal::serde::str")]
    pub size_matched: Decimal,
    pub asset_id: String,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    pub expiration: u64,
    #[serde(rename = "type")]
    pub order_type: OrderType,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    pub created_at: u64,
}

#[derive(Debug)]
pub struct OpenOrderParams {
    pub id: Option<String>,
    pub asset_id: Option<String>,
    pub market: Option<String>,
}

impl OpenOrderParams {
    pub fn to_query_params(&self) -> Vec<(&str, &String)> {
        let mut params = Vec::with_capacity(4);

        if let Some(x) = &self.id {
            params.push(("id", x));
        }

        if let Some(x) = &self.asset_id {
            params.push(("asset_id", x));
        }

        if let Some(x) = &self.market {
            params.push(("market", x));
        }
        params
    }
}

fn deserialize_number_from_string<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: FromStr + serde::Deserialize<'de>,
    <T as FromStr>::Err: Display,
{
    #[derive(Deserialize)]
    #[serde(untagged)]
    enum StringOrInt<T> {
        String(String),
        Number(T),
    }

    match StringOrInt::<T>::deserialize(deserializer)? {
        StringOrInt::String(s) => s.parse::<T>().map_err(serde::de::Error::custom),
        StringOrInt::Number(i) => Ok(i),
    }
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PostOrder {
    order: SignedOrderRequest,
    owner: String,
    order_type: OrderType,
}

impl PostOrder {
    pub fn new(order: SignedOrderRequest, owner: String, order_type: OrderType) -> Self {
        PostOrder {
            order,
            owner,
            order_type,
        }
    }
}

#[derive(Debug)]
pub struct OrderArgs {
    pub token_id: String,
    pub price: Decimal,
    pub size: Decimal,
    pub side: Side,
}

#[derive(Debug, Deserialize)]
pub struct OrderBookSummary {
    pub market: String,
    pub asset_id: String,
    pub hash: String,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    pub timestamp: u64,
    pub bids: Vec<OrderSummary>,
    pub asks: Vec<OrderSummary>,
}

#[derive(Debug)]
pub struct MarketOrderArgs {
    pub token_id: String,
    pub amount: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct OrderSummary {
    #[serde(with = "rust_decimal::serde::str")]
    pub price: Decimal,
    #[serde(with = "rust_decimal::serde::str")]
    pub size: Decimal,
}

impl OrderArgs {
    pub fn new(token_id: &str, price: Decimal, size: Decimal, side: Side) -> Self {
        OrderArgs {
            token_id: token_id.to_owned(),
            price,
            size,
            side,
        }
    }
}

#[derive(Debug)]
pub struct ExtraOrderArgs {
    pub fee_rate_bps: u32,
    pub nonce: U256,
    pub taker: String,
}

impl Default for ExtraOrderArgs {
    fn default() -> Self {
        ExtraOrderArgs {
            fee_rate_bps: 0,
            nonce: U256::ZERO,
            taker: ZERO_ADDRESS.into(),
        }
    }
}

#[derive(Debug, Default)]
pub struct CreateOrderOptions {
    pub tick_size: Option<Decimal>,
    pub neg_risk: Option<bool>,
}

#[derive(Debug, Deserialize)]
pub struct ApiKeysResponse {
    #[serde(rename = "apiKeys")]
    pub api_keys: Vec<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct MidpointResponse {
    #[serde(with = "rust_decimal::serde::str")]
    pub mid: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct PriceResponse {
    #[serde(with = "rust_decimal::serde::str")]
    pub price: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct SpreadResponse {
    #[serde(with = "rust_decimal::serde::str")]
    pub spread: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct TickSizeResponse {
    pub minimum_tick_size: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct NegRiskResponse {
    pub neg_risk: bool,
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
pub enum OrderType {
    GTC,
    FOK,
    GTD,
}

impl OrderType {
    pub fn as_str(&self) -> &'static str {
        match self {
            OrderType::GTC => "GTC",
            OrderType::FOK => "FOK",
            OrderType::GTD => "GTD",
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)]
pub enum Side {
    BUY = 0,
    SELL = 1,
}

impl Side {
    pub fn as_str(&self) -> &'static str {
        match self {
            Side::BUY => "BUY",
            Side::SELL => "SELL",
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct BookParams {
    pub token_id: String,
    pub side: Side,
}

#[derive(Default, Debug, Serialize, Deserialize)]
pub struct ApiCreds {
    #[serde(rename = "apiKey")]
    pub api_key: String,
    pub secret: String,
    pub passphrase: String,
}