polynode 0.7.1

Rust SDK for the PolyNode API — real-time Polymarket data
Documentation
//! E2E test: Rust SDK trading — place a BUY order at $0.01 size 10, then cancel it.
//!
//! Build and run:
//!   cd /opt/polynode/sdks/rust && cargo build --features trading --example test_rust_sdk_trading
//!   ./target/debug/examples/test_rust_sdk_trading

use polynode::trading::{
    PolyNodeTrader, TraderConfig, PrivateKeySigner, BuilderCredentials,
    OrderParams, OrderSide, OrderType,
};

const POLYNODE_KEY: &str = "pn_live_test_session_tracking_51eca107e9b347b589f5b0a04f98eb1d";
const PRIVATE_KEY: &str = "0x50c2ee0264deda68347eac6d415e232ace9c458d24cb540a04098a2ff9d8406d";

const BUILDER_KEY: &str = "019b2911-5f21-74eb-a450-ad02d194649b";
const BUILDER_SECRET: &str = "0kRhmA8x35CzZniZKz1p7xlbfVIxRPx-EhpCEgMNZ-s=";
const BUILDER_PASSPHRASE: &str = "f61602d5aac0865acbbb96585692418f3930f84793af3fcf2a78cc4313e60a0f";

#[tokio::main]
async fn main() {
    println!("=== Rust SDK Trading E2E Test ===\n");

    // 1. Create trader with builder credentials
    println!("[1] Creating PolyNodeTrader...");
    let mut trader = PolyNodeTrader::new(TraderConfig {
        polynode_key: POLYNODE_KEY.into(),
        db_path: "/home/polygon/trading-lab/rust-sdk-test.db".into(),
        fallback_direct: true,
        builder_credentials: Some(BuilderCredentials {
            key: BUILDER_KEY.into(),
            secret: BUILDER_SECRET.into(),
            passphrase: BUILDER_PASSPHRASE.into(),
        }),
        ..Default::default()
    }).expect("Failed to create trader");
    println!("  OK\n");

    // 2. Onboard with private key
    println!("[2] Calling ensure_ready()...");
    let signer = PrivateKeySigner::from_hex(PRIVATE_KEY).expect("Invalid private key");
    let status = trader.ensure_ready(Box::new(signer), None).await.expect("ensure_ready failed");
    println!("  wallet:     {}", status.wallet);
    println!("  safe:       {}", status.funder_address);
    println!("  sig_type:   {:?}", status.signature_type);
    println!("  safe_deployed: {}", status.safe_deployed);
    println!("  approvals:  {}", status.approvals_set);
    println!("  actions:    {:?}", status.actions);
    println!("  OK\n");

    // 3. Check balance
    println!("[3] Checking balance...");
    match trader.check_balance(None).await {
        Ok(balance) => {
            println!("  USDC.e: {}", balance.usdc);
            println!("  MATIC:  {}", balance.matic);
            println!("  OK\n");
        }
        Err(e) => {
            println!("  WARNING: balance check failed (non-fatal): {}\n", e);
        }
    }

    // 4. Find a liquid market
    println!("[4] Finding a liquid market...");
    let client = reqwest::Client::builder()
        .user_agent("@polymarket/clob-client")
        .build()
        .expect("http client");
    let resp: serde_json::Value = client
        .get("https://clob.polymarket.com/sampling-markets")
        .send().await.expect("sampling-markets request failed")
        .json().await.expect("sampling-markets parse failed");

    let mut token_id: Option<String> = None;
    let mut market_question: Option<String> = None;
    if let Some(markets) = resp.get("data").and_then(|d| d.as_array()) {
        for market in markets {
            if let Some(tokens) = market.get("tokens").and_then(|t| t.as_array()) {
                if tokens.len() >= 2 {
                    if let Some(tid) = tokens[0].get("token_id").and_then(|v| v.as_str()) {
                        token_id = Some(tid.to_string());
                        market_question = market.get("question")
                            .and_then(|q| q.as_str())
                            .map(|s| s.to_string());
                        break;
                    }
                }
            }
        }
    }

    let token_id = token_id.expect("No liquid market found");
    println!("  market: {}", market_question.as_deref().unwrap_or("unknown"));
    println!("  tokenId: {}", token_id);
    println!("  OK\n");

    // 5. Place a BUY order at $0.01, size 10 (won't fill)
    println!("[5] Placing BUY order at $0.01, size 10...");
    let order_result = trader.order(OrderParams {
        token_id: token_id.clone(),
        side: OrderSide::Buy,
        price: 0.01,
        size: 10.0,
        order_type: OrderType::GTC,
        expiration: None,
        post_only: false,
    }).await;

    let order_result = match order_result {
        Ok(r) => r,
        Err(e) => {
            println!("  ERROR: order failed: {}", e);
            println!("\n=== FAIL ===");
            std::process::exit(1);
        }
    };

    println!("  success:  {}", order_result.success);
    println!("  orderId:  {:?}", order_result.order_id);
    println!("  status:   {:?}", order_result.status);
    println!("  error:    {:?}", order_result.error);
    println!("  making:   {:?}", order_result.making_amount);
    println!("  taking:   {:?}", order_result.taking_amount);

    if !order_result.success || order_result.order_id.is_none() {
        println!("\n  ERROR: order was not successful!");
        println!("\n=== FAIL ===");
        std::process::exit(1);
    }
    let order_id = order_result.order_id.unwrap();
    println!("  OK\n");

    // 6. Cancel the order
    println!("[6] Cancelling order {}...", order_id);
    let cancel_result = trader.cancel_order(&order_id).await;
    match cancel_result {
        Ok(cr) => {
            println!("  canceled:    {:?}", cr.canceled);
            println!("  notCanceled: {:?}", cr.not_canceled);
            println!("  OK\n");
        }
        Err(e) => {
            println!("  WARNING: cancel failed (order may have already expired): {}\n", e);
        }
    }

    // 7. Cleanup
    trader.close();

    println!("=== PASS: Rust SDK order placed and cancelled successfully ===");
}