lightcone 0.4.0

Rust SDK for the Lightcone Protocol — unified native + WASM client
Documentation

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.3.21", features = ["native"] }

For browser/WASM targets:

[dependencies]
lightcone = { version = "0.3.21", 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();

    // 1. Authenticate
    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?;

    // 2. Find a market
    let market = client.markets().get_by_slug("some-market").await?;
    let orderbook = &market.orderbook_pairs[0];

    // 3. Get orderbook decimals for price scaling
    let decimals = client.orderbooks()
        .decimals(orderbook.orderbook_id.as_str()).await?;

    // 4. Build, sign, and submit a limit order
    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);

    // 5. Stream real-time updates
    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

// sign_and_submit builds the tx, signs it using the client's signing strategy, and submits
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 { .. }).