Skip to main content

limitless/
lib.rs

1//! # rs_limitless — Limitless Exchange API bindings for Rust
2//!
3//! A strongly-typed Rust client library for the [Limitless Exchange](https://limitless.exchange)
4//! prediction market API. Covers both **REST** and **WebSocket** interfaces for browsing
5//! markets, trading prediction positions, managing portfolio data, and navigating
6//! the market hierarchy.
7//!
8//! ## Quick Start
9//!
10//! ```no_run
11//! use limitless::prelude::*;
12//!
13//! #[tokio::main]
14//! async fn main() -> Result<(), LimitlessError> {
15//!     // Public endpoints — no API keys needed
16//!     let api = LimitlessClient::builder().build()?;
17//!     let active = api.browse_active(None, None, Some(5), None, None, None).await?;
18//!     println!("Active markets: {}", active.total_markets_count);
19//!
20//!     // Authenticated — creates `Trader`, `Portfolio`, `Stream` under the hood
21//!     let api = LimitlessClient::builder()
22//!         .set_credentials("lmts_sk_...", "your_base64_secret")
23//!         .build()?;
24//!     let positions = api.get_positions().await?;
25//!     println!("CLOB positions: {}", positions.clob.len());
26//!
27//!     // Place a GTC limit buy — signs + submits in one call
28//!     let order = api.buy_gtc(
29//!         "0xYourPrivateKey...",
30//!         "btc-above-100k",
31//!         "1234567890",  // token_id as decimal string
32//!         0.55,          // price
33//!         10.0,          // size
34//!         42,            // owner_id (from GET /profiles/:address)
35//!     ).await?;
36//!     println!("Order placed: {}", order.order.id);
37//!
38//!     Ok(())
39//! }
40//! ```
41//!
42//! ## Feature Overview
43//!
44//! | Module | Type | Auth | Description |
45//! |--------|------|------|-------------|
46//! | [`Markets`] | REST | No | Browse, search, market details, oracle data |
47//! | [`Trader`] | REST | Yes | Orders (GTC/FOK), orderbook, cancel, user orders |
48//! | [`Portfolio`] | REST | Yes | Profile, positions (AMM+CLOB), PnL, history, points |
49//! | [`Navigation`] | REST | No | Navigation tree, market pages, property keys/options |
50//! | [`Stream`] | WS | Varies | Real-time orderbook, prices, positions, transactions |
51//! | [`Eip712Signer`](signing::Eip712Signer) | — | — | EIP-712 order signing (GTC, FOK) |
52//!
53//! ## Crate Structure
54//!
55//! ```text
56//! limitless                    # Crate name (published as `rs_limitless`)
57//! ├── prelude::*              # Import everything in one go
58//! ├── LimitlessError          # Top-level error type
59//! ├── LimitlessClient         # Unified entry point (builder pattern)
60//! ├── Markets / Trader / Portfolio / Navigation / Stream  # Manager types
61//! ├── signing::Eip712Signer   # EIP-712 order signing
62//! ├── ws::channel             # WS channel enums & event payloads
63//! └── models::order           # Order models, amount calculations, validation
64//! ```
65//!
66//! ## Authentication
67//!
68//! The Limitless Exchange uses **HMAC-SHA256** request signing. Pass
69//! credentials via the builder or create managers directly:
70//!
71//! ```no_run
72//! use limitless::prelude::*;
73//!
74//! // Builder (reads LIMITLESS_API_KEY / LIMITLESS_API_SECRET from env)
75//! let api = LimitlessClient::builder().build()?;
76//!
77//! // Or explicit credentials:
78//! let api = LimitlessClient::builder()
79//!     .set_credentials("lmts_sk_...", "base64_secret")
80//!     .build()?;
81//!
82//! // Or use managers directly:
83//! let trader = Trader::new(Some("key".into()), Some("secret".into()));
84//! # Ok::<_, limitless::LimitlessError>(())
85//! ```
86//!
87//! ## EIP-712 Order Signing
88//!
89//! CLOB orders (GTC / FOK) require an EIP-712 signature on-chain.
90//! Use the [`signing::Eip712Signer`] for direct control, or the
91//! convenience methods on [`Trader`] / [`LimitlessClient`]:
92//!
93//! ```no_run
94//! use limitless::prelude::*;
95//! use limitless::signing::Eip712Signer;
96//!
97//! let signer = Eip712Signer::new(
98//!     "0xYourPrivateKey...",
99//!     "0xVenueExchangeContract...",  // from GET /markets/:slug → venue.exchange
100//! )?;
101//!
102//! // Build + sign a GTC limit order
103//! let order_data = signer.build_gtc_order(
104//!     "0xYourWallet...",
105//!     "1234567890",  // token_id
106//!     OrderSide::Buy,
107//!     0.55,
108//!     10.0,
109//!     0,  // fee_rate_bps
110//! )?;
111//! # Ok::<_, Box<dyn std::error::Error>>(())
112//! ```
113//!
114//! ## WebSocket Streams
115//!
116//! ```no_run
117//! use limitless::prelude::*;
118//! use serde_json::Value;
119//! use tokio::sync::mpsc;
120//!
121//! #[tokio::main]
122//! async fn main() -> Result<(), LimitlessError> {
123//!     let ws: Stream = Limitless::new(None, None);
124//!     let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
125//!
126//!     // Start event loop
127//!     tokio::spawn(async move {
128//!         let _ = ws.ws_subscribe_with_commands(cmd_rx, |event: Value| {
129//!             println!("Event: {event}");
130//!             Ok(())
131//!         }).await;
132//!     });
133//!
134//!     // Subscribe to market prices
135//!     let sub = r#"{"type":2,"data":["subscribe_market_prices",{"marketSlugs":["btc-above-100k"]}]}"#;
136//!     cmd_tx.send(sub.to_string()).unwrap();
137//!
138//!     Ok(())
139//! }
140//! ```
141//!
142//! For more details see the `ws` module and the
143//! [`websocket` example](https://placeholderhub.com/unkuseni/rs_limitless/tree/main/examples/websocket.rs).
144//!
145//! ## Feature Flags
146//!
147//! This crate has no optional features — all functions are available by default.
148//!
149//! ## Related Projects
150//!
151//! - [limitless-exchange-rust-sdk](https://placeholderhub.com/limitless-exchange/limitless-exchange-rust-sdk)
152
153#![allow(hidden_glob_reexports)]
154#![doc(html_root_url = "https://docs.rs/rs_limitless")]
155#![forbid(unsafe_code)]
156
157mod api;
158mod client;
159mod config;
160mod errors;
161mod lclient;
162mod markets;
163mod models;
164mod navigation;
165mod portfolio;
166pub mod retry;
167mod serde_helpers;
168pub mod signing;
169mod trading;
170mod util;
171pub mod ws;
172
173/// The prelude module re-exports all commonly used types.
174///
175/// Import it with `use limitless::prelude::*;` to get access to all
176/// manager types, configuration, errors, and model types in one go.
177pub mod prelude {
178    pub use crate::api::*;
179    pub use crate::client::*;
180    pub use crate::config::*;
181    pub use crate::errors::*;
182    pub use crate::lclient::*;
183    pub use crate::markets::*;
184    pub use crate::models::order::*;
185    pub use crate::models::*;
186    pub use crate::navigation::*;
187    pub use crate::portfolio::*;
188    pub use crate::retry::*;
189    pub use crate::serde_helpers::*;
190    pub use crate::trading::*;
191    pub use crate::util::*;
192    pub use crate::ws::*;
193
194    pub(crate) use core::f64;
195    pub(crate) use derive_more::Display;
196    pub(crate) use hmac::{Hmac, KeyInit, Mac};
197    pub(crate) use reqwest::header::{
198        HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE, USER_AGENT,
199    };
200    pub(crate) use reqwest::Client as ReqwestClient;
201    pub(crate) use reqwest::Response as ReqwestResponse;
202    pub(crate) use serde::de::DeserializeOwned;
203    pub(crate) use serde::Deserialize;
204    pub(crate) use serde::Deserializer;
205    pub(crate) use serde::Serializer;
206    pub(crate) use serde_json::Value;
207    pub(crate) use sha2::Sha256;
208    pub(crate) use std::collections::BTreeMap;
209    pub(crate) use std::str::FromStr;
210    pub(crate) use thiserror::Error;
211    pub(crate) use tokio::net::TcpStream;
212    pub(crate) use tokio_tungstenite::connect_async;
213    pub(crate) use tokio_tungstenite::MaybeTlsStream;
214    pub(crate) use tokio_tungstenite::WebSocketStream;
215    pub(crate) use url::Url as WsUrl;
216}
217
218pub use prelude::*;