Home Source Router

Introduction

flow-trades is a self-hosted Solana swap API. One Rust binary: it discovers pools, prices them with each venue's own arithmetic, and returns unsigned transactions that settle through the on-chain flow-router. It never holds keys or funds.

Source: flow-trades-solana (server) and flow-router-solana (on-chain router). Both MIT.

Quick Start

git clone https://github.com/sigint-zero/flow-trades-solana.git
cd flow-trades-solana
cargo build --release
# config.toml
rpc_url = "https://your-solana-rpc.com"
[streaming]
geyser_endpoint = "http://your-geyser-node:10000"   # optional
./target/release/flow-trades
curl "http://localhost:8080/quote?input=So11111111111111111111111111111111111111112&output=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&amount=1000000000"

Without a Geyser endpoint the server streams blocks over RPC blockSubscribe; same features, slightly higher latency.

Configuration

OptionEnvDefaultPurpose
rpc_urlRPC_URLrequiredSolana RPC
geyser_endpointGEYSER_ENDPOINT--Yellowstone gRPC, optional
geyser_tokenGEYSER_TOKEN--gRPC auth token
listenLISTEN_ADDR127.0.0.1:8080API address
router_program_idROUTER_PROGRAM_IDfirst-generation routerRouter to wrap swaps with
referral_accountREFERRAL_ACCOUNT--Integrator wallet for the referral fee share
alt_addressesALT_ADDRESSES--Address lookup tables for v0 transactions
pool_dbPOOL_DB_PATH./pools.dbSQLite pool registry
swap_stream_enabledSWAP_STREAM_ENABLEDtrue/swap-stream on/off
log_levelLOG_LEVELwarntrace … error

Priority: CLI flags > env vars > config.toml > defaults.

Requirements

API Reference

GET /quote
POST /swap
GET /health
GET /metrics
GET /program-id-to-label
WS /quote-ws
WS /swap-stream

GET /quote

ParameterRequiredDefaultDescription
inputyes--input mint
outputyes--output mint
amountyes--raw amount, smallest units
slippageno50basis points
direct_onlynotruefalse allows 2- and 3-hop routes
dexes / excludeno--DEX label allow / deny list
{
  "input_token": "So111…", "output_token": "EPjFW…",
  "amount_in": "1000000000", "amount_out": "162340000", "minimum_out": "161528300",
  "slippage_bps": 50, "price_impact": "0.12",
  "routes": [ { "pool": { "pool_address": "…", "dex": "Orca", "amount_in": "1000000000", "amount_out": "162340000", "fee": "40000" }, "percent": 100 } ],
  "platform_fee": { "amount": "0", "fee_bps": 0, "fee_token": "EPjFW…", "side": "output" },
  "quote_time_ms": 0.05
}

amount_out is the DEX output before the router fee. On multi-hop routes each later hop is quoted on the previous hop's guaranteed output, so the quote is what the route delivers.

POST /swap

FieldRequiredDefaultDescription
walletyes--signer public key
quoteyes--the /quote response
tx_versionno01 = transaction format v1 (4,096 B, no lookup tables); use when a v0 build is rejected as too large
simulatenofalsesimulate and return CU + logs
priority_feeno5000lamports
compute_limitnoautocompute units
tipno--{ "address": "…", "lamports": 10000 }
{ "transaction": "base64…", "tx_version": 0, "block_height": 412875100, "compute_limit": 400000, "priority_fee": 5000 }

Sign the transaction with the wallet and submit it with sendTransaction.

GET /health

{ "status": "ok", "poolCacheSize": 2352, "registrySize": 25031, "streamUpdates": 2919, "streamErrors": 0,
  "blockhashCacheAgeMs": 398, "lastStreamUpdateMs": 65,
  "router": { "programId": "…", "feeBps": 0, "layout": "TransferChecked" } }

GET /metrics

Prometheus format: quotes served, stream updates and errors, cache, registry and database sizes.

WS /quote-ws

{ "input_mint": "So111…", "output_mint": "EPjFW…", "amount": 1000000000, "slippage_bps": 50, "interval_ms": 1000 }

Server pushes a full quote at the interval (minimum 100 ms).

WS /swap-stream

Every confirmed DEX swap parsed from blocks, with native and USD prices. Optional first frame:

{ "type": "subscribe", "filter": { "dex": ["PumpFun AMM"], "mint": "…", "pool": "…", "min_amount_usd": 10.0 } }
{ "type": "swap", "signature": "…", "slot": 412341234, "dex": "PumpFun AMM", "pool": "…", "user": "…",
  "input":  { "mint": "So111…", "amount": "1000000", "decimals": 9, "ui_amount": "0.001" },
  "output": { "mint": "…", "amount": "24637903819", "decimals": 6, "ui_amount": "24637.9" },
  "price_native": "24637903.82", "price_usd": "0.00000647", "amount_usd": "0.16" }

Frames: subscribed once, swap, ping every 30 s, lagged when a slow consumer drops messages. USDC, USDT and PYUSD count as $1; SOL comes from a built-in oracle; other pairs have null USD fields.

Fee Structure

Ratefee_bps in the router's config account; 0 = no fee, no fee accounts
Sideoutput token, after the slippage check
Splitreferral_split_bps to a whitelisted integrator, rest to the treasury

The server reads the fee from the router named by ROUTER_PROGRAM_ID and reports it in every quote's platform_fee.

On-Chain Router

Source: flow-router-solana. One instruction runs 1–5 DEX legs as CPIs, checks the final output against min_amount_out, and collects the fee with TransferChecked if one is configured.

GenerationProgramFeeNotes
FirstFLoWxxKoBrZtNj5NTPuy1tZcSU6Nnjtz7v5snrrUsNqm50 bps, 70/30immutable; plain Transfer, so Token-2022 mints with extensions cannot be traded through it; audited by SigIntZero
Secondbuilt from flow-router-solanaconfigurable, including 0TransferChecked; upgradeable

Integrators

If the router charges a fee, a whitelisted integrator wallet receives referral_split_bps of it. Set referral_account to that wallet; the admin whitelists it with add_integrator.

Supported DEXes

VenueQuoted
Raydium CPMM, Raydium CLMM, Raydium LaunchLab, Orca Whirlpool, PancakeSwap, DefiTuna Fusion, Meteora DAMM v2, Meteora Standard, Dooaryes, exact venue math
pump.fun AMMyes, exact curve and fee tiers
Sarosyes, fee observed from its swaps
Raydium V4, pump.fun bonding, Meteora DLMM, Meteora DBC, FluxBeamstreamed, not quoted
Byrealnot quotable (Raydium CLMM fork, parser uses the Orca layout)
FlashTrade, Pumpup AMM, Pumpup Bonding, DefiTuna Poolsstale
OnChain Labs DEX V2aggregator into private market makers; discovery only

Performance

MetricValue
/quote, warm, direct or multi-hop0.1–0.5 ms end to end
/quote, first quote of an unseen pair0.5–2 s, then warm
Quote engine, 300 pools50 µs direct / 70 µs routed (p50)
/swap build0.5–1.6 ms cached; 160–520 ms with RPC lookups
Quote vs simulated output0 bps on fresh state; ~5 bps multi-hop
Swap stream~110 swaps per slot
Memory~35 MB

Error Codes

Router
1SlippageExceeded
2InvalidFeeBps
5NotEnoughAccounts
6InvalidAccount
7AlreadyInitialized
8Unauthorized
API
400bad parameters, or a v0 transaction over 1,232 bytes (retry with tx_version: 1)
404no route
500router not configured, RPC failure

Security

Source Code