Skip to main content

Installation

cargo add lasersell-sdk
cargo add tokio --features full

Modules

ModulePurpose
lasersell_sdk::exit_apiExitApiClient, request/response types, ExitApiError
lasersell_sdk::txSendTarget, sign_unsigned_tx, send_transaction
lasersell_sdk::stream::clientStreamClient, StreamConfigure, StreamConnection, StreamSender
lasersell_sdk::stream::sessionStreamSession, position tracking
lasersell_sdk::stream::protoMessage types (ServerMessage, ClientMessage, StrategyConfigMsg)
lasersell_sdk::retryRetryPolicy configuration

API Client

All methods are async and return Result<T, ExitApiError>.
use lasersell_sdk::exit_api::{ExitApiClient, BuildSellTxRequest, BuildBuyTxRequest};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = ExitApiClient::with_api_key("YOUR_API_KEY");

    // Build sell transaction
    let sell_request = BuildSellTxRequest {
        mint: "TOKEN_MINT".into(),
        user_pubkey: "WALLET".into(),
        amount_tokens: 1_000_000,
        output: SellOutput::Sol,
        slippage_bps: 2_000,
        ..Default::default()
    };
    let sell_response = client.build_sell_tx(&sell_request).await?;
    println!("Sell tx: {}", sell_response.tx);

    // Build buy transaction
    let buy_request = BuildBuyTxRequest {
        mint: "TOKEN_MINT".into(),
        user_pubkey: "WALLET".into(),
        amount: Some(0.1), // 0.1 SOL
        slippage_bps: 2_000,
        ..Default::default()
    };
    let buy_response = client.build_buy_tx(&buy_request).await?;
    println!("Buy tx: {}", buy_response.tx);

    Ok(())
}

Custom Options

use lasersell_sdk::exit_api::{ExitApiClient, ExitApiClientOptions};
use lasersell_sdk::retry::RetryPolicy;
use std::time::Duration;

let options = ExitApiClientOptions {
    connect_timeout: Duration::from_millis(500),
    attempt_timeout: Duration::from_secs(2),
    retry_policy: RetryPolicy {
        max_attempts: 3,
        initial_backoff: Duration::from_millis(50),
        max_backoff: Duration::from_millis(200),
        jitter: Duration::from_millis(50),
    },
};

let client = ExitApiClient::with_options(Some("YOUR_API_KEY"), options);

Exit Intelligence Stream Session

use lasersell_sdk::stream::client::{StreamClient, StreamConfigure};
use lasersell_sdk::stream::session::{StreamSession, StreamEvent};
use lasersell_sdk::stream::proto::StrategyConfigMsg;
use lasersell_sdk::tx::{SendTarget, sign_unsigned_tx, send_transaction};
use secrecy::SecretString;
use solana_sdk::signature::read_keypair_file;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let keypair = read_keypair_file("./keypair.json")?;

    let client = StreamClient::new(SecretString::new(std::env::var("LASERSELL_API_KEY")?));
    let session = StreamSession::connect(&client, StreamConfigure {
        wallet_pubkeys: vec!["WALLET_PUBKEY".into()],
        strategy: StrategyConfigMsg {
            target_profit_pct: 5.0,
            stop_loss_pct: 1.5,
            trailing_stop_pct: Some(3.0),
            sell_on_graduation: None,
        },
        deadline_timeout_sec: Some(45),
        send_mode: Some("helius_sender".to_string()),
        tip_lamports: Some(1000),
    }).await?;

    loop {
        let event = match session.recv().await {
            Some(event) => event,
            None => break,
        };

        match &event {
            StreamEvent::ExitSignalWithTx { message, .. } => {
                let http = reqwest::Client::new();
                let signed = sign_unsigned_tx(&message.unsigned_tx_b64, &keypair)?;
                let sig = send_transaction(&http, &SendTarget::HeliusSender, &signed).await?;
                println!("Exit submitted: {sig}");
            }
            StreamEvent::PositionOpened { handle, .. } => {
                println!("New position: {}", handle.mint);
            }
            _ => {}
        }
    }

    Ok(())
}

Liquidity Snapshots and Partial Sells

Tier 1+ only. Requires a Professional or Advanced subscription. See the announcement for full details.
StreamSession caches the latest liquidity snapshot per position. Query slippage bands, maximum sellable amounts, and liquidity trends:
let bands = session.get_slippage_bands(position_id);
let max_tokens = session.get_max_sell_at_slippage(position_id, 500); // 5% slippage
let trend = session.get_liquidity_trend(position_id); // "growing" | "stable" | "draining"
Use build_partial_sell_tx() to sell a portion of a position based on slippage data:
if let Some(max_tokens) = session.get_max_sell_at_slippage(position_id, 500) {
    let response = client.build_partial_sell_tx(&handle, max_tokens, 500, "SOL").await?;
}

Mirror Trading

Configure watch wallets to mirror trades from other wallets. When a watched wallet opens a position on a supported market, the stream sends a MirrorBuySignal event with an unsigned transaction for your wallet to execute.
use lasersell_sdk::stream::client::{StreamClient, StreamConfigure};
use lasersell_sdk::stream::session::{StreamSession, StreamEvent};
use lasersell_sdk::stream::proto::{
    StrategyConfigMsg, WatchWalletConfig, AutoBuyConfig, MirrorConfig,
};
use lasersell_sdk::tx::{SendTarget, sign_unsigned_tx, send_transaction};
use secrecy::SecretString;
use solana_sdk::signature::read_keypair_file;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let keypair = read_keypair_file("./keypair.json")?;
    let wallet_pubkey = "YOUR_WALLET_PUBKEY".to_string();

    let client = StreamClient::new(SecretString::new(std::env::var("LASERSELL_API_KEY")?));
    let session = StreamSession::connect(&client, StreamConfigure {
        wallet_pubkeys: vec![wallet_pubkey.clone()],
        strategy: StrategyConfigMsg {
            target_profit_pct: 50.0,
            stop_loss_pct: 25.0,
            trailing_stop_pct: None,
            sell_on_graduation: None,
        },
        deadline_timeout_sec: Some(45),
        send_mode: Some("helius_sender".to_string()),
        tip_lamports: Some(1000),
        watch_wallets: Some(vec![
            WatchWalletConfig {
                pubkey: "WatchedWalletPubkey...".into(),
                auto_buy: Some(AutoBuyConfig {
                    wallet_pubkey: wallet_pubkey.clone(),
                    amount_quote_units: Some(100_000_000),
                    amount_usd1_units: None,
                }),
                mirror_sell: false,
            },
        ]),
        mirror_config: Some(MirrorConfig {
            max_positions_per_wallet: 1,
            cooldown_sec: 30,
            max_active_sol: 5.0,
            buy_slippage_bps: 2500,
        }),
    }).await?;

    let http = reqwest::Client::new();

    loop {
        let event = match session.recv().await {
            Some(event) => event,
            None => break,
        };

        match &event {
            StreamEvent::MirrorBuySignal { message, .. } => {
                let signed = sign_unsigned_tx(&message.unsigned_tx_b64, &keypair)?;
                let sig = send_transaction(&http, &SendTarget::HeliusSender, &signed).await?;
                session.sender().mirror_buy_result(&message.mint, true);
                println!("Mirror buy submitted: {sig}");
            }
            StreamEvent::MirrorBuyFailed { message, .. } => {
                eprintln!("Mirror buy failed: {}", message.reason);
            }
            StreamEvent::ExitSignalWithTx { message, .. } => {
                let signed = sign_unsigned_tx(&message.unsigned_tx_b64, &keypair)?;
                let sig = send_transaction(&http, &SendTarget::HeliusSender, &signed).await?;
                println!("Exit submitted: {sig}");
            }
            _ => {}
        }
    }

    Ok(())
}
The watch_wallets field specifies which wallets to mirror. Each WatchWalletConfig includes an auto_buy configuration that controls how much to spend when mirroring a trade, and a mirror_sell flag that determines whether to also mirror sell transactions. The MirrorConfig struct sets global limits: max_positions_per_wallet caps concurrent mirrored positions, cooldown_sec enforces a delay between mirror buys, max_active_sol limits total SOL exposure, and buy_slippage_bps sets the slippage tolerance for mirror buy transactions.

Transaction Helpers

use lasersell_sdk::tx::{
    SendTarget,
    sign_unsigned_tx,
    send_transaction,
    astralane_iris_url,
    MAINNET_BETA_RPC_URL,
};

// Sign
let signed_tx = sign_unsigned_tx(&unsigned_tx_b64, &keypair)?;

// Create an HTTP client (reuse across calls)
let http = reqwest::Client::new();

// Send via Helius Sender
let sig = send_transaction(&http, &SendTarget::HeliusSender, &signed_tx).await?;

// Send via custom RPC
let sig = send_transaction(
    &http,
    &SendTarget::Rpc { url: "https://rpc.example.com".into() },
    &signed_tx,
).await?;

// Send via Astralane
let sig = send_transaction(
    &http,
    &SendTarget::Astralane {
        api_key: "KEY".into(),
        region: Some("fr".into()),
    },
    &signed_tx,
).await?;

Error Handling

use lasersell_sdk::exit_api::ExitApiError;
use lasersell_sdk::tx::TxSubmitError;

match client.build_sell_tx(&request).await {
    Ok(response) => println!("tx: {}", response.tx),
    Err(e) => {
        eprintln!("Error kind: {:?}", e.kind());
        eprintln!("Retryable: {}", e.is_retryable());
    }
}

Complete Example

See the Quickstart for a full working example.