polynode 0.13.11

Rust SDK for the PolyNode API — real-time Polymarket data
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# polynode

Rust SDK for the [PolyNode](https://polynode.dev) real-time Polymarket API.

Stream settlements, trades, positions, deposits, oracle events, orderbook updates, and more through a single WebSocket connection. All events enriched with full market metadata.

**New in v0.13.11:** Rust V2 trading now separates user CLOB credentials, public builder attribution, and relayer auth. The default path uses PolyNode's public builder code plus PolyNode managed relay with a per-user relayer key; users can still bring their own builder code, direct Polymarket builder credentials, or direct RPC submission for Safe/proxy calls.

**New in v0.13.10:** Published docs now include explicit Rust examples for both supported V2 wallet identities: existing Safe users (`POLY_GNOSIS_SAFE`) and new deposit-wallet users (`POLY_1271`).

**New in v0.13.9:** Rust V2 trading keeps Safe and deposit-wallet identities separate: pass an explicit `signature_type` plus `funder_address` to route existing Safe users through `POLY_GNOSIS_SAFE`, while new deposit-wallet users continue through `POLY_1271`. Safe V2 onboarding can now relay pUSD/CTF approvals, and stored credentials are reused without silently flipping wallet type.

**New in v0.13.8:** Rust deposit-wallet V2 trading now self-heals deployed-wallet onboarding, includes USDC.e collateral-onramp approval, uses the deposit wallet as both order maker and signer for `POLY_1271`, wraps available USDC.e into pUSD before buy orders, and matches Polymarket's current V2 `POLY_1271` order body and wrapped signature format.

**New in v0.12:** V2 order flow — place orders on the Polymarket V2 CLOB with pUSD collateral and builder attribution. See [`src/trading/V2_ORDER_FLOW.md`](src/trading/V2_ORDER_FLOW.md) for the full reference.

**In v0.5:** [Local Cache](#local-cache) — SQLite-backed local storage. Backfill wallet history in seconds, query trades and positions instantly with zero API calls.

## Install

```toml
[dependencies]
polynode = "0.13.11"
tokio = { version = "1", features = ["rt", "macros"] }

# For local cache (optional):
# polynode = { version = "0.13.11", features = ["cache"] }
```

## Quick Start

```rust
use polynode::PolyNodeClient;

#[tokio::main]
async fn main() -> polynode::Result<()> {
    let client = PolyNodeClient::new("pn_live_...")?;

    // Fetch top markets
    let markets = client.markets(Some(10)).await?;
    println!("{} markets, {} total", markets.count, markets.total);

    // Search
    let results = client.search("bitcoin", Some(5), None).await?;
    for r in &results.results {
        println!("{}", r.question.as_deref().unwrap_or("?"));
    }

    Ok(())
}
```

## Rust V2 Trading Wallet Modes

Polymarket V2 supports two smart-wallet identities. They are not interchangeable:

- Existing Safe users: `SignatureType::PolyGnosisSafe`, with the Safe address as `funder_address`.
- New deposit-wallet users: `SignatureType::Poly1271`, with the deposit wallet address as `funder_address`.

Pass both values explicitly whenever you know the user's Polymarket wallet type. This prevents the SDK from deriving a different wallet and sending orders with a maker address that the CLOB does not allow.

The SDK also separates three credential paths:

- User CLOB API credentials are created or loaded by `ensure_ready()` and always authenticate order placement, cancels, open orders, and balance/allowance checks.
- V2 builder codes are public bytes32 attribution values. `TraderConfig::default()` uses PolyNode's builder code; users can override it with `TraderConfig.builder_code` or per order with `OrderParams.builder`.
- Relayer auth is used only for gasless smart-wallet calls such as deploy, approve, wrap, and unwrap. `RelayerMode::Auto` uses PolyNode managed relay when `polynode_key` and `cosigner_url` are configured, then falls back to direct builder credentials when supplied. `RelayerMode::DirectRpc` bypasses the relayer for Safe/proxy calls and pays gas from the signer.

Existing Safe user:

```rust
use polynode::trading::{
    EnsureReadyOpts, ExchangeVersion, PolyNodeTrader, PrivateKeySigner,
    SignatureType, TraderConfig,
};

let signer = PrivateKeySigner::from_hex(&private_key)?;
let mut trader = PolyNodeTrader::new(TraderConfig {
    polynode_key: "pn_live_...".into(),
    exchange_version: ExchangeVersion::V2,
    ..Default::default()
})?;

trader.ensure_ready(
    Box::new(signer),
    Some(EnsureReadyOpts {
        signature_type: Some(SignatureType::PolyGnosisSafe),
        funder_address: Some(safe_address),
    }),
).await?;
```

Deposit-wallet user:

```rust
use polynode::trading::{
    onboarding::derive_deposit_wallet_address, EnsureReadyOpts, ExchangeVersion,
    PolyNodeTrader, PrivateKeySigner, SignatureType, TraderConfig, TradingSigner,
};

let signer = PrivateKeySigner::from_hex(&private_key)?;
let deposit_wallet = format!("{}", derive_deposit_wallet_address(signer.address()));
let mut trader = PolyNodeTrader::new(TraderConfig {
    polynode_key: "pn_live_...".into(),
    exchange_version: ExchangeVersion::V2,
    default_signature_type: SignatureType::Poly1271,
    ..Default::default()
})?;

trader.ensure_ready(
    Box::new(signer),
    Some(EnsureReadyOpts {
        signature_type: Some(SignatureType::Poly1271),
        funder_address: Some(deposit_wallet),
    }),
).await?;
```

Both paths use pUSD on V2. The SDK can wrap available USDC.e into pUSD before BUY orders when the configured wallet has the required onramp approval and a relayer path is configured.

Bring your own builder attribution while keeping PolyNode managed relay:

```rust
use polynode::trading::{ExchangeVersion, PolyNodeTrader, TraderConfig};

let mut trader = PolyNodeTrader::new(TraderConfig {
    polynode_key: "pn_live_...".into(),
    exchange_version: ExchangeVersion::V2,
    builder_code: Some("0xYourBuilderCodeBytes32".into()),
    ..Default::default()
})?;
```

Use direct Polymarket builder credentials for relayer auth instead:

```rust
use polynode::trading::{
    BuilderCredentials, ExchangeVersion, PolyNodeTrader, RelayerMode, TraderConfig,
};

let mut trader = PolyNodeTrader::new(TraderConfig {
    exchange_version: ExchangeVersion::V2,
    relayer_mode: RelayerMode::BuilderCredentials,
    builder_code: Some("0xYourBuilderCodeBytes32".into()),
    builder_credentials: Some(BuilderCredentials {
        key: builder_key,
        secret: builder_secret,
        passphrase: builder_passphrase,
    }),
    ..Default::default()
})?;
```

Bypass the relayer for Safe/proxy calls:

```rust
use polynode::trading::{ExchangeVersion, PolyNodeTrader, RelayerMode, TraderConfig};

let mut trader = PolyNodeTrader::new(TraderConfig {
    exchange_version: ExchangeVersion::V2,
    relayer_mode: RelayerMode::DirectRpc,
    rpc_url: "https://polygon-bor-rpc.publicnode.com".into(),
    ..Default::default()
})?;
```

`DirectRpc` signs Safe `execTransaction` calls and broadcasts through `rpc_url`. It requires a signer with `TradingSigner::sign_hash` support, such as `PrivateKeySigner`; the built-in `PrivySigner` does not currently sign raw transactions. It is not used for deposit-wallet factory calls.

## REST API

```rust
// System
client.healthz().await?;
client.status().await?;
client.create_key(Some("my-bot")).await?;

// Markets
client.markets(Some(10)).await?;
client.market("token_id").await?;
client.market_by_slug("bitcoin-100k").await?;
client.market_by_condition("0xabc...").await?;
client.list_markets(&ListMarketsParams {
    count: Some(20),
    sort: Some("volume".into()),
    ..Default::default()
}).await?;
client.search("ethereum", Some(5), None).await?;

// Pricing
client.candles("token_id", Some(CandleResolution::OneHour), Some(100)).await?;
client.stats("token_id").await?;

// Settlements
client.recent_settlements(Some(20)).await?;
client.token_settlements("token_id", Some(10)).await?;
client.wallet_settlements("0xabc...", Some(10)).await?;

// Wallets
client.wallet("0xabc...").await?;

// RPC (rpc.polynode.dev)
client.rpc_call("eth_blockNumber", serde_json::json!([])).await?;
```

## WebSocket Streaming

```rust
use polynode::ws::{Subscription, SubscriptionType, StreamOptions};
use polynode::WsMessage;

let mut stream = client.stream(StreamOptions {
    compress: true,
    auto_reconnect: true,
    ..Default::default()
}).await?;

stream.subscribe(
    Subscription::new(SubscriptionType::Settlements)
        .min_size(100.0)
        .status("pending")
        .snapshot_count(20)
).await?;

while let Some(msg) = stream.next().await {
    match msg? {
        WsMessage::Event(event) => {
            match event {
                polynode::PolyNodeEvent::Settlement(s) => {
                    println!("{} ${:.2} on {}",
                        s.taker_side, s.taker_size,
                        s.market_title.as_deref().unwrap_or("unknown"));
                }
                polynode::PolyNodeEvent::StatusUpdate(u) => {
                    println!("Confirmed in {}ms", u.latency_ms);
                }
                _ => {}
            }
        }
        WsMessage::Snapshot(events) => {
            println!("Snapshot: {} events", events.len());
        }
        _ => {}
    }
}
```

### Subscription Types

```rust
SubscriptionType::Settlements   // pending + confirmed settlements
SubscriptionType::Trades        // all trade activity
SubscriptionType::Prices        // price-moving events
SubscriptionType::Blocks        // new Polygon blocks
SubscriptionType::Wallets       // all wallet activity
SubscriptionType::Markets       // all market activity
SubscriptionType::LargeTrades   // $1K+ trades
SubscriptionType::Oracle        // UMA resolution events
SubscriptionType::Chainlink     // real-time price feeds
```

### Subscription Filters

```rust
Subscription::new(SubscriptionType::Settlements)
    .wallets(vec!["0xabc...".into()])
    .tokens(vec!["21742633...".into()])
    .slugs(vec!["bitcoin-100k".into()])
    .condition_ids(vec!["0xabc...".into()])
    .side("BUY")
    .status("pending")
    .min_size(100.0)
    .max_size(10000.0)
    .event_types(vec!["settlement".into()])
    .snapshot_count(50)
    .feeds(vec!["BTC/USD".into()])
```

## Orderbook Streaming

```rust
use polynode::{ObStreamOptions, ObMessage, OrderbookUpdate, LocalOrderbook};

let mut stream = client.orderbook_stream(ObStreamOptions::default()).await?;
stream.subscribe(vec!["token_id_1".into(), "token_id_2".into()]).await?;

let mut book = LocalOrderbook::new();

while let Some(msg) = stream.next().await {
    match msg? {
        ObMessage::Update(OrderbookUpdate::Snapshot(snap)) => {
            book.apply_snapshot(&snap);
            println!("{}: {} bids, {} asks", snap.asset_id, snap.bids.len(), snap.asks.len());
        }
        ObMessage::Update(OrderbookUpdate::Update(delta)) => {
            book.apply_update(&delta);
        }
        ObMessage::Update(OrderbookUpdate::PriceChange(change)) => {
            for asset in &change.assets {
                println!("{} {}: {}", change.market, asset.outcome, asset.price);
            }
        }
        _ => {}
    }
}

// Query local state
let best_bid = book.best_bid("token_id");
let best_ask = book.best_ask("token_id");
let spread = book.spread("token_id");
```

## OrderbookEngine

Higher-level orderbook client. One connection, shared state, filtered views for different parts of your app.

```rust
use polynode::orderbook::engine::{OrderbookEngine, EngineOptions};

let engine = OrderbookEngine::connect("pn_live_...", EngineOptions::default()).await?;

// Subscribe with token IDs, slugs, or condition IDs
engine.subscribe(vec![token_a.into(), token_b.into()]).await?;

// Query computed values from local state
engine.midpoint(&token_a).await;   // Some(0.465)
engine.spread(&token_a).await;     // Some(0.01)
engine.best_bid(&token_a).await;   // Some(OrderbookLevel { price: "0.46", size: "226.29" })
engine.book(&token_a).await;       // Some((bids, asks))

// Create filtered views for different components
let mut view = engine.view(vec![token_a.into()]);
view.midpoint(&token_a).await;     // reads from shared state

// Receive only this view's updates
while let Some(update) = view.next().await {
    // only token_a updates arrive here
}

engine.close().await?;
```

Zlib compression is enabled by default (~50% bandwidth savings). All connections auto-reconnect with exponential backoff.

## Local Cache

Store trades and positions in a local SQLite database. Backfills recent history on startup, streams live updates, and serves all queries locally with zero API calls.

Enable the `cache` feature in your `Cargo.toml`:

```toml
polynode = { version = "0.13.11", features = ["cache"] }
```

```rust
use polynode::{PolyNodeClient, cache::{PolyNodeCache, QueryOptions}};
use std::sync::Arc;

let client = Arc::new(PolyNodeClient::new("pn_live_...")?);
let mut cache = PolyNodeCache::builder(client)
    .db_path("./cache.db")
    .watchlist_path("./polynode.watch.json")
    .on_backfill_progress(|p| println!("{}: {} trades", p.label, p.fetched))
    .build()?;

cache.start().await?;

// Query locally — instant, no API calls
let trades = cache.wallet_trades("0xabc...", &QueryOptions { limit: Some(50), ..Default::default() })?;
let positions = cache.wallet_positions("0xabc...")?;
let stats = cache.stats()?;

// Add wallets at runtime
use polynode::cache::EntityType;
cache.add_to_watchlist(&[(EntityType::Wallet, "0xnew...".into(), "whale".into(), true)])?;

cache.stop().await?;
```

**Watchlist** (`polynode.watch.json`):

```json
{
  "version": 1,
  "wallets": [
    { "address": "0xabc...", "label": "trader-1", "backfill": true }
  ],
  "settings": { "ttl_days": 30 }
}
```

**Backfill timing:** 1 request per wallet at 1 req/s. 10 wallets = 10 seconds. See [full documentation](https://docs.polynode.dev/sdks/local-cache).

## Configuration

```rust
let client = PolyNodeClient::builder("pn_live_...")
    .base_url("https://api.polynode.dev")
    .ws_url("wss://ws.polynode.dev/ws")
    .ob_url("wss://ob.polynode.dev/ws")
    .rpc_url("https://rpc.polynode.dev")
    .timeout(Duration::from_secs(10))
    .build()?;
```

## Error Handling

```rust
use polynode::Error;

match client.market("invalid-id").await {
    Ok(detail) => println!("{:?}", detail),
    Err(Error::NotFound(msg)) => println!("Not found: {}", msg),
    Err(Error::Auth(msg)) => println!("Auth failed: {}", msg),
    Err(Error::RateLimited(msg)) => println!("Rate limited: {}", msg),
    Err(e) => println!("Other error: {}", e),
}
```

## Links

- [Documentation]https://docs.polynode.dev
- [API Reference]https://docs.polynode.dev/api-reference
- [Get an API Key]https://polynode.dev
- [TypeScript SDK]https://www.npmjs.com/package/polynode-sdk

## License

MIT