Rust solver for UniswapX Dutch auction intents. The solver internalises retail flow by pricing against it off-chain and hedging residual inventory on Binance/OKX.
┌──────────────────────────────────────────────────────────────────────┐
│ rfq-solver │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Auction Loop (200ms) │ │
│ │ poll UniswapX → parse intent → quote → fill on-chain → hedge │ │
│ └──────┬──────────────────────────────────┬────────────────────────┘ │
│ │ │ │
│ ┌──────▼──────────┐ ┌──────────▼──────────────────────┐ │
│ │ Pricing Engine │ │ Hedge Engine │ │
│ │ │ │ │ │
│ │ mid_price │ │ Binance REST ←→ OKX REST │ │
│ │ + fee_model │ │ Market order (immediate delta │ │
│ │ + slippage_est │ │ neutral hedge after each fill) │ │
│ │ + inv_skew │ └──────────────────────────────────┘ │
│ └──────┬──────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────┐ │
│ │ CEX Feed Manager │ │
│ │ Binance WS ──► BookCache │ │
│ │ OKX WS ──► (DashMap, lockfree) │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
execution_price = mid ± (fee_component + slippage_component + inventory_skew)
| Component | Formula |
|---|---|
| Fee | (protocol_fee + cex_taker_fee + gas + margin) / qty |
| Slippage | Book-walk simulation on top-of-book; square-root for large sizes |
| Inventory Skew | γ × (net_pos / max_pos) × mid × qty |
Where γ (gamma) is the inventory risk-aversion parameter (default 0.5%).
UniswapX orders linearly decay their output:
output(t) = start_amount − (start_amount − end_amount) × (t − t₀) / (T − t₀)
The solver must fill at or above end_amount (floor). The edge is:
edge = execution_price − hedge_cost_per_unit
Trades are only executed when edge_bps ≥ min_edge_bps (default: 1 bps).
rfq-solver/
├── crates/
│ ├── pricing/ Core pricing: quote engine, fees, slippage, inventory
│ ├── cex-client/ Binance + OKX WebSocket feeds + REST hedging clients
│ ├── chain-client/ UniswapX order book, intent parsing, reactor settlement
│ ├── hedger/ Hedge engine, position tracker, P&L accounting
│ └── solver/ Main binary: auction loop, config, Prometheus metrics
├── config/
│ └── .env.example Environment variable template
└── README.md
- Rust 1.78+ (
rustup update stable) - Funded Ethereum wallet (solver address)
- Binance and/or OKX API keys with trading permissions
- Ethereum RPC endpoint (Infura, Alchemy, or public LlamaRPC)
cp config/.env.example .env
# fill in .env with your credentials
cargo build --release
RUST_LOG=info ./target/release/rfq-solverPrometheus metrics are exposed on http://localhost:9090/metrics.
Key metrics to alert on:
| Metric | Alert Condition |
|---|---|
rfq_hedge_failures_total |
Any increment → CRITICAL |
rfq_pnl_usd |
Falling below threshold |
rfq_inventory_utilisation |
> 80% → WARNING |
- Wire
hmac+sha2crates for real HMAC-SHA256 (replace stub inrest.rs) - Wire Alloy provider in
ReactorClient::fill_intent(replace stub) - Wire Alloy provider in
EthRpcClient::gas_snapshotfor live gas - Add
ERC20.approve(reactor, MAX_UINT)setup script for output tokens - Implement emergency hedge / circuit breaker on hedge failure
- Add max slippage check before on-chain settlement
- Persist fills to PostgreSQL for audit trail
- Add Grafana dashboard for monitoring P&L and inventory
- Implement MEV protection (Flashbots / MEV Blocker RPC)
- Add multi-asset support beyond ETH pairs
Why immediate full hedge? Simpler P&L accounting and eliminates directional risk during the latency window. The 12-second Ethereum block time is the main exposure window.
Why DashMap for book cache?
Lock-free reads on the hot path (quote generation). The book is updated at
~50-100 msg/sec; a Mutex<HashMap> would create contention.
Why Decimal (not f64) for prices?
Floating-point rounding errors compound in financial arithmetic.
rust_decimal is 128-bit fixed-point and exact for the precision needed.
Why market orders for hedging? Speed over price. For retail-sized flow (< 10 ETH), market impact on Binance is minimal (~1-2 bps) and the priority is delta neutrality within milliseconds.