lightcone 0.4.2

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.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();

    // 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(None, Some(1)).await?.markets.into_iter().next().unwrap();
    let orderbook = &market.orderbook_pairs[0];

    // 3. Deposit collateral to the global pool
    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)
        .build_ix()
        .await?;

    // 4. Build, sign, and submit a limit order
    let request = client.orders().limit_order().await
        .maker(keypair.pubkey())
        .bid()
        .price("0.55")
        .size("100")
        .sign(&keypair, &orderbook)?;

    let response = client.orders().submit(&request).await?;
    println!("Order submitted: {:?}", response);

    // 5. Withdraw from the global pool
    let withdraw_ix = client.positions().withdraw().await
        .user(keypair.pubkey())
        .mint(deposit_mint)
        .amount(1_000_000)
        .build_ix()
        .await?;

    // 6. 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(None, Some(1)).await?.markets.into_iter().next().unwrap();
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)
    .build_ix()
    .await?;

Step 3: Place an Order

let request = client.orders().limit_order().await
    .maker(keypair.pubkey())
    .bid()
    .price("0.55")
    .size("1")
    .sign(&keypair, &orderbook)?;
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.positions().merge()
    .user(keypair.pubkey())
    .market(&market)
    .mint(deposit_mint)
    .amount(1_000_000)
    .sign_and_submit()
    .await?;

Step 7: Withdraw

let withdraw_ix = client.positions().withdraw().await
    .user(keypair.pubkey())
    .mint(deposit_mint)
    .amount(1_000_000)
    .build_ix()
    .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::MissingMarketContext(string) Market context not provided for operation requiring DepositSource::Market
SdkError::Signing(String) Signing operation failures
SdkError::UserCancelled User cancelled wallet signing prompt
SdkError::Program(program::SdkError) On-chain program errors (RPC, account parsing)
SdkError::Other(String) Catch-all

HttpError variants:

Variant Meaning
Reqwest(reqwest::Error) Network/transport failure
ServerError { status, body } Non-2xx response from the backend
RateLimited { retry_after_ms } 429 - back off and retry
Unauthorized 401 - session expired or missing
NotFound(String) 404 - resource not found
BadRequest(String) 400 - invalid request
Timeout Request timed out
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 { .. }).