@lyslabs.ai/lys-flash
v2.2.1
Published
High-performance TypeScript client for Solana transaction execution - Ultra-low latency trading on Solana with Pump.fun, AMM, and SPL Token support
Maintainers
Keywords
Readme
@lyslabs.ai/lys-flash
High-performance TypeScript client for the LYS Solana Execution Engine — ultra-low latency transaction execution for trading bots.
Features
- 🚀 Multi-Broadcast: Multi-broadcast strategy for fast, reliable confirmations
- 📦 50+ Operations: Pump.fun, Meteora (DBC, DAMM v1/v2, DLMM), Raydium (LaunchPad, CLMM, CPMM, AMMv4), SPL Token, System Transfer
- 🔐 Secure Wallet Creation: Dual-encrypted wallet generation with user-side decryption
- 🔧 Type-Safe: Full TypeScript support with comprehensive types
- ⚡ High Performance: MessagePack over ZeroMQ for efficient communication
- 🎯 Builder Pattern: Fluent API for easy transaction composition
- 🔄 Auto-Reconnect: Automatic connection recovery
- 📊 Statistics: Built-in performance tracking
- 🛡️ MEV Protection: Jito integration for high-value transactions
Table of Contents
- Installation
- Getting Started
- Connection Modes
- API Keys & Authentication
- Usage
- Wallet Management
- Error Handling
- Advanced Usage
- Detailed Documentation
- Contributing
- License
Installation
npm install @lyslabs.ai/lys-flash @solana/web3.jsRPC Connection (Required for Meteora & Raydium)
Meteora and Raydium integrations require a Solana RPC connection — these DEXs build transactions client-side using their SDKs, which need RPC access to fetch pool data.
| Feature | RPC Required | Reason | |---------|--------------|--------| | Pump.fun | No | Server-side transaction building | | Pump.fun AMM | No | Server-side transaction building | | SPL Token | No | Server-side transaction building | | System Transfer | No | Server-side transaction building | | Meteora | Yes | Client-side SDK fetches pool data | | Raydium | Yes | Client-side SDK fetches pool data |
import { Connection } from '@solana/web3.js';
import { LysFlash } from '@lyslabs.ai/lys-flash';
const connection = new Connection('https://api.mainnet-beta.solana.com');
const client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
connection, // Required for Meteora/Raydium
});Optional Peer Dependencies
Install the SDK packages for the DEX products you need:
# Meteora
npm install @meteora-ag/dynamic-bonding-curve-sdk # DBC
npm install @meteora-ag/dynamic-amm-sdk # DAMM v1
npm install @meteora-ag/cp-amm-sdk # DAMM v2
npm install @meteora-ag/dlmm # DLMM
# Raydium
npm install @raydium-io/raydium-sdk-v2 # All Raydium productsGetting Started
Step 1: Get an API Key
Generate your API key at https://dev.lyslabs.ai/api-keys.
Store it as an environment variable:
export LYS_API_KEY=sk_live_your_key_hereStep 2: Connect to the Execution Engine
The execution engine endpoint is http://execution.lyslabs.ai.
Use LysFlash.external() with your API key and a Signer. The signer's public key is used by the execution engine's TEE to deterministically generate wallets, and each request is signed to prove you own that key.
import { LysFlash, TransactionBuilder, Signer } from '@lyslabs.ai/lys-flash';
import { Keypair } from '@solana/web3.js';
const client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
});
// The signer's public key is used for deterministic wallet generation in the TEE
const signer = new Signer(Keypair.fromSecretKey(yourSecretKey));Step 3: Create a Wallet (required for FLASH)
Before using the FLASH transport, you must create a wallet inside the execution engine's TEE. The wallet is deterministically derived from your signer's public key.
import { decryptWallet } from '@lyslabs.ai/lys-flash';
const wallet = await client.createWallet(signer.publicKey());
const walletKeypair = decryptWallet(wallet, yourKeypair);
console.log("Wallet address:", walletKeypair.publicKey.toBase58());Step 4: Execute a Transaction
const result = await new TransactionBuilder(client, signer)
.pumpFunBuy({
pool: "TokenMintAddress",
poolAccounts: { coinCreator: "CreatorWallet" },
user: walletKeypair.publicKey.toBase58(),
solAmountIn: 10_000_000, // 0.01 SOL
tokenAmountOut: 50_000_000_000, // Minimum tokens expected
mayhemModeEnabled: false,
})
.setFeePayer(walletKeypair.publicKey.toBase58())
.setPriorityFee(1_000_000)
.setBribe(1_000_000) // Required for FLASH transport
.setTransport("FLASH")
.send();
console.log("Transaction signature:", result.signature);
client.close();Connection Modes
The client auto-detects the transport based on the URL scheme:
| URL Scheme | Transport | API Key | Use Case |
|------------|-----------|---------|----------|
| ipc:// | ZeroMQ IPC | Not required | Local deployment (fastest) |
| tcp:// | ZeroMQ TCP | Not required | Local network deployment |
| http:// or https:// | HTTP | Required | Remote/cloud deployment |
// Local deployment via ZeroMQ IPC (no API key needed)
const client = new LysFlash();
// or explicitly:
const client = new LysFlash({ address: 'ipc:///tmp/tx-executor.ipc' });
// Local network via ZeroMQ TCP
const client = new LysFlash({ address: 'tcp://127.0.0.1:5555' });
// Remote/cloud via HTTP (API key required)
const client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
});API Keys & Authentication
API Key
Obtain your API key at https://dev.lyslabs.ai/api-keys. The API key authenticates every HTTP request to the execution engine.
Signer
The Signer wraps a Keypair and serves two purposes:
- Wallet generation — the execution engine's TEE uses the signer's public key to deterministically generate wallets
- Request verification — each request is signed with Ed25519, proving the caller owns the public key that was used to create the wallets
Both the API key and a Signer are required when using LysFlash.external():
import { Keypair } from '@solana/web3.js';
import { LysFlash, TransactionBuilder, Signer } from '@lyslabs.ai/lys-flash';
const client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
contentType: 'msgpack', // optional, default: 'msgpack'
});
const signer = new Signer(Keypair.fromSecretKey(mySecretKey));
await new TransactionBuilder(client, signer)
.pumpFunBuy({ /* ... */ })
.setFeePayer('wallet')
.send();
// Different builders can use different signers (multi-wallet support)
const signerB = new Signer(Keypair.fromSecretKey(otherSecretKey));
await new TransactionBuilder(client, signerB)
.pumpFunSell({ /* ... */ })
.setFeePayer('other_wallet')
.send();Each Signer automatically signs every HTTP request. If you forget to pass a signer, send() will throw an error.
Internal Keys (Backend-to-Backend)
Internal keys are for trusted server-to-server environments where request signing is not required. Use LysFlash.internal():
const client = LysFlash.internal({
address: 'http://execution.lyslabs.ai',
apiKey: 'sk_live_internal_key',
});Internal keys are not issued from the developer portal. They are for backend deployments where the caller is trusted.
Signature Protocol
Each signed request includes three headers:
| Header | Value |
|--------|-------|
| X-API-Key | Your API key string |
| X-Timestamp | Date.now() in milliseconds (string) |
| X-Signature | Base58-encoded Ed25519 detached signature |
The signed message is: timestamp_as_u64_big_endian (8 bytes) + serialized_request_body.
The server validates the timestamp is within a 60-second window and the signature matches the public key that was used to create the wallets.
Configuration Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| address | string | ipc:///tmp/tx-executor.ipc | Server URL (http/https/tcp/ipc) |
| apiKey | string | — | API key for HTTP transport (required) |
| contentType | 'json' \| 'msgpack' | 'msgpack' | Request serialization format |
| timeout | number | 30000 | Request timeout in milliseconds |
| autoReconnect | boolean | true | Auto-reconnect on connection loss |
| maxReconnectAttempts | number | 5 | Max reconnection attempts |
| reconnectDelay | number | 1000 | Delay between reconnects (ms) |
| verbose | boolean | false | Enable debug logging |
| connection | Connection | — | Solana RPC connection (required for Meteora/Raydium) |
Usage
Builder API
The TransactionBuilder is the recommended API — fluent, type-safe, and easy to compose.
Basic Example (Pump.fun)
import { LysFlash, TransactionBuilder, Signer } from '@lyslabs.ai/lys-flash';
import { Keypair } from '@solana/web3.js';
const client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
});
const signer = new Signer(Keypair.fromSecretKey(yourSecretKey));
// Buy tokens
const result = await new TransactionBuilder(client, signer)
.pumpFunBuy({
pool: "TokenMintAddress",
poolAccounts: { coinCreator: "CreatorWallet" }, // optional but speeds up execution
user: "YourWallet",
solAmountIn: 10_000_000, // 0.01 SOL in lamports
tokenAmountOut: 50_000_000_000, // minimum tokens expected
mayhemModeEnabled: false,
})
.setFeePayer("YourWallet")
.setPriorityFee(5_000_000)
.setBribe(1_000_000) // required for all MEV-protected transports
.setTransport("FLASH")
.send();
console.log("Signature:", result.signature);Batched Operations
Chain multiple operations in a single atomic transaction:
import { Keypair } from '@solana/web3.js';
const mintKeypair = Keypair.generate();
const result = await new TransactionBuilder(client, signer)
.pumpFunCreate({
user: "CreatorWallet",
pool: mintKeypair.publicKey.toBase58(),
mintSecretKey: Buffer.from(mintKeypair.secretKey).toString('base64'),
meta: { name: "My Token", symbol: "MTK", uri: "https://arweave.net/metadata.json" },
})
.pumpFunBuy({
pool: mintKeypair.publicKey.toBase58(),
poolAccounts: { coinCreator: "CreatorWallet" },
user: "CreatorWallet",
solAmountIn: 10_000_000,
tokenAmountOut: 100_000_000_000,
mayhemModeEnabled: false,
})
.setFeePayer("CreatorWallet")
.setPriorityFee(10_000_000)
.setBribe(1_000_000)
.setTransport("FLASH")
.send();Meteora Operations
Meteora operations are accessed via the .meteora namespace (requires a Connection in the client config):
// DBC (Dynamic Bonding Curve)
await new TransactionBuilder(client, signer).meteora.dbc.buy({ pool, user, solAmountIn, minTokensOut });
// DAMM v1 (Dynamic AMM)
await new TransactionBuilder(client, signer).meteora.dammV1.buy({ pool, user, tokenMint, solAmountIn, minTokensOut });
// DAMM v2 (CP-AMM)
await new TransactionBuilder(client, signer).meteora.dammV2.buy({ pool, user, tokenMint, solAmountIn, minTokensOut });
// DLMM (Dynamic Liquidity Market Maker)
await new TransactionBuilder(client, signer).meteora.dlmm.buy({ pool, user, tokenMint, solAmountIn, minTokensOut });See docs/METEORA_DBC.md and related guides for full parameter reference.
Raydium Operations
Raydium operations are accessed via the .raydium namespace (requires a Connection in the client config):
// LaunchPad
await new TransactionBuilder(client, signer).raydium.launchpad.buy({ /* ... */ });
// CLMM (Concentrated Liquidity)
await new TransactionBuilder(client, signer).raydium.clmm.buy({ /* ... */ });
// CPMM (Constant Product)
await new TransactionBuilder(client, signer).raydium.cpmm.buy({ /* ... */ });
// AMMv4
await new TransactionBuilder(client, signer).raydium.ammv4.buy({ /* ... */ });See docs/RAYDIUM_CLMM.md and related guides for full parameter reference.
Simulate Before Sending
const simulation = await new TransactionBuilder(client, signer)
.pumpFunBuy({ /* params */ })
.setFeePayer("wallet")
.simulate(); // uses SIMULATE transport automatically
if (!simulation.success) {
console.error("Simulation failed:", simulation.error);
return;
}
// Then execute
const result = await new TransactionBuilder(client, signer)
.pumpFunBuy({ /* same params */ })
.setFeePayer("wallet")
.setBribe(1_000_000)
.setTransport("FLASH")
.send();Client API
The client.execute() method provides direct, low-level access for maximum control:
const result = await client.execute({
data: {
executionType: "PUMP_FUN",
eventType: "BUY",
pool: "TokenMintAddress",
poolAccounts: { coinCreator: "CreatorWallet" },
user: "YourWallet",
solAmountIn: 10_000_000,
tokenAmountOut: 50_000_000_000,
mayhemModeEnabled: false,
},
feePayer: "YourWallet",
priorityFeeLamports: 1_000_000,
bribeLamports: 1_000_000,
transport: "FLASH",
});For full raw API documentation see docs/RAW_API.md.
Supported Operations
Pump.fun (7 operations)
pumpFunBuy()— Buy tokens on bonding curvepumpFunBuyExactSolIn()— Buy with exact SOL inputpumpFunSell()— Sell tokens on bonding curvepumpFunCreate()— Create new tokenpumpFunCreateV2()— Create new token with mayhem mode + cashbackpumpFunClaimCashback()— Claim cashback rewardspumpFunMigrate()— Migrate to Pump.fun AMM
Pump.fun AMM (4 operations)
pumpFunAmmBuy()— Buy on AMM (max quote in, expected base out)pumpFunAmmBuyExactQuoteIn()— Buy with exact SOL amountpumpFunAmmSell()— Sell on AMMpumpFunAmmClaimCashback()— Claim AMM cashback rewards
Meteora DBC — Dynamic Bonding Curve (8 operations)
meteora.dbc.buy()/sell()/swap()meteora.dbc.buy2()/sell2()/swap2()— ExactIn / PartialFill / ExactOut modesmeteora.dbc.buyExactOut()/sellExactOut()
Meteora DAMM v1 — Dynamic AMM (3 operations)
meteora.dammV1.buy()/sell()/swap()
Meteora DAMM v2 — CP-AMM (8 operations)
meteora.dammV2.buy()/sell()/swap()meteora.dammV2.buy2()/sell2()/swap2()meteora.dammV2.buyExactOut()/sellExactOut()
Meteora DLMM — Dynamic Liquidity Market Maker (6 operations)
meteora.dlmm.buy()/sell()/swap()meteora.dlmm.swapExactOut()/buyExactOut()/sellExactOut()
Raydium LaunchPad
raydium.launchpad.buy()/sell()
Raydium CLMM — Concentrated Liquidity (3+ operations)
raydium.clmm.buy()/sell()/swap()
Raydium CPMM — Constant Product (3+ operations)
raydium.cpmm.buy()/sell()/swap()
Raydium AMMv4 (3+ operations)
raydium.ammv4.buy()/sell()/swap()
System Transfer (1 operation)
systemTransfer()— Transfer native SOL
SPL Token (9 operations)
splTokenTransfer()/splTokenTransferChecked()splTokenCreateATA()/splTokenCloseAccount()splTokenApprove()/splTokenRevoke()splTokenMintTo()/splTokenBurn()/splTokenSyncNative()
Raw Transaction (1 operation)
rawTransaction()— Execute a pre-built@solana/web3.jsTransaction
Raw Transactions
Execute pre-built @solana/web3.js transactions directly — useful when you need full control over transaction construction or want to integrate with other libraries.
import { Transaction, SystemProgram, PublicKey } from '@solana/web3.js';
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: new PublicKey('SenderWallet'),
toPubkey: new PublicKey('RecipientWallet'),
lamports: 1_000_000,
})
);
const result = await new TransactionBuilder(client, signer)
.rawTransaction({ transaction })
.setFeePayer('SenderWallet')
.setPriorityFee(1_000_000)
.setBribe(1_000_000)
.setTransport('FLASH')
.send();For multi-signer transactions:
const result = await new TransactionBuilder(client, signer)
.rawTransaction({
transaction,
additionalSigners: ['AdditionalSignerPublicKey1', 'AdditionalSignerPublicKey2'],
})
.setFeePayer('FeePayerPublicKey')
.setTransport('FLASH')
.setBribe(1_000_000)
.send();Supports both legacy Transaction and VersionedTransaction (v0 with address lookup tables). The server handles all signing — never send secret keys over the wire.
Transport Modes
| Mode | Description | MEV Protection | Bribe Required | |------|-------------|----------------|----------------| | FLASH ⭐ | Multi-broadcast to 6 endpoints (recommended) | Yes | Yes (min 1,000,000) | | ZERO_SLOT | Ultra-fast specialized endpoint | Yes | Yes (min 1,000,000) | | LUNAR_LANDER | HelloMoon low-latency endpoint | Yes | Yes (min 1,000,000) | | NOZOMI | Low-latency Temporal endpoint | Yes | Yes (min 1,000,000) | | HELIUS_SENDER | Premium reliability | Yes | Yes (min 1,000,000) | | JITO | MEV-protected via Jito | Yes | Yes (min 1,000,000) | | VANILLA | Standard RPC | No | No | | SIMULATE | Test without broadcasting | No | No | | DEVNET | Devnet endpoint for testing | No | No | | SIMULATE_DEVNET | Devnet simulation (no broadcast) | No | No |
All transports except VANILLA, SIMULATE, DEVNET, and SIMULATE_DEVNET require a minimum bribe of 1,000,000 lamports (0.001 SOL). Use FLASH for production trading.
Wallet Management
The wallet management system runs inside the execution engine's TEE and is required when using the FLASH transport layer.
The client provides dual-encrypted wallet creation:
- Server-side (AES-256-GCM): Secret key encrypted with the server's master secret
- Client-side (TweetNaCl box): Additional encryption with your Ed25519 public key — only you can decrypt
Creating a Wallet
import { LysFlash } from '@lyslabs.ai/lys-flash';
import { Keypair } from '@solana/web3.js';
import nacl from 'tweetnacl';
const client = new LysFlash();
const userKeypair = Keypair.generate(); // your encryption keypair
const wallet = await client.createWallet(userKeypair.publicKey.toBase58());
console.log("New wallet:", wallet.publicKey);Response format:
{
success: true,
publicKey: string, // new wallet address
encryptedSecretKey: string, // base64, encrypted with your public key
nonce: string, // base64
ephemeralPublicKey: string // base64, needed for decryption
}Decrypting a Wallet
import { decryptWallet } from '@lyslabs.ai/lys-flash';
// Handles Ed25519 → Curve25519 conversion internally
const walletKeypair = decryptWallet(wallet, userKeypair);
console.log("Ready:", walletKeypair.publicKey.toBase58());Security guarantees: the server never stores plaintext secret keys; only you can decrypt with your private key. See docs/WALLET_MANAGEMENT.md for storage best practices and security checklist.
Error Handling
import { ExecutionError, ErrorCode } from '@lyslabs.ai/lys-flash';
try {
const result = await new TransactionBuilder(client, signer)
.pumpFunBuy({ /* ... */ })
.setFeePayer("wallet")
.setBribe(1_000_000)
.setTransport("FLASH")
.send();
if (result.success) {
console.log("Signature:", result.signature);
console.log("Slot:", result.slot);
console.log("Latency:", result.latency, "ms");
} else {
console.error("Failed:", result.error);
console.log("Logs:", result.logs);
}
} catch (error) {
if (error instanceof ExecutionError) {
switch (error.code) {
case ErrorCode.NONCE_POOL_EXHAUSTED:
console.log("Nonce pool exhausted, wait and retry");
break;
case ErrorCode.TIMEOUT:
console.log("Request timeout");
break;
case ErrorCode.NETWORK_ERROR:
console.log("Network error, check connection");
break;
default:
console.log(error.getUserMessage());
}
if (error.isRetryable()) console.log("This error can be retried");
}
}Advanced Usage
Trading Bot Example
import { LysFlash, TransactionBuilder, Signer, ExecutionError, ErrorCode } from '@lyslabs.ai/lys-flash';
import { Keypair } from '@solana/web3.js';
class TradingBot {
private client: LysFlash;
private signer: Signer;
constructor() {
this.client = LysFlash.external({
address: 'http://execution.lyslabs.ai',
apiKey: process.env.LYS_API_KEY!,
timeout: 30000,
autoReconnect: true,
});
this.signer = new Signer(Keypair.fromSecretKey(
Buffer.from(process.env.SIGNING_KEY!, 'base64')
));
}
async buyToken(mint: string, creator: string, wallet: string, solAmount: number, minTokens: number) {
const params = {
pool: mint,
poolAccounts: { coinCreator: creator },
user: wallet,
solAmountIn: solAmount,
tokenAmountOut: minTokens,
mayhemModeEnabled: false,
};
// Simulate first
const sim = await new TransactionBuilder(this.client, this.signer)
.pumpFunBuy(params)
.setFeePayer(wallet)
.simulate();
if (!sim.success) throw new Error(`Simulation failed: ${sim.error}`);
// Execute with FLASH
return new TransactionBuilder(this.client, this.signer)
.pumpFunBuy(params)
.setFeePayer(wallet)
.setPriorityFee(5_000_000)
.setBribe(1_000_000)
.setTransport("FLASH")
.send();
}
shutdown() {
this.client.close();
}
}Performance Tips
- Reuse the client instance — never create a new
LysFlashper transaction - Use FLASH transport — multi-broadcast for redundancy and speed
- Simulate before production — catch failures before spending gas
- Batch related operations — multiple operations in one atomic transaction
- Set appropriate priority fees — higher fees land faster
- Provide pool accounts — supplying
coinCreator/poolCreatoravoids RPC lookups - Monitor statistics — use
client.getStats()to track success rate and latency
const stats = client.getStats();
console.log(`Success rate: ${(stats.requestsSuccessful / stats.requestsSent * 100).toFixed(2)}%`);
console.log(`Average latency: ${stats.averageLatency.toFixed(2)}ms`);Detailed Documentation
API Guides
- Transaction Builder Guide — Complete
TransactionBuilderAPI reference (recommended) - Raw API Guide — Complete
client.execute()reference for low-level control - Wallet Management — Wallet creation, encryption, and security best practices
Protocol Integration Guides
Pump.fun
- Pump.fun Bonding Curve — buy, sell, create, migrate
- Pump.fun AMM — post-graduation AMM trading
Meteora
- Meteora DBC — Dynamic Bonding Curve
- Meteora DAMM v1 — Dynamic AMM v1
- Meteora DAMM v2 — Dynamic AMM v2 / CP-AMM
- Meteora DLMM — Dynamic Liquidity Market Maker
Raydium
- Raydium LaunchPad — LaunchPad token launches
- Raydium CLMM — Concentrated Liquidity Market Maker
- Raydium CPMM — Constant Product Market Maker
- Raydium AMMv4 — AMM v4 pools
Examples
Working code examples in examples/:
| File | Description |
|------|-------------|
| basic-usage.ts | Simple buy/sell with error handling |
| transaction-builder-usage.ts | Builder API patterns |
| raw-api-usage.ts | Direct client.execute() API |
| raw-transaction-usage.ts | Pre-built transaction execution |
| wallet-management.ts | Wallet creation and decryption |
| meteora-dbc-usage.ts | Meteora DBC operations |
| meteora-damm-v1-usage.ts | Meteora DAMM v1 operations |
| meteora-damm-v2-usage.ts | Meteora DAMM v2 operations |
| meteora-dlmm-usage.ts | Meteora DLMM operations |
| raydium-launchpad-usage.ts | Raydium LaunchPad operations |
| raydium-clmm-usage.ts | Raydium CLMM operations |
| raydium-cpmm-usage.ts | Raydium CPMM operations |
| raydium-ammv4-usage.ts | Raydium AMMv4 operations |
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT © LYS Labs
See LICENSE for more information.
