polynode 0.12.1

Rust SDK for the PolyNode API — real-time Polymarket data
Documentation
//! All event types. Mirrors server's PolymarketEvent exactly.

use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::common::*;

/// Discriminated union of all PolyNode event types.
/// Uses `event_type` as the serde tag, matching the server's serialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event_type")]
pub enum PolyNodeEvent {
    #[serde(rename = "settlement")]
    Settlement(SettlementEvent),
    #[serde(rename = "trade")]
    Trade(TradeEvent),
    #[serde(rename = "position_change")]
    PositionChange(PositionChangeEvent),
    #[serde(rename = "deposit")]
    Deposit(DepositEvent),
    #[serde(rename = "block")]
    Block(BlockEvent),
    #[serde(rename = "status_update")]
    StatusUpdate(StatusUpdateEvent),
    #[serde(rename = "position_split")]
    PositionSplit(PositionSplitEvent),
    #[serde(rename = "position_merge")]
    PositionMerge(PositionMergeEvent),
    #[serde(rename = "oracle")]
    Oracle(OracleEvent),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecodedTrade {
    pub maker: String,
    pub signer: String,
    pub taker: String,
    pub token_id: String,
    pub side: TradeSide,
    pub price: f64,
    pub size: f64,
    pub maker_amount: String,
    pub taker_amount: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SettlementEvent {
    pub tx_hash: String,
    pub status: EventStatus,
    pub detected_at: i64,
    pub block_number: Option<u64>,
    pub taker_wallet: String,
    pub taker_token: String,
    pub taker_side: String,
    pub taker_price: f64,
    pub taker_size: f64,
    pub trades: Vec<DecodedTrade>,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub condition_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcomes: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_slug: Option<String>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradeEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub exchange: Exchange,
    pub maker: String,
    pub taker: String,
    pub token_id: String,
    pub side: TradeSide,
    pub price: f64,
    pub size: f64,
    pub maker_amount: String,
    pub taker_amount: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub fee: Option<f64>,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub condition_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcomes: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_slug: Option<String>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatusUpdateEvent {
    pub tx_hash: String,
    pub token_id: String,
    pub block_number: u64,
    pub confirmed_at: i64,
    pub pending_detected_at: i64,
    pub latency_ms: i64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub taker_wallet: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub maker_wallets: Option<Vec<String>>,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub condition_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcomes: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockEvent {
    pub block_number: u64,
    pub block_hash: String,
    pub timestamp: i64,
    pub parent_hash: String,
    pub tx_count: u32,
    pub polymarket_tx_count: u32,
    pub settlement_count: u32,
    pub trade_volume_usd: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionChangeEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub from: String,
    pub to: String,
    pub token_id: String,
    pub amount: f64,
    pub transfer_type: TransferType,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcome: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub condition_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcomes: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DepositEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub from: String,
    pub to: String,
    pub amount: f64,
    pub direction: DepositDirection,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionSplitEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub stakeholder: String,
    pub collateral_token: String,
    pub condition_id: String,
    pub amount: f64,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionMergeEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub stakeholder: String,
    pub collateral_token: String,
    pub condition_id: String,
    pub amount: f64,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tick_size: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
    #[serde(default)]
    pub taker_base_fee: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OracleEvent {
    pub tx_hash: String,
    pub block_number: u64,
    pub log_index: u32,
    pub timestamp: i64,
    pub question_id: String,
    pub oracle_type: OracleEventType,
    pub adapter_address: String,
    // Resolution fields
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub resolved_price: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub resolved_outcome: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub payouts: Option<Vec<u64>>,
    // Proposal fields
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub proposer: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub bond_amount: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub proposed_price: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub expiration_timestamp: Option<i64>,
    // Dispute fields
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub disputer: Option<String>,
    // Enrichment
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_slug: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub condition_id: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub event_title: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub market_image: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tokens: Option<HashMap<String, String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub token_ids: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub outcomes: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub neg_risk: Option<bool>,
}

/// Chainlink price feed event (sent separately from PolyNodeEvent).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PriceFeedEvent {
    pub feed: String,
    pub feed_id: String,
    pub price: f64,
    pub bid: f64,
    pub ask: f64,
    /// Observation timestamp in seconds (not milliseconds).
    pub timestamp: u32,
    pub source: String,
}