PerpCity Rust SDK
Rust SDK for the PerpCity perpetual futures protocol on Base L2. Built for high-frequency trading with lock-free nonce management, multi-endpoint transport with circuit breakers, and a 2-tier state cache.
Installation
[]
= "0.1"
Quickstart
Prerequisites: Rust 1.85+, a Base Sepolia RPC (the public endpoint works fine).
- Clone and create a
.envfile:
PERPCITY_PRIVATE_KEY=your_hex_private_key
PERPCITY_MANAGER=0x826e1ddEab1f9586e336F2beb7621Edb8FB6138D
PERPCITY_PERP_ID=0x...
- Fund your wallet on Base Sepolia — you need a small amount of ETH for gas and some USDC for margin. The testnet USDC has a public
mintfunction:
# Mint 10,000 USDC to your address
- Run the quickstart:
Examples
All examples load configuration from .env automatically via dotenvy.
| Example | Run | What it does |
|---|---|---|
| quickstart | cargo run --example quickstart |
Open a 2x long, close it. ~20 lines of setup. |
| open_position | cargo run --example open_position |
Full taker lifecycle: market data, open, monitor PnL/funding/liquidation, close. |
| market_maker | cargo run --example market_maker |
LP position: calculate tick range around mark, estimate liquidity, open maker position. Note: makers are currently subject to a 7-day lockup, so this example shouldn't run. |
| hft_bot | cargo run --example hft_bot |
Full trading loop: multi-endpoint transport, momentum strategy, position manager with SL/TP/trailing stop, latency stats. |
API Overview
Client Setup
use *;
// 1. Transport — single or multi-endpoint
let transport = new?;
// 2. Client
let client = new?;
// 3. Warm caches (required before first transaction)
client.sync_nonce.await?;
client.refresh_gas.await?;
client.ensure_approval.await?;
Trading
// Open a 5x long with 10 USDC margin
let pos_id = client.open_taker.await?;
// Close it
let result = client.close_position.await?;
Every write method takes an Urgency level that scales the EIP-1559 priority fee:
| Urgency | Priority fee multiplier | Use case |
|---|---|---|
Low |
0.8x | Background tasks |
Normal |
1.0x | Standard trading |
High |
1.5x | Time-sensitive fills |
Critical |
2.0x | Liquidation defense |
Market Data
let config = client.get_perp_config.await?; // mark, fees, bounds
let mark = client.get_mark_price.await?; // f64 price
let funding = client.get_funding_rate.await?; // daily rate
let oi = client.get_open_interest.await?; // long/short OI
let live = client.get_live_details.await?; // PnL, funding, liquidation
let balance = client.get_usdc_balance.await?; // wallet USDC
HFT Infrastructure
The SDK is designed for sub-millisecond transaction preparation on the hot path.
Multi-endpoint transport — configure multiple RPCs with automatic failover:
let transport = new?;
Lock-free nonce management — AtomicU64::fetch_add for O(1) nonce acquisition. No mutex on the hot path.
Gas cache — EIP-1559 base fee + priority fee cached from block headers. Refreshed per block, never estimated per-tx.
2-tier state cache:
- Slow tier (60s TTL): fees, margin bounds — rarely change
- Fast tier (2s TTL): mark price, funding rate, USDC balance — refreshed per block
Position manager — track open positions with automated triggers:
pos_manager.track;
Latency tracker — rolling-window stats (P50/P95/P99) for monitoring RPC and tx latency.
Math Utilities
use ;
use ;
use estimate_liquidity;
let tick = price_to_tick?;
let price = tick_to_price?;
let liq = estimate_liquidity?;
All math functions are pure, O(1), and ported faithfully from PerpCity's Solidity contracts and Uniswap V4's TickMath.
Environment Variables
| Variable | Required | Description |
|---|---|---|
PERPCITY_PRIVATE_KEY |
Yes | Hex-encoded private key (with or without 0x prefix) |
PERPCITY_MANAGER |
Yes | PerpManager contract address |
PERPCITY_PERP_ID |
Yes | bytes32 perp market identifier |
RPC_URL |
No | RPC endpoint (default: https://sepolia.base.org) |
RPC_URL_1, RPC_URL_2 |
No | Multi-endpoint config for hft_bot example |
Configuration
Deployments
The Deployments struct holds contract addresses. For Base Sepolia:
let deployments = Deployments ;
let client = new?; // Base Sepolia
For Base mainnet, use PerpClient::new_base_mainnet() which sets chain ID 8453 and the mainnet USDC address (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913).
Release Profile
The crate ships with aggressive release optimizations for trading workloads:
[]
= "fat" # Cross-crate inlining
= 1 # Maximum optimization
= "abort" # No unwind overhead
= "symbols" # Smaller binary
Benchmarks
| Benchmark | What it measures |
|---|---|
math_bench |
Tick math, price conversions, entry/liquidation price |
hft_bench |
Nonce acquire (~1-5ns), gas peek, cache hit/miss, position triggers |
transport_bench |
Endpoint selection, circuit breaker state checks, struct sizes |
License
MIT