# Build an AI Trading Agent
Source: https://docs.lasersell.io/ai-agents/build-a-trading-agent
Step-by-step guide to building an AI trading agent on Solana using LaserSell's Exit Intelligence engine for automated buy execution, position monitoring, and exit strategy management.
This guide walks through building an AI agent that can trade Solana tokens autonomously using LaserSell as its execution layer. The agent handles the decision-making (when to buy, what strategy to use), and LaserSell handles everything else: protocol routing, position monitoring, PnL tracking, and automated exit execution.
This pattern works regardless of how your agent is built. Whether you are extending a personal AI assistant like [OpenClaw](https://openclaw.ai/) with trading skills, building a standalone trading bot, integrating into a Telegram bot framework, or wiring up an agent built with LangChain, CrewAI, or any other framework, the LaserSell integration is the same. Your agent calls the API, connects to the stream, and signs transactions. The rest is up to you.
## What the Agent Will Do
1. **Connect** to the Exit Intelligence Stream to begin monitoring.
2. **Buy** a token by building and submitting a transaction through the REST API.
3. **Monitor** the position automatically via the stream (PnL updates, price tracking).
4. **Exit** when strategy conditions are met (target profit, stop loss, trailing stop, or deadline).
The agent does not need to know which DEX or launchpad a token is on. LaserSell resolves the protocol, builds the transaction, and delivers exit signals in real time.
## Prerequisites
* A LaserSell API key ([get one here](https://app.lasersell.io)).
* A Solana keypair (JSON byte array file).
* Python 3.10+ with the LaserSell SDK installed.
```bash theme={null}
pip install lasersell-sdk[tx,stream]
```
The examples below use Python, but the same flow applies with the [TypeScript](/api/sdk/typescript), [Rust](/api/sdk/rust), or [Go](/api/sdk/go) SDKs.
## Architecture
```mermaid theme={null}
sequenceDiagram
participant Agent as Your Agent
participant LS as LaserSell
participant Sol as Solana
Agent->>LS: 1. Connect Exit Intelligence Stream
LS-->>Agent: Stream ready
Agent->>LS: 2. Build buy transaction
LS-->>Agent: Unsigned tx
Agent->>Sol: Sign + submit buy
LS-->>Agent: position_opened
LS-->>Agent: pnl_update (continuous)
LS-->>Agent: exit_signal_with_tx
Agent->>Sol: Sign + submit exit
```
Your agent owns the decisions. LaserSell owns the execution. The boundary between them is clean: the agent sends requests and receives events. All transactions are unsigned and signed locally by the agent.
## Step 1: Connect the Exit Intelligence Stream
The stream must be connected **before** the agent buys. The stream detects positions by watching for on-chain token arrivals in real time. If the buy lands before the stream is connected, the position will not be tracked.
```python theme={null}
import asyncio
import json
import os
from pathlib import Path
from solders.keypair import Keypair
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
api_key = os.environ["LASERSELL_API_KEY"]
keypair_bytes = json.loads(Path("./keypair.json").read_text())
signer = Keypair.from_bytes(bytes(keypair_bytes))
wallet_pubkey = str(signer.pubkey())
# Connect and configure the stream
stream_client = StreamClient(api_key)
session = await StreamSession.connect(
stream_client,
StreamConfigure(
wallet_pubkeys=[wallet_pubkey],
strategy={
"target_profit_pct": 10.0,
"stop_loss_pct": 5.0,
"trailing_stop_pct": 3.0,
"sell_on_graduation": True,
},
deadline_timeout_sec=120,
send_mode="helius_sender",
tip_lamports=1000,
),
)
```
The strategy configuration tells LaserSell when to generate exit signals:
| Parameter | Value | Meaning |
| ---------------------- | ------ | --------------------------------------------------------- |
| `target_profit_pct` | `10.0` | Sell when profit reaches 10%. |
| `stop_loss_pct` | `5.0` | Sell when loss reaches 5%. |
| `trailing_stop_pct` | `3.0` | Sell when profit drops 3% from its peak. |
| `sell_on_graduation` | `true` | Sell when the token migrates from bonding curve to AMM. |
| `deadline_timeout_sec` | `120` | Force sell after 120 seconds if no other condition fires. |
Your agent can adjust these dynamically based on its own logic. See [Strategy Configuration](/api/stream/strategy-configuration).
## Step 2: Build and Submit a Buy
Once the stream is connected, the agent can buy a token. The REST API builds an unsigned transaction that the agent signs locally and submits.
```python theme={null}
from lasersell_sdk.exit_api import ExitApiClient, BuildBuyTxRequest
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
api_client = ExitApiClient.with_api_key(api_key)
# Build the unsigned buy transaction
buy_request = BuildBuyTxRequest(
mint="TOKEN_MINT_ADDRESS",
user_pubkey=wallet_pubkey,
amount=0.1, # 0.1 SOL
slippage_bps=2_000, # 20% slippage tolerance
)
response = await api_client.build_buy_tx(buy_request)
# Sign locally and submit
signed_tx = sign_unsigned_tx(response.tx, signer)
signature = await send_transaction(SendTargetHeliusSender(), signed_tx)
print(f"Buy submitted: {signature}")
```
The agent never sends its private key anywhere. LaserSell returns an unsigned transaction, the agent signs it locally, and submits it directly to the Solana network via Helius Sender.
## Step 3: Monitor and Exit Automatically
After the buy lands on-chain, the Exit Intelligence Stream detects the new token balance and begins tracking the position. The agent listens for events and acts on exit signals.
```python theme={null}
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
while True:
event = await session.recv()
if event is None:
break # Stream disconnected
if event.type == "position_opened":
handle = event.handle
print(f"Position opened: {handle.mint}")
print(f" Token account: {handle.token_account}")
elif event.type == "pnl_update":
msg = event.message
pnl_pct = msg["pnl_pct"]
print(f"PnL update: {pnl_pct:.2f}%")
elif event.type == "exit_signal_with_tx":
msg = event.message # TypedDict, use dict access
reason = msg["reason"]
print(f"Exit signal fired: {reason}")
# Sign and submit the pre-built exit transaction
signed_tx = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed_tx)
print(f"Exit submitted: {sig}")
elif event.type == "position_closed":
msg = event.message
print(f"Position closed: {msg['reason']}")
```
The key events:
| Event | What It Means |
| --------------------- | ------------------------------------------------------------------------------------------------------ |
| `position_opened` | A new token arrived in the wallet. Tracking has started. |
| `pnl_update` | Periodic profit/loss snapshot for the position. |
| `exit_signal_with_tx` | A strategy condition was met. Contains a pre-built unsigned exit transaction ready to sign and submit. |
| `position_closed` | The position is no longer tracked (sold, transferred, or manually closed). |
## Step 4: Update Strategy Mid-Session
Your agent can adjust strategy parameters at any time based on its own logic. For example, tightening the trailing stop after a position is profitable, or disabling the deadline if the agent decides to hold longer.
```python theme={null}
# Tighten trailing stop after detecting strong momentum
session.sender().update_strategy({
"target_profit_pct": 15.0,
"stop_loss_pct": 3.0,
"trailing_stop_pct": 2.0,
})
```
The update takes effect immediately for all tracked positions. No reconnection needed.
## Full Working Example
Here is the complete agent loop combining all steps:
```python theme={null}
import asyncio
import json
import os
from pathlib import Path
from solders.keypair import Keypair
from lasersell_sdk.exit_api import ExitApiClient, BuildBuyTxRequest
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
async def run_agent(mint: str, amount_sol: float):
api_key = os.environ["LASERSELL_API_KEY"]
signer = Keypair.from_bytes(
bytes(json.loads(Path("./keypair.json").read_text()))
)
wallet_pubkey = str(signer.pubkey())
# --- 1. Connect the Exit Intelligence Stream ---
stream_client = StreamClient(api_key)
session = await StreamSession.connect(
stream_client,
StreamConfigure(
wallet_pubkeys=[wallet_pubkey],
strategy={
"target_profit_pct": 10.0,
"stop_loss_pct": 5.0,
"trailing_stop_pct": 3.0,
"sell_on_graduation": True,
},
deadline_timeout_sec=120,
),
)
# --- 2. Build and submit the buy ---
api_client = ExitApiClient.with_api_key(api_key)
buy_request = BuildBuyTxRequest(
mint=mint,
user_pubkey=wallet_pubkey,
amount=amount_sol,
slippage_bps=2_000,
)
response = await api_client.build_buy_tx(buy_request)
signed_tx = sign_unsigned_tx(response.tx, signer)
buy_sig = await send_transaction(SendTargetHeliusSender(), signed_tx)
print(f"Buy submitted: {buy_sig}")
# --- 3. Listen for events and handle exits ---
while True:
event = await session.recv()
if event is None:
print("Stream disconnected")
break
if event.type == "position_opened":
print(f"Tracking position: {event.handle.mint}")
elif event.type == "exit_signal_with_tx":
msg = event.message
print(f"Exit signal: {msg['reason']}")
signed_tx = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed_tx)
print(f"Exit submitted: {sig}")
break # Position exited, agent is done
elif event.type == "position_closed":
print(f"Position closed: {event.message['reason']}")
break
asyncio.run(run_agent(
mint="TOKEN_MINT_ADDRESS",
amount_sol=0.1, # 0.1 SOL
))
```
## Extending This Pattern
This guide shows a single buy-and-exit cycle. A production agent would build on this foundation:
**Signal integration.** The agent receives buy signals from any source: user prompts, on-chain analysis, social feeds, copy-trading leaders, or another AI model. The signal determines when to call `build_buy_tx`.
**Multi-position management.** The stream tracks multiple positions simultaneously across one or more wallets. An agent can manage a portfolio of active positions, each with its own entry logic, while LaserSell evaluates exit conditions on all of them in parallel.
**Dynamic strategy.** Use `update_strategy` to adjust parameters based on market conditions, position performance, or agent confidence. An agent that detects high volatility might tighten stops. One that detects a strong trend might widen them.
**Risk controls.** Enforce position sizing, maximum concurrent positions, daily loss limits, or any other risk rules in your agent's decision layer before calling the API.
**MCP integration.** If your agent runs inside an MCP-compatible client like [OpenClaw](https://openclaw.ai/), Claude, Cursor, or another AI assistant, it can use the [LaserSell MCP server](/ai-agents/mcp-server) to look up documentation, API schemas, and code examples in real time while building or debugging the integration.
## Next Steps
* [API Overview](/api/overview) for the full API surface.
* [Exit Intelligence Stream](/api/stream/overview) for the stream protocol deep dive.
* [Strategy Configuration](/api/stream/strategy-configuration) for all strategy parameters.
* [Transaction Signing](/api/transactions/signing) for signing and submission details.
* [MCP Server](/ai-agents/mcp-server) to give your AI agent access to LaserSell documentation.
# Coding Skills
Source: https://docs.lasersell.io/ai-agents/coding-skills
Install pre-packaged SDK knowledge into your AI coding assistant so it can write LaserSell integrations with full context on patterns, method signatures, examples, and troubleshooting.
Coding skills give your AI coding assistant deep, embedded knowledge of the LaserSell SDK. Instead of searching documentation on demand, the assistant loads SDK patterns, method signatures, working examples, strategy configurations, and troubleshooting guidance directly into its context window. This means it can write correct LaserSell code from the start, without needing to look anything up.
## Skills vs MCP
LaserSell offers two AI integration paths. They serve different purposes and work best together.
| | MCP Server | Coding Skills |
| ------------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| **How it works** | Searches LaserSell documentation on demand when the assistant needs to look something up | Loads deep SDK knowledge directly into the assistant's context before it starts writing code |
| **Best for** | Looking up API schemas, finding specific docs pages, answering questions about LaserSell | Writing SDK integrations, building transactions, connecting to the stream, handling errors |
| **When knowledge is available** | On demand (the assistant must ask for it) | Immediately (the assistant already has it in context) |
| **Setup** | Add MCP server config to your editor | Install the skill package |
The MCP server is great for discovery and reference lookups. Coding skills are great for writing code that integrates with the SDK. Use both for the best experience.
## What's Included
The LaserSell coding skill ships a structured knowledge tree that the assistant loads when working on LaserSell code:
* **SKILL.md** — root skill file with an overview of the SDK, key concepts, and pointers to detailed references
* **Working examples in 4 languages** — TypeScript, Python, Rust, and Go examples covering buy/sell transactions, stream connections, strategy configuration, and transaction submission
* **Strategy guide** — detailed reference for target profit, stop loss, trailing stop, sell on graduation, and deadline timeout configuration
* **Troubleshooting guide** — common errors, their causes, and fixes
* **API and type references** — request/response schemas, enum values, and field descriptions for every endpoint
* **Stream protocol reference** — the full Exit Intelligence Stream message format, event types, and connection lifecycle
* **Templates** — starter code templates for common integration patterns
## Install
### Option 1: npx (Recommended)
```bash theme={null}
npx skills add lasersell/skills
```
This downloads and installs the LaserSell coding skill into your project. The assistant picks it up automatically on the next session.
### Option 2: Claude Code CLI
```bash theme={null}
claude install-skill https://github.com/lasersell/skills/tree/main/skills/lasersell
```
### Option 3: Manual
Copy the `skills/lasersell/` directory from the [LaserSell skills repository](https://github.com/lasersell/skills) into your project's `.claude/skills/` folder:
```bash theme={null}
mkdir -p .claude/skills
cp -r path/to/skills/lasersell .claude/skills/
```
The assistant will detect the skill files and load them into context when you work on LaserSell code.
## What the Skill Covers
Once installed, your AI assistant has full context on these topics:
**Building buy and sell transactions.** The assistant knows how to construct `BuildBuyTxRequest` and `BuildSellTxRequest` objects, call the LaserSell API, and handle the response across all four SDK languages. It understands output types (SOL vs USD1), slippage configuration, and partial sells.
**Exit Intelligence Stream.** The assistant can write stream connection code, configure strategy parameters, handle all server event types (`pnl_update`, `exit_signal_with_tx`, `position_opened`, `position_closed`, `liquidity_snapshot`), and process exit signals end to end.
**Partial and multi-leg sells.** The assistant understands how to query slippage bands, determine the maximum sellable amount at a given slippage threshold, and build partial sell transactions. It can also construct multi-leg sell sequences for large positions.
**Liquidity snapshots.** The assistant knows how to read slippage bands, interpret liquidity trends (`growing`, `stable`, `draining`), and use snapshot data to size sells optimally.
**Transaction signing and submission.** The assistant can write code for local transaction signing, and understands all send targets: default RPC, custom RPC, and Helius Sender. It knows when to use each and how to configure them.
**Error handling.** The assistant has context on common error codes, retry patterns, slippage failures, and connection recovery for both the REST API and the stream.
## Combining Skills and MCP
For the best AI coding experience, use both the coding skill and the MCP server together:
1. **Install the coding skill** so your assistant has deep SDK knowledge loaded into context whenever you write LaserSell code.
2. **Connect the MCP server** so your assistant can search the full documentation when it needs to look up something outside the skill's scope, like partner fee details, rate limit tiers, or changelog entries.
With both active, the assistant can write correct SDK code immediately (from the skill) and look up supplementary information on demand (from MCP).
See [MCP Server](/ai-agents/mcp-server) for MCP setup instructions.
# MCP Server
Source: https://docs.lasersell.io/ai-agents/mcp-server
Connect AI agents and MCP-compatible clients to the LaserSell documentation via the Model Context Protocol.
The LaserSell MCP server lets any MCP-compatible AI client search and retrieve LaserSell documentation in real time. This gives AI agents access to API references, SDK guides, code examples, and integration details without leaving their workflow.
## Server URL
```
https://docs.lasersell.io/mcp
```
## Available Tools
| Tool | Description |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SearchLaserSellDocumentation` | Search across the LaserSell documentation knowledge base to find relevant information, code examples, API references, and guides. Returns contextual content with titles and direct links to documentation pages. |
## Setup
### Claude Desktop
Add the following to your Claude Desktop MCP configuration file:
* **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
```json theme={null}
{
"mcpServers": {
"lasersell-docs": {
"command": "npx",
"args": [
"@mintlify/mcp",
"--url",
"https://docs.lasersell.io/mcp"
]
}
}
}
```
Requires Node.js 18+ installed on your machine. Restart Claude Desktop after saving the configuration.
### Claude Code
```bash theme={null}
claude mcp add lasersell-docs -- npx @mintlify/mcp --url https://docs.lasersell.io/mcp
```
### Cursor
Open Cursor Settings, navigate to **MCP**, click **Add new MCP server**, and enter:
* **Name:** `lasersell-docs`
* **Type:** `command`
* **Command:** `npx @mintlify/mcp --url https://docs.lasersell.io/mcp`
### Windsurf
Add to your Windsurf MCP configuration:
```json theme={null}
{
"mcpServers": {
"lasersell-docs": {
"command": "npx",
"args": [
"@mintlify/mcp",
"--url",
"https://docs.lasersell.io/mcp"
]
}
}
}
```
### Other MCP Clients
Any client that supports the Model Context Protocol can connect using the server URL:
```
https://docs.lasersell.io/mcp
```
The transport is Streamable HTTP. Pass the URL to your MCP client's remote server configuration.
## What Agents Can Search
Once connected, the `SearchLaserSellDocumentation` tool gives agents access to the full LaserSell documentation, including:
* **API reference** for `/v1/buy`, `/v1/sell`, and the Exit Intelligence Stream WebSocket.
* **SDK guides** for TypeScript, Python, Rust, and Go with code examples.
* **Strategy configuration** for target profit, stop loss, trailing stop, sell on graduation, and deadline timeout.
* **Transaction signing and submission** across Helius Sender, Astralane, and custom RPC.
* **Partner fee configuration** for integrators monetizing through LaserSell.
* **Market context schemas** for all supported protocols (Pump.fun, PumpSwap, Meteora, Raydium).
## Example Queries
Once the MCP server is connected, you can ask your AI client questions like:
* "How do I build a sell transaction with the LaserSell Python SDK?"
* "What are the fields in a BuildBuyTxRequest?"
* "Show me how to connect to the Exit Intelligence Stream in TypeScript."
* "What send targets are available for transaction submission?"
* "How do partner fees work?"
# AI Agents and LaserSell
Source: https://docs.lasersell.io/ai-agents/overview
Connect AI agents to LaserSell's Exit Intelligence engine for automated Solana token trading, strategy management, and real-time position monitoring.
AI agents that interact with Solana tokens can use LaserSell as their execution and exit management layer. Instead of building swap routing, protocol handling, and exit logic from scratch, an agent connects to LaserSell and gets pre-built transactions, real-time PnL tracking, and automated exits out of the box.
## What Agents Can Do with LaserSell
**Build and execute trades.** The [REST API](/api/overview) returns unsigned swap transactions for any supported Solana DEX or launchpad. An agent sends a mint and amount, gets back a transaction, signs it, and submits. No protocol-specific logic required.
**Automate exits with strategy.** The [Exit Intelligence Stream](/api/stream/overview) watches wallets in real time and delivers pre-built exit transactions when configurable thresholds are met: target profit, stop loss, trailing stop, sell on graduation, and deadline timeout. An agent connects once and receives exit signals automatically.
**Monitor positions.** The stream provides continuous PnL updates, balance changes, position opens, and position closes. Agents can use this data to make decisions, surface information to users, or feed into other systems.
**Cover every major Solana protocol.** A single integration covers Pump.fun, PumpSwap, Meteora DBC, Meteora DAMM V2, Raydium Launchpad, and Raydium CPMM. The agent does not need to know which protocol a token is on.
## How to Connect
There are three ways for AI agents to work with LaserSell:
### 1. MCP Server (Documentation Access)
The LaserSell MCP server gives any MCP-compatible AI client (Claude, Cursor, Windsurf, and others) the ability to search and retrieve LaserSell documentation in real time. This is useful for agents that need to look up API schemas, SDK patterns, code examples, or integration details while working.
See [MCP Server](/ai-agents/mcp-server) for setup instructions.
### 2. Coding Skills (Embedded SDK Knowledge)
Install the LaserSell coding skill to give your AI assistant deep knowledge of SDK patterns, method signatures, examples, and troubleshooting loaded directly into context when writing LaserSell code.
See [Coding Skills](/ai-agents/coding-skills) for install instructions.
### 3. SDK and API (Direct Integration)
For agents that need to build transactions, manage positions, or execute trades programmatically, integrate using the LaserSell SDK or API directly.
* [API Overview](/api/overview) for the full API surface.
* [Quickstart](/api/quickstart) to build your first transaction.
* SDKs available in [TypeScript](/api/sdk/typescript), [Python](/api/sdk/python), [Rust](/api/sdk/rust), and [Go](/api/sdk/go).
## Why Agents Should Use LaserSell
### The agent integration problem
An AI agent that wants to trade Solana tokens today has to solve a stack of hard problems: identify which DEX protocol a token is on, deserialize on-chain pool state, compute swap amounts using protocol-specific math, construct the right instruction set, manage priority fees, handle slippage retries, and poll for price changes to know when to exit. Each protocol (Pump.fun, PumpSwap, Raydium, Meteora) has a different program, different account layouts, and different swap logic. This is hundreds of hours of integration work, and it breaks every time a new launchpad ships.
LaserSell collapses all of that into two API calls: one to build a transaction, one WebSocket to receive exit signals.
### Technical advantages
| Capability | Without LaserSell | With LaserSell |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Protocol routing** | Agent must detect which DEX a token is on, deserialize pool state, and build protocol-specific instructions for each of 6+ programs | Single API call. Send a mint address, get back a signed-ready transaction. LaserSell resolves the protocol, pool, and route server-side |
| **Exit logic** | Agent must poll RPC for price data, calculate PnL, track peak profit for trailing stops, manage timers for deadlines, and build sell transactions manually | Connect once to the Exit Intelligence Stream. Receive `exit_signal_with_tx` events with pre-built unsigned transactions when strategy conditions fire |
| **PnL tracking** | Agent must fetch token balances, resolve decimals, query pool reserves, compute spot price, and derive PnL at regular intervals | Stream delivers `pnl_update` events continuously. No polling, no RPC calls, no price math |
| **New position detection** | Agent must monitor wallet transaction history or subscribe to on-chain logs and parse each transaction to identify buys | Stream fires `position_opened` with full context (cost basis, mint, market context) the moment a purchase lands |
| **Multi-protocol coverage** | Separate integration for Pump.fun bonding curves, PumpSwap AMM, Raydium Launchpad, Raydium CPMM, Meteora DBC, Meteora DAMM V2 | One integration covers all six. Agent code does not change when a new protocol is added |
| **Transaction signing** | N/A | All transactions returned unsigned. Agent signs locally. No private key ever leaves the agent's environment |
| **Slippage handling** | Agent must estimate slippage, retry on failure, and bump tolerance incrementally | Server computes slippage. Client retries are automatic in the SDK with configurable bump logic |
| **New launchpad support** | Agent must reverse-engineer each new program, build instructions from scratch, and maintain ongoing compatibility | LaserSell adds protocol support server-side. Agent gets coverage with zero code changes |
### What this means in practice
**Fewer RPC calls.** An agent without LaserSell needs to poll price data, fetch balances, and read pool state constantly. With the Exit Intelligence Stream, the server pushes everything. The agent's RPC usage drops to transaction submission only.
**Deterministic exit execution.** Strategy conditions (TP, SL, trailing stop, deadline, graduation) are evaluated server-side on every price tick. The agent does not need to stay online continuously or worry about missing a price spike. When a threshold is met, the exit transaction is already built and waiting.
**No protocol-specific code.** The agent sends `{ mint, user_pubkey, amount }` to `/v1/sell` or `/v1/buy`. Whether that token is on a Pump.fun bonding curve, a PumpSwap AMM, or a Raydium CPMM pool, the returned transaction just works.
**Event-driven architecture.** The stream is a natural fit for agents. Instead of running poll loops, the agent subscribes and reacts: `position_opened` to log a new trade, `pnl_update` to surface data, `exit_signal_with_tx` to sign and submit. Clean async patterns with no wasted compute.
**SDK parity across 4 languages.** TypeScript, Python, Rust, and Go SDKs expose identical schemas, method names, and behavior. An agent built in Python works the same way as one built in Rust. No SDK is a second-class citizen.
# Authentication
Source: https://docs.lasersell.io/api/authentication
Obtain your API key from the LaserSell dashboard and authenticate requests across all four SDKs.
## Getting Your API Key
1. Sign in at [app.lasersell.io](https://app.lasersell.io).
2. Navigate to **Settings > API Keys**.
3. Click **Create API Key**, give it a name, and copy the value.
Store the key securely. It cannot be displayed again after creation.
## Authenticating Requests
Every request to the LaserSell API and every WebSocket connection to the Exit Intelligence Stream requires an API key passed via the `x-api-key` HTTP header.
### curl
```bash theme={null}
curl -X POST https://api.lasersell.io/v1/sell \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{"mint":"TOKEN_MINT","user_pubkey":"WALLET","amount_tokens":1000000}'
```
### SDK Examples
```typescript TypeScript theme={null}
import { ExitApiClient } from "@lasersell/lasersell-sdk";
const client = ExitApiClient.withApiKey("YOUR_API_KEY");
```
```python Python theme={null}
from lasersell_sdk.exit_api import ExitApiClient
client = ExitApiClient.with_api_key("YOUR_API_KEY")
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::ExitApiClient;
let client = ExitApiClient::with_api_key("YOUR_API_KEY");
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
client := lasersell.NewExitAPIClientWithAPIKey("YOUR_API_KEY")
```
### Exit Intelligence Stream Authentication
The Exit Intelligence Stream WebSocket also uses `x-api-key`. The SDK handles this automatically when you construct a `StreamClient`:
```typescript TypeScript theme={null}
import { StreamClient } from "@lasersell/lasersell-sdk";
const client = new StreamClient("YOUR_API_KEY");
```
```python Python theme={null}
from lasersell_sdk.stream.client import StreamClient
client = StreamClient("YOUR_API_KEY")
```
```rust Rust theme={null}
use lasersell_sdk::stream::client::StreamClient;
use secrecy::SecretString;
let client = StreamClient::new(SecretString::new("YOUR_API_KEY".into()));
```
```go Go theme={null}
import "github.com/lasersell/lasersell-sdk/go/stream"
client := stream.NewStreamClient("YOUR_API_KEY")
```
## Security Best Practices
* **Never commit your API key** to version control. Use environment variables or a secrets manager.
* **Rotate keys** periodically through the dashboard.
* **Scope access** by creating separate keys for different bots or environments.
* If a key is compromised, revoke it immediately from the dashboard and create a replacement.
# Get Account
Source: https://docs.lasersell.io/api/exit-api/account
GET /v1/account
Retrieve your account tier, plan name, and usage limits.
# Build Buy Transaction
Source: https://docs.lasersell.io/api/exit-api/buy
POST /v1/buy
Build an unsigned buy transaction to swap SOL or USD1 for tokens via one of the supported DEXs.
The response contains a base64-encoded unsigned `VersionedTransaction`. Sign it locally with your keypair and submit it to the network.
If you plan to track this buy with the Exit Intelligence Stream, you **must** connect the stream **before** submitting the buy transaction. The stream detects new positions by observing on-chain token arrivals in real time. If the stream is not connected when the buy lands, the position will not be tracked and no exit signals will be generated for it.
Exactly one of `amount` or `amount_in_total` must be provided. `amount` is automatically converted to atomic units based on the `input` asset (SOL: 10^9, USD1: 10^6).
## SDK Examples
```typescript TypeScript theme={null}
import { ExitApiClient, type BuildBuyTxRequest } from "@lasersell/lasersell-sdk";
const client = ExitApiClient.withApiKey("YOUR_API_KEY");
const request: BuildBuyTxRequest = {
mint: "TOKEN_MINT_ADDRESS",
user_pubkey: "YOUR_WALLET_PUBKEY",
amount: 0.1, // 0.1 SOL
slippage_bps: 2_000,
};
const response = await client.buildBuyTx(request);
console.log("Unsigned tx (base64):", response.tx);
```
```python Python theme={null}
from lasersell_sdk.exit_api import ExitApiClient, BuildBuyTxRequest
client = ExitApiClient.with_api_key("YOUR_API_KEY")
request = BuildBuyTxRequest(
mint="TOKEN_MINT_ADDRESS",
user_pubkey="YOUR_WALLET_PUBKEY",
amount=0.1, # 0.1 SOL
slippage_bps=2_000,
)
response = await client.build_buy_tx(request)
print("Unsigned tx (base64):", response.tx)
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::{ExitApiClient, BuildBuyTxRequest};
let client = ExitApiClient::with_api_key("YOUR_API_KEY");
let request = BuildBuyTxRequest {
mint: "TOKEN_MINT_ADDRESS".into(),
user_pubkey: "YOUR_WALLET_PUBKEY".into(),
amount: Some(0.1), // 0.1 SOL
slippage_bps: 2_000,
..Default::default()
};
let response = client.build_buy_tx(&request).await?;
println!("Unsigned tx (base64): {}", response.tx);
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
client := lasersell.NewExitAPIClientWithAPIKey("YOUR_API_KEY")
amount := 0.1 // 0.1 SOL
resp, err := client.BuildBuyTx(ctx, lasersell.BuildBuyTxRequest{
Mint: "TOKEN_MINT_ADDRESS",
UserPubkey: "YOUR_WALLET_PUBKEY",
Amount: &amount,
SlippageBps: 2_000,
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Unsigned tx (base64):", resp.Tx)
```
## Error Responses
See [Error Handling](/api/exit-api/error-handling) for the full error envelope specification and retryable error logic.
# Error Handling
Source: https://docs.lasersell.io/api/exit-api/error-handling
SDK error types, retryability, and retry configuration for the LaserSell API.
## `ExitApiError` Kinds
Every SDK exposes an `ExitApiError` (or equivalent) with a `kind` discriminator:
| Kind | Trigger | Retryable |
| ----------------- | ----------------------------------------- | --------------------------- |
| `transport` | Network failure, DNS error, timeout | Yes |
| `http_status` | Non 2xx HTTP response | Yes if status >= 500 or 429 |
| `envelope_status` | Server returned `{ "status": "error" }` | No |
| `parse` | Response body could not be parsed as JSON | No |
### Checking Retryability
```typescript TypeScript theme={null}
import { ExitApiError } from "@lasersell/lasersell-sdk";
try {
const response = await client.buildSellTx(request);
} catch (error) {
if (error instanceof ExitApiError) {
console.log("Kind:", error.kind);
console.log("Retryable:", error.isRetryable());
console.log("HTTP status:", error.status);
console.log("Body:", error.body);
}
}
```
```python Python theme={null}
from lasersell_sdk.exit_api import ExitApiError
try:
response = await client.build_sell_tx(request)
except ExitApiError as error:
print("Kind:", error.kind)
print("Retryable:", error.is_retryable())
print("HTTP status:", error.status)
print("Body:", error.body)
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::ExitApiError;
match client.build_sell_tx(&request).await {
Ok(response) => { /* use response */ }
Err(error) => {
eprintln!("Kind: {:?}", error.kind());
eprintln!("Retryable: {}", error.is_retryable());
}
}
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
resp, err := client.BuildSellTx(ctx, request)
if err != nil {
var apiErr *lasersell.ExitAPIError
if errors.As(err, &apiErr) {
fmt.Println("Kind:", apiErr.Kind)
fmt.Println("Retryable:", apiErr.IsRetryable())
fmt.Println("HTTP status:", apiErr.Status)
}
}
```
## Built in Retry Behavior
All SDKs include automatic retry with these defaults:
| Setting | Default Value |
| --------------- | ------------- |
| Max attempts | 2 |
| Initial backoff | 25 ms |
| Max backoff | 25 ms |
| Jitter | 25 ms |
| Connect timeout | 200 ms |
| Attempt timeout | 900 ms |
Retries only trigger for errors where `isRetryable()` returns `true` (transport failures, 5xx responses, and 429 rate limits).
### Customizing Retry Policy
```typescript TypeScript theme={null}
const client = ExitApiClient.withOptions("YOUR_API_KEY", {
attempt_timeout_ms: 2000,
retry_policy: {
max_attempts: 3,
initial_backoff_ms: 50,
max_backoff_ms: 200,
jitter_ms: 50,
},
});
```
```python Python theme={null}
from lasersell_sdk.exit_api import ExitApiClient, ExitApiClientOptions
from lasersell_sdk.retry import RetryPolicy
client = ExitApiClient.with_options(
"YOUR_API_KEY",
ExitApiClientOptions(
attempt_timeout_s=2.0,
retry_policy=RetryPolicy(
max_attempts=3,
initial_backoff_ms=50,
max_backoff_ms=200,
jitter_ms=50,
),
),
)
```
```rust Rust theme={null}
use std::time::Duration;
use lasersell_sdk::exit_api::{ExitApiClient, ExitApiClientOptions};
use lasersell_sdk::retry::RetryPolicy;
use secrecy::SecretString;
let client = ExitApiClient::with_options(
Some(SecretString::new("YOUR_API_KEY".into())),
ExitApiClientOptions {
connect_timeout: Duration::from_millis(200),
attempt_timeout: Duration::from_millis(2000),
retry_policy: RetryPolicy {
max_attempts: 3,
initial_backoff: Duration::from_millis(50),
max_backoff: Duration::from_millis(200),
jitter: Duration::from_millis(50),
},
},
)?;
```
```go Go theme={null}
import (
"time"
lasersell "github.com/lasersell/lasersell-sdk/go"
)
client := lasersell.NewExitAPIClientWithOptions("YOUR_API_KEY", lasersell.ExitAPIClientOptions{
ConnectTimeout: 200 * time.Millisecond,
AttemptTimeout: 2000 * time.Millisecond,
RetryPolicy: lasersell.RetryPolicy{
MaxAttempts: 3,
InitialBackoff: 50 * time.Millisecond,
MaxBackoff: 200 * time.Millisecond,
Jitter: 50 * time.Millisecond,
},
})
```
# Get Trade History
Source: https://docs.lasersell.io/api/exit-api/history
GET /v1/history
Fetch paginated trade history for your account, ordered by most recent first.
# Build Sell Transaction
Source: https://docs.lasersell.io/api/exit-api/sell
POST /v1/sell
Build an unsigned sell transaction to swap tokens for SOL or USD1 via one of the supported DEXs.
The response contains a base64-encoded unsigned `VersionedTransaction`. Sign it locally with your keypair and submit it to the network.
## SDK Examples
```typescript TypeScript theme={null}
import { ExitApiClient, type BuildSellTxRequest } from "@lasersell/lasersell-sdk";
const client = ExitApiClient.withApiKey("YOUR_API_KEY");
const request: BuildSellTxRequest = {
mint: "TOKEN_MINT_ADDRESS",
user_pubkey: "YOUR_WALLET_PUBKEY",
amount_tokens: 1_000_000,
slippage_bps: 2_000,
output: "SOL",
};
const response = await client.buildSellTx(request);
console.log("Unsigned tx (base64):", response.tx);
// Or get just the base64 string:
const txB64 = await client.buildSellTxB64(request);
```
```python Python theme={null}
from lasersell_sdk.exit_api import ExitApiClient, BuildSellTxRequest, SellOutput
client = ExitApiClient.with_api_key("YOUR_API_KEY")
request = BuildSellTxRequest(
mint="TOKEN_MINT_ADDRESS",
user_pubkey="YOUR_WALLET_PUBKEY",
amount_tokens=1_000_000,
slippage_bps=2_000,
output=SellOutput.SOL,
)
response = await client.build_sell_tx(request)
print("Unsigned tx (base64):", response.tx)
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::{ExitApiClient, BuildSellTxRequest, SellOutput};
let client = ExitApiClient::with_api_key("YOUR_API_KEY");
let request = BuildSellTxRequest {
mint: "TOKEN_MINT_ADDRESS".into(),
user_pubkey: "YOUR_WALLET_PUBKEY".into(),
amount_tokens: 1_000_000,
output: SellOutput::Sol,
slippage_bps: 2_000,
..Default::default()
};
let response = client.build_sell_tx(&request).await?;
println!("Unsigned tx (base64): {}", response.tx);
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
client := lasersell.NewExitAPIClientWithAPIKey("YOUR_API_KEY")
resp, err := client.BuildSellTx(ctx, lasersell.BuildSellTxRequest{
Mint: "TOKEN_MINT_ADDRESS",
UserPubkey: "YOUR_WALLET_PUBKEY",
AmountTokens: 1_000_000,
Output: lasersell.SellOutputSOL,
SlippageBps: 2_000,
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Unsigned tx (base64):", resp.Tx)
```
## Error Responses
See [Error Handling](/api/exit-api/error-handling) for the full error envelope specification and retryable error logic.
# Register Wallet
Source: https://docs.lasersell.io/api/exit-api/wallets-register
POST /v1/wallets/register
Register a wallet by proving ownership with an Ed25519 signature. Registration is required before connecting wallets to the Exit Intelligence Stream.
No on-chain transaction is needed. Generate the proof locally using your keypair and the SDK's `proveOwnership` helper.
## SDK Usage
### Generate Proof
Use the SDK's `proveOwnership` helper to generate a `WalletProof` locally. This is a pure cryptographic operation with no network call.
```typescript TypeScript theme={null}
import { proveOwnership } from "@lasersell/lasersell-sdk";
const proof = proveOwnership(keypair);
// proof = { walletPubkey, signature, message }
```
```python Python theme={null}
from lasersell_sdk.exit_api import prove_ownership
proof = prove_ownership(keypair)
# proof = WalletProof(wallet_pubkey, signature, message)
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::prove_ownership;
let proof = prove_ownership(&keypair);
// proof = WalletProof { wallet_pubkey, signature, message }
```
```go Go theme={null}
proof := lasersell.ProveOwnership(privateKey)
// proof = WalletProof{WalletPubkey, Signature, Message}
```
### Register
Send the proof to the API to register the wallet.
```typescript TypeScript theme={null}
const client = ExitApiClient.withApiKey(apiKey);
await client.registerWallet(proof, "My Wallet");
```
```python Python theme={null}
client = ExitApiClient.with_api_key(api_key)
await client.register_wallet(proof, label="My Wallet")
```
```rust Rust theme={null}
client.register_wallet(&proof, Some("My Wallet")).await?;
```
```go Go theme={null}
client.RegisterWallet(ctx, proof, lasersell.Ptr("My Wallet"))
```
### Connect with Wallets (one step)
Convenience method that registers wallets and connects to the stream in one call.
```typescript TypeScript theme={null}
import { StreamClient, proveOwnership } from "@lasersell/lasersell-sdk";
const proof = proveOwnership(keypair);
const client = new StreamClient(apiKey);
const session = await client.connectWithWallets(
[proof],
{ target_profit_pct: 50, stop_loss_pct: 10 },
120, // deadline_timeout_sec
);
```
```python Python theme={null}
from lasersell_sdk.exit_api import prove_ownership
from lasersell_sdk.stream.client import StreamClient
proof = prove_ownership(keypair)
client = StreamClient(api_key)
session = await client.connect_with_wallets(
[proof],
{"target_profit_pct": 50, "stop_loss_pct": 10},
deadline_timeout_sec=120,
)
```
```rust Rust theme={null}
let proof = prove_ownership(&keypair);
let client = StreamClient::new(api_key);
let session = client.connect_with_wallets(
&[proof],
strategy,
120,
).await?;
```
```go Go theme={null}
// Go SDK: register separately, then connect
proof := lasersell.ProveOwnership(privateKey)
client.RegisterWallet(ctx, proof, nil)
session, _ := stream.ConnectSession(ctx, streamClient, configure)
```
# Unregister Wallet
Source: https://docs.lasersell.io/api/exit-api/wallets-unregister
DELETE /v1/wallets
Remove a wallet registration from your account.
# API and SDK Overview
Source: https://docs.lasersell.io/api/overview
High level introduction to the LaserSell API, Exit Intelligence Stream, and SDKs for programmatic token trading on Solana.
## What is the LaserSell API?
The LaserSell API lets you build, sign, and submit Solana swap transactions programmatically. It exposes two surfaces:
* **LaserSell API** (REST): Build unsigned buy and sell transactions on demand via `POST /v1/sell` and `POST /v1/buy`. Retrieve your account details with `GET /v1/account` and query your trade history with `GET /v1/history`.
* **Exit Intelligence Stream** (WebSocket): Connect a persistent session that watches your wallets, tracks positions, evaluates your strategy in real time, and delivers pre built exit transactions when thresholds are met.
Both surfaces return **unsigned transactions**. Your private key never leaves your machine. You sign locally, then submit through the send target of your choice.
## Non Custodial Model
LaserSell is fully non custodial. The server constructs optimized swap instructions but cannot execute them without your signature. This means:
1. You hold the keypair at all times.
2. The API returns a base64 encoded unsigned transaction.
3. You sign with your local keypair.
4. You submit via RPC, Helius Sender, or Astralane.
No funds, tokens, or keys are ever stored or accessed by LaserSell infrastructure.
## Architecture at a Glance
```mermaid theme={null}
flowchart LR
A["Your Code
(SDK)"] -->|REST| B["LaserSell API
api.lasersell.io"]
A -->|WebSocket| C["Exit Intelligence Stream
stream.lasersell.io"]
B -->|unsigned tx| A
C -->|unsigned tx| A
A -->|"sign locally,
then submit"| D["Solana RPC
or Sender"]
```
## SDK Languages
Official SDKs are available in four languages, each providing the same capabilities:
| Language | Package | Modules |
| ---------- | --------------------------------------- | -------------------------------------------------------------------------- |
| TypeScript | `@lasersell/lasersell-sdk` | `ExitApiClient`, `StreamClient`, `StreamSession`, tx helpers |
| Python | `lasersell-sdk` | `ExitApiClient`, `StreamClient`, `StreamSession`, tx helpers |
| Rust | `lasersell-sdk` | `exit_api`, `stream`, `tx` |
| Go | `github.com/lasersell/lasersell-sdk/go` | `ExitAPIClient`, `stream.StreamClient`, `stream.StreamSession`, tx helpers |
All SDKs share the same request and response schemas, error types, and retry behavior. Pick the language that fits your stack and follow the corresponding SDK guide.
## What to Read Next
* [Authentication](/api/authentication): Get your API key and start making requests.
* [Quickstart](/api/quickstart): Build your first sell transaction in under five minutes.
* [Exit Intelligence Stream](/api/stream/overview): Learn when to use the WebSocket stream instead of REST.
# Quickstart
Source: https://docs.lasersell.io/api/quickstart
Build, sign, and submit your first sell transaction in under five minutes, then set up an auto sell stream.
## Prerequisites
* A LaserSell API key ([get one here](/api/authentication)).
* A Solana keypair file (JSON array of bytes).
* Node.js 18+ (for the TypeScript example) or the runtime for your preferred language.
**Using AI to code?** Add the [LaserSell MCP server](/ai-agents/mcp-server) to your editor so your AI assistant can search LaserSell documentation in real time. Works with Claude Code, Claude Desktop, Cursor, and Windsurf.
## Example 1: Build, Sign, and Send a Sell Transaction
This example calls the LaserSell API to build an unsigned sell transaction, signs it locally, and submits through Helius Sender.
```typescript TypeScript theme={null}
import { readFile } from "node:fs/promises";
import { Keypair } from "@solana/web3.js";
import {
ExitApiClient,
sendTransaction,
sendTargetHeliusSender,
signUnsignedTx,
type BuildSellTxRequest,
} from "@lasersell/lasersell-sdk";
const apiKey = process.env.LASERSELL_API_KEY!;
const keypairPath = "./keypair.json";
// Load keypair
const raw = await readFile(keypairPath, "utf8");
const keypair = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(raw))
);
// Build unsigned tx
const client = ExitApiClient.withApiKey(apiKey);
const request: BuildSellTxRequest = {
mint: "So11111111111111111111111111111111111111112",
user_pubkey: keypair.publicKey.toBase58(),
amount_tokens: 1_000_000,
slippage_bps: 2_000,
output: "SOL",
};
const unsignedTxB64 = await client.buildSellTxB64(request);
// Sign locally
const signedTx = signUnsignedTx(unsignedTxB64, keypair);
// Submit
const signature = await sendTransaction(
sendTargetHeliusSender(),
signedTx
);
console.log("Signature:", signature);
```
```python Python theme={null}
import asyncio, json
from pathlib import Path
from solders.keypair import Keypair
from lasersell_sdk.exit_api import ExitApiClient, BuildSellTxRequest, SellOutput
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
async def main():
api_key = "YOUR_API_KEY"
keypair_bytes = json.loads(Path("./keypair.json").read_text())
signer = Keypair.from_bytes(bytes(keypair_bytes))
client = ExitApiClient.with_api_key(api_key)
request = BuildSellTxRequest(
mint="So11111111111111111111111111111111111111112",
user_pubkey=str(signer.pubkey()),
amount_tokens=1_000_000,
slippage_bps=2_000,
output=SellOutput.SOL,
)
unsigned_tx_b64 = await client.build_sell_tx_b64(request)
signed_tx = sign_unsigned_tx(unsigned_tx_b64, signer)
signature = await send_transaction(SendTargetHeliusSender(), signed_tx)
print("Signature:", signature)
asyncio.run(main())
```
```rust Rust theme={null}
use lasersell_sdk::exit_api::{ExitApiClient, BuildSellTxRequest};
use lasersell_sdk::tx::{SendTarget, sign_unsigned_tx, send_transaction};
use solana_sdk::signature::read_keypair_file;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("LASERSELL_API_KEY")?;
let keypair = read_keypair_file("./keypair.json")?;
let http = reqwest::Client::new();
let client = ExitApiClient::with_api_key(&api_key);
let request = BuildSellTxRequest {
mint: "So11111111111111111111111111111111111111112".into(),
user_pubkey: keypair.pubkey().to_string(),
amount_tokens: 1_000_000,
output: SellOutput::Sol,
slippage_bps: 2_000,
..Default::default()
};
let unsigned_tx_b64 = client.build_sell_tx_b64(&request).await?;
let signed_tx = sign_unsigned_tx(&unsigned_tx_b64, &keypair)?;
let signature = send_transaction(&http, &SendTarget::HeliusSender, &signed_tx).await?;
println!("Signature: {signature}");
Ok(())
}
```
```go Go theme={null}
package main
import (
"context"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
lasersell "github.com/lasersell/lasersell-sdk/go"
)
func main() {
ctx := context.Background()
apiKey := "YOUR_API_KEY"
privateKey, err := solana.PrivateKeyFromSolanaKeygenFile("./keypair.json")
if err != nil {
log.Fatal(err)
}
client := lasersell.NewExitAPIClientWithAPIKey(apiKey)
resp, err := client.BuildSellTx(ctx, lasersell.BuildSellTxRequest{
Mint: "So11111111111111111111111111111111111111112",
UserPubkey: privateKey.PublicKey().String(),
AmountTokens: 1_000_000,
Output: lasersell.SellOutputSOL,
SlippageBps: 2_000,
})
if err != nil {
log.Fatal(err)
}
signedTx, err := lasersell.SignUnsignedTx(resp.Tx, privateKey)
if err != nil {
log.Fatal(err)
}
sig, err := lasersell.SendTransaction(ctx, nil, lasersell.SendTargetHeliusSender(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Println("Signature:", sig)
}
func intPtr(v int) *int { return &v }
func strPtr(v string) *string { return &v }
```
## Example 2: Auto Sell with the Exit Intelligence Stream
Connect the Exit Intelligence Stream to monitor wallets and automatically execute exits when your strategy thresholds are reached.
**Connect the stream before you buy.** The stream detects positions by watching for on chain token arrivals. If you call `/v1/buy` before the stream is connected and configured, the position will not be tracked. Always start the stream first, then submit your buy transaction.
```typescript TypeScript theme={null}
import { readFile } from "node:fs/promises";
import { Keypair } from "@solana/web3.js";
import {
StreamClient,
StreamSession,
sendTransaction,
sendTargetHeliusSender,
signUnsignedTx,
} from "@lasersell/lasersell-sdk";
const apiKey = process.env.LASERSELL_API_KEY!;
const raw = await readFile("./keypair.json", "utf8");
const signer = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(raw)));
const client = new StreamClient(apiKey);
const session = await StreamSession.connect(client, {
wallet_pubkeys: [signer.publicKey.toBase58()],
strategy: {
target_profit_pct: 5,
stop_loss_pct: 1.5,
},
deadline_timeout_sec: 45,
send_mode: "helius_sender",
tip_lamports: 1000,
});
while (true) {
const event = await session.recv();
if (event === null) break;
if (event.type === "exit_signal_with_tx") {
const signed = signUnsignedTx(event.message.unsigned_tx_b64, signer);
const sig = await sendTransaction(sendTargetHeliusSender(), signed);
console.log("Exit submitted:", sig);
}
}
```
```python Python theme={null}
import asyncio, json
from pathlib import Path
from solders.keypair import Keypair
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
async def main():
signer = Keypair.from_bytes(bytes(json.loads(Path("./keypair.json").read_text())))
client = StreamClient("YOUR_API_KEY")
session = await StreamSession.connect(
client,
StreamConfigure(
wallet_pubkeys=[str(signer.pubkey())],
strategy={"target_profit_pct": 5.0, "stop_loss_pct": 1.5},
deadline_timeout_sec=45,
send_mode="helius_sender",
tip_lamports=1000,
),
)
while True:
event = await session.recv()
if event is None:
break
if event.type == "exit_signal_with_tx":
msg = event.message
signed = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed)
print("Exit submitted:", sig)
asyncio.run(main())
```
```rust Rust theme={null}
use lasersell_sdk::stream::client::{StreamClient, StreamConfigure, strategy_config_from_optional};
use lasersell_sdk::stream::session::{StreamSession, StreamEvent};
use lasersell_sdk::stream::proto::ServerMessage;
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 api_key = std::env::var("LASERSELL_API_KEY")?;
let keypair = read_keypair_file("./keypair.json")?;
let http = reqwest::Client::new();
let client = StreamClient::new(SecretString::new(api_key));
let configure = StreamConfigure {
wallet_pubkeys: vec![keypair.pubkey().to_string()],
strategy: strategy_config_from_optional(
Some(5.0), Some(1.5), None, None,
),
deadline_timeout_sec: 45,
send_mode: Some("helius_sender".to_string()),
tip_lamports: Some(1000),
};
let mut session = StreamSession::connect(&client, configure).await?;
while let Some(event) = session.recv().await {
if let StreamEvent::ExitSignalWithTx {
message: ServerMessage::ExitSignalWithTx {
unsigned_tx_b64, ..
},
..
} = event
{
let signed = sign_unsigned_tx(&unsigned_tx_b64, &keypair)?;
let sig = send_transaction(&http, &SendTarget::HeliusSender, &signed).await?;
println!("Exit submitted: {sig}");
}
}
Ok(())
}
```
```go Go theme={null}
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"github.com/gagliardetto/solana-go"
lasersell "github.com/lasersell/lasersell-sdk/go"
"github.com/lasersell/lasersell-sdk/go/stream"
)
func main() {
ctx := context.Background()
apiKey := "YOUR_API_KEY"
privateKey, err := solana.PrivateKeyFromSolanaKeygenFile("./keypair.json")
if err != nil {
log.Fatal(err)
}
client := stream.NewStreamClient(apiKey)
sendMode := "helius_sender"
tipLamports := uint64(1000)
session, err := stream.ConnectSession(ctx, client, stream.StreamConfigure{
WalletPubkeys: []string{privateKey.PublicKey().String()},
Strategy: stream.StrategyConfigMsg{
TargetProfitPct: 5.0,
StopLossPct: 1.5,
},
DeadlineTimeoutSec: 45,
SendMode: &sendMode,
TipLamports: &tipLamports,
})
if err != nil {
log.Fatal(err)
}
for {
event, err := session.Recv(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Fatal(err)
}
if msg, ok := event.Message.(stream.ExitSignalWithTxServerMessage); ok {
signedTx, err := lasersell.SignUnsignedTx(msg.UnsignedTxB64, privateKey)
if err != nil {
log.Printf("sign failed: %v", err)
continue
}
sig, err := lasersell.SendTransaction(ctx, nil, lasersell.SendTargetHeliusSender(), signedTx)
if err != nil {
log.Printf("send failed: %v", err)
continue
}
fmt.Println("Exit submitted:", sig)
}
}
}
```
## Next Steps
* [POST /v1/sell](/api/exit-api/sell): Full request and response schema for sell transactions.
* [Exit Intelligence Stream](/api/stream/overview): Deep dive into the WebSocket stream.
* [Transaction Signing](/api/transactions/signing): Understand the non custodial signing flow.
# Market Context
Source: https://docs.lasersell.io/api/reference/market-context
Full MarketContextMsg schema with nested objects for each supported DEX protocol.
## Overview
`MarketContextMsg` describes the DEX pool and protocol used for a token's market. It appears in:
* `position_opened` server events
* `exit_signal_with_tx` server events
* `BuildSellTxRequest` (optional, to skip server side resolution)
If omitted from a build request, the server resolves the context automatically.
## `MarketContextMsg` Schema
| Field | Type | Required | Description |
| ------------------- | -------- | -------- | ---------------------------------------------- |
| `market_type` | `string` | Yes | One of the supported market types (see below). |
| `pumpfun` | `object` | No | Pump.fun bonding curve context. |
| `pumpswap` | `object` | No | PumpSwap AMM context. |
| `meteora_dbc` | `object` | No | Meteora DBC context. |
| `meteora_damm_v2` | `object` | No | Meteora DAMM V2 context. |
| `raydium_launchpad` | `object` | No | Raydium Launchpad context. |
| `raydium_cpmm` | `object` | No | Raydium CPMM context. |
Exactly one of the nested objects should be present, matching the `market_type`.
The `market_type` value uses underscores (e.g. `pump_fun`) while the nested object key does not (e.g. `pumpfun`). Match the exact casing shown in the tables below.
## `market_type` Values
| Value | Protocol |
| ------------------- | ----------------- |
| `pump_fun` | Pump.fun |
| `pump_swap` | PumpSwap |
| `meteora_dbc` | Meteora DBC |
| `meteora_damm_v2` | Meteora DAMM V2 |
| `raydium_launchpad` | Raydium Launchpad |
| `raydium_cpmm` | Raydium CPMM |
## Nested Context Objects
### `PumpFunContextMsg`
Empty object. Pump.fun bonding curve tokens do not require additional pool context.
```json theme={null}
{
"market_type": "pump_fun",
"pumpfun": {}
}
```
### `PumpSwapContextMsg`
| Field | Type | Required | Description |
| --------------- | -------- | -------- | ------------------------------- |
| `pool` | `string` | Yes | PumpSwap pool address. |
| `global_config` | `string` | No | PumpSwap global config address. |
```json theme={null}
{
"market_type": "pump_swap",
"pumpswap": {
"pool": "PoolAddr...",
"global_config": "ConfigAddr..."
}
}
```
### `MeteoraDbcContextMsg`
| Field | Type | Required | Description |
| ------------ | -------- | -------- | -------------------------------- |
| `pool` | `string` | Yes | Meteora DBC pool address. |
| `config` | `string` | Yes | Pool configuration address. |
| `quote_mint` | `string` | Yes | Quote asset mint (SOL or other). |
```json theme={null}
{
"market_type": "meteora_dbc",
"meteora_dbc": {
"pool": "PoolAddr...",
"config": "ConfigAddr...",
"quote_mint": "So111..."
}
}
```
### `MeteoraDammV2ContextMsg`
| Field | Type | Required | Description |
| ------ | -------- | -------- | ----------------------------- |
| `pool` | `string` | Yes | Meteora DAMM V2 pool address. |
```json theme={null}
{
"market_type": "meteora_damm_v2",
"meteora_damm_v2": {
"pool": "PoolAddr..."
}
}
```
### `RaydiumLaunchpadContextMsg`
| Field | Type | Required | Description |
| -------------------- | -------- | -------- | ------------------------------- |
| `pool` | `string` | Yes | Raydium Launchpad pool address. |
| `config` | `string` | Yes | Pool configuration address. |
| `platform` | `string` | Yes | Platform address. |
| `quote_mint` | `string` | Yes | Quote asset mint. |
| `user_quote_account` | `string` | Yes | User's quote token account. |
```json theme={null}
{
"market_type": "raydium_launchpad",
"raydium_launchpad": {
"pool": "PoolAddr...",
"config": "ConfigAddr...",
"platform": "PlatformAddr...",
"quote_mint": "So111...",
"user_quote_account": "QuoteATA..."
}
}
```
### `RaydiumCpmmContextMsg`
| Field | Type | Required | Description |
| -------------------- | -------- | -------- | --------------------------- |
| `pool` | `string` | Yes | Raydium CPMM pool address. |
| `config` | `string` | Yes | Pool configuration address. |
| `quote_mint` | `string` | Yes | Quote asset mint. |
| `user_quote_account` | `string` | Yes | User's quote token account. |
```json theme={null}
{
"market_type": "raydium_cpmm",
"raydium_cpmm": {
"pool": "PoolAddr...",
"config": "ConfigAddr...",
"quote_mint": "So111...",
"user_quote_account": "QuoteATA..."
}
}
```
## Passing Market Context in Build Requests
You can include `market_context` in a `BuildSellTxRequest` to skip server side pool resolution. This is useful when you already have the context from a `position_opened` or `exit_signal_with_tx` event.
```typescript TypeScript theme={null}
const request: BuildSellTxRequest = {
mint: "TOKEN_MINT",
user_pubkey: "WALLET",
amount_tokens: 1_000_000,
slippage_bps: 2_000,
market_context: {
market_type: "pump_swap",
pumpswap: { pool: "PoolAddr..." },
},
};
```
```python Python theme={null}
request = BuildSellTxRequest(
mint="TOKEN_MINT",
user_pubkey="WALLET",
amount_tokens=1_000_000,
slippage_bps=2_000,
market_context={
"market_type": "pump_swap",
"pumpswap": {"pool": "PoolAddr..."},
},
)
```
```rust Rust theme={null}
let request = BuildSellTxRequest {
mint: "TOKEN_MINT".into(),
user_pubkey: "WALLET".into(),
amount_tokens: 1_000_000,
output: SellOutput::Sol,
slippage_bps: 2_000,
market_context: Some(MarketContextMsg {
market_type: "pump_swap".into(),
pumpswap: Some(PumpSwapContextMsg { pool: "PoolAddr...".into(), ..Default::default() }),
..Default::default()
}),
..Default::default()
};
```
```go Go theme={null}
resp, err := client.BuildSellTx(ctx, lasersell.BuildSellTxRequest{
Mint: "TOKEN_MINT",
UserPubkey: "WALLET",
AmountTokens: 1_000_000,
Output: lasersell.SellOutputSOL,
SlippageBps: 2_000,
MarketContext: &stream.MarketContextMsg{
MarketType: "pump_swap",
PumpSwap: &stream.PumpSwapContextMsg{Pool: "PoolAddr..."},
},
})
```
## TypeScript Type Reference
```typescript theme={null}
type MarketTypeMsg =
| "pump_fun"
| "pump_swap"
| "meteora_dbc"
| "meteora_damm_v2"
| "raydium_launchpad"
| "raydium_cpmm";
interface MarketContextMsg {
market_type: MarketTypeMsg;
pumpfun?: PumpFunContextMsg;
pumpswap?: PumpSwapContextMsg;
meteora_dbc?: MeteoraDbcContextMsg;
meteora_damm_v2?: MeteoraDammV2ContextMsg;
raydium_launchpad?: RaydiumLaunchpadContextMsg;
raydium_cpmm?: RaydiumCpmmContextMsg;
}
```
# Partner Fees
Source: https://docs.lasersell.io/api/reference/partner-fees
Configure partner fees on buy and sell transactions using basis points or flat lamport amounts.
## Overview
The LaserSell API supports adding a partner fee to any buy or sell transaction. This lets integrators collect a fee routed to a specified wallet as part of the swap instruction.
Partner fees are optional and configured per request. They are **not** related to LaserSell's own protocol fee.
## Fee Modes
You can specify the fee in one of two ways. These modes are mutually exclusive; do not set both on the same request.
### Basis Points Mode
Set `partner_fee_bps` to charge a percentage of the swap value.
| Field | Type | Description |
| ----------------------- | -------- | ----------------------------------------- |
| `partner_fee_recipient` | `string` | Recipient wallet address (base58 pubkey). |
| `partner_fee_bps` | `number` | Fee in basis points. Max `50` (0.5%). |
**Example**: A 25 bps (0.25%) fee on a 1 SOL swap routes 0.0025 SOL to the recipient.
### Flat Lamports Mode
Set `partner_fee_lamports` to charge a fixed amount in lamports.
| Field | Type | Description |
| ----------------------- | -------- | --------------------------------------------- |
| `partner_fee_recipient` | `string` | Recipient wallet address (base58 pubkey). |
| `partner_fee_lamports` | `number` | Fee in lamports. Max `50,000,000` (0.05 SOL). |
## Limits
| Mode | Maximum Value | Equivalent |
| -------- | ------------------- | ---------- |
| bps | 50 bps | 0.5% |
| lamports | 50,000,000 lamports | 0.05 SOL |
Requests exceeding these limits will be rejected with a `400` error.
## Code Examples
### Sell with BPS Fee
```typescript TypeScript theme={null}
const request: BuildSellTxRequest = {
mint: "TOKEN_MINT",
user_pubkey: "WALLET",
amount_tokens: 1_000_000,
slippage_bps: 2_000,
output: "SOL",
partner_fee_recipient: "FEE_WALLET_PUBKEY",
partner_fee_bps: 25, // 0.25%
};
const response = await client.buildSellTx(request);
```
```python Python theme={null}
request = BuildSellTxRequest(
mint="TOKEN_MINT",
user_pubkey="WALLET",
amount_tokens=1_000_000,
slippage_bps=2_000,
output=SellOutput.SOL,
partner_fee_recipient="FEE_WALLET_PUBKEY",
partner_fee_bps=25,
)
response = await client.build_sell_tx(request)
```
```rust Rust theme={null}
let request = BuildSellTxRequest {
mint: "TOKEN_MINT".into(),
user_pubkey: "WALLET".into(),
amount_tokens: 1_000_000,
output: SellOutput::Sol,
slippage_bps: 2_000,
partner_fee_recipient: Some("FEE_WALLET_PUBKEY".into()),
partner_fee_bps: Some(25),
..Default::default()
};
let response = client.build_sell_tx(&request).await?;
```
```go Go theme={null}
resp, err := client.BuildSellTx(ctx, lasersell.BuildSellTxRequest{
Mint: "TOKEN_MINT",
UserPubkey: "WALLET",
AmountTokens: 1_000_000,
Output: lasersell.SellOutputSOL,
SlippageBps: 2_000,
PartnerFeeRecipient: lasersell.Ptr("FEE_WALLET_PUBKEY"),
PartnerFeeBps: lasersell.Ptr(uint16(25)),
})
```
### Buy with Flat Lamports Fee
```typescript TypeScript theme={null}
const request: BuildBuyTxRequest = {
mint: "TOKEN_MINT",
user_pubkey: "WALLET",
amount: 0.1, // 0.1 SOL
slippage_bps: 2_000,
partner_fee_recipient: "FEE_WALLET_PUBKEY",
partner_fee_lamports: 5_000_000, // 0.005 SOL
};
const response = await client.buildBuyTx(request);
```
```python Python theme={null}
request = BuildBuyTxRequest(
mint="TOKEN_MINT",
user_pubkey="WALLET",
amount=0.1, # 0.1 SOL
slippage_bps=2_000,
partner_fee_recipient="FEE_WALLET_PUBKEY",
partner_fee_lamports=5_000_000,
)
response = await client.build_buy_tx(request)
```
```rust Rust theme={null}
let request = BuildBuyTxRequest {
mint: "TOKEN_MINT".into(),
user_pubkey: "WALLET".into(),
amount: Some(0.1), // 0.1 SOL
slippage_bps: 2_000,
partner_fee_recipient: Some("FEE_WALLET_PUBKEY".into()),
partner_fee_lamports: Some(5_000_000),
..Default::default()
};
let response = client.build_buy_tx(&request).await?;
```
```go Go theme={null}
resp, err := client.BuildBuyTx(ctx, lasersell.BuildBuyTxRequest{
Mint: "TOKEN_MINT",
UserPubkey: "WALLET",
Amount: lasersell.Ptr(0.1), // 0.1 SOL
SlippageBps: 2_000,
PartnerFeeRecipient: lasersell.Ptr("FEE_WALLET_PUBKEY"),
PartnerFeeLamports: lasersell.Ptr(uint64(5_000_000)),
})
```
## Enterprise: Custom Monetization
For Enterprise partners processing significant volume, we offer custom monetization arrangements that go beyond the standard partner fee system. These are structured on a per-partner basis and can include:
* **Custom revenue sharing** routed on-chain as part of every transaction your users execute through LaserSell.
* **Custom split configurations** tailored to your business model and volume.
All enterprise monetization is enforced atomically on-chain, so payouts are transparent, verifiable, and require no manual reconciliation.
To discuss a custom arrangement, contact [sales@lasersell.io](mailto:sales@lasersell.io).
## Important Notes
* Partner fees are available on the **Advanced** and **Enterprise** tiers. See [Rate Limits and Tiers](/api/reference/rate-limits).
* The `partner_fee_recipient` wallet must be a valid base58 Solana public key.
* The recipient does not need to have an existing account; the fee instruction will create one if necessary.
* Setting both `partner_fee_bps` and `partner_fee_lamports` on the same request will result in a `400` error.
* Partner fees are deducted from the swap output and routed atomically within the same transaction.
# Rate Limits and Tiers
Source: https://docs.lasersell.io/api/reference/rate-limits
Understand tier capacity, the limits object from hello_ok, and how to handle 429 responses.
## Pricing Tiers
LaserSell offers four tiers. Each tier determines your rate limits for both the REST API and the Exit Intelligence Stream.
| Tier | Name | Price |
| ---- | -------------------- | -------- |
| 0 | Personal | Free |
| 1 | Professional | \$99/mo |
| 2 | Advanced Exit Engine | \$249/mo |
| 3 | Enterprise | Custom |
## Exit Intelligence Stream Limits by Tier
| Limit | Personal (Free) | Professional | Advanced | Enterprise |
| ------------------------------------------------------------------- | --------------- | ------------ | -------- | ---------- |
| Wallets per session | 1 | 5 | 200 | Custom |
| Positions per wallet | 2 | 100 | 100 | Custom |
| Positions per session | 2 | 500 | 20,000 | Custom |
| Sessions per API key | 1 | 1 | 1 | Custom |
| High priority buffer (`hi_capacity`) | 256 | 256 | 256 | Custom |
| PnL flush interval (`pnl_flush_ms`) | 100 ms | 100 ms | 100 ms | Custom |
| Inactivity timeout | 45 min | None | None | None |
| [Liquidity snapshots](/api/stream/server-events#liquidity_snapshot) | No | Yes | Yes | Yes |
| Partner fees | No | No | Yes | Yes |
## REST API Rate Limits
The REST API (`/v1/sell`, `/v1/buy`) enforces a per key request limit using a sliding window.
| Tier | Requests per minute |
| --------------- | ------------------- |
| Personal (Free) | 60 |
| Professional | 60 |
| Advanced | 60 |
| Enterprise | Custom |
## Reading Your Limits on Connect
When you connect to the Exit Intelligence Stream, the `hello_ok` message includes a `limits` object that reflects your API key's tier:
```json theme={null}
{
"type": "hello_ok",
"session_id": 42,
"server_time_ms": 1706000000000,
"limits": {
"hi_capacity": 256,
"pnl_flush_ms": 100,
"max_positions_per_session": 500,
"max_wallets_per_session": 5,
"max_positions_per_wallet": 100,
"max_sessions_per_api_key": 1
}
}
```
## `LimitsMsg` Schema
| Field | Type | Description |
| --------------------------- | -------- | ---------------------------------------------------------- |
| `hi_capacity` | `number` | Maximum high priority messages the server will buffer. |
| `pnl_flush_ms` | `number` | Interval in milliseconds between `pnl_update` flushes. |
| `max_positions_per_session` | `number` | Maximum positions tracked in a single session. |
| `max_wallets_per_session` | `number` | Maximum wallets that can be monitored in a single session. |
| `max_positions_per_wallet` | `number` | Maximum positions tracked per wallet. |
| `max_sessions_per_api_key` | `number` | Maximum concurrent stream sessions per API key. |
## Reading Limits in Code
`StreamSession.connect()` consumes the `hello_ok` message internally during the handshake. To read the limits, use the low-level `StreamClient` directly:
```typescript TypeScript theme={null}
const client = new StreamClient(apiKey);
const connection = await client.connect(configure);
const [sender, receiver] = connection.split();
const helloOk = await receiver.recv();
if (helloOk?.type === "hello_ok") {
const limits = helloOk.limits;
console.log("Max positions:", limits.max_positions_per_session);
console.log("Max wallets:", limits.max_wallets_per_session);
console.log("PnL interval:", limits.pnl_flush_ms, "ms");
}
```
```python Python theme={null}
client = StreamClient("YOUR_API_KEY")
connection = await client.connect(configure)
sender, receiver = connection.split()
msg = await receiver.recv()
if msg and msg.get("type") == "hello_ok":
limits = msg["limits"]
print("Max positions:", limits["max_positions_per_session"])
print("Max wallets:", limits["max_wallets_per_session"])
```
```rust Rust theme={null}
let client = StreamClient::new(SecretString::new(api_key));
let connection = client.connect(configure).await?;
let (sender, mut receiver) = connection.split();
if let Some(ServerMessage::HelloOk { limits, .. }) = receiver.recv().await {
println!("Max positions: {}", limits.max_positions_per_session);
println!("Max wallets: {}", limits.max_wallets_per_session);
println!("PnL interval: {} ms", limits.pnl_flush_ms);
}
```
```go Go theme={null}
client := stream.NewStreamClient(apiKey)
connection, err := client.Connect(ctx, configure)
sender, receiver := connection.Split()
msg, err := receiver.Recv(ctx)
if err == nil {
if helloOk, ok := msg.(stream.HelloOkServerMessage); ok {
fmt.Println("Max positions:", helloOk.Limits.MaxPositionsPerSession)
fmt.Println("Max wallets:", helloOk.Limits.MaxWalletsPerSession)
fmt.Println("PnL interval:", helloOk.Limits.PnlFlushMs, "ms")
}
}
```
## Handling 429 Responses
When you exceed the REST API rate limit, the server responds with HTTP `429 Too Many Requests`. The SDKs automatically retry `429` responses using the built in retry policy. If you see persistent 429 errors:
1. **Check your request frequency.** Reduce the rate of API calls.
2. **Batch requests.** If building multiple transactions, space them out.
3. **Increase retry backoff.** Configure a longer backoff to give the server time to recover.
See [Error Handling](/api/exit-api/error-handling) for details on customizing the retry policy.
## Exit Intelligence Stream Capacity
If you exceed the Exit Intelligence Stream's capacity limits (e.g., too many positions or wallets), the server sends an `error` message:
```json theme={null}
{
"type": "error",
"code": "capacity_exceeded",
"message": "max_positions_per_session limit reached"
}
```
The stream connection remains open. Remove positions with `close_position` or reduce wallets with `update_wallets` to free capacity.
## Best Practices
* **Read limits on connect.** Use the `limits` object to validate your configuration before sending positions.
* **Monitor for capacity errors.** Log and alert on `error` messages with capacity related codes.
* **Use lane splitting (TypeScript).** For high position counts, use `connectLanes()` to prevent `pnl_update` messages from blocking time sensitive events.
* **Respect 429 responses.** Do not retry immediately; use exponential backoff.
# Supported DEXs
Source: https://docs.lasersell.io/api/reference/supported-dexs
Protocols, market types, graduation support, and launchpad coverage for LaserSell swap routing.
## Supported Protocols
LaserSell routes swaps through multiple Solana DEX protocols. The `market_type` field in `MarketContextMsg` identifies which protocol is being used for a given token.
| `market_type` | Protocol | Type | Description |
| ------------------- | ----------------- | --------------- | ---------------------------------------------- |
| `pump_fun` | Pump.fun | Bonding curve | Original Pump.fun bonding curve. |
| `pump_swap` | PumpSwap | AMM | Graduated Pump.fun tokens on the PumpSwap AMM. |
| `meteora_dbc` | Meteora DBC | Dynamic bonding | Meteora dynamic bonding curve pools. |
| `meteora_damm_v2` | Meteora DAMM V2 | AMM | Meteora dynamic AMM V2 pools. |
| `raydium_launchpad` | Raydium Launchpad | Bonding curve | Raydium launchpad bonding curve tokens. |
| `raydium_cpmm` | Raydium CPMM | AMM | Raydium constant product market maker pools. |
## Bonding Curves vs AMM Pools
**Bonding curve** protocols (`pump_fun`, `meteora_dbc`, `raydium_launchpad`) use a mathematical curve to determine price. Tokens on bonding curves may later **graduate** to a full AMM pool.
**AMM** protocols (`pump_swap`, `meteora_damm_v2`, `raydium_cpmm`) use constant product or dynamic liquidity pools. These offer deeper liquidity and standard swap mechanics.
## Graduation
Some tokens launch on a bonding curve and later "graduate" to an AMM when certain conditions are met (usually a market cap threshold). LaserSell detects graduation events and can automatically sell on graduation if `sell_on_graduation: true` is set in your strategy.
| Source Curve | Graduates To |
| ------------------- | ----------------- |
| `pump_fun` | `pump_swap` |
| `meteora_dbc` | `meteora_damm_v2` |
| `raydium_launchpad` | `raydium_cpmm` |
When graduation occurs, the Exit Intelligence Stream updates the `market_context` and continues tracking seamlessly.
## Launchpad Coverage
LaserSell monitors all major Solana token launchpads:
* **Pump.fun**: The most popular memecoin launchpad. Tokens start on a bonding curve and graduate to PumpSwap.
* **Meteora**: Dynamic bonding curves that graduate to DAMM V2 pools.
* **Raydium Launchpad**: Raydium's native launchpad with graduation to CPMM pools.
New protocols are added as they gain adoption on Solana. The `market_type` enum will expand accordingly.
## Using Market Context
The `market_context` is included in `position_opened` and `exit_signal_with_tx` events. You can also pass it in a `BuildSellTxRequest` to skip server side resolution:
```json theme={null}
{
"market_type": "pump_swap",
"pumpswap": {
"pool": "PoolAddress..."
}
}
```
See [Market Context](/api/reference/market-context) for the full nested schema per protocol.
# Go SDK
Source: https://docs.lasersell.io/api/sdk/go
Install, configure, and use the LaserSell Go SDK with context.Context patterns for building transactions and streaming positions.
## Installation
```bash theme={null}
go get github.com/lasersell/lasersell-sdk/go
```
## Packages
| Package | Purpose |
| ---------------------------------------------- | ------------------------------------------------------------------ |
| `github.com/lasersell/lasersell-sdk/go` | `ExitAPIClient`, `SendTarget`, `SignUnsignedTx`, `SendTransaction` |
| `github.com/lasersell/lasersell-sdk/go/stream` | `StreamClient`, `StreamSession`, `StreamConfigure`, message types |
## API Client
All methods accept a `context.Context` for cancellation and timeouts.
```go theme={null}
package main
import (
"context"
"fmt"
"log"
lasersell "github.com/lasersell/lasersell-sdk/go"
)
func main() {
ctx := context.Background()
client := lasersell.NewExitAPIClientWithAPIKey("YOUR_API_KEY")
// Build sell
sellResp, err := client.BuildSellTx(ctx, lasersell.BuildSellTxRequest{
Mint: "TOKEN_MINT",
UserPubkey: "WALLET",
AmountTokens: 1_000_000,
Output: lasersell.SellOutputSOL,
SlippageBps: 2_000,
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Sell tx:", sellResp.Tx)
// Build buy
buyResp, err := client.BuildBuyTx(ctx, lasersell.BuildBuyTxRequest{
Mint: "TOKEN_MINT",
UserPubkey: "WALLET",
AmountInTotal: 100_000_000,
SlippageBps: 2_000,
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Buy tx:", buyResp.Tx)
}
// Helper functions for pointer fields (not part of the SDK)
func intPtr(v int) *int { return &v }
func strPtr(v string) *string { return &v }
```
Optional fields in the Go SDK use pointer types (`*int`, `*string`). The `intPtr` and `strPtr` helpers above are not part of the SDK. Define them in your own code or use inline expressions like `&[]int{2000}[0]`.
### Custom Options
```go theme={null}
opts := lasersell.ExitAPIClientOptions{
ConnectTimeout: 500 * time.Millisecond,
AttemptTimeout: 2 * time.Second,
RetryPolicy: lasersell.RetryPolicy{
MaxAttempts: 3,
InitialBackoff: 50 * time.Millisecond,
MaxBackoff: 200 * time.Millisecond,
Jitter: 50 * time.Millisecond,
},
}
client := lasersell.NewExitAPIClientWithOptions("YOUR_API_KEY", opts)
```
## Exit Intelligence Stream Session
```go theme={null}
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"github.com/gagliardetto/solana-go"
lasersell "github.com/lasersell/lasersell-sdk/go"
"github.com/lasersell/lasersell-sdk/go/stream"
)
func main() {
ctx := context.Background()
privateKey, err := solana.PrivateKeyFromSolanaKeygenFile("./keypair.json")
if err != nil {
log.Fatal(err)
}
client := stream.NewStreamClient("YOUR_API_KEY")
sendMode := "helius_sender"
tipLamports := uint64(1000)
session, err := stream.ConnectSession(ctx, client, stream.StreamConfigure{
WalletPubkeys: []string{"WALLET_PUBKEY"},
Strategy: stream.StrategyConfigMsg{
TargetProfitPct: 5.0,
StopLossPct: 1.5,
},
DeadlineTimeoutSec: 45,
SendMode: &sendMode,
TipLamports: &tipLamports,
})
if err != nil {
log.Fatal(err)
}
for {
event, err := session.Recv(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Fatal(err)
}
switch msg := event.Message.(type) {
case stream.PositionOpenedServerMessage:
fmt.Printf("New position: %s\n", msg.Mint)
case stream.ExitSignalWithTxServerMessage:
signedTx, err := lasersell.SignUnsignedTx(msg.UnsignedTxB64, privateKey)
if err != nil {
log.Printf("Sign error: %v", err)
continue
}
sig, err := lasersell.SendTransaction(
ctx, nil, lasersell.SendTargetHeliusSender(), signedTx,
)
if err != nil {
log.Printf("Send error: %v", err)
continue
}
fmt.Printf("Exit submitted: %s\n", sig)
case stream.ErrorServerMessage:
log.Printf("Stream error: code=%s message=%s", msg.Code, msg.Message)
}
}
}
```
## Liquidity Snapshots and Partial Sells
**Tier 1+ only.** Requires a Professional or Advanced subscription. See the [announcement](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for full details.
`StreamSession` caches the latest liquidity snapshot per position. Query slippage bands, maximum sellable amounts, and liquidity trends:
```go theme={null}
bands := session.GetSlippageBands(positionID)
maxTokens := session.GetMaxSellAtSlippage(positionID, 500) // 5% slippage
trend := session.GetLiquidityTrend(positionID) // "growing" | "stable" | "draining"
```
Use `BuildPartialSellTx()` to sell a portion of a position based on slippage data:
```go theme={null}
maxTokens := session.GetMaxSellAtSlippage(positionID, 500)
if maxTokens != nil {
response, err := client.BuildPartialSellTx(ctx, handle, *maxTokens, 500, lasersell.SellOutputSOL)
}
```
## 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 `MirrorBuySignalServerMessage` with an unsigned transaction for your wallet to execute.
```go theme={null}
package main
import (
"context"
"errors"
"fmt"
"io"
"log"
"github.com/gagliardetto/solana-go"
lasersell "github.com/lasersell/lasersell-sdk/go"
"github.com/lasersell/lasersell-sdk/go/stream"
)
func main() {
ctx := context.Background()
privateKey, err := solana.PrivateKeyFromSolanaKeygenFile("./keypair.json")
if err != nil {
log.Fatal(err)
}
walletPubkey := "YOUR_WALLET_PUBKEY"
sendMode := "helius_sender"
tipLamports := uint64(1000)
session, err := stream.ConnectSession(ctx, stream.NewStreamClient("YOUR_API_KEY"), stream.StreamConfigure{
WalletPubkeys: []string{walletPubkey},
Strategy: stream.StrategyConfigMsg{
TargetProfitPct: 50.0,
StopLossPct: 25.0,
},
DeadlineTimeoutSec: 45,
SendMode: &sendMode,
TipLamports: &tipLamports,
WatchWallets: []stream.WatchWalletConfig{
{
Pubkey: "WatchedWalletPubkey...",
AutoBuy: &stream.AutoBuyConfig{
WalletPubkey: walletPubkey,
AmountQuoteUnits: intPtr(100_000_000),
},
MirrorSell: false,
},
},
MirrorConfig: &stream.MirrorConfig{
MaxPositionsPerWallet: 1,
CooldownSec: 30,
MaxActiveSOL: 5.0,
BuySlippageBps: 2500,
},
})
if err != nil {
log.Fatal(err)
}
for {
event, err := session.Recv(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
log.Fatal(err)
}
switch msg := event.Message.(type) {
case stream.MirrorBuySignalServerMessage:
signedTx, err := lasersell.SignUnsignedTx(msg.UnsignedTxB64, privateKey)
if err != nil {
log.Printf("Sign error: %v", err)
continue
}
sig, err := lasersell.SendTransaction(
ctx, nil, lasersell.SendTargetHeliusSender(), signedTx,
)
if err != nil {
log.Printf("Send error: %v", err)
continue
}
session.Sender().MirrorBuyResult(msg.Mint, true)
fmt.Printf("Mirror buy submitted: %s\n", sig)
case stream.MirrorBuyFailedServerMessage:
log.Printf("Mirror buy failed: %s", msg.Reason)
case stream.ExitSignalWithTxServerMessage:
signedTx, err := lasersell.SignUnsignedTx(msg.UnsignedTxB64, privateKey)
if err != nil {
log.Printf("Sign error: %v", err)
continue
}
sig, err := lasersell.SendTransaction(
ctx, nil, lasersell.SendTargetHeliusSender(), signedTx,
)
if err != nil {
log.Printf("Send error: %v", err)
continue
}
fmt.Printf("Exit submitted: %s\n", sig)
}
}
}
func intPtr(v int) *int { return &v }
```
The `WatchWallets` slice specifies which wallets to mirror. Each `WatchWalletConfig` includes an `AutoBuy` configuration that controls how much to spend when mirroring a trade, and a `MirrorSell` flag that determines whether to also mirror sell transactions.
The `MirrorConfig` struct sets global limits: `MaxPositionsPerWallet` caps concurrent mirrored positions, `CooldownSec` enforces a delay between mirror buys, `MaxActiveSOL` limits total SOL exposure, and `BuySlippageBps` sets the slippage tolerance for mirror buy transactions.
## Transaction Helpers
The second parameter to `SendTransaction` is an `*http.Client`. Pass `nil` to use the SDK's default HTTP client.
```go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
// Sign
signedTx, err := lasersell.SignUnsignedTx(unsignedTxB64, privateKey)
// Send via Helius Sender (nil = use default HTTP client)
sig, err := lasersell.SendTransaction(ctx, nil, lasersell.SendTargetHeliusSender(), signedTx)
// Send via custom RPC
sig, err = lasersell.SendTransaction(ctx, nil,
lasersell.SendTargetRpc("https://rpc.example.com"), signedTx)
// Send via Astralane
sig, err = lasersell.SendTransaction(ctx, nil,
lasersell.SendTargetAstralane("KEY", "fr"), signedTx)
// With a custom HTTP client
customClient := &http.Client{Timeout: 5 * time.Second}
sig, err = lasersell.SendTransaction(ctx, customClient,
lasersell.SendTargetHeliusSender(), signedTx)
```
## Error Handling
```go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
resp, err := client.BuildSellTx(ctx, request)
if err != nil {
var apiErr *lasersell.ExitAPIError
if errors.As(err, &apiErr) {
fmt.Println("Kind:", apiErr.Kind)
fmt.Println("Retryable:", apiErr.IsRetryable())
fmt.Println("Status:", apiErr.Status)
}
}
```
## `context.Context` Patterns
All Go SDK methods accept `context.Context` as their first argument. Use this to control:
* **Timeouts**: `context.WithTimeout(ctx, 5*time.Second)`
* **Cancellation**: `context.WithCancel(ctx)` for graceful shutdown
* **Deadlines**: `context.WithDeadline(ctx, time.Now().Add(30*time.Second))`
```go theme={null}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := client.BuildSellTx(ctx, request)
```
## Complete Example
See the [Quickstart](/api/quickstart) for a full working example.
# Python SDK
Source: https://docs.lasersell.io/api/sdk/python
Install, configure, and use the LaserSell Python SDK with async patterns for building transactions and streaming positions.
## Installation
```bash theme={null}
pip install lasersell-sdk
```
Optional extras for additional features:
```bash theme={null}
# Transaction signing (requires solders)
pip install lasersell-sdk[tx]
# WebSocket stream (requires websockets)
pip install lasersell-sdk[stream]
# Both
pip install lasersell-sdk[tx,stream]
```
## Modules
| Import Path | Purpose |
| ------------------------------ | --------------------------------------------------------------------- |
| `lasersell_sdk.exit_api` | `ExitApiClient`, request/response types, `ExitApiError` |
| `lasersell_sdk.tx` | `sign_unsigned_tx`, `send_transaction`, `SendTarget*` classes |
| `lasersell_sdk.stream.client` | `StreamClient`, `StreamConfigure`, `StreamSender` |
| `lasersell_sdk.stream.session` | `StreamSession`, `StreamEvent`, `PositionHandle` |
| `lasersell_sdk.stream.proto` | Message types (`ServerMessage`, `ClientMessage`, `StrategyConfigMsg`) |
## API Client
All API client methods are `async` and must be awaited.
```python theme={null}
import asyncio
from lasersell_sdk.exit_api import ExitApiClient, BuildSellTxRequest, SellOutput
async def main():
client = ExitApiClient.with_api_key("YOUR_API_KEY")
request = BuildSellTxRequest(
mint="TOKEN_MINT",
user_pubkey="WALLET",
amount_tokens=1_000_000,
slippage_bps=2_000,
output=SellOutput.SOL,
)
response = await client.build_sell_tx(request)
print("Unsigned tx:", response.tx)
asyncio.run(main())
```
### Buy Transactions
```python theme={null}
from lasersell_sdk.exit_api import BuildBuyTxRequest
request = BuildBuyTxRequest(
mint="TOKEN_MINT",
user_pubkey="WALLET",
amount=0.1, # 0.1 SOL
slippage_bps=2_000,
)
response = await client.build_buy_tx(request)
```
### Custom Options
```python theme={null}
from lasersell_sdk.exit_api import ExitApiClient, ExitApiClientOptions
from lasersell_sdk.retry import RetryPolicy
client = ExitApiClient.with_options(
"YOUR_API_KEY",
ExitApiClientOptions(
attempt_timeout_s=2.0,
retry_policy=RetryPolicy(
max_attempts=3,
initial_backoff_ms=50,
max_backoff_ms=200,
jitter_ms=50,
),
),
)
```
## Exit Intelligence Stream Session
`event.message` is a `TypedDict` union. At runtime, access fields using dict syntax (e.g. `msg["unsigned_tx_b64"]`).
```python theme={null}
import asyncio
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
async def main():
client = StreamClient("YOUR_API_KEY")
session = await StreamSession.connect(
client,
StreamConfigure(
wallet_pubkeys=["WALLET_PUBKEY"],
strategy={
"target_profit_pct": 5.0,
"stop_loss_pct": 1.5,
},
deadline_timeout_sec=45,
send_mode="helius_sender",
tip_lamports=1000,
),
)
while True:
event = await session.recv()
if event is None:
break
if event.type == "position_opened" and event.handle is not None:
print(f"New position: {event.handle.mint}")
if event.type == "exit_signal_with_tx":
msg = event.message # TypedDict, use dict access
signed = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed)
print(f"Exit submitted: {sig}")
asyncio.run(main())
```
## Liquidity Snapshots and Partial Sells
**Tier 1+ only.** Requires a Professional or Advanced subscription. See the [announcement](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for full details.
`StreamSession` caches the latest liquidity snapshot per position. Query slippage bands, maximum sellable amounts, and liquidity trends:
```python theme={null}
bands = session.get_slippage_bands(position_id)
max_tokens = session.get_max_sell_at_slippage(position_id, 500) # 5% slippage
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:
```python theme={null}
max_tokens = session.get_max_sell_at_slippage(position_id, 500)
if max_tokens is not None:
response = await client.build_partial_sell_tx(handle, max_tokens, 500, "SOL")
```
## 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 `mirror_buy_signal` with an unsigned transaction for your wallet to execute.
```python theme={null}
import asyncio
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
from lasersell_sdk.tx import SendTargetHeliusSender, send_transaction, sign_unsigned_tx
async def main():
wallet_pubkey = "YOUR_WALLET_PUBKEY"
client = StreamClient("YOUR_API_KEY")
session = await StreamSession.connect(
client,
StreamConfigure(
wallet_pubkeys=[wallet_pubkey],
strategy={
"target_profit_pct": 50.0,
"stop_loss_pct": 25.0,
},
watch_wallets=[
{
"pubkey": "WatchedWalletPubkey...",
"auto_buy": {
"wallet_pubkey": wallet_pubkey,
"amount_quote_units": 100_000_000,
"amount_usd1_units": None,
},
"mirror_sell": False,
},
],
mirror_config={
"max_positions_per_wallet": 1,
"cooldown_sec": 30,
"max_active_sol": 5.0,
"buy_slippage_bps": 2500,
},
),
)
while True:
event = await session.recv()
if event is None:
break
if event.type == "mirror_buy_signal":
msg = event.message
signed = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed)
session.sender().mirror_buy_result(str(msg["mint"]), True)
print(f"Mirror buy submitted: {sig}")
if event.type == "mirror_buy_failed":
msg = event.message
print(f"Mirror buy failed: {msg['reason']}")
if event.type == "exit_signal_with_tx":
msg = event.message
signed = sign_unsigned_tx(str(msg["unsigned_tx_b64"]), signer)
sig = await send_transaction(SendTargetHeliusSender(), signed)
print(f"Exit submitted: {sig}")
asyncio.run(main())
```
The `watch_wallets` list specifies which wallets to mirror. Each entry 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 `mirror_config` dict 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
```python theme={null}
from lasersell_sdk.tx import (
sign_unsigned_tx,
send_transaction,
send_transaction_b64_to,
encode_signed_tx,
SendTargetRpc,
SendTargetHeliusSender,
SendTargetAstralane,
)
# Sign
signed_tx = sign_unsigned_tx(unsigned_tx_b64, keypair)
# Send
signature = await send_transaction(SendTargetHeliusSender(), signed_tx)
# Send with custom RPC
signature = await send_transaction(
SendTargetRpc(url="https://rpc.example.com"),
signed_tx,
)
# Astralane
signature = await send_transaction(
SendTargetAstralane(api_key="KEY", region="fr"),
signed_tx,
)
```
## Error Handling
```python theme={null}
from lasersell_sdk.exit_api import ExitApiError
from lasersell_sdk.tx import TxSubmitError
from lasersell_sdk.stream.client import StreamClientError
try:
response = await client.build_sell_tx(request)
except ExitApiError as e:
print(f"Kind: {e.kind}, Retryable: {e.is_retryable()}")
print(f"Status: {e.status}, Body: {e.body}")
except TxSubmitError as e:
print(f"Kind: {e.kind}, Target: {e.target}")
except StreamClientError as e:
print(f"Kind: {e.kind}")
```
## Loading a Keypair
```python theme={null}
import json
from pathlib import Path
from solders.keypair import Keypair
raw = json.loads(Path("./keypair.json").read_text())
keypair = Keypair.from_bytes(bytes(raw))
```
Requires the `solders` package, which is included in the `[tx]` extra.
## Complete Example
See the [Quickstart](/api/quickstart) for a complete build, sign, and submit workflow.
# Rust SDK
Source: https://docs.lasersell.io/api/sdk/rust
Install, configure, and use the LaserSell Rust SDK with async/.await for building transactions and streaming positions.
## Installation
```bash theme={null}
cargo add lasersell-sdk
cargo add tokio --features full
```
## Modules
| Module | Purpose |
| -------------------------------- | --------------------------------------------------------------------- |
| `lasersell_sdk::exit_api` | `ExitApiClient`, request/response types, `ExitApiError` |
| `lasersell_sdk::tx` | `SendTarget`, `sign_unsigned_tx`, `send_transaction` |
| `lasersell_sdk::stream::client` | `StreamClient`, `StreamConfigure`, `StreamConnection`, `StreamSender` |
| `lasersell_sdk::stream::session` | `StreamSession`, position tracking |
| `lasersell_sdk::stream::proto` | Message types (`ServerMessage`, `ClientMessage`, `StrategyConfigMsg`) |
| `lasersell_sdk::retry` | `RetryPolicy` configuration |
## API Client
All methods are `async` and return `Result`.
```rust theme={null}
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
```rust theme={null}
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
```rust theme={null}
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](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for full details.
`StreamSession` caches the latest liquidity snapshot per position. Query slippage bands, maximum sellable amounts, and liquidity trends:
```rust theme={null}
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:
```rust theme={null}
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.
```rust theme={null}
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
```rust theme={null}
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
```rust theme={null}
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](/api/quickstart) for a full working example.
# TypeScript SDK
Source: https://docs.lasersell.io/api/sdk/typescript
Install, configure, and use the LaserSell TypeScript SDK for building transactions, streaming positions, and submitting trades.
## Installation
```bash theme={null}
npm install @lasersell/lasersell-sdk
```
The package ships with TypeScript declarations and supports both ESM and CommonJS.
## Modules
| Import | Purpose |
| ---------------------------------------------------------------- | -------------------------------------------- |
| `ExitApiClient` | Build unsigned buy/sell transactions. |
| `StreamClient` | Low level WebSocket stream client. |
| `StreamSession` | High level stream with position tracking. |
| `signUnsignedTx` | Sign an unsigned base64 transaction. |
| `signUnsignedTxB64Fast` | Fast path in place signing (returns base64). |
| `sendTransaction` | Submit a signed transaction. |
| `sendTargetRpc`, `sendTargetHeliusSender`, `sendTargetAstralane` | Send target constructors. |
## API Client
```typescript theme={null}
import {
ExitApiClient,
type BuildSellTxRequest,
type BuildBuyTxRequest,
} from "@lasersell/lasersell-sdk";
const client = ExitApiClient.withApiKey("YOUR_API_KEY");
// Build sell
const sellRequest: BuildSellTxRequest = {
mint: "TOKEN_MINT",
user_pubkey: "WALLET",
amount_tokens: 1_000_000,
slippage_bps: 2_000,
output: "SOL",
};
const sellResponse = await client.buildSellTx(sellRequest);
const txB64 = sellResponse.tx;
// Build buy
const buyRequest: BuildBuyTxRequest = {
mint: "TOKEN_MINT",
user_pubkey: "WALLET",
amount: 0.1, // 0.1 SOL
slippage_bps: 2_000,
};
const buyResponse = await client.buildBuyTx(buyRequest);
```
### Client Options
```typescript theme={null}
const client = ExitApiClient.withOptions("YOUR_API_KEY", {
connect_timeout_ms: 500,
attempt_timeout_ms: 2000,
retry_policy: {
max_attempts: 3,
initial_backoff_ms: 50,
max_backoff_ms: 200,
jitter_ms: 50,
},
});
```
## Exit Intelligence Stream Session
```typescript theme={null}
import {
StreamClient,
StreamSession,
sendTransaction,
sendTargetHeliusSender,
signUnsignedTx,
} from "@lasersell/lasersell-sdk";
const client = new StreamClient("YOUR_API_KEY");
const session = await StreamSession.connect(client, {
wallet_pubkeys: ["WALLET_PUBKEY"],
strategy: {
target_profit_pct: 5,
stop_loss_pct: 1.5,
trailing_stop_pct: 3,
},
deadline_timeout_sec: 45,
send_mode: "helius_sender",
tip_lamports: 1000,
});
while (true) {
const event = await session.recv();
if (event === null) break;
switch (event.type) {
case "position_opened":
console.log("New position:", event.handle.mint);
break;
case "exit_signal_with_tx":
if (event.message.type === "exit_signal_with_tx") {
const signed = signUnsignedTx(event.message.unsigned_tx_b64, keypair);
const sig = await sendTransaction(sendTargetHeliusSender(), signed);
console.log("Exit submitted:", sig);
}
break;
case "pnl_update":
if (event.message.type === "pnl_update") {
console.log(
`PnL position=${event.message.position_id} profit=${event.message.profit_units}`
);
}
break;
}
}
```
### Lane Splitting
For high throughput scenarios, split the Exit Intelligence Stream into priority lanes:
```typescript theme={null}
const connection = await client.connectLanes(configure, {
lowPriorityCapacity: 512,
});
const [sender, highRx, lowRx] = connection.split();
// High priority: exit signals, position events
for await (const msg of highRx) {
// Handle critical messages
}
```
## Liquidity Snapshots and Partial Sells
**Tier 1+ only.** Requires a Professional or Advanced subscription. See the [announcement](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for full details.
`StreamSession` caches the latest liquidity snapshot per position. Query slippage bands, maximum sellable amounts, and liquidity trends:
```typescript theme={null}
const bands = session.getSlippageBands(positionId);
const maxTokens = session.getMaxSellAtSlippage(positionId, 500); // 5% slippage
const trend = session.getLiquidityTrend(positionId); // "growing" | "stable" | "draining"
```
Use `buildPartialSellTx()` to sell a portion of a position based on slippage data:
```typescript theme={null}
const maxTokens = session.getMaxSellAtSlippage(positionId, 500);
if (maxTokens !== undefined) {
const response = await client.buildPartialSellTx(handle, maxTokens, 500, "SOL");
}
```
## 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 `mirror_buy_signal` with an unsigned transaction for your wallet to execute.
```typescript theme={null}
import {
StreamClient,
StreamSession,
sendTransaction,
sendTargetHeliusSender,
signUnsignedTx,
} from "@lasersell/lasersell-sdk";
const walletPubkey = "YOUR_WALLET_PUBKEY";
const client = new StreamClient("YOUR_API_KEY");
const session = await StreamSession.connect(client, {
wallet_pubkeys: [walletPubkey],
strategy: {
target_profit_pct: 50,
stop_loss_pct: 25,
},
watch_wallets: [
{
pubkey: "WatchedWalletPubkey...",
auto_buy: {
wallet_pubkey: walletPubkey,
amount_quote_units: 100_000_000,
amount_usd1_units: null,
},
mirror_sell: false,
},
],
mirror_config: {
max_positions_per_wallet: 1,
cooldown_sec: 30,
max_active_sol: 5.0,
buy_slippage_bps: 2500,
},
});
while (true) {
const event = await session.recv();
if (event === null) break;
switch (event.type) {
case "mirror_buy_signal": {
const { mint, unsigned_tx_b64 } = event.message;
const signed = signUnsignedTx(unsigned_tx_b64, keypair);
const sig = await sendTransaction(sendTargetHeliusSender(), signed);
session.sender().mirrorBuyResult(mint, true);
console.log("Mirror buy submitted:", sig);
break;
}
case "mirror_buy_failed":
console.log(`Mirror buy failed: ${event.message.reason}`);
break;
case "exit_signal_with_tx": {
if (event.message.type === "exit_signal_with_tx") {
const signed = signUnsignedTx(event.message.unsigned_tx_b64, keypair);
const sig = await sendTransaction(sendTargetHeliusSender(), signed);
console.log("Exit submitted:", sig);
}
break;
}
}
}
```
The `watch_wallets` array specifies which wallets to mirror. Each entry 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 `mirror_config` object 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
```typescript theme={null}
import {
signUnsignedTx,
signUnsignedTxB64Fast,
signAndSendUnsignedTxB64,
sendTransaction,
sendTransactionB64To,
encodeSignedTx,
sendTargetRpc,
sendTargetHeliusSender,
sendTargetAstralane,
sendTargetDefaultRpc,
} from "@lasersell/lasersell-sdk";
// Standard sign
const signedTx = signUnsignedTx(unsignedTxB64, keypair);
// Fast sign (returns base64 string)
const signedB64 = signUnsignedTxB64Fast(unsignedTxB64, keypair);
// Sign + send in one step
const sig = await signAndSendUnsignedTxB64(
sendTargetHeliusSender(),
unsignedTxB64,
keypair,
);
// Send a pre-signed transaction
const sig2 = await sendTransaction(sendTargetRpc("https://rpc.example.com"), signedTx);
// Send a pre-signed base64 string
const sig3 = await sendTransactionB64To(sendTargetHeliusSender(), signedB64);
```
## Error Handling
```typescript theme={null}
import { ExitApiError, TxSubmitError, StreamClientError } from "@lasersell/lasersell-sdk";
try {
await client.buildSellTx(request);
} catch (error) {
if (error instanceof ExitApiError) {
// error.kind, error.status, error.body, error.isRetryable()
}
if (error instanceof TxSubmitError) {
// error.kind, error.target, error.status
}
if (error instanceof StreamClientError) {
// error.kind
}
}
```
## Complete Example
See the [Quickstart](/api/quickstart) for a full working example that builds, signs, and submits a sell transaction.
# Client Messages
Source: https://docs.lasersell.io/api/stream/client-messages
Complete schema and JSON examples for all 9 client message types sent to the Exit Intelligence Stream.
## Message Types
| Type | Description |
| -------------------------- | ------------------------------------------------ |
| `ping` | Latency probe; server responds with `pong`. |
| `configure` | Initial session setup with wallets and strategy. |
| `update_strategy` | Change strategy parameters mid session. |
| `close_position` | Remove a position from tracking. |
| `request_exit_signal` | Manually request an exit transaction. |
| `update_wallets` | Add or remove wallets mid session. |
| `update_position_strategy` | Override strategy for a single position. |
| `update_watch_wallets` | Add or update watch wallets for copy trading. |
| `mirror_buy_result` | Report outcome of a mirror buy transaction. |
## `ping`
```json theme={null}
{
"type": "ping",
"client_time_ms": 1706000000000
}
```
| Field | Type | Required | Description |
| ---------------- | -------- | -------- | ---------------------------------------------------------------------------------------- |
| `client_time_ms` | `number` | Yes | Client timestamp in milliseconds. Compare with `pong.server_time_ms` to measure latency. |
## `configure`
Sent once after receiving `hello_ok`. Sets the wallets to watch and the strategy to evaluate.
```json theme={null}
{
"type": "configure",
"wallet_pubkeys": [
"WalletPubkey1...",
"WalletPubkey2..."
],
"strategy": {
"target_profit_pct": 5.0,
"stop_loss_pct": 1.5,
"trailing_stop_pct": 3.0,
"sell_on_graduation": false
},
"deadline_timeout_sec": 45,
"send_mode": "helius_sender",
"tip_lamports": 1000,
"watch_wallets": [
{ "pubkey": "WatchWalletPubkey1..." },
{ "pubkey": "WatchWalletPubkey2...", "auto_buy": { "wallet_pubkey": "YourBuyWallet...", "amount_quote_units": 100000000 } }
],
"mirror_config": {
"max_positions_per_wallet": 1,
"cooldown_sec": 30,
"skip_creator_tokens": false,
"max_active_sol": 5.0,
"buy_slippage_bps": 2500,
"min_liquidity_sol": null,
"max_entry_drift_pct": null,
"max_consecutive_losses": null
}
}
```
| Field | Type | Required | Description |
| ---------------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `wallet_pubkeys` | `string[]` | Yes | One or more wallet public keys to monitor. |
| `strategy` | `object` | Yes | Strategy configuration. See [Strategy Configuration](/api/stream/strategy-configuration). |
| `deadline_timeout_sec` | `number` | No | Seconds of inactivity before the server auto-generates an exit signal. Omit or set to `0` to disable. |
| `send_mode` | `string` | No | Transaction submission mode: `"rpc"`, `"helius_sender"`, or `"astralane"`. Default: `"rpc"`. |
| `tip_lamports` | `number` | No | Priority fee tip in lamports. Default: `1000` (0.001 SOL). |
| `watch_wallets` | `WatchWalletEntryMsg[]` | No | Optional list of external wallets to mirror for copy trading. Each entry has `pubkey` (string) and optional `auto_buy` (AutoBuyConfigMsg). |
| `mirror_config` | `MirrorConfigMsg` | No | Optional hardening settings for mirror trading. See [Mirror Config](#mirror-config) below. |
### Mirror Config
The `mirror_config` object controls safety limits and behavior for mirror (copy) trading. All fields are optional; the server uses sensible defaults when omitted.
| Field | Type | Default | Description |
| -------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `max_positions_per_wallet` | `number` | `1` | Maximum concurrent mirrored positions per watched wallet. Additional signals are ignored until an existing position closes. |
| `cooldown_sec` | `number` | `30` | Minimum seconds between mirror buys from the same watched wallet. |
| `skip_creator_tokens` | `boolean` | `false` | When `true`, ignore buy signals where the watched wallet is the token creator. |
| `max_active_sol` | `number` | `5.0` | Maximum total SOL exposure across all active mirrored positions. New signals are skipped when this cap would be exceeded. |
| `buy_slippage_bps` | `number` | `2500` | Slippage tolerance in basis points for mirror buy transactions (2500 = 25%). |
| `min_liquidity_sol` | `number` | `null` | Minimum pool liquidity in SOL required to execute a mirror buy. `null` to disable. |
| `max_entry_drift_pct` | `number` | `null` | Maximum allowed price drift (%) from the watched wallet's entry price. `null` to disable. |
| `max_consecutive_losses` | `number` | `null` | After this many consecutive losing trades from a watched wallet, the wallet is auto-disabled and a `mirror_wallet_auto_disabled` event is sent. `null` to disable. |
The SDKs send `configure` automatically when you call `StreamClient.connect()` or `StreamSession.connect()`.
All wallets in `wallet_pubkeys` must be [registered](/api/exit-api/wallets) to your account before connecting. The stream will reject unregistered wallets with error code `wallet_not_registered`.
## `update_strategy`
Update the strategy without reconnecting. Takes effect immediately for all tracked positions.
```json theme={null}
{
"type": "update_strategy",
"strategy": {
"target_profit_pct": 15.0,
"stop_loss_pct": 3.0,
"trailing_stop_pct": 7.0,
"sell_on_graduation": true
}
}
```
| Field | Type | Required | Description |
| ---------- | -------- | -------- | ---------------------- |
| `strategy` | `object` | Yes | New strategy to apply. |
```typescript TypeScript theme={null}
session.updateStrategy({
target_profit_pct: 15,
stop_loss_pct: 3,
trailing_stop_pct: 7,
});
```
```python Python theme={null}
session.sender().update_strategy({
"target_profit_pct": 15.0,
"stop_loss_pct": 3.0,
"trailing_stop_pct": 7.0,
})
```
```rust Rust theme={null}
session.update_strategy(StrategyConfigMsg {
target_profit_pct: 15.0,
stop_loss_pct: 3.0,
trailing_stop_pct: Some(7.0),
sell_on_graduation: None,
}, None);
```
```go Go theme={null}
session.Sender().UpdateStrategy(stream.StrategyConfigMsg{
TargetProfitPct: 15.0,
StopLossPct: 3.0,
TrailingStopPct: 7.0,
})
```
## `close_position`
Remove a position from tracking. The server will stop monitoring PnL and will not generate exit signals for it. Identify the position by either `position_id` or `token_account`.
```json theme={null}
{
"type": "close_position",
"position_id": 1
}
```
Or by token account:
```json theme={null}
{
"type": "close_position",
"token_account": "ATA..."
}
```
| Field | Type | Required | Description |
| --------------- | -------- | -------- | -------------------------------------------------------------- |
| `position_id` | `number` | No\* | Position ID to close. Mutually exclusive with `token_account`. |
| `token_account` | `string` | No\* | Token account address. Mutually exclusive with `position_id`. |
\*One of `position_id` or `token_account` is required.
```typescript TypeScript theme={null}
// By handle (from StreamSession)
session.close(handle);
// By position ID (via sender)
session.sender().closeById(1);
// By token account
session.sender().closePosition("ATA...");
```
```python Python theme={null}
# By position ID
session.sender().close_by_id(1)
# By token account
session.sender().close_position("ATA...")
```
```rust Rust theme={null}
// By position handle
session.close(handle);
// By position ID
session.sender().close_by_id(1);
// By token account
session.sender().close_position("ATA...");
```
```go Go theme={null}
// By position ID
sender.CloseByID(1)
// By token account
sender.ClosePosition("ATA...")
```
## `request_exit_signal`
Manually request the server to generate an exit transaction for a position, regardless of whether the strategy thresholds have been met.
```json theme={null}
{
"type": "request_exit_signal",
"position_id": 1,
"slippage_bps": 3000
}
```
Or by token account:
```json theme={null}
{
"type": "request_exit_signal",
"token_account": "ATA...",
"slippage_bps": 3000
}
```
| Field | Type | Required | Description |
| --------------- | -------- | -------- | ----------------------------------------------------- |
| `position_id` | `number` | No\* | Position ID. Mutually exclusive with `token_account`. |
| `token_account` | `string` | No\* | Token account. Mutually exclusive with `position_id`. |
| `slippage_bps` | `number` | No | Override slippage for this exit transaction. |
\*One of `position_id` or `token_account` is required.
The server responds with an `exit_signal_with_tx` containing the unsigned transaction.
```typescript TypeScript theme={null}
// Using StreamSession
session.requestExitSignal(handle, 3000);
// Using sender directly
session.sender().requestExitSignal("ATA...", 3000);
session.sender().requestExitSignalById(1, 3000);
```
```python Python theme={null}
session.sender().request_exit_signal("ATA...", slippage_bps=3000)
session.sender().request_exit_signal_by_id(1, slippage_bps=3000)
```
```rust Rust theme={null}
session.request_exit_signal(handle, Some(3000));
// Using sender directly
session.sender().request_exit_signal("ATA...", Some(3000));
session.sender().request_exit_signal_by_id(1, Some(3000));
```
```go Go theme={null}
sender.RequestExitSignal("ATA...", intPtr(3000))
sender.RequestExitSignalByID(1, intPtr(3000))
```
## `update_wallets`
Add or remove wallets from the session without reconnecting. The server will start monitoring the new set and stop tracking wallets that are no longer listed.
```json theme={null}
{
"type": "update_wallets",
"wallet_pubkeys": [
"WalletPubkey1...",
"WalletPubkey3..."
]
}
```
| Field | Type | Required | Description |
| ---------------- | ---------- | -------- | ----------------------------------- |
| `wallet_pubkeys` | `string[]` | Yes | Updated list of wallets to monitor. |
```typescript TypeScript theme={null}
session.sender().updateWallets([
"WalletPubkey1...",
"WalletPubkey3...",
]);
```
```python Python theme={null}
session.sender().update_wallets([
"WalletPubkey1...",
"WalletPubkey3...",
])
```
```rust Rust theme={null}
session.sender().update_wallets(vec![
"WalletPubkey1...".into(),
"WalletPubkey3...".into(),
]);
```
```go Go theme={null}
sender.UpdateWallets([]string{
"WalletPubkey1...",
"WalletPubkey3...",
})
```
## `update_position_strategy`
Override the strategy for a single position without affecting other tracked positions.
```json theme={null}
{
"type": "update_position_strategy",
"position_id": 1,
"strategy": {
"target_profit_pct": 200.0,
"stop_loss_pct": 5.0,
"trailing_stop_pct": 10.0
}
}
```
| Field | Type | Required | Description |
| ------------- | -------- | -------- | ----------------------------------------- |
| `position_id` | `number` | Yes | Position to override. |
| `strategy` | `object` | Yes | Strategy override for this position only. |
```typescript TypeScript theme={null}
session.sender().updatePositionStrategy(positionId, {
target_profit_pct: 200,
stop_loss_pct: 5,
trailing_stop_pct: 10,
});
```
```python Python theme={null}
session.sender().update_position_strategy(position_id, {
"target_profit_pct": 200.0,
"stop_loss_pct": 5.0,
"trailing_stop_pct": 10.0,
})
```
```rust Rust theme={null}
session.sender().update_position_strategy(position_id, StrategyConfigMsg {
target_profit_pct: 200.0,
stop_loss_pct: 5.0,
trailing_stop_pct: 10.0,
..Default::default()
})?;
```
```go Go theme={null}
sender.UpdatePositionStrategy(positionID, stream.StrategyConfigMsg{
TargetProfitPct: 200.0,
StopLossPct: 5.0,
TrailingStopPct: 10.0,
})
```
## `update_watch_wallets`
Add or update watch wallets mid-session for copy trading. Replaces the current watch wallet list.
```json theme={null}
{
"type": "update_watch_wallets",
"watch_wallets": [
{ "pubkey": "WatchWalletPubkey1..." },
{ "pubkey": "WatchWalletPubkey2...", "auto_buy": { "wallet_pubkey": "YourBuyWallet...", "amount_quote_units": 100000000 } }
]
}
```
| Field | Type | Required | Description |
| --------------- | ----------------------- | -------- | ---------------------------------- |
| `watch_wallets` | `WatchWalletEntryMsg[]` | Yes | Updated list of wallets to mirror. |
Each `WatchWalletEntryMsg` has:
| Field | Type | Required | Description |
| ------------- | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `pubkey` | `string` | Yes | Public key of the wallet to watch. |
| `auto_buy` | `AutoBuyConfigMsg` | No | If set, automatically mirror buys from this wallet. |
| `mirror_sell` | `boolean` | No | Reserved for future use. When `true`, mirror sells from this wallet in addition to buys. Default: `false`. Currently unused. |
`AutoBuyConfigMsg`:
| Field | Type | Required | Description |
| -------------------- | -------- | -------- | ------------------------------------------- |
| `wallet_pubkey` | `string` | Yes | Your wallet that will execute the buy. |
| `amount_quote_units` | `number` | Yes | Amount in quote asset atomic units per buy. |
| `amount_usd1_units` | `number` | No | Amount in USD1 atomic units (alternative). |
```typescript TypeScript theme={null}
session.sender().updateWatchWallets([
{ pubkey: "WatchWalletPubkey1..." },
{ pubkey: "WatchWalletPubkey2...", auto_buy: { wallet_pubkey: "YourBuyWallet...", amount_quote_units: 100000000 } },
]);
```
```python Python theme={null}
session.sender().update_watch_wallets([
{"pubkey": "WatchWalletPubkey1..."},
{"pubkey": "WatchWalletPubkey2...", "auto_buy": {"wallet_pubkey": "YourBuyWallet...", "amount_quote_units": 100000000}},
])
```
```rust Rust theme={null}
session.sender().update_watch_wallets(vec![
WatchWalletEntryMsg { pubkey: "WatchWalletPubkey1...".into(), auto_buy: None },
WatchWalletEntryMsg {
pubkey: "WatchWalletPubkey2...".into(),
auto_buy: Some(AutoBuyConfigMsg {
wallet_pubkey: "YourBuyWallet...".into(),
amount_quote_units: 100000000,
amount_usd1_units: None,
}),
},
])?;
```
```go Go theme={null}
sender.UpdateWatchWallets([]stream.WatchWalletEntryMsg{
{Pubkey: "WatchWalletPubkey1..."},
{Pubkey: "WatchWalletPubkey2...", AutoBuy: &stream.AutoBuyConfigMsg{
WalletPubkey: "YourBuyWallet...", AmountQuoteUnits: 100000000,
}},
})
```
## `mirror_buy_result`
Reports the outcome of a mirror buy transaction back to the stream. Send this after you sign and submit (or fail to submit) the transaction from a `mirror_buy_signal` event. The stream uses this feedback to update internal state, track consecutive losses, and manage cooldown timers.
```json theme={null}
{
"type": "mirror_buy_result",
"mint": "TokenMint...",
"success": true
}
```
| Field | Type | Required | Description |
| --------- | --------- | -------- | ----------------------------------------------------------------------------------- |
| `mint` | `string` | Yes | Token mint address from the original `mirror_buy_signal`. |
| `success` | `boolean` | Yes | `true` if the transaction was signed and submitted successfully, `false` otherwise. |
```typescript TypeScript theme={null}
session.sender().mirrorBuyResult("TokenMint...", true);
```
```python Python theme={null}
session.sender().mirror_buy_result("TokenMint...", True)
```
```rust Rust theme={null}
session.sender().mirror_buy_result("TokenMint...", true);
```
```go Go theme={null}
sender.MirrorBuyResult("TokenMint...", true)
```
# Connection Lifecycle
Source: https://docs.lasersell.io/api/stream/connection-lifecycle
WebSocket handshake, hello_ok, limits, reconnection behavior, ping/pong, and the StreamClient vs StreamSession abstraction for the Exit Intelligence Stream.
## Connection Steps
### 1. Open WebSocket
Connect to the Exit Intelligence Stream endpoint with your API key in the `x-api-key` header:
```
wss://stream.lasersell.io/v1/ws
```
### 2. Receive `hello_ok`
Immediately after the connection opens, the server sends a `hello_ok` message:
```json theme={null}
{
"type": "hello_ok",
"session_id": 42,
"server_time_ms": 1706000000000,
"limits": {
"hi_capacity": 1024,
"pnl_flush_ms": 1000,
"max_positions_per_session": 100,
"max_wallets_per_session": 10,
"max_positions_per_wallet": 20,
"max_sessions_per_api_key": 5
}
}
```
The `limits` object reflects your tier's capacity. See [Rate Limits and Tiers](/api/reference/rate-limits) for details.
### 3. Send `configure`
After receiving `hello_ok`, send a `configure` message with your wallets and strategy:
```json theme={null}
{
"type": "configure",
"wallet_pubkeys": ["WALLET_PUBKEY_1", "WALLET_PUBKEY_2"],
"strategy": {
"target_profit_pct": 5.0,
"stop_loss_pct": 1.5,
"trailing_stop_pct": 3.0
},
"send_mode": "helius_sender",
"tip_lamports": 1000
}
```
### 4. Receive Acknowledgement
The server responds with initial `balance_update` messages for tokens already held in the configured wallets. If a position is detected, you will also receive `position_opened`.
## The `limits` Object
| Field | Type | Description |
| --------------------------- | -------- | ----------------------------------------------- |
| `hi_capacity` | `number` | Maximum high priority messages buffered. |
| `pnl_flush_ms` | `number` | Interval at which PnL updates are flushed (ms). |
| `max_positions_per_session` | `number` | Max tracked positions per session. |
| `max_wallets_per_session` | `number` | Max wallets per session. |
| `max_positions_per_wallet` | `number` | Max positions tracked per wallet. |
| `max_sessions_per_api_key` | `number` | Max concurrent sessions per API key. |
## Reconnection
The SDKs handle reconnection automatically. When the WebSocket disconnects:
1. The client waits with exponential backoff starting at 100 ms.
2. Backoff doubles on each attempt up to a maximum of 2,000 ms.
3. On successful reconnect, the client re sends the `configure` message.
4. The server will re emit `balance_update` and `position_opened` events for existing holdings.
You do not need to implement reconnection logic yourself.
## Ping and Pong
### Client Ping
Send a `ping` to measure round trip latency:
```json theme={null}
{
"type": "ping",
"client_time_ms": 1706000000000
}
```
The server responds with `pong`:
```json theme={null}
{
"type": "pong",
"server_time_ms": 1706000000001
}
```
### Server Ping
The server may also send WebSocket protocol level pings. The SDKs respond with pong frames automatically.
## StreamClient vs StreamSession
### `StreamClient`
The low level client manages the raw WebSocket connection:
* Handles connect, reconnect, and message framing.
* Returns raw `ServerMessage` objects from `recv()`.
* Provides a `StreamSender` for sending client messages.
* Supports **lane splitting** via `connectLanes()` which separates high priority messages (exit signals, position events) from low priority messages (`pnl_update`).
### `StreamSession`
The high level session wraps `StreamClient` with:
* **Position tracking**: Automatically maintains a map of open positions by ID and token account.
* **Typed events**: Returns `StreamEvent` objects with a `PositionHandle` attached.
* **Deadline timers**: Automatically requests exit signals after `deadline_timeout_sec`.
* **Strategy updates**: `updateStrategy()` and `updateStrategyOptional()` methods.
For most integrations, use `StreamSession`.
## Lanes
Lane splitting separates the message stream into two receivers:
* **High priority lane**: `hello_ok`, `error`, `balance_update`, `position_opened`, `position_closed`, `exit_signal_with_tx`
* **Low priority lane**: `pnl_update`
This prevents high frequency PnL updates from blocking time sensitive exit signals.
Lane splitting is currently available in the **TypeScript SDK only** via `connectLanes()`:
```typescript theme={null}
const client = new StreamClient("YOUR_API_KEY");
const connection = await client.connectLanes(configure, {
lowPriorityCapacity: 512,
});
const [sender, highRx, lowRx] = connection.split();
// Process high priority in main loop
for await (const msg of highRx) {
// exit signals, position events...
}
// Process low priority in background
for await (const msg of lowRx) {
// pnl_update...
}
```
When the low priority buffer is full, the oldest message is dropped to keep the Exit Intelligence Stream responsive.
# Exit Intelligence Stream Overview
Source: https://docs.lasersell.io/api/stream/overview
Introduction to the LaserSell Exit Intelligence Stream for real time position tracking and automated exit execution.
## What is the Exit Intelligence Stream?
The Exit Intelligence Stream is a persistent WebSocket connection that monitors your wallets on chain, tracks token positions, evaluates your profit and loss strategy in real time, and delivers pre built unsigned exit transactions when your thresholds are met.
**Professional and Advanced tier** subscribers also receive real time [liquidity snapshots](/api/stream/server-events#liquidity_snapshot) with slippage bands and liquidity trend data, giving you visibility into how much of a position can be sold at a given price impact and whether pool liquidity is growing, stable, or draining. Read the [full announcement](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for details.
## Endpoint
```
wss://stream.lasersell.io/v1/ws
```
Authentication is handled via the `x-api-key` header, which the SDKs set automatically.
## When to Use the Exit Intelligence Stream vs REST
| Scenario | Use |
| --------------------------------------------- | ------------------------ |
| Automated sell when profit/loss target is hit | Exit Intelligence Stream |
| One off buy or sell transaction | REST (LaserSell API) |
| Continuous position monitoring | Exit Intelligence Stream |
| Building a transaction for user confirmation | REST (LaserSell API) |
| Bot that reacts to wallet activity | Exit Intelligence Stream |
Use the **Exit Intelligence Stream** when you want the server to watch your positions and deliver exit transactions automatically. Use the **REST API** when you need a single transaction built on demand.
**Connect the stream before you buy.** The Exit Intelligence Stream detects new positions by observing on chain token arrivals. If you call `/v1/buy` before the stream is connected and configured, the resulting position will not be tracked and no exit signals will fire. Always connect and configure the stream first, then submit your buy.
## High Level Flow
1. **Connect** to `wss://stream.lasersell.io/v1/ws` with your API key.
2. Receive `hello_ok` from the server (includes session ID and rate limits).
3. **Send `configure`** with wallet public keys and your strategy parameters.
4. Receive initial `balance_update` messages for existing token holdings.
5. **The stream monitors** your wallets for new token arrivals and tracks profit and loss.
6. When a position hits your target profit, stop loss, trailing stop, or deadline, the server sends an `exit_signal_with_tx`.
7. **Sign locally** and submit the unsigned transaction.
```mermaid theme={null}
sequenceDiagram
participant C as Client
participant S as Server
participant Sol as Solana
C->>S: connect (x-api-key)
S->>C: hello_ok
C->>S: configure
S->>C: balance_update
S->>C: position_opened
S->>C: pnl_update
S->>C: exit_signal_with_tx
C->>Sol: sign + submit
```
## SDK Entry Points
The SDKs provide two abstraction levels:
* **`StreamClient`**: Low level client. Manages the WebSocket connection, reconnection, and message framing. Returns raw `ServerMessage` objects.
* **`StreamSession`**: High level wrapper. Wraps `StreamClient` with position tracking, deadline timers, liquidity snapshot caching, and typed `StreamEvent` objects that include a `PositionHandle`.
For most use cases, start with `StreamSession`.
```typescript TypeScript theme={null}
import { StreamClient, StreamSession } from "@lasersell/lasersell-sdk";
const client = new StreamClient("YOUR_API_KEY");
const session = await StreamSession.connect(client, {
wallet_pubkeys: ["WALLET_PUBKEY"],
strategy: { target_profit_pct: 5, stop_loss_pct: 1.5 },
deadline_timeout_sec: 45,
send_mode: "helius_sender",
tip_lamports: 1000,
});
while (true) {
const event = await session.recv();
if (event === null) break;
// Handle event...
}
```
```python Python theme={null}
from lasersell_sdk.stream.client import StreamClient, StreamConfigure
from lasersell_sdk.stream.session import StreamSession
client = StreamClient("YOUR_API_KEY")
session = await StreamSession.connect(
client,
StreamConfigure(
wallet_pubkeys=["WALLET_PUBKEY"],
strategy={"target_profit_pct": 5.0, "stop_loss_pct": 1.5},
deadline_timeout_sec=45,
),
)
while True:
event = await session.recv()
if event is None:
break
# Handle event...
```
```rust Rust theme={null}
use lasersell_sdk::stream::client::{StreamClient, StreamConfigure};
use lasersell_sdk::stream::session::StreamSession;
use lasersell_sdk::stream::proto::StrategyConfigMsg;
use secrecy::SecretString;
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,
..Default::default()
},
deadline_timeout_sec: Some(45),
}).await?;
loop {
let event = match session.recv().await {
Some(event) => event,
None => break,
};
// Handle event...
}
```
```go Go theme={null}
import "github.com/lasersell/lasersell-sdk/go/stream"
client := stream.NewStreamClient("YOUR_API_KEY")
session, err := stream.ConnectSession(ctx, client, stream.StreamConfigure{
WalletPubkeys: []string{"WALLET_PUBKEY"},
Strategy: stream.StrategyConfigMsg{
TargetProfitPct: 5.0,
StopLossPct: 1.5,
},
DeadlineTimeoutSec: 45,
})
if err != nil {
log.Fatal(err)
}
for {
event, err := session.Recv(ctx)
if errors.Is(err, io.EOF) {
break
}
// Handle event...
}
```
## Next Steps
* [Connection Lifecycle](/api/stream/connection-lifecycle): Detailed handshake, reconnection, and lane splitting.
* [Strategy Configuration](/api/stream/strategy-configuration): Configure profit targets, stop losses, and trailing stops.
* [Server Events](/api/stream/server-events): Complete schema for all 9 server message types, including liquidity snapshots.
* [Client Messages](/api/stream/client-messages): All 6 client message types and their schemas.
# Server Events
Source: https://docs.lasersell.io/api/stream/server-events
Complete schema and JSON examples for all 13 server message types sent by the Exit Intelligence Stream.
## Message Types
The server sends messages with a `type` discriminator. All SDKs parse these into typed objects.
| Type | Priority | Description |
| ----------------------------- | -------- | ------------------------------------------------------- |
| `hello_ok` | High | Connection accepted, session started. |
| `pong` | High | Response to client ping. |
| `error` | High | Server error notification. |
| `balance_update` | High | Token balance changed for a tracked wallet. |
| `position_opened` | High | New position detected and tracking started. |
| `position_closed` | High | Position tracking ended. |
| `exit_signal_with_tx` | High | Exit threshold met; unsigned tx included. |
| `mirror_buy_signal` | High | Watched wallet bought; unsigned buy tx for your wallet. |
| `mirror_buy_failed` | High | Mirror buy could not be executed. |
| `mirror_wallet_auto_disabled` | High | Watch wallet disabled by consecutive loss breaker. |
| `pnl_update` | Low | Periodic profit and loss snapshot. |
| `liquidity_snapshot` | Low | Slippage bands and liquidity trend (Tier 1+). |
| `trade_tick` | Low | Real-time trade notification for a tracked position. |
## `hello_ok`
Sent once immediately after the WebSocket opens. Contains the session ID and tier limits.
```json theme={null}
{
"type": "hello_ok",
"session_id": 42,
"server_time_ms": 1706000000000,
"limits": {
"hi_capacity": 1024,
"pnl_flush_ms": 1000,
"max_positions_per_session": 100,
"max_wallets_per_session": 10,
"max_positions_per_wallet": 20,
"max_sessions_per_api_key": 5
}
}
```
| Field | Type | Description |
| ---------------- | -------- | ----------------------------------------------------------------------------- |
| `session_id` | `number` | Unique session identifier. |
| `server_time_ms` | `number` | Server timestamp in milliseconds. |
| `limits` | `object` | Tier capacity and rate limits. See [Rate Limits](/api/reference/rate-limits). |
## `pong`
Response to a client `ping`. Use for latency measurement.
```json theme={null}
{
"type": "pong",
"server_time_ms": 1706000000001
}
```
| Field | Type | Description |
| ---------------- | -------- | --------------------------------- |
| `server_time_ms` | `number` | Server timestamp in milliseconds. |
## `error`
Server side error notification. Check the `code` to determine severity.
```json theme={null}
{
"type": "error",
"code": "invalid_strategy",
"message": "target_profit_pct must be >= 0"
}
```
| Field | Type | Description |
| --------- | -------- | ---------------------------- |
| `code` | `string` | Machine readable error code. |
| `message` | `string` | Human readable error detail. |
## `balance_update`
Emitted when a token balance changes in a tracked wallet (new arrivals, transfers, sells).
```json theme={null}
{
"type": "balance_update",
"wallet_pubkey": "Abc123...",
"mint": "TokenMint...",
"token_account": "ATA...",
"token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"tokens": 5000000,
"slot": 280000000
}
```
| Field | Type | Description |
| --------------- | -------- | ---------------------------------------------------------- |
| `wallet_pubkey` | `string` | Wallet that owns the token account. |
| `mint` | `string` | Token mint address. |
| `token_account` | `string` | Associated token account address. May be absent. |
| `token_program` | `string` | Token program ID (SPL Token or Token 2022). May be absent. |
| `tokens` | `number` | Current balance in atomic units. |
| `slot` | `number` | Solana slot at which the balance was observed. |
## `position_opened`
A new position has been detected and tracking has begun.
```json theme={null}
{
"type": "position_opened",
"position_id": 1,
"wallet_pubkey": "Abc123...",
"mint": "TokenMint...",
"token_account": "ATA...",
"token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"tokens": 5000000,
"entry_quote_units": 100000000,
"market_context": {
"market_type": "pump_swap",
"pumpswap": {
"pool": "PoolAddress..."
}
},
"slot": 280000000
}
```
| Field | Type | Description |
| ------------------- | -------- | ---------------------------------------------------------------------------------------- |
| `position_id` | `number` | Server assigned position identifier. |
| `wallet_pubkey` | `string` | Wallet holding the position. |
| `mint` | `string` | Token mint address. |
| `token_account` | `string` | Associated token account. |
| `token_program` | `string` | Token program ID. May be absent. |
| `tokens` | `number` | Token balance at entry. |
| `entry_quote_units` | `number` | Entry value in quote asset atomic units. |
| `market_context` | `object` | Market/pool context. May be absent. See [Market Context](/api/reference/market-context). |
| `slot` | `number` | Solana slot at detection. |
## `position_closed`
A position has been removed from tracking.
```json theme={null}
{
"type": "position_closed",
"position_id": 1,
"wallet_pubkey": "Abc123...",
"mint": "TokenMint...",
"token_account": "ATA...",
"reason": "stop_loss",
"slot": 280000100
}
```
| Field | Type | Description |
| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `position_id` | `number` | Position identifier. |
| `wallet_pubkey` | `string` | Wallet that held the position. |
| `mint` | `string` | Token mint address. |
| `token_account` | `string` | Associated token account. May be absent. |
| `reason` | `string` | Why the position was closed (e.g., `"stop_loss"`, `"target_profit"`, `"trailing_stop"`, `"deadline"`, `"manual"`). |
| `slot` | `number` | Solana slot at close. |
## `exit_signal_with_tx`
The most important event. Sent when a position meets your exit criteria. Includes a pre built unsigned transaction ready for signing.
```json theme={null}
{
"type": "exit_signal_with_tx",
"session_id": 42,
"position_id": 1,
"wallet_pubkey": "Abc123...",
"mint": "TokenMint...",
"token_account": "ATA...",
"token_program": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"position_tokens": 5000000,
"profit_units": 50000000,
"reason": "target_profit",
"triggered_at_ms": 1706000005000,
"market_context": {
"market_type": "pump_swap",
"pumpswap": { "pool": "PoolAddress..." }
},
"unsigned_tx_b64": "AQAAAA..."
}
```
| Field | Type | Description |
| ----------------- | -------- | ----------------------------------------------------------------------------------------- |
| `session_id` | `number` | Session that generated this signal. |
| `position_id` | `number` | Position identifier. |
| `wallet_pubkey` | `string` | Wallet holding the position. |
| `mint` | `string` | Token mint address. |
| `token_account` | `string` | Associated token account. May be absent. |
| `token_program` | `string` | Token program ID. May be absent. |
| `position_tokens` | `number` | Tokens held at signal time. |
| `profit_units` | `number` | Estimated profit in quote asset atomic units. |
| `reason` | `string` | Trigger reason (e.g., `"target_profit"`, `"stop_loss"`, `"trailing_stop"`, `"deadline"`). |
| `triggered_at_ms` | `number` | Millisecond timestamp when the signal was generated. |
| `market_context` | `object` | Market/pool context for the sell route. May be absent. |
| `unsigned_tx_b64` | `string` | Base64 encoded unsigned `VersionedTransaction`. |
## `mirror_buy_signal`
Sent when a watched wallet opens a position on a supported market and the stream builds an unsigned buy transaction for your wallet. Sign and submit this transaction to mirror the trade.
```json theme={null}
{
"type": "mirror_buy_signal",
"session_id": 42,
"watched_wallet": "WatchedWalletPubkey...",
"mint": "TokenMint...",
"user_wallet": "YourWalletPubkey...",
"amount_quote_units": 100000000,
"input": "SOL",
"unsigned_tx_b64": "AQAAAA...",
"slippage_bps": 2500,
"send_mode": "helius_sender",
"tip_lamports": 1000,
"market_context": {
"market_type": "pump_swap",
"pumpswap": { "pool": "PoolAddress..." }
}
}
```
| Field | Type | Description |
| -------------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `session_id` | `number` | Session that generated this signal. |
| `watched_wallet` | `string` | Public key of the watched wallet that triggered the buy. |
| `mint` | `string` | Token mint address being purchased. |
| `user_wallet` | `string` | Your wallet that will execute the buy transaction. |
| `amount_quote_units` | `number` | Buy amount in quote asset atomic units. |
| `input` | `string` | Quote asset used for the buy: `"SOL"` or `"USD1"`. |
| `unsigned_tx_b64` | `string` | Base64 encoded unsigned `VersionedTransaction`. |
| `slippage_bps` | `number` | Slippage tolerance in basis points. |
| `send_mode` | `string` | Transaction submission mode. May be absent. |
| `tip_lamports` | `number` | Priority fee tip in lamports. May be absent. |
| `market_context` | `object` | Market/pool context for the buy route. May be absent. See [Market Context](/api/reference/market-context). |
## `mirror_buy_failed`
Sent when a mirror buy cannot be executed. Common reasons include insufficient balance, no route found, or pool liquidity below the minimum threshold.
```json theme={null}
{
"type": "mirror_buy_failed",
"watched_wallet": "WatchedWalletPubkey...",
"mint": "TokenMint...",
"reason": "insufficient_balance"
}
```
| Field | Type | Description |
| ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `watched_wallet` | `string` | Public key of the watched wallet that triggered the buy. |
| `mint` | `string` | Token mint address that was being purchased. |
| `reason` | `string` | Machine readable reason the buy failed (e.g., `"insufficient_balance"`, `"no_route"`, `"below_min_liquidity"`, `"cooldown_active"`, `"max_positions_reached"`). |
## `mirror_wallet_auto_disabled`
Sent when a watched wallet is automatically disabled by the consecutive loss breaker. After receiving this event the stream will no longer mirror trades from this wallet until you re-enable it.
```json theme={null}
{
"type": "mirror_wallet_auto_disabled",
"watched_wallet": "WatchedWalletPubkey...",
"reason": "consecutive_loss_limit_reached",
"loss_count": 3
}
```
| Field | Type | Description |
| ---------------- | -------- | --------------------------------------------------------------- |
| `watched_wallet` | `string` | Public key of the wallet that was disabled. |
| `reason` | `string` | Human readable description of why the wallet was disabled. |
| `loss_count` | `number` | Number of consecutive losing trades that triggered the disable. |
## `pnl_update`
Periodic profit and loss snapshot for a tracked position.
```json theme={null}
{
"type": "pnl_update",
"position_id": 1,
"profit_units": 25000000,
"proceeds_units": 125000000,
"server_time_ms": 1706000003000
}
```
| Field | Type | Description |
| ---------------- | -------- | ----------------------------------------------------- |
| `position_id` | `number` | Position identifier. |
| `profit_units` | `number` | Current estimated profit in quote asset atomic units. |
| `proceeds_units` | `number` | Current estimated total proceeds. |
| `server_time_ms` | `number` | Server timestamp in milliseconds. |
`pnl_update` messages are delivered at the interval defined by `limits.pnl_flush_ms`. When using lane splitting, they flow through the low priority channel.
## `liquidity_snapshot`
**Tier 1+ only.** Liquidity snapshots are delivered to Professional and Advanced tier subscribers. Free tier sessions receive `pnl_update` messages only.
Real time slippage bands and liquidity trend for a tracked position. Delivered alongside `pnl_update` at the same flush interval. Use this data to understand how much of a position can be sold at a given slippage threshold and whether pool liquidity is growing, stable, or draining. See the [announcement blog post](https://www.lasersell.io/blog/liquidity-snapshots-and-sdk-0-3) for background and examples.
```json theme={null}
{
"type": "liquidity_snapshot",
"position_id": 1,
"bands": [
{ "slippage_bps": 100, "max_tokens": 500000, "coverage_pct": 10.0 },
{ "slippage_bps": 200, "max_tokens": 1200000, "coverage_pct": 24.0 },
{ "slippage_bps": 500, "max_tokens": 3000000, "coverage_pct": 60.0 },
{ "slippage_bps": 1000, "max_tokens": 5000000, "coverage_pct": 100.0 }
],
"liquidity_trend": "stable",
"server_time_ms": 1706000003000
}
```
| Field | Type | Description |
| ----------------- | -------- | -------------------------------------------------------------------------------------------- |
| `position_id` | `number` | Position identifier. |
| `bands` | `array` | Slippage bands, each describing how many tokens are sellable within that slippage threshold. |
| `liquidity_trend` | `string` | Current liquidity direction: `"growing"`, `"stable"`, or `"draining"`. |
| `server_time_ms` | `number` | Server timestamp in milliseconds. |
### `SlippageBandMsg`
Each entry in the `bands` array:
| Field | Type | Description |
| -------------- | -------- | ----------------------------------------------------------------- |
| `slippage_bps` | `number` | Slippage threshold in basis points (e.g. `100` = 1%). |
| `max_tokens` | `number` | Maximum tokens sellable within this slippage band (atomic units). |
| `coverage_pct` | `number` | Percentage of the full position covered by this band (0 to 100). |
### Interpreting slippage bands
Slippage bands answer the question: "How much can I sell without exceeding X% slippage?" For example, if the 500 bps band shows `max_tokens: 3000000` and `coverage_pct: 60.0`, you can sell up to 3,000,000 tokens (60% of your position) with no more than 5% price impact.
Use `session.getSlippageBands(positionId)` or `session.getMaxSellAtSlippage(positionId, slippageBps)` in any SDK to query the latest snapshot.
### Liquidity trend
The `liquidity_trend` field tracks how pool liquidity is changing over recent observations:
* **`"growing"`**: Liquidity is increasing. Sell conditions are improving.
* **`"stable"`**: Liquidity is steady. No significant change.
* **`"draining"`**: Liquidity is decreasing. Consider exiting sooner if selling a large position.
Use `session.getLiquidityTrend(positionId)` in any SDK to read the trend.
## `trade_tick`
Real-time notification when a trade occurs on a tracked position's token. Useful for monitoring trading activity on tokens you hold.
```json theme={null}
{
"type": "trade_tick",
"position_id": 1,
"time_ms": 1706000005000,
"side": "buy",
"token_amount": 500000,
"quote_amount": 25000000,
"price_quote": 50000,
"maker": "TraderWallet...",
"tx_signature": "5abc..."
}
```
| Field | Type | Description |
| -------------- | --------- | ----------------------------------------------- |
| `position_id` | `number` | Position identifier. |
| `time_ms` | `number` | Trade timestamp in milliseconds. |
| `side` | `string` | Trade direction: `"buy"` or `"sell"`. |
| `token_amount` | `number` | Token amount traded (atomic units). |
| `quote_amount` | `number` | Quote asset amount traded (atomic units). |
| `price_quote` | `number` | Price per token in quote asset (atomic units). |
| `maker` | `string` | Wallet that initiated the trade. May be absent. |
| `tx_signature` | `string` | On-chain transaction signature. May be absent. |
| `watched` | `boolean` | `true` if this trade is from a watched wallet. |
# Strategy Configuration
Source: https://docs.lasersell.io/api/stream/strategy-configuration
Configure target profit, stop loss, trailing stop, and sell on graduation thresholds for the Exit Intelligence Stream.
## `StrategyConfigMsg` Schema
| Field | Type | Required | Description |
| --------------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `target_profit_pct` | `number` | Yes | Take profit threshold as a percentage (e.g., `5` = 5%). |
| `stop_loss_pct` | `number` | Yes | Stop loss threshold as a percentage (e.g., `1.5` = 1.5%). |
| `trailing_stop_pct` | `number` | No | Trailing stop percentage from the peak. Activates only after the position is in profit. |
| `sell_on_graduation` | `boolean` | No | If `true`, auto sells when a bonding curve token graduates to an AMM pool. |
| `take_profit_levels` | `TakeProfitLevelMsg[]` | No | Exit ladder: sell partial amounts at multiple profit thresholds. Each level has `profit_pct` (trigger), `sell_pct` (portion to sell), and `trailing_stop_pct` (optional trailing stop for that level). |
| `liquidity_guard` | `boolean` | No | When enabled, the stream checks available pool liquidity before generating an exit signal. Prevents exits into thin liquidity. Default: `false`. |
| `breakeven_trail_pct` | `number` | No | A trailing stop that activates once the position breaks even, trailing from the breakeven point rather than the peak. |
At least one of `target_profit_pct`, `stop_loss_pct`, `trailing_stop_pct`, or `deadline_timeout_sec` (on the configure message) must be greater than zero.
### `TakeProfitLevelMsg` Schema
| Field | Type | Required | Description |
| ------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `profit_pct` | `number` | Yes | Profit percentage at which this level triggers (e.g., `20` = 20%). |
| `sell_pct` | `number` | Yes | Percentage of the position to sell at this level (e.g., `50` = sell 50%). |
| `trailing_stop_pct` | `number` | No | Optional trailing stop for this level. If set, instead of selling immediately at `profit_pct`, a trailing stop activates at this level. Default: `0` (sell immediately). |
## Setting Strategy at Connection Time
Strategy is provided in the `configure` message when you first connect:
```typescript TypeScript theme={null}
const session = await StreamSession.connect(client, {
wallet_pubkeys: ["WALLET_PUBKEY"],
strategy: {
target_profit_pct: 10,
stop_loss_pct: 2,
trailing_stop_pct: 5,
sell_on_graduation: true,
},
deadline_timeout_sec: 60,
send_mode: "helius_sender",
tip_lamports: 1000,
});
```
```python Python theme={null}
session = await StreamSession.connect(
client,
StreamConfigure(
wallet_pubkeys=["WALLET_PUBKEY"],
strategy={
"target_profit_pct": 10.0,
"stop_loss_pct": 2.0,
"trailing_stop_pct": 5.0,
"sell_on_graduation": True,
},
deadline_timeout_sec=60,
send_mode="helius_sender",
tip_lamports=1000,
),
)
```
```rust Rust theme={null}
let session = StreamSession::connect(&client, StreamConfigure {
wallet_pubkeys: vec!["WALLET_PUBKEY".into()],
strategy: StrategyConfigMsg {
target_profit_pct: 10.0,
stop_loss_pct: 2.0,
trailing_stop_pct: Some(5.0),
sell_on_graduation: Some(true),
},
deadline_timeout_sec: Some(60),
send_mode: Some("helius_sender".to_string()),
tip_lamports: Some(1000),
}).await?;
```
```go Go theme={null}
sendMode := "helius_sender"
tipLamports := uint64(1000)
session, err := stream.ConnectSession(ctx, client, stream.StreamConfigure{
WalletPubkeys: []string{"WALLET_PUBKEY"},
Strategy: stream.StrategyConfigMsg{
TargetProfitPct: 10.0,
StopLossPct: 2.0,
TrailingStopPct: 5.0,
SellOnGraduation: true,
},
DeadlineTimeoutSec: 60,
SendMode: &sendMode,
TipLamports: &tipLamports,
})
```
## Using StrategyConfigBuilder
All 4 SDKs provide a fluent builder for strategy configuration. The builder validates that at least one exit condition is set when you call `build()`.
```typescript TypeScript theme={null}
import { StrategyConfigBuilder } from "@lasersell/lasersell-sdk";
const strategy = new StrategyConfigBuilder()
.targetProfitPct(50)
.stopLossPct(10)
.trailingStopPct(5)
.takeProfitLevels([
{ profit_pct: 20, sell_pct: 25, trailing_stop_pct: 0 },
{ profit_pct: 50, sell_pct: 50, trailing_stop_pct: 3 },
{ profit_pct: 100, sell_pct: 100, trailing_stop_pct: 5 },
])
.liquidityGuard(true)
.breakevenTrailPct(2)
.build();
```
```python Python theme={null}
from lasersell_sdk.stream.client import StrategyConfigBuilder
strategy = (
StrategyConfigBuilder()
.target_profit_pct(50)
.stop_loss_pct(10)
.trailing_stop_pct(5)
.take_profit_levels([
{"profit_pct": 20, "sell_pct": 25, "trailing_stop_pct": 0},
{"profit_pct": 50, "sell_pct": 50, "trailing_stop_pct": 3},
{"profit_pct": 100, "sell_pct": 100, "trailing_stop_pct": 5},
])
.liquidity_guard(True)
.breakeven_trail_pct(2.0)
.build()
)
```
```rust Rust theme={null}
use lasersell_sdk::stream::client::StrategyConfigBuilder;
use lasersell_sdk::stream::proto::TakeProfitLevelMsg;
let strategy = StrategyConfigBuilder::new()
.target_profit_pct(50.0)
.stop_loss_pct(10.0)
.trailing_stop_pct(5.0)
.take_profit_levels(vec![
TakeProfitLevelMsg { profit_pct: 20.0, sell_pct: 25.0, trailing_stop_pct: 0.0 },
TakeProfitLevelMsg { profit_pct: 50.0, sell_pct: 50.0, trailing_stop_pct: 3.0 },
TakeProfitLevelMsg { profit_pct: 100.0, sell_pct: 100.0, trailing_stop_pct: 5.0 },
])
.liquidity_guard(true)
.breakeven_trail_pct(2.0)
.build();
```
```go Go theme={null}
import "github.com/lasersell/lasersell-sdk/go/stream"
strategy := stream.NewStrategyConfigBuilder().
TargetProfitPct(50.0).
StopLossPct(10.0).
TrailingStopPct(5.0).
TakeProfitLevels([]stream.TakeProfitLevelMsg{
{ProfitPct: 20.0, SellPct: 25.0, TrailingStopPct: 0.0},
{ProfitPct: 50.0, SellPct: 50.0, TrailingStopPct: 3.0},
{ProfitPct: 100.0, SellPct: 100.0, TrailingStopPct: 5.0},
}).
LiquidityGuard(true).
BreakevenTrailPct(2.0).
Build()
```
## Exit Ladder (Take Profit Levels)
An exit ladder lets you take partial profits at multiple thresholds instead of exiting your entire position at a single price. Each level specifies a profit target and the percentage of the remaining position to sell.
### How it works
1. When profit reaches a level's `profit_pct`, the stream sells `sell_pct` of the current position.
2. If a level has `trailing_stop_pct > 0`, a trailing stop activates at that level instead of selling immediately.
3. Levels are evaluated in order from lowest to highest `profit_pct`.
4. The global `target_profit_pct` still applies as a hard cap; if reached before all levels fire, the remaining position is sold.
### Example
With this exit ladder and an entry of 1 SOL:
```json theme={null}
{
"take_profit_levels": [
{ "profit_pct": 20, "sell_pct": 25, "trailing_stop_pct": 0 },
{ "profit_pct": 50, "sell_pct": 50, "trailing_stop_pct": 3 },
{ "profit_pct": 100, "sell_pct": 100, "trailing_stop_pct": 5 }
],
"stop_loss_pct": 15
}
```
* At 20% profit (position worth 1.2 SOL): sell 25% immediately.
* At 50% profit (position worth 1.5 SOL): activate a 3% trailing stop on 50% of the remaining position.
* At 100% profit (position worth 2 SOL): activate a 5% trailing stop on the remaining position.
## Liquidity Guard
When `liquidity_guard` is enabled, the stream checks pool liquidity before generating an exit signal. If the pool cannot absorb the sell at a reasonable slippage, the signal is deferred until liquidity improves or a timeout forces the exit.
This is useful for large positions in thin pools where an immediate sell would result in excessive slippage.
```json theme={null}
{
"target_profit_pct": 50,
"stop_loss_pct": 10,
"liquidity_guard": true
}
```
## Breakeven Trail
The `breakeven_trail_pct` field enables a trailing stop that activates once a position breaks even (profit >= 0), trailing from the breakeven point rather than the peak.
Unlike the standard `trailing_stop_pct` which trails from the highest observed profit, the breakeven trail protects you from giving back all gains on a position that briefly went profitable.
### Example
With `breakeven_trail_pct: 2` and an entry of 1 SOL:
| Time | Profit (%) | Breakeven Trail Active | Trigger |
| ---- | ---------- | ---------------------- | ----------------------------------------- |
| t=0 | -5 | No (not at breakeven) | — |
| t=1 | 0 | Yes (at breakeven) | — |
| t=2 | 3 | Yes | No trigger (profit > 0) |
| t=3 | -1 | Yes | No trigger (drop \< 2% from breakeven) |
| t=4 | -2.5 | Yes | **Triggered** (drop >= 2% from breakeven) |
Combine with a standard `trailing_stop_pct` to protect both the breakeven point and the profit peak.
## Trailing Stop Explained
The trailing stop tracks the highest profit observed since the position opened. When profit drops from that peak by `trailing_stop_pct`, an exit signal fires.
### Numerical Example
| Time | Profit (%) | Peak (%) | Drop from Peak (%) | Trailing Stop (5%) |
| ---- | ---------- | -------- | ------------------ | -------------------------- |
| t=0 | 0 | 0 | 0 | Not active |
| t=1 | 3 | 3 | 0 | Not active |
| t=2 | 8 | 8 | 0 | Active, no trigger |
| t=3 | 6 | 8 | 2 | Active, no trigger |
| t=4 | 4 | 8 | 4 | Active, no trigger |
| t=5 | 2.5 | 8 | 5.5 | **Triggered** (drop >= 5%) |
The trailing stop only activates once the position is in profit. If the position goes negative before ever being in profit, the stop loss triggers instead.
## Updating Strategy Mid Session
You can change the strategy at any time without reconnecting by sending an `update_strategy` message:
```json theme={null}
{
"type": "update_strategy",
"strategy": {
"target_profit_pct": 15,
"stop_loss_pct": 3,
"trailing_stop_pct": 7
}
}
```
Using the SDK:
```typescript TypeScript theme={null}
session.updateStrategy({
target_profit_pct: 15,
stop_loss_pct: 3,
trailing_stop_pct: 7,
});
```
```python Python theme={null}
session.sender().update_strategy({
"target_profit_pct": 15.0,
"stop_loss_pct": 3.0,
"trailing_stop_pct": 7.0,
})
```
```rust Rust theme={null}
session.update_strategy(StrategyConfigMsg {
target_profit_pct: 15.0,
stop_loss_pct: 3.0,
trailing_stop_pct: Some(7.0),
sell_on_graduation: None,
}, None);
```
```go Go theme={null}
session.Sender().UpdateStrategy(stream.StrategyConfigMsg{
TargetProfitPct: 15.0,
StopLossPct: 3.0,
TrailingStopPct: 7.0,
})
```
The new strategy takes effect immediately for all tracked positions. No positions are closed or reopened; the server simply re evaluates existing positions against the new thresholds.
## Deadline Timeout
The `deadline_timeout_sec` is set on the `configure` message (not on the strategy object itself). It defines the maximum time in seconds to hold a position before requesting an exit signal, regardless of profit or loss.
Setting `deadline_timeout_sec: 60` means the Exit Intelligence Stream will attempt to exit any position held for more than 60 seconds.
When using `StreamSession`, the deadline timer runs client side and automatically calls `requestExitSignal` when it fires. If you are using `StreamClient` directly, you must implement deadline logic yourself.
## Validation Rules
* `target_profit_pct` must be >= 0.
* `stop_loss_pct` must be >= 0.
* `trailing_stop_pct` (if provided) must be >= 0.
* `deadline_timeout_sec` must be >= 0.
* At least one of these values must be > 0.
Violating these rules raises a `StreamClientError` with kind `"protocol"`.
## Per-Position Strategy Override
You can override the global strategy for individual positions without affecting other tracked positions. This is useful when you want different exit rules for specific tokens.
```json theme={null}
{
"type": "update_position_strategy",
"position_id": 1,
"strategy": {
"target_profit_pct": 200,
"stop_loss_pct": 5,
"trailing_stop_pct": 10
}
}
```
Using the SDK:
```typescript TypeScript theme={null}
session.sender().updatePositionStrategy(positionId, {
target_profit_pct: 200,
stop_loss_pct: 5,
trailing_stop_pct: 10,
});
```
```python Python theme={null}
session.sender().update_position_strategy(position_id, {
"target_profit_pct": 200.0,
"stop_loss_pct": 5.0,
"trailing_stop_pct": 10.0,
})
```
```rust Rust theme={null}
session.sender().update_position_strategy(position_id, StrategyConfigMsg {
target_profit_pct: 200.0,
stop_loss_pct: 5.0,
trailing_stop_pct: 10.0,
..Default::default()
})?;
```
```go Go theme={null}
sender.UpdatePositionStrategy(positionID, stream.StrategyConfigMsg{
TargetProfitPct: 200.0,
StopLossPct: 5.0,
TrailingStopPct: 10.0,
})
```
The override applies only to the specified position. All other positions continue using the global strategy. To revert a position back to the global strategy, send another `update_position_strategy` with the current global strategy values.
Per-position overrides are ephemeral and are not persisted across reconnections.
# Send Targets
Source: https://docs.lasersell.io/api/transactions/send-targets
Choose how to submit signed transactions: Default RPC, custom RPC, Helius Sender, or Astralane Iris.
## What is a Send Target?
A `SendTarget` tells the SDK where to submit your signed transaction. The LaserSell SDKs support four targets, each optimized for different use cases.
## Available Targets
### Default RPC
Uses the Solana public mainnet beta endpoint (`https://api.mainnet-beta.solana.com`). This endpoint is rate limited and best suited for testing only.
```typescript TypeScript theme={null}
import { sendTargetDefaultRpc } from "@lasersell/lasersell-sdk";
const target = sendTargetDefaultRpc();
```
```python Python theme={null}
from lasersell_sdk.tx import SendTargetRpc
target = SendTargetRpc() # defaults to mainnet-beta
```
```rust Rust theme={null}
use lasersell_sdk::tx::SendTarget;
let target = SendTarget::Rpc { url: lasersell_sdk::tx::MAINNET_BETA_RPC_URL.into() };
```
```go Go theme={null}
target := lasersell.SendTargetDefaultRpc()
```
### Custom RPC
Specify your own RPC endpoint URL. Recommended for production with a private RPC provider such as Helius or Chainstack (free tiers available).
```typescript TypeScript theme={null}
import { sendTargetRpc } from "@lasersell/lasersell-sdk";
const target = sendTargetRpc("https://your-private-rpc.example.com");
```
```python Python theme={null}
from lasersell_sdk.tx import SendTargetRpc
target = SendTargetRpc(url="https://your-private-rpc.example.com")
```
```rust Rust theme={null}
let target = SendTarget::Rpc { url: "https://your-private-rpc.example.com".into() };
```
```go Go theme={null}
target := lasersell.SendTargetRpc("https://your-private-rpc.example.com")
```
### Helius Sender
Routes through the Helius Sender `/fast` endpoint for optimized transaction landing. No API key required for Helius Sender.
```typescript TypeScript theme={null}
import { sendTargetHeliusSender } from "@lasersell/lasersell-sdk";
const target = sendTargetHeliusSender();
```
```python Python theme={null}
from lasersell_sdk.tx import SendTargetHeliusSender
target = SendTargetHeliusSender()
```
```rust Rust theme={null}
let target = SendTarget::HeliusSender;
```
```go Go theme={null}
target := lasersell.SendTargetHeliusSender()
```
**Connection Warming (TypeScript only)**: For the best latency, the TypeScript SDK provides helpers to keep the Helius Sender connection warm:
```typescript theme={null}
import { startHeliusSenderWarmLoop, pingHeliusSender } from "@lasersell/lasersell-sdk";
// One-time ping
const ok = await pingHeliusSender();
// Continuous warm loop (pings every 30s)
const { stop } = startHeliusSenderWarmLoop({ intervalMs: 30_000 });
// Call stop() when done
```
The Rust SDK sends a single ping on first use. The Python and Go SDKs do not require connection warming.
### Astralane Iris
Routes through the Astralane Iris gateway. Requires an Astralane API key and supports regional endpoints for latency optimization.
```typescript TypeScript theme={null}
import { sendTargetAstralane } from "@lasersell/lasersell-sdk";
const target = sendTargetAstralane("ASTRALANE_API_KEY", "fr");
```
```python Python theme={null}
from lasersell_sdk.tx import SendTargetAstralane
target = SendTargetAstralane(api_key="ASTRALANE_API_KEY", region="fr")
```
```rust Rust theme={null}
let target = SendTarget::Astralane {
api_key: "ASTRALANE_API_KEY".into(),
region: Some("fr".into()),
};
```
```go Go theme={null}
target := lasersell.SendTargetAstralane("ASTRALANE_API_KEY", "fr")
```
## Available Regions (Astralane)
| Code | Location | Notes |
| ------ | ------------- | ----------- |
| `fr` | Frankfurt | Recommended |
| `fr2` | Frankfurt 2 | |
| `ams` | Amsterdam | Recommended |
| `ams2` | Amsterdam 2 | |
| `la` | San Francisco | |
| `ny` | New York | |
| `jp` | Tokyo | |
| `sg` | Singapore | |
| `lim` | Limburg | |
| `lit` | Lithuania | |
If no region is specified, the SDK defaults to `fr` (Frankfurt).
## Recommendations
| Use Case | Recommended Target |
| ------------------------------- | ------------------ |
| Testing and development | Default RPC |
| Production (general) | Helius Sender |
| Production (latency sensitive) | Astralane Iris |
| Production (existing RPC setup) | Custom RPC |
For automated trading bots, **Helius Sender** or **Astralane Iris** provide the best transaction landing rates. Avoid using the public Solana RPC in production as it is heavily rate limited.
## Send Mode vs Send Target
The `send_mode` field on `BuildSellTxRequest` and `BuildBuyTxRequest` tells the **server** which endpoint context to optimize the transaction for. The `SendTarget` controls where **your client** submits the signed transaction. For best results, align them:
| `send_mode` (server) | `SendTarget` (client) |
| -------------------- | -------------------------- |
| `"rpc"` | `sendTargetRpc(url)` |
| `"helius_sender"` | `sendTargetHeliusSender()` |
| `"astralane"` | `sendTargetAstralane(...)` |
# Transaction Signing
Source: https://docs.lasersell.io/api/transactions/signing
Non custodial signing flow, signing functions per language, and loading Solana keypairs.
## Non Custodial Flow
LaserSell never touches your private key. Every transaction follows this pattern:
1. **Build**: The API returns a base64 encoded unsigned `VersionedTransaction`.
2. **Sign**: You decode, sign, and re encode locally using your keypair.
3. **Submit**: You send the signed transaction to the Solana network via a [send target](/api/transactions/send-targets).
```mermaid theme={null}
flowchart LR
A[LaserSell API] -->|unsigned_tx_b64| B[Sign Locally] -->|signed_tx_b64| C[Solana]
```
## Loading a Keypair
All SDKs expect a Solana keypair. The standard format is a JSON file containing an array of 64 bytes (as used by `solana-keygen`).
```typescript TypeScript theme={null}
import { readFile } from "node:fs/promises";
import { Keypair } from "@solana/web3.js";
const raw = await readFile("./keypair.json", "utf8");
const keypair = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(raw))
);
```
```python Python theme={null}
import json
from pathlib import Path
from solders.keypair import Keypair
raw = json.loads(Path("./keypair.json").read_text())
keypair = Keypair.from_bytes(bytes(raw))
```
```rust Rust theme={null}
use solana_sdk::signature::read_keypair_file;
let keypair = read_keypair_file("./keypair.json")?;
```
```go Go theme={null}
import "github.com/gagliardetto/solana-go"
privateKey, err := solana.PrivateKeyFromSolanaKeygenFile("./keypair.json")
```
## Signing Functions
### Standard Sign
Decodes the unsigned transaction, signs it with your keypair, and returns a signed `VersionedTransaction` object.
```typescript TypeScript theme={null}
import { signUnsignedTx } from "@lasersell/lasersell-sdk";
const signedTx = signUnsignedTx(unsignedTxB64, keypair);
```
```python Python theme={null}
from lasersell_sdk.tx import sign_unsigned_tx
signed_tx = sign_unsigned_tx(unsigned_tx_b64, keypair)
```
```rust Rust theme={null}
use lasersell_sdk::tx::sign_unsigned_tx;
let signed_tx = sign_unsigned_tx(&unsigned_tx_b64, &keypair)?;
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
signedTx, err := lasersell.SignUnsignedTx(unsignedTxB64, privateKey)
```
### Fast Sign (TypeScript)
The TypeScript SDK provides `signUnsignedTxB64Fast`, which signs the raw bytes in place without deserializing through `VersionedTransaction`. This avoids allocation on the hot path and returns the signed transaction as a base64 string directly.
```typescript theme={null}
import { signUnsignedTxB64Fast } from "@lasersell/lasersell-sdk";
const signedTxB64 = signUnsignedTxB64Fast(unsignedTxB64, keypair);
```
### Sign and Send (One Step)
Combines signing and submission into a single call.
```typescript TypeScript theme={null}
import { signAndSendUnsignedTxB64, sendTargetHeliusSender } from "@lasersell/lasersell-sdk";
const signature = await signAndSendUnsignedTxB64(
sendTargetHeliusSender(),
unsignedTxB64,
keypair,
);
```
## Submitting the Signed Transaction
After signing, submit to the Solana network using a send target:
```typescript TypeScript theme={null}
import { sendTransaction, sendTargetHeliusSender } from "@lasersell/lasersell-sdk";
const signature = await sendTransaction(sendTargetHeliusSender(), signedTx);
```
```python Python theme={null}
from lasersell_sdk.tx import send_transaction, SendTargetHeliusSender
signature = await send_transaction(SendTargetHeliusSender(), signed_tx)
```
```rust Rust theme={null}
use lasersell_sdk::tx::{SendTarget, send_transaction};
let http = reqwest::Client::new();
let signature = send_transaction(&http, &SendTarget::HeliusSender, &signed_tx).await?;
```
```go Go theme={null}
import lasersell "github.com/lasersell/lasersell-sdk/go"
sig, err := lasersell.SendTransaction(ctx, nil, lasersell.SendTargetHeliusSender(), signedTx)
```
## Error Types
Signing and submission errors are surfaced through `TxSubmitError`:
| Kind | Description |
| ------------------------- | --------------------------------------------------- |
| `decode_unsigned_tx` | Base64 decoding of the unsigned transaction failed. |
| `deserialize_unsigned_tx` | Transaction bytes could not be deserialized. |
| `sign_tx` | Signing failed (wrong keypair, corrupted data). |
| `serialize_tx` | Serialization of the signed transaction failed. |
| `request_send` | Network error during submission. |
| `response_read` | Could not read the RPC response body. |
| `http_status` | Non 2xx HTTP response from the RPC endpoint. |
| `decode_response` | Response body was not valid JSON. |
| `rpc_error` | RPC returned an error object. |
| `missing_result` | Response did not contain a transaction signature. |
## Security Best Practices
* **Never log or transmit** your private key.
* **Load keys from environment variables** or encrypted files in production.
* **Verify the wallet address** matches the `user_pubkey` used in the build request. The API constructs the transaction for that specific signer.
# Transaction Build Latency
Source: https://docs.lasersell.io/benchmarks/tx-build-latency
Open-source benchmarks comparing Solana transaction build latency across LaserSell API, PumpFun SDK, PumpPortal API, and Jupiter's Metis API.
When LaserSell detects an exit condition, the sell transaction needs to be built and returned as fast as possible. Every millisecond of build latency is a millisecond your transaction is not on its way to the network.
We open-sourced our transaction build benchmarks so anyone can verify these numbers. The benchmarks compare four methods of building Solana swap transactions: LaserSell API, PumpFun SDK, PumpPortal API, and Jupiter's Metis API.
All benchmarks run 20 iterations with a warmup round, measuring the time from request to receiving an unsigned transaction.
## Co-Located Server (Exit Intelligence Stream)
This is the latency that matters for automated exits. When the Exit Intelligence Stream detects that your take profit, stop loss, or trailing stop has been triggered, it calls the LaserSell API over the co-located network to build the sell transaction. The transaction is then returned to your client for signing.
### Buy Transaction Build
0.01 SOL buy, 20% slippage, 20 iterations:
| Method | Avg | Min | Max | Median |
| :---------------- | :---------- | :---------- | :---------- | :---------- |
| **LaserSell API** | **4.53 ms** | **3.39 ms** | **6.84 ms** | **4.10 ms** |
| PumpPortal API | 65.24 ms | 26.18 ms | 264.29 ms | 46.34 ms |
| Jupiter Metis API | 95.03 ms | 79.30 ms | 115.76 ms | 93.73 ms |
| PumpFun SDK | 123.34 ms | 103.46 ms | 208.71 ms | 118.24 ms |
### Sell Transaction Build
1,000,000 tokens, 20% slippage, 20 iterations:
| Method | Avg | Min | Max | Median |
| :---------------- | :---------- | :---------- | :----------- | :---------- |
| **LaserSell API** | **5.32 ms** | **3.38 ms** | **15.03 ms** | **4.46 ms** |
| PumpPortal API | 80.74 ms | 27.75 ms | 576.28 ms | 38.18 ms |
| PumpFun SDK | 120.81 ms | 96.53 ms | 143.23 ms | 121.65 ms |
| Jupiter Metis API | 168.84 ms | 159.10 ms | 177.35 ms | 169.11 ms |
On the co-located path, LaserSell builds transactions in under 5 ms on average. The next fastest method is over 12x slower. This is the path used by the Exit Intelligence Stream for automated exits.
## Public Internet (SDK and API)
Standard API latency over the public internet, measured from a MacBook Pro. This represents the latency experienced by developers making direct REST API calls from their own applications.
Desktop App users do not experience these numbers. The Desktop App receives pre-built exit transactions through the Exit Intelligence Stream, which builds transactions on the co-located path (the sub-5ms numbers above) and pushes them to the client over a persistent WebSocket connection.
### Buy Transaction Build
0.01 SOL buy, 20% slippage, 20 iterations:
| Method | Avg | Min | Max | Median |
| :---------------- | :----------- | :----------- | :----------- | :----------- |
| **LaserSell API** | **71.21 ms** | **62.77 ms** | **81.30 ms** | **71.16 ms** |
| PumpPortal API | 151.08 ms | 120.96 ms | 246.40 ms | 141.44 ms |
| Jupiter Metis API | 179.83 ms | 153.17 ms | 370.01 ms | 168.11 ms |
| PumpFun SDK | 790.94 ms | 278.57 ms | 4,570.26 ms | 364.92 ms |
### Sell Transaction Build
1,000,000 tokens, 20% slippage, 20 iterations:
| Method | Avg | Min | Max | Median |
| :---------------- | :----------- | :----------- | :------------ | :----------- |
| **LaserSell API** | **74.77 ms** | **65.13 ms** | **108.67 ms** | **71.72 ms** |
| PumpPortal API | 152.08 ms | 111.91 ms | 554.96 ms | 120.59 ms |
| Jupiter Metis API | 181.73 ms | 155.88 ms | 222.54 ms | 174.92 ms |
| PumpFun SDK | 541.00 ms | 277.65 ms | 2,029.10 ms | 317.28 ms |
Over the public internet, LaserSell is consistently the fastest with the tightest variance. The PumpFun SDK shows the most volatility, with worst-case latencies exceeding 4 seconds on buys and 2 seconds on sells. These numbers are relevant for SDK and API integrators building their own tools.
## What is Being Compared
Each method takes a different approach to building a swap transaction:
| Method | Approach |
| :-------------------- | :-------------------------------------------------------------------------------------------------- |
| **LaserSell API** | Single API call. The server builds the full transaction and returns it ready to sign. |
| **PumpFun SDK** | Local build. Makes multiple RPC calls to fetch on-chain state, then assembles instructions locally. |
| **PumpPortal API** | Single API call to PumpPortal. |
| **Jupiter Metis API** | Two API calls. First a quote request, then a swap build request. |
LaserSell's single-call architecture eliminates round trips, which is why it consistently outperforms multi-step approaches.
## Why This Matters
In automated trading, transaction build latency directly affects execution quality. When a memecoin starts dumping, the difference between a 5 ms build and a 170 ms build can determine whether your exit lands in the same block as the trigger or several blocks later, after the price has already moved further against you.
For the Exit Intelligence Stream, this means your automated exits fire with minimal overhead between detection and broadcast. For SDK and API users building their own tools, it means faster round trips and more responsive applications.
## Run the Benchmarks Yourself
The benchmarks are open source. Clone the repository and run them against your own infrastructure:
```bash theme={null}
git clone https://github.com/lasersell/benchmarks
cd benchmarks
# Buy transaction build
cd buy-tx-build
npm install && npm run build
npm run bench
# Sell transaction build
cd ../sell-tx-build
npm install && npm run build
npm run bench
```
You will need a LaserSell API key (free at [app.lasersell.io](https://app.lasersell.io)), a Solana RPC URL, and a Pump.fun token mint address. See the repository README for full configuration options.
# Community
Source: https://docs.lasersell.io/community
Official LaserSell channels and social links.
| Channel | Link |
| ----------- | ---------------------------------------------------------------------------- |
| Discord | [discord.gg/lasersell](https://discord.gg/lasersell) |
| X / Twitter | [x.com/lasersellhq](https://x.com/lasersellhq) |
| Telegram | [t.me/lasersell](https://t.me/lasersell) |
| GitHub | [github.com/lasersell](https://github.com/lasersell) |
| LinkedIn | [linkedin.com/company/lasersell](https://www.linkedin.com/company/lasersell) |
# Active Positions
Source: https://docs.lasersell.io/desktop/active-positions
Monitor all your open positions in real time with live PnL, market data, and manual exit controls.
The Active Positions tab is the main screen of the app. It shows every token position across your active wallets, updated in real time as new blocks are produced on Solana.
## Position List
The left panel lists all open positions. Each entry shows the token symbol, your current PnL percentage, and how long the position has been open.
**Sort options:**
* **Time** — Newest positions first
* **P\&L** — Best performing first
* **Size** — Largest positions first
You can also search positions by token name or symbol.
When connected to the stream with no open positions, the list shows "Waiting for positions..." until your next buy is detected on-chain.
## Position Detail
Click any position in the list to open the detail view in the right panel.
### Entry and Market Info
* **Entry price and amount** in SOL or USD1
* **Current price** and **market cap**
* **Time open** (elapsed since entry)
* **Token mint address** (click to copy)
* **Market type** with DEX icon (Pump.fun, PumpSwap, Raydium, Meteora)
* **Pool liquidity** with slippage bands showing how much of your position can be exited at different slippage levels
* **Liquidity trend** indicator (stable, increasing, or decreasing)
### Real-Time PnL
* Current profit or loss in SOL/USD1 and as a percentage
* Current proceeds if you sold now
* PnL history chart
### Trade History
The trade overlay shows every transaction executed on this position: time, side (buy/sell), token amount, SOL amount, maker wallet, and a link to view the transaction on Solscan.
## Actions
### Exit Now
Triggers an immediate manual exit at market price. LaserSell requests an exit signal from the stream, constructs the transaction, signs it locally, and broadcasts it. Useful when you want to override your strategy and get out right now.
### Buy More
Increase your position size with an additional buy. Uses your configured quick buy amounts.
### Partial Sell
Sell a portion of your position instead of the full amount. You can specify either a token amount or a percentage of your holdings. This is useful for taking profits while keeping a runner.
### Per-Position Strategy Override
Expand the strategy section on any position to customize its exit thresholds independently from your global strategy. You can override:
* Target Profit %
* Stop Loss %
* Trailing Stop %
* Take-profit levels (trigger %, sell %, trailing adjustment %)
When a position has custom thresholds, it shows a **Custom** badge. Click "Reset to Global" to revert to your default strategy.
These overrides are session-scoped. They apply immediately and persist until you close the app, at which point positions revert to the global strategy.
## Sidebar
The sidebar shows your aggregate portfolio:
* **Portfolio Balance** — Combined SOL + USD1 value converted to your display currency
* **SOL Balance** — Native SOL across all active wallets
* **USD1 Balance** — USD1 stablecoin balance (when enabled in settings)
The sidebar is resizable. Drag the edge to adjust width, or collapse it to icon-only mode for more screen space.
## Mirrored Positions
Positions opened through [Mirror Trading](/desktop/mirror-trading) appear in the same list, marked with the source wallet. They follow the same exit strategy rules and can be manually exited the same way.
# Activity Log
Source: https://docs.lasersell.io/desktop/activity-log
View a real-time log of every trade, send, and receive event across your wallets.
The Activity Log records every trade and transfer that happens across your active wallets during your session.
## What Gets Logged
Every position open, close, send, and receive event appears as a row in the log. Each entry shows:
* **Time** — When the event occurred (HH:MM:SS)
* **Side** — Buy, Sell, Send, or Receive
* **Token** — Symbol and coin image
* **Wallet** — Which wallet was involved (truncated public key)
* **Reason** — For auto-exits: what triggered the sell (Target Profit, Stop Loss, Trailing Stop, Graduation, Deadline)
* **P\&L** — Profit or loss in SOL or USD1 for closed positions
The market type is shown as a small colored badge (Pump.fun, PumpSwap, Raydium, Meteora) next to each entry.
## Session Summary
At the top of the log, a summary line shows your session totals: how many buys, sells, and sends have occurred since you opened the app.
## Export
Click the export button to download your activity history as a CSV file. This is useful for record keeping, tax reporting, or analyzing your trades in a spreadsheet.
## Persistence
The activity log stores up to 500 entries locally at `~/.lasersell-app/activity.json`. Older entries are trimmed automatically when the limit is reached. The log persists across app restarts.
# Analytics & Achievements
Source: https://docs.lasersell.io/desktop/analytics
Track your trading performance with PnL charts, win rate, trade insights, and unlock achievements across 10 categories.
The Analytics page gives you a data-driven view of your trading performance and a gamified achievement system to track milestones.
> **Note:** All analytics, insights, and achievements are based exclusively on trading activity done through LaserSell. Trades executed through other tools or directly on-chain are not included.
## Overview
Select a time period (24h, 7d, 30d, or All Time) to view your key metrics:
* **Total P\&L** — Net profit or loss in your display currency
* **Total Volume** — Total trading volume in quote units
* **Win Rate** — Percentage of trades that closed in profit
* **Total Trades** — Number of closed positions
* **Avg P\&L** — Average profit or loss per trade
* **Avg Hold Time** — Average time positions were open
* **Best Trade** — Your highest single-trade profit
* **Worst Trade** — Your deepest single-trade loss
### Daily PnL Chart
A bar chart showing your daily profit and loss over the last 90 days. Green bars represent profitable days, red bars represent losing days. Hover over any bar to see the exact figure.
### Exit Reasons Breakdown
A visual breakdown of what triggered your exits:
* **Target Profit** — Strategy hit your take-profit threshold
* **Trailing Stop** — Price pulled back from peak by your trailing percentage
* **Stop Loss** — Price hit your stop-loss threshold
* **Graduation** — Token migrated from bonding curve to DEX
* **Deadline** — Position timed out
This helps you understand which exit conditions are doing the most work and whether your strategy needs tuning.
## Insights (Tier 1+)
Personalized suggestions based on patterns in your recent trades. Each insight has a severity level (high, medium, low) and a specific recommendation.
Insights are generated server-side from your trade history and update periodically. They are gated to Professional tier and above.
If you are on the free tier, you will see an upgrade prompt.
## Achievements
Track your progress across **10 categories** with rarity tiers from Common to Legendary.
### Categories
| Category | What It Tracks |
| :----------- | :--------------------------------------------- |
| Volume | Trading volume milestones |
| Trades | Number of completed trades |
| Streak | Consecutive profitable trading days |
| Profit | Cumulative profit targets |
| Speed | Fast execution times |
| Single Trade | Single-trade PnL records |
| Win Rate | Win rate percentage milestones |
| Mirror | Copy trading milestones |
| Secret | Hidden achievements (discover them by trading) |
| Mastery | Advanced combination achievements |
### Rarity Tiers
* **Common** — Entry-level milestones
* **Uncommon** — Moderate accomplishments
* **Rare** — Significant achievements
* **Epic** — Exceptional trading milestones
* **Legendary** — The rarest achievements in the system
### Streaks
Your current and longest winning streaks are displayed at the top of the Achievements tab. A streak day is counted when you have at least one profitable trade routed through the LaserSell protocol.
### How Achievements Work
Achievements are checked hourly by the server. Only trades routed through the LaserSell protocol count toward achievement progress. When you unlock a new achievement, the app sends a notification.
Unlocked achievements will be redeemable for rewards in the future. There is no LaserSell token and there will not be an airdrop.
# Exit Strategy
Source: https://docs.lasersell.io/desktop/exit-strategy
Configure automated exit rules including take profit, stop loss, trailing stop, exit ladders, liquidity guard, and more.
The Exit Strategy page is where you define the rules that determine when LaserSell sells your positions. All conditions run in parallel on every price update. Whichever triggers first executes the exit.
## Basic Exit Conditions
These are available on all tiers. Each condition has a toggle to enable or disable it and a value to configure.
### Target Profit
When your position's profit reaches this percentage, LaserSell sells. For example, a 25% target profit means the app exits when your position is worth 125% of your entry.
### Stop Loss
When your position's loss reaches this percentage, LaserSell exits to protect your remaining capital. A 15% stop loss means the app sells if the value drops to 85% of your entry.
A 10% loss is recoverable. A 99% loss is a blown account. Stop losses are your first line of defense.
### Trailing Stop
Instead of exiting at a fixed profit target, the trailing stop tracks the peak profit of your position. If the price reverses and drops by the configured percentage from that peak, LaserSell triggers the exit.
For example, if a token pumps 50% and your trailing stop is set to 5%:
* LaserSell holds through the pump.
* If the price drops from +50% to +45% (a 5% drawdown from peak), the exit fires.
* You capture 45% profit instead of exiting at a fixed 25%.
This lets winning trades run while still providing automatic downside protection on reversals.
### Sell on Graduation
Automatically exit when a token migrates from a bonding curve to a DEX (for example, from Pump.fun to PumpSwap). Many tokens experience a sharp sell-off immediately after graduation, so this acts as an early exit before the post-migration dump.
### Deadline Timeout
Force an exit after a set number of seconds regardless of PnL. This is a safety net for positions that go sideways and tie up capital that could be deployed elsewhere.
## Exit Ladder (Tier 1+)
The exit ladder replaces the single take-profit target with a multi-level staged sell. Instead of exiting 100% of your position at one price, you lock in profits progressively as the price rises.
You can configure up to **10 levels**. Each level defines:
* **Profit Trigger %** — The profit threshold that activates this level.
* **Sell %** — The percentage of your remaining position to sell at this level.
* **Trailing Adj %** — An optional trailing stop adjustment applied to the remainder after this level fires.
**Example (3-level ladder):**
| Level | Trigger | Sell | Effect |
| :---- | :------ | :--- | :---------------------------------------- |
| 1 | +25% | 30% | Sell 30% of position, lock in early gains |
| 2 | +50% | 40% | Sell 40% of remainder, capture mid-run |
| 3 | +100% | 100% | Sell everything that is left |
Profit triggers must be in ascending order, and the total sell percentage across all levels cannot exceed 100%.
### Liquidity Guard
When enabled, the liquidity guard automatically scales down sells if pool liquidity has thinned. If a level tries to sell more than the pool can absorb at reasonable slippage, the guard reduces the sell amount and redistributes the unsold portion to the next level. This prevents large exits from crashing the price of your own position.
### Breakeven Trail (Tier 1)
Once your profit hits a configurable threshold, the breakeven trail moves your stop loss to your entry price. From that point forward, the position cannot lose money. It either exits at profit or at breakeven.
## Strategy Changes
Changes to your strategy are auto-saved and pushed to the LaserSell stream immediately. There is no "deploy" step. Any open positions begin using the updated strategy on the next price update.
## Per-Position Overrides
You can override the global strategy for individual positions from the [Active Positions](/desktop/active-positions) detail view. See [Per-Position Strategy Override](/desktop/active-positions#per-position-strategy-override) for details.
# Installation
Source: https://docs.lasersell.io/desktop/installation
Download and install the LaserSell Desktop app on macOS.
## System Requirements
* **macOS 10.15** (Catalina) or later
* Both Apple Silicon (M1/M2/M3/M4) and Intel Macs are supported
* An internet connection for real-time position monitoring
## Download
Download the latest version from [lasersell.io](https://www.lasersell.io).
The app is code-signed and notarized by Apple.
## Auto-Updates
LaserSell Desktop checks for updates automatically. When a new version is available, you will see a prompt in the app. Updates are signed and verified before installation.
You can also check manually from the app menu: **LaserSell > Check for Updates**.
## Uninstalling
To uninstall, drag the app to Trash. If you also want to remove your local data (wallets, config, activity log), delete the `~/.lasersell-app/` directory:
```bash theme={null}
rm -rf ~/.lasersell-app
```
This permanently deletes your encrypted wallet keystores. Make sure you have backed up your private keys before removing this directory.
## Next Step
Once installed, open the app and follow the [onboarding guide](/desktop/onboarding) to sign in and set up your first wallet.
# Mirror Trading
Source: https://docs.lasersell.io/desktop/mirror-trading
Copy-trade from external wallets by watching their on-chain activity and automatically mirroring their buys.
Mirror Trading lets you watch external Solana wallets and automatically copy their trades. When a watched wallet buys a token on a supported market, LaserSell buys the same token from your wallet using your configured buy amount. Your exit strategy then manages the position automatically, just like any other trade.
## Adding a Watch Wallet
Enter a Solana wallet address or a `.sol` domain (SNS) to start watching. The app resolves SNS domains automatically, so you can type `alice.sol` instead of a full public key.
Each watch wallet has its own configuration:
* **Buy Amount (SOL)** — How much SOL to spend when mirroring a buy.
* **Buy Amount (USD1)** — How much USD1 to spend (when USD1 mirroring is enabled in settings).
* **Nickname** — An optional label for easy identification. Click the pencil icon to edit inline.
* **Enabled/Disabled** — Toggle to pause or resume mirroring without removing the wallet.
## Watch Wallet Limits
Your account tier determines how many wallets you can watch:
| Tier | Watch Wallet Slots |
| :-------------- | :----------------- |
| Personal (Free) | 2 |
| Professional | 10 |
| Advanced | 25 |
## How Mirroring Works
1. A watched wallet buys a token on a supported market.
2. LaserSell detects the buy on-chain.
3. The app automatically buys the same token from your wallet using the configured buy amount for that watch wallet.
4. Your exit strategy (take profit, stop loss, trailing stop, etc.) applies to the mirrored position.
5. LaserSell exits the position automatically when conditions are met.
Mirrored positions appear in the [Active Positions](/desktop/active-positions) dashboard alongside your own trades. They are marked with the source wallet so you can tell which positions came from mirroring.
## Mirror Stats
Each watch wallet shows performance statistics:
* Total mirrored trades
* Win rate
* Best and worst trade
* Average hold time
* Total profit
There is also a global summary across all watched wallets.
## Safety Settings
Mirror trading includes a set of hardening controls that let you limit risk exposure. Open them from the gear icon in the Mirror tab. All settings apply globally across every watched wallet in the session.
### Max Positions Per Wallet
The maximum number of concurrent mirrored positions allowed per watched wallet. When a watched wallet triggers a new buy signal but already has this many open positions, the signal is skipped. Default: **1**.
### Cooldown Between Buys
A minimum waiting period (in seconds) after each mirror buy before the next buy from the same watched wallet can execute. This prevents rapid fire entries when a wallet makes multiple trades in quick succession. Default: **30 seconds**.
### Max Active SOL Exposure
The total SOL value that can be allocated across all active mirrored positions at any given time. If a new mirror buy would push total exposure above this cap, the signal is skipped. Default: **5.0 SOL**.
### Buy Slippage
The maximum slippage tolerance for mirror buy transactions, expressed in basis points. A value of 2500 means up to 25% slippage is allowed. You may want to lower this for high liquidity tokens or raise it for newly launched tokens where price moves quickly. Default: **2500 bps (25%)**.
### Skip Creator Tokens
When enabled, the stream ignores buy signals where the watched wallet is the token creator. This helps avoid situations where a watched trader launches a token and buys their own supply. Default: **off**.
### Minimum Pool Liquidity
Sets a floor on pool liquidity (in SOL) required before a mirror buy will execute. If the token's pool has less than this amount of liquidity, the signal is skipped and you receive a `mirror_buy_failed` event. Useful for filtering out extremely thin markets. Default: **disabled**.
### Max Entry Drift
The maximum percentage that the current token price can differ from the watched wallet's entry price before the mirror buy is skipped. For example, setting this to 5 means that if the price has already moved more than 5% since the watched wallet bought, your mirror buy will not execute. This protects you from entering at a significantly worse price. Default: **disabled**.
### Consecutive Loss Auto Disable
After a watched wallet produces this many consecutive losing trades, the wallet is automatically disabled and you receive a `mirror_wallet_auto_disabled` notification. You can re-enable the wallet manually at any time from the Mirror tab. Default: **disabled**.
## Watched Position Behavior
Positions opened through mirroring follow the same exit rules as your own positions. You can manually exit them, apply per-position strategy overrides, or use partial sells. The only difference is that watched positions do not count toward trade streak achievements.
# Onboarding
Source: https://docs.lasersell.io/desktop/onboarding
Sign in, set up your wallet, configure your initial exit strategy, and connect to the LaserSell stream.
When you open LaserSell Desktop for the first time, the onboarding wizard walks you through five steps. The whole process takes about two minutes.
## Step 1: Sign In
Sign in with your LaserSell account to get an API key. You have two options:
* **Browser sign-in:** Click "Sign In" and authenticate in your browser. The app receives your API key automatically via a deep link.
* **Manual paste:** If you already have an API key, paste it directly.
If you do not have an account yet, click "Sign Up" to create one. Accounts are free and instant.
## Step 2: Wallet Setup
You need at least one Solana wallet for LaserSell to monitor. Choose one:
* **Import an existing wallet.** Paste your Base58 private key. This is useful if you already have a funded wallet you trade with.
* **Generate a new wallet.** LaserSell creates a fresh keypair locally. You will need to fund it with SOL before trading.
Either way, you will set an encryption passphrase (8+ characters). This passphrase encrypts your private key on disk using XChaCha20Poly1305. LaserSell never stores or transmits your keys in plaintext.
You can add more wallets later from the [Wallet > Manage](/desktop/wallet) tab. Your account tier determines how many wallets you can use simultaneously.
## Step 3: Initial Strategy
Set your starting exit strategy:
* **Target Profit %** — The profit threshold where LaserSell auto-sells (default: 25%).
* **Stop Loss %** — The loss threshold where LaserSell cuts the position (default: 15%).
These are starting values. You can configure trailing stops, deadlines, sell-on-graduation, exit ladders, and more from the [Exit Strategy](/desktop/exit-strategy) page after onboarding.
## Step 4: RPC URL
Optionally set a custom Solana RPC endpoint. If you have a dedicated RPC from Helius, QuickNode, Triton, or another provider, paste the URL here for faster and more reliable transaction delivery.
If you skip this step, the app uses a default public endpoint. You can change it later in [Settings > Connection](/desktop/settings).
## Step 5: Crash Reporting
Choose whether to send anonymous crash reports. This helps us find and fix bugs. No wallet data, keys, or trade information is ever included. You can change this at any time in Settings.
## Unlocking
After onboarding, you will see the unlock screen on subsequent launches. Enter your wallet passphrase to decrypt your keys and start monitoring.
If you have multiple wallets with different passphrases, you can unlock them individually or skip wallets you do not need for this session.
On macOS, you can enable **Touch ID** in [Settings > Security](/desktop/security) for faster unlocking.
## What Happens Next
Once unlocked, the app connects to the LaserSell Exit Intelligence stream and begins monitoring your wallets. Any tokens you hold (or buy from this point forward) will appear in the [Active Positions](/desktop/active-positions) dashboard with real-time PnL tracking.
# Desktop App
Source: https://docs.lasersell.io/desktop/overview
LaserSell Desktop is a native macOS trading application that monitors your Solana positions in real time and executes automated exits the moment your conditions are met.
LaserSell Desktop is a native macOS application for traders who want full local control. It connects directly to the LaserSell stream, monitors every position across your wallets in real time, and executes sells automatically when your take profit, stop loss, trailing stop, deadline, or sell-on-graduation conditions are met. Your keys stay on your machine and you sign every transaction locally.
No browser tabs to manage. No wallet popups to approve. You set your strategy, buy tokens wherever you normally buy them, and let the app handle the rest.
## What the Desktop App Does
1. **Detects your buys automatically.** The moment you buy a token on any platform, the app picks it up and starts monitoring.
2. **Evaluates your exit strategy on every price update.** Take profit, stop loss, trailing stop, deadline timeout, and sell-on-graduation all run in parallel. Whichever triggers first executes the exit.
3. **Sells automatically.** The moment your exit condition is triggered, LaserSell builds the transaction in under 5 ms, signs it locally with your encrypted keys, and broadcasts it directly to the Solana network. No human input required once the strategy is set. That is over 12x faster than the next closest method (see [benchmarks](/benchmarks/tx-build-latency)).
4. **Keeps your keys safe.** Every wallet is encrypted on your machine with a passphrase only you know. LaserSell never has access to your private keys.
## Key Features
* **Active Positions Dashboard** with real time PnL, market data, and one-click manual exits
* **Exit Strategy Configuration** with single-level or multi-level take profit ladders, trailing stops, and liquidity guards
* **Mirror Trading** to copy-trade from external wallets with per-wallet buy amounts and performance tracking
* **Built-in Wallet** with send, receive, swap, and multi-wallet management
* **Analytics** with daily PnL charts, win rate, exit reason breakdowns, and personalized trade insights
* **Achievements** across 10 categories with rarity tiers
* **Touch ID** for fast wallet unlock on macOS
* **7 Languages** (English, Chinese, Japanese, Korean, Spanish, Russian, Arabic)
## Supported Markets
The app monitors and auto-sells on all major Solana launchpads and DEXs:
* Pump.fun and PumpSwap
* Meteora DBC and DAMM V2
* Raydium Launchpad and CPMM
Tokens on unsupported markets can still be swapped manually through the built-in Jupiter integration.
## Getting Started
1. [Download and install](/desktop/installation) the app.
2. [Sign in and set up your wallet](/desktop/onboarding).
3. [Configure your exit strategy](/desktop/exit-strategy).
4. Buy a token on any platform. LaserSell handles the exit.
## For Developers
If you want to build your own application on top of LaserSell instead of using the desktop app, the same Exit Intelligence engine is available through the [LaserSell SDK and API](/api/overview). The desktop app is built on it.
# Security
Source: https://docs.lasersell.io/desktop/security
How LaserSell Desktop protects your wallets with local encryption, Touch ID, and auto-lock.
LaserSell Desktop is non-custodial. Your private keys are encrypted on your machine and never leave your device. Here is how the security model works.
## Wallet Encryption
Every wallet is encrypted at rest using **XChaCha20-Poly1305** with a key derived from your passphrase via **Argon2id** (memory-hard KDF). Each wallet has its own random salt.
The encrypted keystore files are stored at `~/.lasersell-app/wallets/`. Even if someone gains access to your file system, they cannot read your keys without your passphrase.
You can use different passphrases for different wallets. The app prompts for each wallet's passphrase on unlock.
## Touch ID
On macOS, you can enable Touch ID for faster wallet unlocking. When enabled, your passphrase is stored securely in the macOS Keychain and retrieved via biometric authentication on each unlock.
To enable: **Settings > Security > Touch ID Unlock**. You will be asked to enter your passphrase once to store it in the Keychain.
To disable: Toggle the setting off. Your passphrase is removed from the Keychain and you will return to manual passphrase entry.
## Auto-Lock
The app automatically locks your wallets after a period of inactivity. This clears all decrypted keys from memory and pauses monitoring until you unlock again.
**Options:** 15 minutes, 30 minutes, 45 minutes, 1 hour, 2 hours, 6 hours, or Disabled.
The inactivity timer resets on any mouse movement, keyboard input, or click. You can also lock manually at any time from **Settings > Security > Lock LaserSell**.
## Transaction Signing
All transactions are signed locally on your device. LaserSell constructs the transaction, your local keypair signs it, and the signed transaction is broadcast directly to the Solana network via your RPC endpoint. No intermediary ever touches your private key.
## What We Never Do
* **Never transmit your keys.** Private keys and seed phrases are never sent over the network.
* **Never store keys in plaintext.** The keystore is always encrypted on disk.
* **Never back up to the cloud.** There is no cloud sync, no remote backup, and no "forgot passphrase" recovery. If you lose your passphrase and have not backed up your key, the wallet is unrecoverable.
* **Never retain custody.** The LaserSell router program is a pass-through. Your funds move directly from your wallet to the DEX and back.
Back up your private keys. You can do this from [Wallet > Manage](/desktop/wallet). If you lose access to your machine and your passphrase, there is no recovery path. LaserSell cannot help you recover lost keys.
# Settings
Source: https://docs.lasersell.io/desktop/settings
Configure your connection, display preferences, trading parameters, security, language, and more.
The Settings page lets you customize every aspect of the app. Changes are saved automatically.
## Connection & Account
* **Connection Status** — Shows whether the app is connected to the LaserSell stream (green = connected, red = disconnected).
* **Account Tier** — Your current subscription: Personal (Free), Professional, or Advanced.
* **Pause Monitoring** — Temporarily stop all stream monitoring and auto-sells without disconnecting. Useful when you want to hold positions manually.
## Network
### API Key
Your LaserSell API key. Masked by default with a toggle to reveal, and a copy button. If you need a new key, use the sign-in flow to generate one.
### RPC URL
Your Solana RPC endpoint. If you have a dedicated RPC from a provider like Helius, QuickNode, or Triton, set it here for faster and more reliable transactions. The default is a public endpoint.
### Send Mode
How LaserSell submits transactions to the network:
* **RPC** (default) — Direct submission through your RPC endpoint. No additional fees.
* **Helius Sender** — Optimized transaction delivery through Helius for better landing rates. Requires a Helius API key and a builder tip (recommended: 0.001 SOL).
* **Astralane Iris** — MEV-protected transaction routing. Requires an Astralane API key and region selection (Frankfurt, Amsterdam, New York, San Francisco, Tokyo, Singapore, and more).
## Display
* **Currency** — Choose your display currency for portfolio values and PnL. Supports 20 currencies across Americas, Europe, Asia Pacific, and Middle East.
* **Show USD1** — Display your USD1 stablecoin balance in the sidebar.
* **Mirror USD1 Trading** — Enable USD1 denominated copy trading.
* **Menu Bar Icon** — Show LaserSell in the macOS menu bar for quick access.
* **Notification Sound** — Play a coin sound when trades execute.
* **Connect on Startup** — Automatically connect to the stream when the app launches. Disable this if you want to use the app as a standalone wallet without stream monitoring.
## Trading
* **Quick Buy Amounts (SOL)** — Preset SOL amounts for the quick buy buttons (e.g., 0.1, 0.5, 1.0 SOL).
* **Quick Buy Amounts (USD1)** — Preset USD1 amounts (shown when USD1 buying is enabled).
* **Quick Sell Percentages** — Preset sell percentages for partial sells (e.g., 10%, 25%, 50%, 100%).
* **Slippage Tolerance** — Maximum allowed price slippage on trades, in basis points.
## Security
See [Security](/desktop/security) for details on Touch ID, auto-lock, and encryption.
## Language & International Support
LaserSell is built for traders everywhere. The entire app interface is fully localized in 7 languages, and the layout adapts automatically to each one.
**Supported languages:**
* English
* Chinese (中文)
* Japanese (日本語)
* Korean (한국어)
* Spanish (Español)
* Russian (Русский)
* Arabic (العربية)
Switching languages is instant. Every screen, every label, every notification updates immediately with no restart required.
### Right-to-Left Support
LaserSell fully supports right-to-left (RTL) layouts for Arabic. When you switch to Arabic, the entire interface mirrors: navigation moves to the right, text aligns right-to-left, and every component reflows naturally. This is not a simple text swap. The entire layout, including the sidebar, position lists, strategy panels, wallet views, and analytics charts, is rebuilt for RTL reading order. Arabic speaking traders get the same polished, native experience as every other language.
### Multi-Currency Display
Your portfolio values, PnL, and trade amounts can be displayed in any of 20 currencies:
* **Americas** — USD, CAD
* **Europe** — EUR, GBP, CHF
* **Asia Pacific** — JPY, CNY, KRW, INR, SGD, HKD, AUD, NZD
* **Middle East & Africa** — AED, SAR, KWD, BHD, QAR, OMR, EGP
All values update in real time as exchange rates change. Choose your local currency and see your performance in the numbers that matter to you.
## Privacy
* **Crash Reports** — Toggle anonymous error reporting via Sentry. No wallet data or trade information is included.
## About
* **Version** — The current app version.
* **Platform** — Your operating system.
* **Get Help** — Link to support resources.
* **Copy Error Report** — Formats recent errors for sharing with support.
* **Logout** — Clears your API key and returns to the sign-in screen.
# Wallet
Source: https://docs.lasersell.io/desktop/wallet
Manage multiple wallets, view token holdings, send and receive SOL and SPL tokens, and swap through Jupiter.
The Wallet page is your hub for managing wallets, viewing holdings, and executing manual transactions. It has five tabs: Overview, Send, Receive, Swap, and Manage.
## Overview
The Overview tab shows all tokens held across your active wallets.
**Each token displays:**
* Symbol and logo
* Current price
* 24-hour price change
* Your balance and its value in your display currency
**Filters:**
* **All** — Every token in your wallets
* **Armed** — Tokens actively monitored by the LaserSell stream
* **Wallet** — Filter by specific wallet
Tokens on supported markets show an **Armed** badge when LaserSell is monitoring them. Tokens on unsupported markets display a note that auto-sell is not available, with the option to swap manually.
### Token Search
Search for any Solana token by name, symbol, or mint address. Results show price, market info, and whether the token is on a market LaserSell supports. You can jump directly to the Swap tab with a token pre-selected.
## Send
Transfer SOL or any SPL token to another wallet.
1. Select the wallet to send from.
2. Enter the recipient address (Solana public key or `.sol` domain).
3. Choose the asset (SOL or any SPL token in your wallet).
4. Enter the amount (or click Max).
5. Review the transaction details.
6. Confirm to sign and broadcast.
After a successful send, you will see a confirmation with a link to view the transaction on Solscan.
## Receive
Displays your wallet address as both text (with a copy button) and a QR code. Use this to receive SOL or SPL tokens from another wallet on Solana mainnet.
If you have multiple wallets, select which one to display using the wallet selector at the top.
## Swap
Swap between any Solana tokens using Jupiter routing.
1. Select the wallet to swap from.
2. Choose the input token and enter the amount.
3. Choose the output token (search by name, symbol, or mint).
4. Review the quote: output amount, price impact, route, and slippage.
5. Confirm to sign and execute.
The swap interface fetches live quotes from Jupiter and auto-updates as you type (debounced for performance).
**Unsupported market disclaimer:** If you swap into a token on a market LaserSell does not monitor yet, you will see a note: auto-sell will not be available for that token. You can still hold and sell it manually.
## Manage
Add, back up, and remove wallets.
### Adding a Wallet
* **Import Existing** — Paste a Base58 private key. The key is encrypted immediately with your chosen passphrase.
* **Generate New** — Creates a fresh keypair locally. Fund it with SOL to start trading.
Each wallet is encrypted independently. You can use different passphrases per wallet.
### Backing Up
Click the backup button on any wallet, enter your passphrase, and your private key is revealed. Copy it and store it securely offline. This is the only way to recover your wallet if you lose access to your machine.
Never share your private key. LaserSell will never ask for it outside of the import flow.
### Removing a Wallet
Removal is a two-step process. The app prompts you to back up your key first, then confirms permanent deletion. Once deleted, the encrypted keystore file is removed from disk and cannot be recovered.
### Wallet Limits
| Tier | Active Wallets |
| :-------------- | :------------- |
| Personal (Free) | 1 |
| Professional | 5 |
| Advanced | 200 |
# Achievements
Source: https://docs.lasersell.io/features/achievements
Track your trading milestones across 10 categories with rarity tiers from Common to Legendary.
LaserSell tracks your trading milestones and awards achievements as you hit them. There are 85+ achievements across 10 categories, ranging from Common milestones anyone can hit to Legendary achievements that require exceptional performance.
Unlocked achievements will be redeemable for rewards unlike anything offered to traders before. There is no LaserSell token and there will not be an airdrop.
## How Achievements Work
Achievements are checked automatically on an hourly cycle. Only trades routed through the LaserSell protocol count toward achievement progress. When you unlock a new achievement, the app sends a notification.
Hidden achievements do not reveal their conditions until you unlock them.
## Rarity Tiers
| Rarity | Description |
| :------------ | :------------------------------------------------------------------------------------------------------------------------------------- |
| **Common** | Entry-level milestones. You will unlock these naturally as you start trading. |
| **Uncommon** | Moderate accomplishments that require sustained trading activity. |
| **Rare** | Significant achievements that take real commitment or skill. |
| **Epic** | Exceptional milestones reached by a small percentage of traders. |
| **Legendary** | The rarest achievements in the system. These require extraordinary volume, consistency, or a single moment of exceptional performance. |
## Categories
### Volume
Cumulative trading volume milestones. The more you trade through LaserSell, the higher you climb.
### Trades
Total completed trade milestones, from your first trade all the way up to the thousands.
### Streak
Consecutive trading day milestones. A streak day is counted when you complete at least one trade routed through LaserSell. Missing a day resets your current streak.
Your current streak and longest streak are displayed at the top of the Achievements tab in the app.
### Profit
Cumulative realized profit milestones in SOL.
### Speed
Achievements for profitable trades completed in short timeframes. These reward fast execution and quick exits.
### Single Trade
Achievements based on the performance of a single position, from solid returns to extraordinary multipliers.
### Win Rate
Win rate milestones with minimum trade requirements to prevent gaming. Sustaining a high win rate over hundreds of trades is one of the hardest things to do.
### Mirror
Achievements for [Mirror Trading](/desktop/mirror-trading). Tracks your copy trading activity and performance across watched wallets.
### Secret
Hidden achievements with conditions that are not revealed until you unlock them. You will discover these naturally as you trade.
### Mastery
Meta-achievements awarded for completing every achievement in a category. There is one Mastery achievement for each of the other categories. Completing every Secret achievement earns the only Legendary-tier Mastery.
## Streaks
Your trading streak tracks consecutive days with at least one completed trade routed through LaserSell.
* **Current Streak** — How many consecutive days you have traded, counting today.
* **Longest Streak** — Your all-time record.
* **Last Trade Date** — When your most recent trade was recorded.
Missing a single day resets your current streak to zero. Your longest streak is preserved permanently.
## API Access
Achievements are available programmatically:
```
GET /v1/achievements
```
Returns all achievements with their unlock status and timestamps. Locked achievements in the Secret category return masked descriptions.
# Analytics
Source: https://docs.lasersell.io/features/analytics
Track your trading performance across every time period with PnL charts, win rate, volume, hold times, and exit reason breakdowns.
LaserSell tracks every trade you make through the protocol and turns it into actionable performance data. The Analytics dashboard is available in the [Desktop App](/desktop/analytics) and through the API.
> **Note:** All analytics, insights, and achievements are based exclusively on trading activity done through LaserSell. Trades executed through other tools or directly on-chain are not included.
## Time Periods
All metrics can be viewed across four time periods:
* **24 Hours** — Today's performance
* **7 Days** — Weekly snapshot
* **30 Days** — Monthly overview
* **All Time** — Your complete trading history
Each period computes its own independent set of metrics, so you can compare how your trading has evolved over time.
## Key Metrics
### Total P\&L
Your net realized profit or loss across all closed positions in the selected period. This is the sum of every trade's final profit, displayed in your chosen display currency.
### Total Volume
The sum of all entry amounts (the SOL or USD1 you spent buying tokens) across the period. This measures how much capital you have deployed, regardless of outcome.
### Win Rate
The percentage of your closed trades that ended in profit.
```
Win Rate = Winning Trades / (Winning Trades + Losing Trades) × 100
```
Only closed positions count. Open positions are excluded from the win rate calculation.
### Total Trades
The number of positions that closed during the selected period.
### Average P\&L
Your average profit or loss per closed trade. This tells you whether your typical trade is profitable and by how much.
### Average Hold Time
How long you hold positions on average before they are closed, whether by your exit strategy or manually. Displayed in hours and minutes.
Short average hold times usually indicate fast-moving memecoin trading. Longer hold times may indicate you are trading larger-cap tokens or using wider trailing stops.
### Best Trade
The single trade with the highest realized profit in the period. Useful for understanding your upside potential and whether your strategy lets winners run far enough.
### Worst Trade
The single trade with the deepest realized loss in the period. If this number is disproportionately large compared to your average P\&L, your stop loss may need tightening.
## Daily PnL Chart
A 90-day bar chart showing your daily net profit or loss. Green bars are profitable days, red bars are losing days. Hover over any bar to see the exact figure and trade count for that day.
This chart helps you spot patterns: are you consistently profitable, or do a few big days carry the average? Are your losing days getting smaller over time?
## Exit Reason Breakdown
A visual breakdown of what triggered your exits during the selected period:
| Reason | What It Means |
| :---------------- | :---------------------------------------------------------- |
| **Target Profit** | Your take-profit threshold was reached |
| **Trailing Stop** | Price pulled back from its peak by your trailing percentage |
| **Stop Loss** | Price hit your stop-loss threshold |
| **Graduation** | Token migrated from a bonding curve to a DEX |
| **Deadline** | Position timed out after your configured deadline |
This breakdown is one of the most useful tools for tuning your strategy. For example:
* If most exits are **Stop Loss**, your stop may be too tight, or you may be entering at bad times.
* If most exits are **Deadline**, your positions may be going sideways. Consider whether the tokens you are trading have enough volatility for your strategy.
* If **Trailing Stop** dominates and your P\&L is healthy, your strategy is likely working well, capturing runs and exiting on pullbacks.
* If **Target Profit** fires frequently but your best trades are much higher than your target, you may be leaving money on the table. Consider widening your target or switching to a trailing stop.
## API Access
Analytics are available programmatically through the LaserSell API:
```
GET /v1/stats
```
Returns all four time periods with every metric listed above. See the [API documentation](/api/exit-api/history) for response format details.
# Insights
Source: https://docs.lasersell.io/features/insights
Personalized trade analysis that detects behavioral patterns, strategy misconfigurations, and missed opportunities across your trading history.
Every trader has blind spots. Insights find yours.
LaserSell analyzes your trading history and generates insights that are personally tailored to you. These are not generic tips or one-size-fits-all advice. They are specific observations drawn from your actual trades, your patterns, and your strategy configuration.
Insights are available to Professional tier and above.
## How Insights Work
LaserSell studies your closed trades over a configurable period (24 hours, 7 days, 30 days, 90 days, or all time) and looks for patterns that may be helping or hurting your performance. When something worth surfacing is found, you receive an insight with a severity level and a suggestion for what to change.
Insights update as new trades close. They are not static. They evolve with your trading.
## Severity Levels
Each insight is tagged with a severity that indicates how much it may be affecting your performance:
* **High** — Significant impact on your P\&L. Address these first.
* **Medium** — Noticeable pattern that could be improved.
* **Low** — Minor observation or positive trend.
## What to Expect
Insights cover a wide range of your trading behavior. They might tell you that a habit you did not notice is costing you money, that a setting in your strategy is not calibrated for how you actually trade, that you perform better at certain times or on certain markets, or that your overall trajectory is heading in the wrong direction.
Some insights are encouraging. Most are constructive. All of them are based on what your data actually shows, not assumptions about how you should be trading.
## API Access
Insights are available programmatically:
```
GET /v1/insights?period=7d
```
Supported periods: `24h`, `7d`, `30d`, `90d`, `all`. Requires Professional tier or above. Free tier returns an empty response with `capped: true`.
# Changelog
Source: https://docs.lasersell.io/getting-support/changelog
Stay up to date with a complete timeline of version releases, feature improvements, and critical fixes.
Welcome to the official record of the **LaserSell** ecosystem's evolution.
This page serves as a comprehensive archive of all version releases, feature updates, and critical patches. While this log is updated with every major release, we broadcast updates in real-time through our community channels.
### GitHub Releases
Full changelogs for every release are published on GitHub:
* **LaserSell (Client):** [github.com/lasersell/lasersell/releases](https://github.com/lasersell/lasersell/releases)
* **LaserSell SDK:** [github.com/lasersell/lasersell-sdk/releases](https://github.com/lasersell/lasersell-sdk/releases)
### Real-Time Announcements
To get notified immediately when a new version or patch goes live, join our announcement channels:
* **Telegram:** [t.me/lasersell](https://t.me/lasersell)
* **Discord:** [discord.gg/lasersell](https://discord.gg/lasersell) (Check the `#announcements` and `#github` channels)
# Report A Bug
Source: https://docs.lasersell.io/getting-support/report-a-bug
How to report software bugs and security vulnerabilities to the LaserSell team.
## Security vulnerabilities
If you discover a security vulnerability in any LaserSell product, please report it responsibly:
* **Email:** [security@lasersell.io](mailto:security@lasersell.io)
* **PGP:** We encourage encrypting your report with our [PGP key](https://www.lasersell.io/lasersell-pgp-key.asc). Full disclosure guidelines are at [www.lasersell.io/.well-known/security.txt](https://www.lasersell.io/.well-known/security.txt).
Do **not** open a public GitHub issue for security vulnerabilities.
## Software bugs
For bugs in LaserSell or the LaserSell SDK, use one of these channels:
* **GitHub Issues:**
* [lasersell/lasersell](https://github.com/lasersell/lasersell/issues) (Client)
* [lasersell/lasersell-sdk](https://github.com/lasersell/lasersell-sdk/issues) (SDK)
* **Discord Ticket:** Open a support ticket in [discord.gg/lasersell](https://discord.gg/lasersell)
When reporting, include as much detail as possible: steps to reproduce, error messages, your OS/platform, and the version (`lasersell --version`).
### Resolution process
We aim to triage and fix confirmed bugs promptly.
* **Notification:** Once a fix is implemented, we will notify you so you can pull the latest update.
* **Updates:** Check the [LaserSell Announcements](https://t.me/lasersell) Telegram channel or the `#announcements` Discord channel to verify the fix is included in your build.
# Support
Source: https://docs.lasersell.io/getting-support/support
How to get help from the LaserSell team.
## Official Support
The best way to get official support is through Whop or email:
Visit our Whop page and click **Contact Support** for direct help from the team.
**[support@lasersell.io](mailto:support@lasersell.io)** — for bug reports, account questions, or any inquiries.
## Community
Join the Discord for community discussion, tips, and general questions. Discord is not official support but the community is active and helpful.
**[discord.gg/lasersell](https://discord.gg/lasersell)**
## Enterprise
For volume pricing, custom integrations, or partnership inquiries:
**[sales@lasersell.io](mailto:sales@lasersell.io)**
# Glossary
Source: https://docs.lasersell.io/glossary
Definitions of key terms, acronyms, and concepts used throughout the LaserSell documentation.
## A
**AMM (Automated Market Maker)**
A decentralized exchange protocol that uses mathematical formulas to determine token prices based on pool reserves, enabling token swaps without order books. PumpSwap, Meteora DAMM V2, and Raydium CPMM are examples of AMMs supported by LaserSell.
**Argon2id**
A memory-hard password hashing algorithm used by LaserSell to derive encryption keys from your passphrase. Makes brute-force attacks against your keystore computationally expensive.
**Associated Token Account (ATA)**
A derived on-chain account that holds a specific SPL token for a given wallet address. Created automatically when a wallet first receives a token.
**Astralane Iris**
A transaction submission gateway that supports regional endpoints for latency optimization. One of the available [send targets](/api/transactions/send-targets) in the LaserSell SDKs. Requires an Astralane API key.
**Atomic Units**
The smallest indivisible unit of a token. For SOL, the atomic unit is a lamport (1 SOL = 1,000,000,000 lamports). Other tokens define their own decimal precision.
## B
**Balance Update**
A server event (`balance_update`) sent by the Exit Intelligence Stream when your wallet's SOL or token balance changes. See [Server Events](/api/stream/server-events).
**Basis Points (BPS)**
A unit equal to 1/100th of a percent. 100 bps = 1%. Used throughout the LaserSell API for slippage tolerance and partner fee configuration.
**Bonding Curve**
A mathematical formula used by launchpads like Pump.fun to determine a token's price based on its circulating supply. Tokens on a bonding curve have not yet migrated to an AMM pool.
## C
**Cold Storage**
Offline wallet storage (such as a hardware wallet) used for long-term holdings. Sweep profits from your hot wallet to cold storage regularly.
**Configure Message**
The first client message sent after connecting to the Exit Intelligence Stream. Specifies your wallet addresses and strategy parameters. The server responds with a `configured` event. See [Client Messages](/api/stream/client-messages).
## D
**Deadline Timeout**
A time limit (in seconds) that forces an automatic exit if no other strategy condition (target profit, stop loss, trailing stop) fires within the configured duration. Prevents capital from being locked in stagnant positions.
## E
**Exit Intelligence**
LaserSell's core technology. A real-time system that monitors wallet positions, evaluates strategy conditions on every price update, and delivers pre-built unsigned exit transactions when thresholds are met.
**Exit Intelligence Stream**
A persistent WebSocket connection to `wss://stream.lasersell.io/v1/ws` that provides real-time position tracking, PnL updates, and automated exit signals. See [Stream Overview](/api/stream/overview).
**Exit Signal**
A server event (`exit_signal_with_tx`) containing a pre-built unsigned sell transaction, fired when a strategy condition is met. The most important event in the Exit Intelligence Stream. See [Server Events](/api/stream/server-events).
**ExitApiClient**
The SDK client for making REST API calls to build buy and sell transactions on demand via `/v1/buy` and `/v1/sell`.
## G
**Graduation**
The moment when a token migrates from a bonding curve (e.g. Pump.fun) to an AMM pool (e.g. PumpSwap). Price dynamics often shift significantly at graduation.
## H
**`hello_ok`**
The first server message received after connecting to the Exit Intelligence Stream. Contains your session ID and tier rate limits. See [Connection Lifecycle](/api/stream/connection-lifecycle).
**Helius Sender**
A transaction submission service that routes through the Helius `/fast` endpoint for optimized transaction landing on Solana. No API key required. The default [send target](/api/transactions/send-targets) for LaserSell.
**Hot Wallet**
Any wallet connected to the internet for active trading. LaserSell creates a local hot wallet on your machine for fast trade execution.
## J
**Jito Tip**
A direct payment to Solana validators to prioritize your transaction above others in the same block. This is a network cost, not a LaserSell fee.
## K
**Keystore**
An encrypted file format used by the LaserSell client to store your private keys locally. Uses XChaCha20-Poly1305 encryption with an Argon2id-derived key. See [Wallet Encryption](/security/wallet-encryption).
## L
**Lamport**
The smallest unit of SOL. 1 SOL = 1,000,000,000 lamports. API fields like `amount_in_total`, `tip_lamports`, and `partner_fee_lamports` are denominated in lamports.
**Lane Splitting**
A feature (TypeScript SDK only) that separates the Exit Intelligence Stream into two receivers: a high-priority lane for exit signals and position events, and a low-priority lane for PnL updates. Prevents frequent PnL updates from blocking time-sensitive exit signals.
## M
**Market Context**
Metadata describing the DEX pool and protocol used for a token's market, including pool addresses and configuration. Returned in stream events and optionally passed to build requests to skip server-side resolution. See [Market Context](/api/reference/market-context).
**Memecoin**
A cryptocurrency often inspired by internet culture, characterized by high volatility and speculative trading. The primary market segment LaserSell is designed for.
**Meteora DBC (Dynamic Bonding Curve)**
Meteora's bonding curve protocol for tokens in their initial launch phase. Supported by LaserSell.
**Meteora DAMM V2 (Dynamic AMM V2)**
Meteora's dynamic automated market maker for tokens that have migrated from their bonding curve. Supported by LaserSell.
**Mode (Routing)**
A field on `/v1/buy` and `/v1/sell` requests with values `"fast"` or `"secure"`. Fast mode optimizes for speed; secure mode adds additional confirmation checks. Not to be confused with Send Mode.
## N
**Non-Custodial**
An architecture where users retain full control of their private keys and funds. LaserSell never stores keys on its servers. All transactions are returned unsigned and signed locally by the user or their application.
## P
**Partner Fee**
An optional fee that integrators can add to any buy or sell transaction, routed atomically to a specified wallet as part of the on-chain swap. Configurable in basis points or flat lamports per request. Available on Advanced and Enterprise tiers. See [Partner Fees](/api/reference/partner-fees).
**Passphrase**
The user-defined password required to unlock an encrypted keystore. If this passphrase is lost, the wallet cannot be decrypted.
**PnL (Profit and Loss)**
The current estimated profit or loss on a position. The Exit Intelligence Stream delivers `pnl_update` events at regular intervals with updated values.
**`pnl_update`**
A server event containing the latest PnL calculation for a tracked position. Delivered at regular intervals. High-frequency delivery is the reason lane splitting exists. See [Server Events](/api/stream/server-events).
**Position Handle**
A reference object returned by `StreamSession` events that identifies a tracked position by its ID, token account, mint, and wallet. Used for closing positions and requesting exit signals.
**`position_opened`**
A server event fired when the Exit Intelligence Stream detects a new token purchase in a tracked wallet. Contains the position ID, mint, cost basis, and market context. See [Server Events](/api/stream/server-events).
**`position_closed`**
A server event fired when a tracked position is fully exited or removed. See [Server Events](/api/stream/server-events).
**Priority Fee**
A standard Solana blockchain fee paid to validators for computational priority. Separate from Jito tips.
**Private Key**
The cryptographic key that grants control over a wallet's assets. The LaserSell client encrypts this locally and never transmits it. The API and SDKs never require a private key; all transactions are returned unsigned.
**Pump.fun**
A Solana token launchpad where tokens begin trading on a bonding curve before graduating to an AMM.
**PumpSwap**
The AMM that Pump.fun tokens migrate to after graduating from the bonding curve. Supported by LaserSell.
## R
**Rate Limits**
Caps on how many requests or resources you can use, determined by your pricing tier. Applies to both the REST API (requests per minute) and the Exit Intelligence Stream (wallets, positions, sessions). See [Rate Limits and Tiers](/api/reference/rate-limits).
**Raydium CPMM (Constant Product Market Maker)**
Raydium's standard AMM pools. Supported by LaserSell.
**Raydium Launchpad**
Raydium's token launch platform. Supported by LaserSell.
**Routing Fee**
A per trade fee applied to every order executed through the LaserSell Router. The rate depends on your subscription tier: 2% (Free), 0.9% (Professional), 0.5% (Advanced), or custom (Enterprise). Enforced atomically on-chain. See [Fee Structure](/pricing/fee-structure).
**RPC (Remote Procedure Call)**
A node that serves as a gateway to the Solana blockchain. LaserSell uses an RPC connection to read chain data and broadcast transactions. A private RPC endpoint is recommended for production use.
**Rug Pull**
A scenario where a token's liquidity is withdrawn or selling is disabled, causing the price to collapse. LaserSell's stop loss, trailing stop, and sell on graduation features help limit exposure.
## S
**Seed Phrase**
A 12 or 24-word mnemonic phrase used to generate or restore a wallet. Displayed only once during initial wallet creation.
**Sell on Graduation**
A strategy condition that automatically exits a position the moment its token migrates from a bonding curve to an AMM pool. Protects against post-graduation dumps.
**Send Mode**
A field on build requests (`"rpc"`, `"helius_sender"`, or `"astralane"`) that tells the server which submission context to optimize the transaction for. Should match your chosen send target.
**Send Target**
A configuration that tells the SDK where to submit a signed transaction. Options: Default RPC, Custom RPC, Helius Sender, Astralane Iris. See [Send Targets](/api/transactions/send-targets).
**Slippage**
The difference between the expected price of a trade and the actual execution price. High volatility and low liquidity increase slippage.
**Stop Loss**
A strategy condition that automatically sells a position when the price drops by a configured percentage from the entry price. Limits downside on any single trade.
**StreamClient**
The low-level SDK client for the Exit Intelligence Stream. Manages the WebSocket connection, reconnection, and message framing. Returns raw server messages. See [Connection Lifecycle](/api/stream/connection-lifecycle).
**StreamSession**
The high-level SDK wrapper around `StreamClient`. Provides automatic position tracking, deadline timers, typed events with position handles, and strategy update methods. Recommended for most integrations.
## T
**Target Profit**
A strategy condition that triggers an automatic sell when a position's PnL reaches a configured profit percentage.
**Trailing Stop**
A dynamic stop loss that tracks the highest profit observed since a position opened. If the profit drops from that peak by a configured percentage, an exit signal fires. Locks in gains on positions that pump before reversing.
## U
**USD1**
A stablecoin supported as an alternative output asset for sell transactions (instead of SOL).
**Unsigned Transaction**
A base64-encoded Solana `VersionedTransaction` returned by the LaserSell API. Must be signed locally with your private key before submission. LaserSell never handles signing.
## V
**VersionedTransaction**
A Solana transaction format that supports versioned instruction layouts and address lookup tables. All transactions returned by the LaserSell API use this format.
## W
**WebSocket (WSS)**
A communication protocol enabling real-time, bidirectional data streams. The Exit Intelligence Stream uses WSS to deliver position events and exit signals with minimal latency.
## X
**XChaCha20-Poly1305**
The authenticated encryption algorithm used by the LaserSell client to secure private keys within the keystore file. Provides both confidentiality and integrity verification.
# Risk Disclosures
Source: https://docs.lasersell.io/how-to-win/risk-disclosures-and-responsible-trading
Risks, responsible configuration, and legal disclosures for using LaserSell.
LaserSell is an automated trading tool for experienced cryptocurrency users. You are solely responsible for the custody of your private keys and the financial outcomes of your trades.
## Software and network risks
* **RPC / network failures**: LaserSell depends on third-party RPC providers and the Solana network. Congestion or downtime can delay or fail sell orders, leaving you in a position longer than intended.
* **Execution slippage**: The fill price may differ from the trigger price during high volatility. The `slippage_pad_bps` setting helps, but cannot eliminate slippage entirely.
* **No warranty**: This software is provided "as is." Bugs or future Solana protocol changes could affect performance.
## Market risks
New token markets are the highest-risk sector in crypto.
* **Liquidity risk**: Tokens can become unsellable if liquidity is withdrawn (rug pull) or the bonding curve stalls.
* **Total loss**: Memecoin values commonly drop to zero within minutes. Never deploy capital you cannot afford to lose.
* **Scams**: LaserSell cannot predict whether a developer will dump supply or whether token metadata is malicious.
## Wallet security
LaserSell creates a hot wallet on your machine, encrypted with Argon2id + XChaCha20-Poly1305.
* Encryption strength depends entirely on your passphrase.
* If your machine is compromised, your keystore and passphrase can be stolen.
* Run LaserSell on a dedicated, clean machine.
* Do not keep large balances in your trading wallet. Sweep profits to a hardware wallet regularly.
## Responsible configuration
* **Always set a stop loss.** Relying on manual intervention is too slow for this market.
* **Use a deadline timeout.** If a trade has not worked out in a set number of seconds, exiting preserves capital.
* **Start small.** Verify LaserSell executes as expected with minimal amounts before increasing position sizes.
## Regulatory compliance
* You are responsible for ensuring your trading activities comply with local laws and regulations.
* Automated trading generates a high volume of taxable events. You are responsible for tracking and reporting transactions.
*By running LaserSell, you affirm that you have read this disclosure and accept these risks.*
# Installation
Source: https://docs.lasersell.io/installation/installation
Install LaserSell on Linux, macOS, Windows (WSL), or Docker.
## Quick Install
The fastest way to install on any platform:
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash
```
```shellscript theme={null}
lasersell --version
```
If that works, skip to the [Quickstart](/start-here/quickstart). Platform-specific options are below.
## Platform Guides
### install.sh (recommended)
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash
```
### Manual APT setup
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/lasersell-archive-keyring.gpg
ARCH="$(dpkg --print-architecture)"
echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/lasersell-archive-keyring.gpg] https://dl.lasersell.io stable main" | sudo tee /etc/apt/sources.list.d/lasersell.list > /dev/null
sudo apt update
sudo apt install lasersell
```
### install.sh (recommended)
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash
```
Supports both Intel and Apple Silicon (M-series).
### Homebrew
```shellscript theme={null}
brew tap lasersell/lasersell
brew install lasersell
```
### Tarball (no Homebrew)
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash -s -- --method tar
```
LaserSell runs on Windows through WSL (Windows Subsystem for Linux).
### 1. Install WSL
Open PowerShell as Administrator:
```shellscript theme={null}
wsl --install
```
Restart your computer.
### 2. Install LaserSell
Open your WSL distro (e.g. Ubuntu) and run:
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash
```
### 1. Prepare
Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) and create an empty folder named `lasersell`.
### 2. Run
Open a terminal, `cd` into the folder, then run:
**macOS / Linux:**
```shellscript theme={null}
docker run -it --rm -v "$(pwd):/app/.lasersell" lasersell/lasersell
```
**Windows (PowerShell):**
```shellscript theme={null}
docker run -it --rm -v ${PWD}:/app/.lasersell lasersell/lasersell
```
On first run, the setup wizard launches inside the container. Config files are written to your mounted folder and persist across runs.
## Upgrading
| Method | Command |
| -------------------- | ------------------------------------------------------------- |
| APT | `sudo apt update && sudo apt install lasersell` |
| Homebrew | `brew upgrade lasersell` |
| install.sh / Tarball | Rerun `curl -fsSL https://dl.lasersell.io/install.sh \| bash` |
| Docker | `docker pull lasersell/lasersell` |
## CLI Flags
| Flag | Short | Description |
| ---------------------- | ----- | -------------------------------------------------- |
| `--setup` | | Force-run the onboarding wizard |
| `--config ` | `-f` | Path to a custom config file |
| `--debug` | | Write debug-level logs to `~/.lasersell/debug.log` |
| `--smoke` | | Run a connectivity health check and exit |
| `--export-private-key` | | Export your wallet's base58 private key to stdout |
`--smoke`, `--setup`, and `--export-private-key` are mutually exclusive.
# Example Configuration
Source: https://docs.lasersell.io/onboarding/example-configuration
Annotated config.yml reference with all available fields, defaults, and environment variable overrides.
The setup wizard generates `~/.lasersell/config.yml`. Edit it directly and restart the daemon to apply changes, or re-run `lasersell --setup`.
The strategy values shown below are examples only, not an official trading strategy. Configure these based on your own risk tolerance and trading style.
```yaml theme={null}
account:
# Encrypted keystore or legacy Solana keypair JSON.
# Default: ~/.lasersell/wallet.keystore.json
# Env: LASERSELL_KEYPAIR_PATH
keypair_path: "~/.lasersell/wallet.keystore.json"
# LaserSell API key (free at https://app.lasersell.io)
# Env: LASERSELL_API_KEY
api_key: "your-lasersell-api-key"
# Private Solana RPC endpoint (https:// required).
# Env: LASERSELL_RPC_URL or LASERSELL_PRIVATE_RPC_URL
rpc_url: "https://your-private-rpc.example.com"
# Transaction submission backend: "helius_sender" (default), "astralane", or "rpc".
# Env: LASERSELL_SEND_TARGET
# send_target: "helius_sender"
# Priority fee tip in lamports (default: 1000000 = 0.001 SOL).
# tip_lamports: 1000000
# Required only when send_target is "astralane".
# Env: LASERSELL_ASTRALANE_API_KEY
# astralane_api_key: "your-astralane-api-key"
strategy:
# Percentages are relative to your detected buy amount. Use "0%" to disable.
# At least one exit condition must be enabled (including take_profit_levels).
target_profit: "20%" # exit when profit reaches this %
stop_loss: "10%" # exit when loss reaches this %
trailing_stop: "5%" # exit when profit drops this % of entry from its peak
deadline_timeout: 0 # force exit after N seconds (0 to disable)
sell_on_graduation: false # auto-sell when token migrates to a new DEX
# Liquidity guard: exit when pool liquidity drops below safe threshold.
liquidity_guard: false
# Breakeven trail: trailing stop that activates once position breaks even.
# Use "0%" to disable.
breakeven_trail: "0%"
# Exit ladder: sell partial amounts at multiple profit thresholds.
# Each level specifies the profit % to trigger, % of position to sell,
# and an optional trailing stop % for that tranche.
take_profit_levels: []
# Example exit ladder:
# take_profit_levels:
# - profit_pct: 25 # at 25% profit...
# sell_pct: 30 # sell 30% of the position
# trailing_stop_pct: 0 # immediately (no trailing)
# - profit_pct: 50
# sell_pct: 50
# trailing_stop_pct: 3 # with 3% trailing stop
# - profit_pct: 100
# sell_pct: 100
# trailing_stop_pct: 5 # with 5% trailing stop
sell:
slippage_pad_bps: 2500 # extra slippage on top of computed value (bps)
slippage_retry_bump_bps_first: 20 # extra pad after first slippage error
slippage_retry_bump_bps_next: 40 # extra pad after subsequent slippage errors
slippage_max_bps: 3000 # max slippage cap (bps)
confirm_timeout_sec: 10 # tx confirmation timeout
max_retries: 3 # retry count for failed sells
# Copy trading: watch other wallets and optionally auto-buy when they buy.
# Positions from watched wallets use the same exit strategy as your own.
watch_wallets: []
# Example:
# watch_wallets:
# - pubkey: "TraderWalletPubkey1..."
# label: "whale" # optional display label
# - pubkey: "TraderWalletPubkey2..."
# auto_buy:
# amount: 0.1 # auto-buy with 0.1 SOL
# amount_usd1: 0.0 # auto-buy with USD1 (0 to disable)
```
## Environment variable overrides
Override any config field with its corresponding env var. Useful for Docker, CI, or secret management.
| Config field | Env var | Example |
| --------------------------- | ----------------------------- | --------------------------------------------- |
| `account.keypair_path` | `LASERSELL_KEYPAIR_PATH` | `/home/user/.lasersell/wallet.keystore.json` |
| `account.api_key` | `LASERSELL_API_KEY` | `ls_abc123...` |
| `account.rpc_url` | `LASERSELL_RPC_URL` | `https://mainnet.helius-rpc.com/?api-key=...` |
| `account.send_target` | `LASERSELL_SEND_TARGET` | `helius_sender`, `astralane`, or `rpc` |
| `account.astralane_api_key` | `LASERSELL_ASTRALANE_API_KEY` | `ast_xyz789...` |
| Wallet passphrase | `LASERSELL_WALLET_PASSPHRASE` | (for headless unlock) |
## Transaction submission targets
LaserSell supports three backends for submitting sell transactions. Most users should stick with the default.
| Target | Description |
| --------------- | ---------------------------------------------------------------------------------- |
| `helius_sender` | Default. Uses Helius's optimized sender. Requires a Helius RPC URL. |
| `astralane` | Uses Astralane's submission service. Requires an `astralane_api_key`. |
| `rpc` | Sends directly via your RPC endpoint. Works with any provider but may land slower. |
# Initial Setup
Source: https://docs.lasersell.io/onboarding/initial-setup
Walkthrough of the LaserSell setup wizard that creates your config and encrypted wallet.
The setup wizard runs automatically on first launch. Run it again any time with `lasersell --setup`.
## What you need
* **RPC URL** from [Helius](https://helius.dev), [Chainstack](https://chainstack.com), or any Solana RPC provider
* **LaserSell API key** from [app.lasersell.io](https://app.lasersell.io) (free)
* A wallet plan: create a new wallet, or have your **seed phrase**, **base58 secret key**, or **Solana keypair JSON** ready to import
## What the wizard creates
* `~/.lasersell/config.yml` (RPC, API key, strategy)
* `~/.lasersell/wallet.keystore.json` (encrypted wallet)
## Wizard walkthrough
### Credentials
```
RPC URL (private recommended): https://your-private-rpc.example.com
LaserSell API key: ********
```
The API key input is masked. A private RPC is strongly recommended for production.
### Strategy
The wizard prompts you to configure each exit condition. Set values that match your trading style and risk tolerance.
* **Target Profit**: sell when profit reaches this % (e.g. `6%`)
* **Stop Loss**: sell when loss reaches this % (e.g. `10%`)
* **Trailing Stop**: sell when profit drops this % from its peak (e.g. `5%`)
* **Deadline Timeout**: force sell after N seconds (e.g. `120`)
* **Slippage Tolerance**: max slippage for sells (e.g. `20%`)
* **Sell on Graduation**: auto-sell when token migrates to an AMM (`y/N`)
At least one exit condition (target profit, stop loss, trailing stop, or deadline) must be enabled. You can change any of these later by editing `~/.lasersell/config.yml` and restarting the daemon, or by re-running `lasersell --setup`.
### Wallet
**Existing keystore detected?** You can reuse it or create/import a different wallet.
**Creating a new wallet:** LaserSell generates a 12-word seed phrase and displays it once. Save it immediately.
You will not be shown the seed phrase again. If you lose it, you lose the wallet.
**Importing an existing wallet:** Choose one of:
1. **Base58 secret key** (input is masked)
2. **Seed phrase** (uses derivation path `m/44'/501'/0'/0'`)
3. **Solana keypair JSON** file path
After import, verify the displayed pubkey matches the wallet you intended.
### Passphrase
Set a passphrase to encrypt your keystore:
```
Keystore passphrase: ********
Confirm passphrase: ********
```
There is no "forgot password." If you lose your passphrase, the keystore cannot be recovered. Your recovery path is re-importing the wallet from your seed phrase or private key.
### Review and write
LaserSell shows a summary of your settings and asks to write the config. Confirm to save.
## After setup
LaserSell continues into normal startup. On future launches, it will ask for your keystore passphrase to unlock the wallet.
For headless/automated runs, set the `LASERSELL_WALLET_PASSPHRASE` environment variable.
## Troubleshooting
**"config file not found; run --setup in an interactive terminal"**
The wizard requires a real TTY. SSH into the machine or open a local terminal and run `lasersell --setup`.
**Imported seed phrase gives the wrong address**
LaserSell uses Solana's standard derivation path (`m/44'/501'/0'/0'`). If your wallet uses a different derivation, import via base58 secret key or keypair JSON instead.
# CLI Reference
Source: https://docs.lasersell.io/onboarding/lasersell-ui
Guide to the LaserSell CLI: daemon behavior, log output, and configuration.
The LaserSell CLI runs as a daemon that monitors your wallet and automatically executes exits when your strategy conditions are met.
## How it works
After unlocking your wallet, the CLI connects to the LaserSell stream and begins monitoring. All activity is logged to the terminal via structured log output.
```shellscript theme={null}
lasersell
```
The daemon runs until you press **Ctrl+C** to gracefully shut down.
## Log output
The CLI outputs structured events as they happen:
| Event | Meaning |
| --------------------- | ------------------------------------------- |
| `startup` | CLI started, wallet unlocked |
| `stream_connected` | Connected to the LaserSell stream |
| `wallet_registered` | Wallet ownership verified with the server |
| `balance_update` | SOL balance polled |
| `session_started` | New position detected and monitoring began |
| `pnl_update` | Profit/loss update for an active position |
| `sell_scheduled` | Exit condition triggered, sell queued |
| `sell_attempt` | Sell transaction submitted |
| `sell_complete` | Sell confirmed on-chain |
| `session_closed` | Position fully exited |
| `liquidity_snapshot` | Pool liquidity update (trend + depth bands) |
| `stream_disconnected` | Stream connection lost |
## CLI flags
| Flag | Short | Description |
| ---------------------- | ----- | -------------------------------------------------- |
| `--setup` | | Force-run the onboarding wizard |
| `--config ` | `-f` | Path to a custom config file |
| `--debug` | | Write debug-level logs to `~/.lasersell/debug.log` |
| `--smoke` | | Run a connectivity health check and exit |
| `--export-private-key` | | Export your wallet's base58 private key to stdout |
`--smoke`, `--setup`, and `--export-private-key` are mutually exclusive.
## Headless operation
The CLI is designed for headless server deployments. For automated unlock, set the passphrase via environment variable:
```shellscript theme={null}
export LASERSELL_WALLET_PASSPHRASE="your-passphrase"
lasersell
```
On a VPS, run in a `tmux` or `screen` session, or use `systemd` to manage the process.
## Exit strategies
The CLI supports all LaserSell exit strategies, configured in `~/.lasersell/config.yml`:
| Strategy | Config field | Description |
| ------------------ | -------------------- | ------------------------------------------------------ |
| Take-profit | `target_profit` | Exit when profit reaches this % of entry |
| Stop-loss | `stop_loss` | Exit when loss reaches this % of entry |
| Trailing stop | `trailing_stop` | Exit when profit drops this % from peak |
| Deadline timeout | `deadline_timeout` | Force exit after N seconds |
| Sell on graduation | `sell_on_graduation` | Exit when token migrates to a new DEX |
| Exit ladder | `take_profit_levels` | Sell partial amounts at multiple profit thresholds |
| Liquidity guard | `liquidity_guard` | Exit when pool liquidity drops below safe threshold |
| Breakeven trail | `breakeven_trail` | Trailing stop that activates once position breaks even |
At least one exit condition must be enabled. See [Example Configuration](/onboarding/example-configuration) for the full config reference.
## Copy trading
Monitor other wallets and optionally auto-buy when they buy:
```yaml theme={null}
watch_wallets:
- pubkey: "TraderWallet1..."
label: "whale"
- pubkey: "TraderWallet2..."
auto_buy:
amount: 0.1 # SOL
amount_usd1: 0.0 # USD1
```
Positions from watched wallets are managed by the same exit strategy as your own positions.
## Modifying your strategy
Edit `~/.lasersell/config.yml` and restart the daemon. Changes take effect on the next launch.
To re-run the setup wizard:
```shellscript theme={null}
lasersell --setup
```
# Platform & Coin Support
Source: https://docs.lasersell.io/onboarding/platform-and-coin-support
Supported launchpads, DEX protocols, and asset types on Solana.
## Supported launchpads
| Platform | Link |
| ----------------- | ----------------------------------------------------- |
| Pump.fun | [pump.fun](https://pump.fun) |
| Bonk.fun | [bonk.fun](https://bonk.fun) |
| Bags.fm | [bags.fm](https://bags.fm) |
| Raydium LaunchLab | [raydium.io/launchpad](https://raydium.io/launchpad/) |
| Trends.fun | [trends.fun](https://trends.fun) |
| Surge | [app.surge.xyz](https://app.surge.xyz/) |
Any additional platform using a supported protocol below also works.
## Supported protocols
| Protocol | Coverage |
| --------------------------- | ------------------------------------------------- |
| **Pump.fun Bonding Curves** | Buy and sell on the Pump.fun bonding curve |
| **PumpSwap** | Trade on PumpSwap markets (post-graduation) |
| **Raydium Launchpad** | Tokens on the Raydium launchpad (pre-migration) |
| **Raydium CPMM** | Standard Raydium liquidity pools (post-migration) |
| **Meteora DBC** | Meteora Dynamic Bonding Curve (pre-migration) |
| **Meteora DAMM V2** | Meteora Dynamic AMM pools (post-migration) |
## Supported assets
| Asset | Details |
| -------------- | ---------------------------------------------- |
| **SOL** | Native SOL and Wrapped SOL (wSOL) |
| **SPL Tokens** | Any SPL token on the supported platforms above |
| **USD1** | USD1 stablecoin trading pairs |
# Fee Structure
Source: https://docs.lasersell.io/pricing/fee-structure
A transparent breakdown of the costs, fees, and limits associated with using the LaserSell platform.
A transparent breakdown of the costs associated with using the LaserSell platform.
LaserSell applies a **tiered routing fee** enforced directly on-chain for maximum speed and transparency. Your fee rate depends on your subscription tier.
## 1. The Routing Fee
| Tier | Fee Rate | Minimum Fee |
| ------------------------------- | ------------- | ------------------ |
| Personal (Free) | 2% (200 bps) | 0.01 SOL per trade |
| Tier 1 — Professional (\$99/mo) | 0.9% (90 bps) | 0.01 SOL per trade |
| Tier 2 — Advanced (\$249/mo) | 0.5% (50 bps) | None |
| Enterprise | Custom | Custom |
* **Simple & Predictable:** No complex profit calculations and no hidden spreads. You pay the percentage listed above on the output volume, subject to the minimum fee for your tier.
* **On-Chain Enforcement:** The fee is handled atomically by the LaserSell Router on-chain program. It is deducted automatically from the SOL or USD1 proceeds during the transaction. USD1 transactions use the percentage fee only (no SOL minimum applies).
## 2. Network & Infrastructure Costs
To ensure your transactions land instantly there are two standard network costs involved. These are pass-through costs that go directly to the Solana network, not to us:
* **Jito Tip (Bribe):** A direct payment to validators to prioritize your transaction above others in the same block.
* **Solana Priority Fee:** A standard blockchain fee required to process high-priority computations on the network.
# Referral Program
Source: https://docs.lasersell.io/pricing/referral-program
Earn from every SOL trade your referrals make. Three tier trading fee referrals plus subscription commissions.
Invite traders to LaserSell and earn a share of their SOL trading fees. Build a referral chain up to three levels deep, and earn subscription commissions when your referrals upgrade to a paid plan. Trading fee earnings are split atomically on chain at trade time, so SOL arrives in your wallet instantly. USD1 trades are excluded.
## How It Works
Sign in to your [LaserSell dashboard](https://app.lasersell.io/referrals), claim a unique referral username, and set your payout wallet (any Solana wallet address). Your link becomes `lasersell.io/@you`.
Send your referral link to friends, communities, or your audience. When someone signs up through your link, they are permanently linked to you.
Earn a percentage of your referrals' SOL trading fees, paid instantly on chain to your payout wallet the moment they trade. You also earn subscription commissions in USD through Whop. USD1 trades are not eligible for trading fee earnings.
## Trading Fee Referrals
Every time one of your referrals executes a SOL trade through the LaserSell router, you earn a percentage of the protocol fee that LaserSell collects. Earnings extend up to three levels deep in your referral chain. USD1 trades are excluded from referral trading fee earnings.
| Tier | Relationship | Your Earning |
| ---------- | ------------------------ | ------------------------------ |
| **Tier 1** | You refer Alice (direct) | **30%** of Alice's trading fee |
| **Tier 2** | Alice refers Bob | **3%** of Bob's trading fee |
| **Tier 3** | Bob refers Carol | **2%** of Carol's trading fee |
A free tier user pays a 2% trading fee on a 1 SOL sell. That is 0.02 SOL in fees.
* **Tier 1 referrer** earns 30% of 0.02 SOL = **0.006 SOL**
* **Tier 2 referrer** earns 3% of 0.02 SOL = **0.0006 SOL**
* **Tier 3 referrer** earns 2% of 0.02 SOL = **0.0004 SOL**
Paid tier users pay lower fees (0.9% on Professional, 0.5% on Advanced), so the per trade earning is smaller, but active traders generate consistent volume.
## Subscription Referrals
When a user you **directly referred** subscribes to a paid plan, you earn a **30% recurring commission** on every invoice they pay, for as long as they remain subscribed. Subscription commissions apply to direct referrals only (Tier 1). The multi tier chain does not apply to subscriptions.
| Plan | Monthly Price | Your Commission |
| -------------------- | ------------- | ---------------------------- |
| Professional Edition | \$99/mo | **\$29.70/mo** (recurring)\* |
| Advanced Exit Engine | \$249/mo | **\$74.70/mo** (recurring)\* |
\*Amounts shown are before Whop's payout fees. Whop may deduct a small fee when you withdraw to your bank account.
Subscription commissions are paid in USD through **Whop** with a **30 day hold**. After connecting your payout method from the referral dashboard, commissions are automatically transferred and paid out after the hold period. Subject to Whop's payout schedule and policies.
## Payouts
### Trading fee earnings (SOL)
Trading fee earnings are paid in SOL instantly at trade time. When one of your referrals executes a trade, the protocol fee is split atomically on chain and your share is sent directly to your payout wallet in the same transaction. There is no hold period and no manual step required. Only SOL denominated trades are eligible; USD1 trades do not generate referral trading fee earnings.
You set your payout wallet when creating your referral code. You can update it at any time from the referral dashboard.
**Rent exemption note:** If your payout wallet is new and empty (0 SOL), very small payouts under \~0.002 SOL may be deferred until a larger trade comes through. Fund your wallet with any amount of SOL to receive all payouts.
### Subscription earnings (USD)
Subscription commissions are paid in USD through Whop with a **30 day hold**. After the hold period, funds are automatically paid out to your connected payout method. Subject to Whop's payout schedule and policies.
## Rules and Requirements
Alphanumeric only, 3 to 15 characters. You choose it once and it cannot be changed. Certain reserved names (admin, support, etc.) are blocked.
SOL trading fee earnings arrive in your payout wallet **instantly** at trade time. Subscription commissions are paid in USD via Whop with a **30 day hold**.
Users who sign up through your referral link get **7 days of Pro tier** features for free. More features means more trades, which means more earnings for you.
Each account can only be referred by one person. The referral link used at signup is permanent and cannot be changed.
## Earning Status
**Trading fee earnings (SOL)** are instant. The moment a referred user trades, your share of the fee is delivered on chain to your payout wallet. There is no pending state or manual action needed. You can verify each payout on a Solana block explorer.
**Subscription commissions (USD)** follow Whop's payout schedule. After a 30 day hold period, commissions are automatically transferred to your connected payout method through Whop.
## Get Started
Sign in to your dashboard to claim your username and start earning.
# Key Safety
Source: https://docs.lasersell.io/security/key-safety
How LaserSell protects your private keys and best practices for wallet security.
## Non-custodial design
Your private keys never leave your machine. LaserSell signs all transactions locally and never transmits keys to any server.
* Keys are generated and stored locally
* All transaction signing happens on your device
* LaserSell cannot freeze funds, initiate withdrawals, or execute trades without your command
## Encrypted keystore
LaserSell encrypts your wallet with **XChaCha20-Poly1305** authenticated encryption and derives the key using **Argon2id**, a memory-hard function that makes brute-force attacks prohibitively expensive.
If you have a legacy plaintext `wallet.json` from another tool, LaserSell will offer to migrate it to the encrypted keystore format automatically.
There is no "forgot password." If you lose your passphrase, the keystore cannot be decrypted. Your recovery path is re-importing from your seed phrase or private key.
## Memory hygiene
During unlocking, your passphrase and decryption intermediates are held in memory only for the instant they are needed. They are explicitly zeroized immediately after, preventing credential scraping by malware.
## Best practices
**Treat LaserSell as a hot wallet.** Do not keep large balances in your trading wallet. Only fund it with what you intend to trade.
**Sweep profits regularly.** Move gains to a hardware wallet or cold storage. Do not let profits accumulate in a hot wallet.
**Use a strong passphrase.** Your keystore security depends entirely on passphrase strength.
## Anti-phishing
LaserSell support will **never** ask for your:
* Private key or seed phrase
* Keystore passphrase
* Wallet "validation" or "synchronization" on any website
Anyone requesting these is a scammer. Block and report them immediately.
# Privacy & Data
Source: https://docs.lasersell.io/security/privacy
What data LaserSell collects, where it goes, and your controls over it.
LaserSell is built on a "least privilege" network philosophy. For the desktop app and API/SDK clients, all trade construction and signing happen locally on your machine. For the web app, signing is handled by Privy's Trusted Execution Environment (TEE). We only transmit data strictly necessary to authenticate your account, provide API services, and power the platform.
This page summarizes what data leaves your machine and where it goes. The full legal privacy policy is at [lasersell.io/privacy](https://www.lasersell.io/privacy).
## What stays local (zero access)
These never leave your machine:
| Data | Details |
| :------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Private keys & seed phrases** | Desktop: encrypted on your disk in the keystore, never transmitted. Web app: stored in Privy's TEE, never accessible to LaserSell servers. |
| **Strategy configuration** | Desktop: runs entirely locally. Web app: stored in our database to maintain your session across browser visits. |
| **Trade construction & signing** | Desktop: transactions built and signed on your machine. Web app: transactions built server-side, signed by Privy's TEE, submitted via LaserSell's RPC. |
## What we collect
### Authentication & accounts
* **Email and credentials**: stored in Supabase for account authentication on [app.lasersell.io](https://app.lasersell.io).
* **Profile pictures**: stored in Supabase Storage if you upload one. You can remove or replace it anytime.
* **OAuth profile data**:if you sign in via GitHub, X/Twitter, or Google, we receive limited profile info (no passwords).
* **MFA**:TOTP-based. We do not collect phone numbers.
### API & trading activity
* **Wallet public keys**:sent to the LaserSell API for authentication and session identification.
* **API keys**:stored encrypted for endpoint authentication.
* **Trading activity**:as the platform evolves, we plan to log activity tied to your API keys (PnL, configuration, trading frequency, wallet addresses) to improve the product and provide analytics.
### Payments
* **Stripe** handles all payment processing. We do not store raw credit card numbers on our servers.
### Technical data
* **IP addresses, user-agents, timestamps, and error traces**:collected in server logs for infrastructure monitoring and abuse prevention.
* **Google Analytics**:used on marketing and documentation sites only, not on the authenticated dashboard.
* **Essential cookies and local storage**:used on app.lasersell.io for session management.
## Network connections
When the LaserSell client is running, it establishes outbound connections to:
### 1. Solana network (your RPC)
* **Destination:** Your configured RPC provider (e.g., Helius, Chainstack).
* **Purpose:** Reading on-chain data (prices, bonding curves) and broadcasting signed transactions.
* **Note:** Your RPC provider can see your IP address and the requests you make.
### 2. Transaction sender (if applicable)
* **Destination:** Depends on your `send_target` setting:Helius sender, Astralane, or direct RPC.
* **Purpose:** Broadcasting your locally-signed transactions for fast on-chain landing.
* **Note:** The sender sees the signed transaction payload.
### 3. LaserSell API
* **Destination:** `api.lasersell.app`
* **Purpose:** Authentication, license verification, and the Exit Intelligence Stream (WebSocket).
## Third-party services
We share data with these providers to operate the platform:
| Provider | Purpose |
| :------------------- | :-------------------------------------------------------------------- |
| **Stripe** | Payment processing and referral commission payouts (Stripe Connect) |
| **Privy** | Wallet key management in Trusted Execution Environments (web trading) |
| **Supabase** | Database and authentication |
| **Better Stack** | Log management (U.S.) |
| **Typeform** | Enterprise intake applications |
| **Solana RPC nodes** | On-chain data resolution |
We do **not** sell your personal information or API usage data to data brokers or for targeted advertising.
## Data retention
* **Account and billing data** is retained while your account is active.
* **Server logs** are routinely cycled or anonymized per operational security practices.
## Your rights
* Update your authentication methods and API keys via the [dashboard](https://app.lasersell.io).
* Request access to, correction of, or deletion of your data by emailing [support@lasersell.io](mailto:support@lasersell.io).
## Security
We use encryption for API keys and session management, along with administrative and technical safeguards. If you discover a security vulnerability, report it to [security@lasersell.io](mailto:security@lasersell.io). We encourage use of our [PGP key](https://www.lasersell.io/lasersell-pgp-key.asc). See our [security.txt](https://www.lasersell.io/.well-known/security.txt) for full disclosure guidelines.
## Jurisdiction
Information may be transferred to the U.S. or other jurisdictions where our service providers operate. Our services are intended for users 18 and older.
# Wallet Encryption
Source: https://docs.lasersell.io/security/wallet-encryption
Learn how LaserSell replaces insecure plain text wallet files with an encrypted local-only keystore system that ensures your keys never leave your machine.
Security is the core of the LaserSell architecture. We understand that your private keys control your assets, which is why LaserSell uses a **zero-trust, local-only encrypted keystore system**. We do not store your keys on our servers, and they are never saved in plain text on your hard drive.
## Security Architecture
LaserSell replaces insecure "plain text" wallet files (like standard `id.json` files) with an encrypted **Keystore**.
* **Encryption:** Your private key is encrypted using **XChaCha20-Poly1305**, a high-performance authenticated encryption algorithm.
* **Passphrase Protection:** You must set a **Passphrase** to lock your wallet. We use **Argon2id** (a memory-hard password hashing algorithm) to derive your encryption key, making your wallet highly resistant to brute-force attacks.
* **Session Locking:** Your wallet is only decrypted in memory while LaserSell is running. If you close the application, the wallet automatically locks.
# Integrate LaserSell Into Your Product
Source: https://docs.lasersell.io/start-here/integrate-lasersell
Add LaserSell's Exit Intelligence engine to your trading bot, Telegram bot, platform, or app with the LaserSell SDK and API.
If your product touches Solana token trading in any way, LaserSell can power the execution layer behind it.
The same Exit Intelligence engine that runs in the LaserSell client is available through the [LaserSell SDK and API](/api/overview). Telegram bots, web platforms, mobile apps, browser extensions, CLI tools, portfolio managers, launchpad services, copy-trading systems, signal groups, analytics dashboards. If your users are buying or selling tokens on Solana, LaserSell gives you the infrastructure to automate that with real-time strategy evaluation, multi-protocol transaction building, and built-in monetization.
You do not need to build an exit engine. You do not need to track AMM state or handle protocol-specific instructions. You integrate once and get coverage across every major Solana launchpad and DEX.
## Real-Time Exit Intelligence
The Exit Intelligence Stream is a persistent WebSocket connection that watches wallets, tracks positions, evaluates strategy in real time, and delivers pre-built exit transactions the moment thresholds are met.
Instead of building your own PnL tracking, price monitoring, and exit logic from scratch, you connect to the stream and receive pre-built transactions. Your application defines the strategy (take profit, stop loss, trailing stop, deadline, sell on graduation) and LaserSell handles the rest.
This means you can ship automated exit features to your users without building or maintaining the execution engine yourself.
## Buy and Sell Transaction Builder
The REST API builds unsigned swap transactions on demand via `/v1/buy` and `/v1/sell`. Full protocol coverage across Pump.fun, PumpSwap, Meteora DBC, Meteora DAMM V2, Raydium Launchpad, and Raydium CPMM.
You do not need to track AMM state, compute routes, or handle protocol-specific instruction formats. Send a request with the mint and amount, and the API returns an optimized unsigned transaction ready to sign and submit.
## Your Keys, Your Signing
Every transaction the API returns is unsigned. You handle signing however your architecture requires. No keys ever touch LaserSell infrastructure.
Whether your application manages wallets on behalf of users or has users sign directly, LaserSell fits into your existing key management setup without imposing any constraints on how you handle signing.
## Built-In Monetization
Every transaction your users execute through LaserSell can include a partner fee routed directly to your wallet. You set the fee (basis points or flat lamports) on each request, and it is enforced atomically on-chain as part of the swap.
No invoicing, no reconciliation, no off-chain payment flows. Your revenue arrives in your wallet in the same transaction as the trade.
See [Partner Fees](/api/reference/partner-fees) for implementation details. Enterprise partners can work with us on custom monetization arrangements.
## Multi-Protocol Coverage
A single integration gives your product coverage across the major Solana trading protocols:
* **Pump.fun** bonding curves and **PumpSwap** markets
* **Meteora** Dynamic Bonding Curve (DBC) and Dynamic AMM V2
* **Raydium** Launchpad and CPMM pools
As new protocols launch on Solana, LaserSell adds support and your integration picks it up automatically. No code changes on your end.
## Four SDKs
Integrate in the language that fits your stack:
* [TypeScript](/api/sdk/typescript)
* [Python](/api/sdk/python)
* [Rust](/api/sdk/rust)
* [Go](/api/sdk/go)
All four SDKs share the same request/response schemas, error types, and retry behavior.
## Where LaserSell Fits
Any product where users hold Solana tokens and need to sell them can benefit from LaserSell. Here are some examples, but this is not an exhaustive list. If your product involves token trading on Solana, there is likely a place for Exit Intelligence in your stack.
### Bots (Telegram, Discord, CLI, or Custom)
Give your bot users automated TP, SL, and trailing stop on every trade. Connect to the Exit Intelligence Stream per user wallet, and deliver exit notifications or auto-execute sells when conditions are met. Partner fees let you monetize every trade your bot processes.
### Web and Mobile Trading Platforms
Add one-click buy/sell and automated exit strategies to your trading UI. The transaction builder handles protocol routing and the Exit Intelligence Stream provides real-time PnL updates to power live dashboards.
### Launchpad and Deployer Tools
Automate post-launch exit workflows for token creators. When a deployer launches through your tool, LaserSell can monitor the position and execute the creator's exit plan automatically.
### Copy Trading and Signal Groups
When a signal fires or a leader enters a position, LaserSell can manage the exit for every follower automatically. Each wallet gets its own strategy evaluation and exit execution without any manual intervention.
### Portfolio Trackers, Analytics, and Alerting
Connect to the Exit Intelligence Stream for real-time position tracking, PnL updates, and exit event data across all supported protocols. Power analytics dashboards, alerting systems, or risk management tools.
### Browser Extensions and Wallet Add-Ons
Embed automated exit functionality directly into the browsing experience. Users buy on any platform and your extension uses LaserSell to manage the sell in the background.
### Anything Else
If your users are trading tokens on Solana, LaserSell's exit engine and transaction builder are designed to plug in. The API is protocol-agnostic from your perspective. You send a mint and an amount, and get back an optimized transaction. The Exit Intelligence Stream watches wallets and delivers exit signals in real time. How you surface that to your users is entirely up to you.
## Getting Started
1. **Get an API key** at [app.lasersell.io](https://app.lasersell.io) (free, instant).
2. **Read the API overview** at [API and SDK Overview](/api/overview).
3. **Build your first transaction** with the [Quickstart](/api/quickstart).
4. **Connect the Exit Intelligence Stream** with the [Stream Overview](/api/stream/overview).
5. **See plans and limits** at [Rate Limits and Tiers](/api/reference/rate-limits).
**Using AI to code?** Add the [LaserSell MCP server](/ai-agents/mcp-server) to your editor so your AI assistant can search LaserSell documentation in real time.
**Building for a team or platform?** See [Enterprise vs Advanced](/pricing/enterprise) for a detailed comparison of tiers, or contact [sales@lasersell.io](mailto:sales@lasersell.io) to discuss custom arrangements.
# LaserSell for Business
Source: https://docs.lasersell.io/start-here/lasersell-for-business
Scale your Solana trading product with LaserSell's business tiers, built-in monetization, whitelabeling, and enterprise infrastructure.
## Monetize Every Trade
Every transaction your users execute through LaserSell can include a [partner fee](/api/reference/partner-fees) routed directly to your wallet. You set the fee on each API request, either as a percentage (basis points) or a flat amount (lamports), and it is enforced atomically on-chain as part of the swap.
There is no invoicing, no reconciliation, and no off-chain payment flow. Revenue arrives in your wallet in the same transaction as the user's trade. You can verify every fee on-chain.
Partner fees are available on the Advanced and Enterprise tiers.
## Whitelabel LaserSell
Both business tiers include whitelabeling. Your users interact with your brand, your interface, and your product. LaserSell powers the exit engine and transaction building behind the scenes.
## Choosing Your Tier
LaserSell offers two tiers designed for business use.
### Advanced Exit Engine — \$249/mo
The Advanced tier gives you full platform access at a predictable price. Every exit strategy, full behavioral insights, whitelabeling, partner fee monetization, and priority support.
| | |
| ---------------------------- | -------------------------- |
| **Routing fee** | 0.5% per trade, no minimum |
| **Wallets per session** | 200 |
| **Positions per session** | 20,000 |
| **Whitelabeling** | Yes |
| **Partner fee monetization** | Yes |
| **Priority support** | Yes |
At 200 wallets and 20,000 concurrent positions, most businesses will never hit the ceiling. This is the right tier for trading teams, bots with a growing user base, and platforms shipping automated exits without needing dedicated infrastructure.
[Get started with Advanced →](https://app.lasersell.io/auth/sign-up?next=/billing)
### Enterprise — Custom Pricing
Enterprise is for teams that have outgrown fixed limits or need guarantees a self-serve tier cannot provide.
**Custom capacity.** The Advanced tier caps at 200 wallets, 20,000 positions, and 1 session per API key. Enterprise removes these ceilings. If you are running a copy trading platform with thousands of followers, a Telegram bot with a large active user base, or an institutional desk managing many wallets simultaneously, Enterprise gives you the room.
**Custom routing fees.** At significant volume, the standard 0.5% fee is negotiable. Enterprise partners receive volume-based fee structures tailored to their throughput, including tiered discounts, monthly volume commitments, or flat fee arrangements.
**Dedicated namespace and SLA.** Enterprise accounts run on isolated infrastructure with guaranteed uptime SLAs. Your traffic is separated from other users and prioritized during high-load periods.
**Partner dashboards.** Real-time visibility into volume, fees collected, active sessions, and position metrics across all your API keys. Useful for finance teams tracking revenue and operations teams monitoring integration health.
**Custom monetization.** Beyond the standard partner fee system, Enterprise partners can work with us on custom on-chain revenue sharing. This includes multi-party splits, custom split configurations, and volume-based incentive structures. All enforced atomically on-chain.
**Dedicated support.** A private channel (Slack Connect, email, or Discord) with direct access to the engineering team. Priority response times for integration questions, incident escalation, and feature requests.
| | |
| ----------------------- | -------------------------------------------------- |
| **Routing fee** | Custom, volume based |
| **Capacity** | Custom (wallets, positions, sessions, rate limits) |
| **Dedicated namespace** | Yes, with traffic isolation |
| **SLA** | Yes, with uptime guarantees |
| **Partner dashboards** | Yes |
| **Custom monetization** | Yes |
| **Dedicated support** | Yes |
[Apply for Enterprise →](https://form.typeform.com/to/pU6iV4dV)
## Full Plan Comparison
For a complete breakdown of every feature across all four tiers (Personal, Professional, Advanced, Enterprise), see:
* [Fee Structure](/pricing/fee-structure) for routing fees and network costs.
* [Rate Limits and Tiers](/api/reference/rate-limits) for capacity limits and stream configuration.
* [lasersell.io](https://www.lasersell.io/#pricing) for the full feature comparison table.
For enterprise inquiries, contact [sales@lasersell.io](mailto:sales@lasersell.io).
# Quickstart
Source: https://docs.lasersell.io/start-here/quickstart
Install LaserSell and start auto-selling in under 5 minutes.
## Prerequisites
* **Private RPC endpoint.** We recommend signing up for a free tier RPC from [Helius](https://helius.dev), [Shyft](https://shyft.to), or [Chainstack](https://chainstack.com). A private RPC gives you faster balance updates and more reliable transaction confirmations than the public Solana RPC.
* **LaserSell API key.** Sign up at [app.lasersell.io](https://app.lasersell.io) to create a free API key. The free tier is enough for most users.
## Install
```shellscript theme={null}
curl -fsSL https://dl.lasersell.io/install.sh | bash
```
Verify it worked:
```shellscript theme={null}
lasersell --version
```
Need platform-specific instructions? See [Installation Overview](/installation/overview).
## Setup
Run LaserSell. The setup wizard starts automatically on first launch:
```shellscript theme={null}
lasersell
```
The wizard will ask for your RPC URL, API key, strategy settings, wallet (create new or import existing), and a passphrase to encrypt your keystore.
There is no "forgot password." If you lose your passphrase, the keystore cannot be recovered.
For a detailed walkthrough of every prompt, see [Initial Setup](/onboarding/initial-setup).
## Verify
Run a health check to confirm everything is connected:
```shellscript theme={null}
lasersell --smoke
```
You should see `SMOKE OK`. Then start LaserSell:
```shellscript theme={null}
lasersell
```
LaserSell is now monitoring your wallet.
## What Happens Now
LaserSell is a **passive monitor**. It does not place trades for you. Instead, it watches your wallet for new token purchases and handles the sell side automatically.
1. You buy tokens using whatever platform you already trade on.
2. LaserSell detects the buy automatically within seconds
3. The CLI logs the position as active with live PnL updates
4. When any exit condition fires (take profit, stop loss, trailing stop, deadline, or graduation), LaserSell builds and signs the sell transaction automatically
5. You keep trading. LaserSell keeps selling.
## Your First Automated Exit
Walk through the full loop to see LaserSell in action:
1. **LaserSell is running.** You see log output confirming the stream is connected and your wallet is registered.
2. **Buy a small position** on any supported platform. Start small so you can watch the mechanics without risking real capital.
3. **Watch the logs.** Within seconds, you'll see `session_started` and `pnl_update` events for your new position.
4. **The exit fires.** When your take profit, stop loss, trailing stop, or deadline condition triggers, you'll see `sell_scheduled` followed by `sell_complete` with the on-chain signature.
That is the entire loop. You trade normally, and LaserSell handles every exit.
## Adjust Your Strategy
Edit `~/.lasersell/config.yml` to tune your strategy, then restart the daemon. Or re-run the setup wizard:
```shellscript theme={null}
lasersell --setup
```
For the full config reference, see [Example Configuration](/onboarding/example-configuration).
## Next Steps
* [CLI Reference](/onboarding/lasersell-ui): Log events, CLI flags, headless operation, and copy trading
* [Example Configuration](/onboarding/example-configuration): Full config reference with all available fields
* [Initial Setup](/onboarding/initial-setup): Re-run the setup wizard with `lasersell --setup`
* [Key Safety](/security/key-safety): Protect your keys
# What Is LaserSell?
Source: https://docs.lasersell.io/start-here/what-is-lasersell
LaserSell is a non-custodial automated exit engine for Solana. It detects your buys, monitors PnL in real time, and sells automatically when your conditions are met.
LaserSell is an automated exit engine for the Solana network. It monitors your wallet on-chain, detects new tokens you buy, tracks your PnL in real time, and executes sells automatically when your conditions are met. Your keys are always under your control.
## How It Works
1. **You buy a token** on any platform you already use.
2. **LaserSell detects the buy** by monitoring your wallet on-chain.
3. **Your exit strategy runs automatically.** LaserSell evaluates take profit, stop loss, trailing stop, deadline, and sell-on-graduation conditions on every price update.
4. **The moment a condition is met, LaserSell sells.** The transaction is constructed, signed, and broadcast directly to the Solana network.
No manual clicks, no confirmation popups, no switching between tabs.
## What LaserSell Is Not
* **Not a custodial service.** We never have access to your private keys.
* **Desktop App:** Keys are encrypted and stored locally on your machine. You sign every transaction.
* **Web App:** Keys are stored in [Privy's Trusted Execution Environment (TEE)](https://docs.privy.io/guide/security), a hardware-isolated enclave that neither LaserSell nor Privy personnel can access. Privy is a [Stripe company](https://privy.io/blog/announcing-our-acquisition-by-stripe). You can export your key at any time.
* **Not financial advice.** LaserSell is execution infrastructure. You set the strategy, LaserSell executes it.
## Choose How You Trade
LaserSell meets you where you are. Every client connects to the same monitoring infrastructure and executes the same strategies.
### Web App
The [LaserSell Web App](/web-app/overview) is the easiest way to get started. Sign in, fund your wallet, set your strategy, and LaserSell trades for you 24/7, even when your browser is closed.
* No downloads, no installation, works on any device
* Automated execution via Privy's Trusted Execution Environment
* Real-time position monitoring with live PnL updates
* Copy trading to mirror any wallet's trades automatically
* Push notifications for trade events while you are away
[Get started with the Web App →](/web-app/overview)
### Desktop App
The [LaserSell Desktop app](/desktop/overview) is built for traders who want full local control. Your keys stay on your machine, you sign every transaction, and you choose your own RPC and transaction sender.
* Native macOS application with real-time position monitoring
* Multi-level exit strategies and mirror trading
* Built-in Solana wallet with swap
* Analytics with daily PnL charts, achievements, and insights
* Touch ID unlock and support for 7 languages
[Get started with the Desktop app →](/desktop/installation)
### API and SDK
The same exit engine is available through the [LaserSell API and SDK](/api/overview) for developers who want to build their own clients, bots, or trading platforms.
* Available in [TypeScript](/api/sdk/typescript), [Python](/api/sdk/python), [Rust](/api/sdk/rust), and [Go](/api/sdk/go)
* Full access to the transaction building API and real-time stream
* Build custom strategies, alerts, dashboards, or autonomous agents
[Explore the API →](/api/overview)
## Who Uses LaserSell
### Traders
If you trade memecoins on Solana, LaserSell gives you automated exits so you do not have to watch every position manually. Set your strategy once and let it run.
* Take profit, stop loss, trailing stop, deadline timeout, and sell-on-graduation all run in parallel.
* Works with any entry method. Buy on any platform or wallet you already use.
### Deployers
If you launch tokens using tools like RapidLaunch, Uxento, or J7 Tracker, LaserSell automates your personal exit plan so you can focus on the launch and community.
### Developers
Build your own trading bots, platforms, or custom strategies on top of LaserSell's real-time exit intelligence, multi-protocol support, and transaction building.
## Supported Protocols
LaserSell covers the major Solana launchpads and DEXs:
* Pump.fun and PumpSwap
* Meteora DBC and DAMM V2
* Raydium Launchpad and CPMM
Full details: [Platform and Coin Support](/onboarding/platform-and-coin-support)
## Getting Started
Trade from your browser with automatic execution, even offline.
Full local control with native macOS experience.
Build your own client or trading bot.
# Account Deletion
Source: https://docs.lasersell.io/web-app/account-deletion
How to permanently delete your LaserSell account and what happens to your data.
You can permanently delete your LaserSell account at any time from the Settings page. Account deletion is immediate and irreversible.
## How to Delete Your Account
Navigate to **Settings** from the sidebar in your [LaserSell dashboard](https://app.lasersell.io/settings).
At the bottom of the Settings page, find the **Delete Account** section.
Click **Delete Account**, then type `DELETE` in the confirmation field to confirm. This step prevents accidental deletion.
## What Gets Deleted
When you delete your account, the following data is permanently removed:
* Your LaserSell account and profile
* All wallets and wallet keys
* Trading sessions and trade history
* Analytics, insights, and achievements
* API keys
* Referral profile, referral relationships, and referral earnings
* Push notification subscriptions
* Billing and subscription data
On chain trading fee referral earnings already sent to your wallet are final and unaffected by account deletion. Pending subscription commission earnings may be forfeited.
## What Is NOT Deleted
* **Whop membership**: If you have an active Whop subscription, it is not automatically canceled. Cancel your subscription on [Whop](https://whop.com/billing/) before deleting your LaserSell account to avoid being charged.
* **On-chain transactions**: Any transactions already submitted to the Solana blockchain are permanent and cannot be reversed.
## After Deletion
* You will be signed out immediately
* You can create a new account with the same email address, but no data will be restored
* If you have an active Whop subscription, cancel it separately at [whop.com/billing](https://whop.com/billing/)
## Need Help?
If you have trouble deleting your account or need assistance, contact us:
Click **Contact Support** on our Whop page.
[support@lasersell.io](mailto:support@lasersell.io)
# Web App Overview
Source: https://docs.lasersell.io/web-app/overview
Trade from your browser with automatic execution, even when you close the tab.
The LaserSell Web App brings automated trading to your browser. Configure your exit strategy, fund your wallet, and LaserSell executes trades on your behalf 24/7, even when your browser is closed.
## How It Works
When you sign in, a secure wallet is automatically created for you by [Privy](https://privy.io). You can also import an existing wallet. Your private keys are stored in a Trusted Execution Environment (TEE) and never touch LaserSell servers.
Send SOL to your wallet address from any external wallet (Phantom, Backpack, etc.). Your wallet address and balance are visible on the Wallet page.
Set your exit strategy: target profit, stop loss, trailing stop, and maximum slippage. These rules determine when LaserSell automatically sells your positions.
Click "Start Web Trading" and LaserSell begins monitoring your wallets. When a position hits your strategy thresholds, the trade executes automatically using your wallet.
## Web App vs Desktop App
Both clients connect to the same LaserSell infrastructure, but they work differently under the hood.
| Feature | Desktop App | Web App |
| ----------------------- | ----------------------- | ---------------------------------------- |
| **Signing** | Your local keypair | Privy TEE wallet (delegated signing) |
| **Offline execution** | No (app must be open) | Yes (server executes while you are away) |
| **Wallet** | Import/generate locally | Privy embedded or imported |
| **Copy trading** | Yes | Yes |
| **Position monitoring** | Yes | Yes |
| **Buying coins** | Yes | Not yet |
| **Token search** | Yes | Not yet |
## Key Concepts
### Privy Wallets
Your web trading wallet is managed by [Privy](https://privy.io), a wallet infrastructure company [acquired by Stripe](https://privy.io/blog/announcing-our-acquisition-by-stripe). Privy stores your private key in a Trusted Execution Environment (TEE), a hardware-isolated enclave that prevents anyone, including LaserSell, from accessing your key directly.
When a trade needs to execute, LaserSell requests Privy to sign the transaction. Privy signs it inside the TEE and returns the signed transaction. LaserSell then submits it to the Solana network.
### Always-On Monitoring
When web trading is active, LaserSell maintains a persistent connection to its monitoring infrastructure on your behalf. Your positions are tracked in real time, and exit signals are executed immediately when your strategy thresholds are hit.
You can toggle web trading on and off using the power icon in the dashboard header. When you switch to the desktop app, toggle web trading off to avoid duplicate sell attempts on the same wallet.
### Session States
| State | What It Means |
| ----------- | ------------------------------------------------------------------- |
| **Active** | Stream connected, positions monitored, trades execute automatically |
| **Paused** | Temporarily stopped (error or manual pause) |
| **Stopped** | Web trading is off, no resources consumed |
Set up your wallet and start trading from your browser.
# Security
Source: https://docs.lasersell.io/web-app/security
How LaserSell protects your funds and private keys in the web app.
The web app uses a different security model from the desktop app. Understanding the differences helps you make informed decisions about which client to use.
## Trust Model
| | Desktop App | Web App |
| ---------------------------- | ------------------------------ | ----------------------------------------------- |
| **Model** | Non-custodial (local signing) | Non-custodial (delegated signing via Privy TEE) |
| **Who signs** | You, locally | Privy's TEE, on your behalf |
| **Who submits** | You choose (RPC, Helius, etc.) | LaserSell's servers via its own RPC |
| **Key location** | Your machine (encrypted) | Privy's Trusted Execution Environment |
| **LaserSell sees your key?** | Never | Never (TEE is hardware-isolated) |
## Privy TEE
[Privy](https://privy.io) is a wallet infrastructure company [acquired by Stripe](https://privy.io/blog/announcing-our-acquisition-by-stripe). Privy stores your private key in a **Trusted Execution Environment** (TEE), a hardware-isolated enclave. The key never leaves the TEE, even when signing transactions. LaserSell requests Privy to sign a transaction, Privy signs it inside the TEE, and returns only the signed result.
This means:
* LaserSell servers never have access to your private key
* A LaserSell server compromise cannot leak your key
* You can export your key at any time via Privy's secure UI
## Wallet Ownership Verification
When you register a wallet, the web app signs a proof message (`lasersell-register::`) using Privy's `signMessage` API. This ed25519 signature is verified by LaserSell's servers, proving you control the wallet. This is the same verification flow used by the desktop app.
## Transaction Flow
When an auto-sell triggers:
1. LaserSell's monitoring server detects a strategy threshold hit
2. An unsigned transaction is built (same as desktop)
3. LaserSell requests Privy to **sign** the transaction (sign-only, not submit)
4. Privy signs inside the TEE and returns the signed transaction
5. LaserSell submits the signed transaction to Solana via its own RPC
6. You receive a notification with the transaction signature
At no point do LaserSell's servers see your private key. The signing and key storage are entirely within Privy's infrastructure.
## Data Protection
| Data | Storage | Access |
| --------------------- | ------------------------ | --------------------------------------------------------- |
| API keys | Encrypted in Supabase | Column-level security prevents browser access to raw keys |
| Wallet private keys | Privy TEE | Never accessible to LaserSell |
| Trading session state | Supabase (RLS-protected) | Only your own sessions visible |
| Notifications | Supabase (RLS-protected) | Only your own notifications visible |
| Push subscriptions | Supabase (RLS-protected) | User-managed |
## Removing Your Key from Privy
If you want to fully remove your private key from Privy's infrastructure:
### Imported Wallets
1. Go to the **Wallet** page in your dashboard
2. Click the trash icon on the imported wallet card
3. This removes the wallet from LaserSell AND deletes the key from Privy's TEE
4. The key is permanently destroyed in the enclave
### Embedded Wallets
Embedded wallets are tied to your Privy account. To remove them:
1. **Export your private key first** (click "Export Key" on the wallet card)
2. Store the exported key safely in your own wallet (Phantom, Backpack, etc.)
3. Contact [support](https://docs.lasersell.io/getting-support/support) to request Privy account deletion
4. Once your Privy account is deleted, the embedded wallet key is permanently destroyed
Deleting a wallet or Privy account is irreversible. If you have not exported your private key, you will permanently lose access to any funds in that wallet.
## Recommendations
* Use a **dedicated trading wallet** for web trading rather than your primary holding wallet
* **Toggle off web trading** when switching to the desktop app to avoid duplicate sell attempts
* **Export your private key** after importing a wallet as a backup
* Review your exit strategy's **max slippage** setting (defaults to 5%) to protect against MEV during unattended execution
# Trading
Source: https://docs.lasersell.io/web-app/trading
Configure exit strategies and monitor positions from your browser.
The Trading page is the main control center for web trading. It has two tabs: **Positions** for monitoring your active trades, and **Copy Trading** for mirroring other wallets.
## Getting Started
If you have not started a web trading session yet, the Trading page shows a setup form:
1. **Select wallets** to monitor (from your registered wallets)
2. **Configure your exit strategy** (target profit, stop loss, trailing stop, max slippage)
3. Click **Start Web Trading**
Once started, LaserSell monitors your wallets and automatically executes trades when your strategy thresholds are hit.
## Positions Tab
The Positions tab uses a three-column layout:
* **Left**: List of all open positions with P\&L summary
* **Middle**: Detailed view of the selected position (token info, entry price, current profit, sell button)
* **Right**: Exit strategy settings (editable at any time)
### Exit Strategy Settings
| Setting | Description |
| ---------------------- | --------------------------------------------------------- |
| **Target Profit %** | Sell when profit reaches this percentage |
| **Stop Loss %** | Sell when loss reaches this percentage |
| **Trailing Stop %** | Lock in profits by trailing the price by this percentage |
| **Max Slippage %** | Maximum acceptable slippage on auto-sells (default 5%) |
| **Sell on Graduation** | Auto-sell when a token graduates from pump.fun to Raydium |
Changes are applied immediately to all active positions.
### Manual Sell
Click **Sell Now** on any position to trigger an immediate sell, regardless of strategy thresholds.
## Copy Trading Tab
Copy trading lets you automatically mirror the trades of other wallets.
### Adding a Watch Wallet
1. Click **Add Watch Wallet**
2. Enter a Solana wallet address or `.sol` domain name (resolved via SNS)
3. Set a nickname and auto-buy amount (in SOL)
4. The wallet is added to your watch list
When a watched wallet buys a token, LaserSell automatically executes the same buy with your wallet.
### Mirror Settings
| Setting | Description |
| ---------------------------- | ----------------------------------------------------------------- |
| **Max positions per wallet** | Limit concurrent mirror positions |
| **Cooldown** | Minimum time between mirror buys |
| **Skip creator tokens** | Ignore tokens created by the watched wallet |
| **Max active SOL** | Cap total SOL deployed across mirror positions |
| **Buy slippage** | Maximum slippage for mirror buys |
| **Min pool liquidity** | Skip tokens with low liquidity |
| **Max entry drift** | Skip if price has drifted too far from the watched wallet's entry |
| **Max consecutive losses** | Auto-disable the watched wallet after N consecutive losses |
## Session Controls
Use the **agent toggle** (power icon in the header) to start or stop web trading. The status dot shows:
* 🟢 **Green**: Active and monitoring
* 🔵 **Blue**: Watching for new positions
* 🟡 **Yellow**: Paused (error or inactivity)
* ⚪ **Gray**: Stopped
If you are monitoring the same wallet from both the desktop app and the web app, both will try to sell when a threshold is hit. Toggle one off when switching to the other.
## Tier Limits
Your subscription tier determines the capacity available for web trading. These same limits apply across the desktop app and API.
| | Free | Professional | Advanced |
| ------------------------ | ------ | ------------ | -------- |
| **Wallets** | 1 | 5 | 200 |
| **Positions per wallet** | 2 | 100 | 100 |
| **Watch wallets** | 2 | 10 | 25 |
| **Session timeout** | 45 min | None | None |
| **Always-on monitoring** | No | Yes | Yes |
| **Exit Ladder** | No | Yes | Yes |
| **Liquidity Guard** | No | Yes | Yes |
| **Behavioral Insights** | No | Yes | Yes |
Free tier users have full access to web trading with basic exit strategies (target profit, stop loss, trailing stop). Upgrade to Professional for advanced strategies, always-on monitoring, and higher capacity.
See the full feature comparison on our pricing page.
# Wallet Management
Source: https://docs.lasersell.io/web-app/wallet
Manage your Privy wallets, import existing wallets, deposit and withdraw SOL.
The Wallet page lets you manage the wallets used for web trading. Every wallet is secured by Privy's Trusted Execution Environment (TEE).
## Embedded Wallet
When you first sign in, Privy automatically creates an embedded Solana wallet for you. This wallet appears on the Wallet page labeled "LaserSell Wallet" and is ready to use immediately.
Your embedded wallet is tied to your account identity, not your browser. You can access the same wallet from any device by signing into your LaserSell account.
## Wallet Limit
Each account can have a maximum of **2 web wallets** (created or imported). This includes the embedded wallet created during onboarding and any wallets you import. If you need to add a different wallet, remove an existing one first.
## Importing a Wallet
You can import an existing Solana wallet to use for web trading.
On the Wallet page, click the "Import Wallet" button.
Paste your base58-encoded private key. The key is sent directly from your browser to Privy's TEE. It never passes through LaserSell servers.
LaserSell verifies ownership by signing a proof message with your wallet, then registers it for trading.
Your private key is encrypted and stored in Privy's Trusted Execution Environment. [Learn about Privy's security](https://docs.privy.io/guide/security).
## Depositing SOL
To trade, your wallet needs SOL for both buying tokens and paying transaction fees.
1. Go to the Wallet page
2. Click **Copy Address** on your wallet
3. Send SOL from your external wallet (Phantom, Backpack, etc.) to that address
## Withdrawing SOL
Click **Withdraw** on any wallet to send SOL or tokens to an external address. The withdrawal is signed by Privy and submitted through LaserSell's Solana RPC.
## Exporting Your Private Key
For embedded wallets, you can export your private key at any time.
1. Click **Export Key** on the wallet card
2. Privy opens a secure window showing your private key
3. Copy it and store it safely
LaserSell never sees your private key during export. The entire process happens within Privy's secure iframe.
## Removing a Wallet
To remove an imported wallet, click the trash icon on the wallet card. This removes the wallet from LaserSell and permanently deletes the private key from Privy's TEE.
Embedded wallets cannot be removed from the dashboard directly since they are tied to your Privy account. To fully remove an embedded wallet, export the private key first, then contact support to request account deletion.
Removing a wallet is irreversible. Export your private key before removing if you want to keep access to the funds.
## Active Wallet
If you have multiple wallets, you can set which one is active by clicking **Set Active** on the wallet card. The active wallet is highlighted in blue and is used as the default for new trading sessions.
## Wallet Security
| Property | Detail |
| ---------------- | ----------------------------------------------------------- |
| **Key storage** | Privy TEE (hardware-isolated enclave) |
| **Key access** | LaserSell can request Privy to sign, but never sees the key |
| **Wallet proof** | ed25519 signature proves ownership at registration |
| **Cross-device** | Same wallet across all browsers and devices |
| **Export** | User can export private key anytime via Privy's secure UI |