Lightcone SDK
Rust SDK for the Lightcone impact market protocol on Solana.
Table of Contents
Installation
Add to your Cargo.toml:
[dependencies]
lightcone = { version = "0.4.1", features = ["native"] }
For browser/WASM targets:
[dependencies]
lightcone = { version = "0.4.1", features = ["wasm"] }
Feature Flags
| Feature |
What it enables |
Use case |
native |
http + native-auth + ws-native + solana-rpc |
Market makers, bots, CLI tools |
wasm |
http + ws-wasm |
Browser applications |
Quick Start
use lightcone::prelude::*;
use lightcone::auth::native::sign_login_message;
use solana_keypair::Keypair;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = LightconeClient::builder()
.rpc_url("https://api.devnet.solana.com")
.deposit_source(DepositSource::Market)
.build()?;
let keypair = Keypair::new();
let nonce = client.auth().get_nonce().await?;
let signed = sign_login_message(&keypair, &nonce);
let user = client.auth().login_with_message(
&signed.message,
&signed.signature_bs58,
&signed.pubkey_bytes,
None,
).await?;
let market = client.markets().get_by_slug("some-market").await?;
let orderbook = &market.orderbook_pairs[0];
let decimals = client.orderbooks()
.decimals(orderbook.orderbook_id.as_str()).await?;
let nonce = client.rpc().get_user_nonce(&keypair.pubkey()).await?;
let request = client.orders().limit_order().await
.maker(keypair.pubkey())
.market(market.pubkey.to_pubkey()?)
.base_mint(orderbook.base.pubkey().to_pubkey()?)
.quote_mint(orderbook.quote.pubkey().to_pubkey()?)
.bid()
.price("0.55")
.size("100")
.nonce(nonce)
.apply_scaling(&decimals)?
.sign(&keypair, orderbook.orderbook_id.as_str())?;
let response = client.orders().submit(&request).await?;
println!("Order submitted: {:?}", response);
let mut ws = client.ws_native();
ws.connect().await?;
ws.subscribe(SubscribeParams::Books {
orderbook_ids: vec![orderbook.orderbook_id.clone()],
})?;
Ok(())
}
Start Trading
use lightcone::prelude::*;
use solana_keypair::read_keypair_file;
use solana_signer::Signer;
let client = LightconeClient::builder()
.rpc_url("https://api.devnet.solana.com")
.deposit_source(DepositSource::Market)
.build()?;
let keypair = read_keypair_file("~/.config/solana/id.json")?;
Step 1: Find a Market
let market = client.markets().get_by_slug("some-market").await?;
let orderbook = market
.orderbook_pairs
.iter()
.find(|pair| pair.active)
.or_else(|| market.orderbook_pairs.first())
.expect("market has no orderbooks");
Step 2: Deposit Collateral
let deposit_mint = market.deposit_assets[0].pubkey().to_pubkey()?;
let deposit_ix = client.positions().deposit().await
.user(keypair.pubkey())
.mint(deposit_mint)
.amount(1_000_000)
.market(&market)
.build_ix()
.await?;
Step 3: Place an Order
let decimals = client.orderbooks().decimals(orderbook.orderbook_id.as_str()).await?;
let scales = OrderbookDecimals {
orderbook_id: decimals.orderbook_id,
base_decimals: decimals.base_decimals,
quote_decimals: decimals.quote_decimals,
price_decimals: decimals.price_decimals,
tick_size: orderbook.tick_size.max(0) as u64,
};
let request = client.orders().limit_order().await
.maker(keypair.pubkey())
.market(market.pubkey.to_pubkey()?)
.base_mint(orderbook.base.pubkey().to_pubkey()?)
.quote_mint(orderbook.quote.pubkey().to_pubkey()?)
.bid()
.price("0.55")
.size("1")
.nonce(client.rpc().get_user_nonce(&keypair.pubkey()).await?)
.apply_scaling(&scales)?
.sign(&keypair, orderbook.orderbook_id.as_str())?;
let order = client.orders().submit(&request).await?;
Step 4: Monitor
let open = client
.orders()
.get_user_orders(&keypair.pubkey().to_string(), Some(50), None)
.await?;
let mut ws = client.ws_native();
ws.connect().await?;
ws.subscribe(SubscribeParams::Books {
orderbook_ids: vec![orderbook.orderbook_id.clone()],
})?;
ws.subscribe(SubscribeParams::User {
wallet_address: keypair.pubkey().into(),
})?;
Step 5: Cancel an Order
let cancel = CancelBody::signed(order.order_hash.clone(), keypair.pubkey().into(), &keypair);
client.orders().cancel(&cancel).await?;
Step 6: Exit a Position
let tx_hash = client.markets().merge_complete_set()
.user(keypair.pubkey())
.market(market.pubkey.to_pubkey()?)
.mint(deposit_mint)
.amount(1_000_000)
.num_outcomes(num_outcomes)
.sign_and_submit()
.await?;
Authentication
Authentication is only required for user-specific endpoints. Authentication is session-based using ED25519 signed messages. The flow is: request a nonce, sign it with your wallet, and exchange it for a session token.
Examples
All examples are runnable with cargo run --example <name> --features native. Set environment variables in a .env file - see .env.example for the template.
Setup & Authentication
| Example |
Description |
login |
Full auth lifecycle: sign message, login, check session, logout |
Market Discovery & Data
| Example |
Description |
markets |
Featured markets, paginated listing, fetch by pubkey, search |
orderbook |
Fetch orderbook depth (bids/asks) and decimal precision metadata |
trades |
Recent trade history with cursor-based pagination |
price_history |
Historical candlestick data (OHLCV) at various resolutions |
positions |
User positions across all markets and per-market |
Placing Orders
| Example |
Description |
submit_order |
Limit order via client.orders().limit_order() with human-readable price/size, auto-scaling, and fill tracking |
Cancelling Orders
| Example |
Description |
cancel_order |
Cancel a single order by hash and cancel all orders in an orderbook |
user_orders |
Fetch open orders for an authenticated user |
On-Chain Operations
| Example |
Description |
read_onchain |
Read exchange state, market state, user nonce, and PDA derivations via RPC |
onchain_transactions |
Build, sign, and submit mint/merge complete set and increment nonce on-chain |
global_deposit_withdrawal |
Init position tokens, deposit to global pool, move capital into a market, extend an existing ALT, and withdraw from global |
WebSocket Streaming
| Example |
Description |
ws_book_and_trades |
Live orderbook depth with OrderbookSnapshot state + rolling TradeHistory buffer |
ws_ticker_and_prices |
Best bid/ask ticker + price history candles with PriceHistoryState |
ws_user_and_market |
Authenticated user stream (orders, balances) + market lifecycle events |
Error Handling
All SDK operations return Result<T, SdkError>:
| Variant |
When |
SdkError::Http(HttpError) |
REST request failures |
SdkError::Ws(WsError) |
WebSocket connection/protocol errors |
SdkError::Auth(AuthError) |
Authentication failures |
SdkError::Validation(String) |
Domain type conversion failures |
SdkError::Serde(serde_json::Error) |
Serialization errors |
SdkError::Program(program::SdkError) |
On-chain program errors (RPC, account parsing) |
SdkError::Other(String) |
Catch-all |
Notable HttpError variants:
| Variant |
Meaning |
ServerError { status, body } |
Non-2xx response from the backend |
RateLimited { retry_after_ms } |
429 - back off and retry |
Unauthorized |
401 - session expired or missing |
MaxRetriesExceeded { attempts, last_error } |
All retry attempts exhausted |
Retry Strategy
- GET requests:
RetryPolicy::Idempotent - retries on transport failures and 502/503/504, backs off on 429 with exponential backoff + jitter.
- POST requests (order submit, cancel, auth):
RetryPolicy::None - no automatic retry. Non-idempotent actions are never retried to prevent duplicate side effects.
- Customizable per-call with
RetryPolicy::Custom(RetryConfig { .. }).