Installation
How to add the PerpCity Rust SDK to your project.
Prerequisites
- Rust 1.85+ (2024 edition)
- A wallet private key
- Access to a Base chain RPC endpoint
- Tokio async runtime
Add as a Dependency
Add to your Cargo.toml:
[dependencies]
perpcity-sdk = "0.2"
alloy = "1"
tokio = { version = "1", features = ["full"] }Environment Configuration
Create a .env file in your project root:
RPC_URL=https://your-rpc-endpoint.example/v2/YOUR_KEY
READ_RPC_URL=https://base-rpc.publicnode.com # optional, for read/write split
PRIVATE_KEY=0xYOUR_PRIVATE_KEY
PERP_MANAGER_ADDRESS=0xPERP_MANAGER
USDC_ADDRESS=0xUSDCNever commit your .env file to version control. Add it to .gitignore to protect your private key.
Basic Setup
use alloy::primitives::{Address, U256};
use alloy::signers::local::PrivateKeySigner;
use perpcity_sdk::{PerpClient, Deployments, HftTransport, TransportConfig};
#[tokio::main]
async fn main() -> perpcity_sdk::Result<()> {
let rpc_url = std::env::var("RPC_URL").expect("RPC_URL required");
let private_key = std::env::var("PRIVATE_KEY").expect("PRIVATE_KEY required");
let perp_manager = std::env::var("PERP_MANAGER_ADDRESS").expect("PERP_MANAGER_ADDRESS required");
let usdc = std::env::var("USDC_ADDRESS").expect("USDC_ADDRESS required");
let signer: PrivateKeySigner = private_key.parse()?;
let transport = HftTransport::new(
TransportConfig::builder()
.shared_endpoint(&rpc_url)
.build()?,
)?;
let deployments = Deployments {
perp_manager: perp_manager.parse::<Address>()?,
usdc: usdc.parse::<Address>()?,
..Default::default()
};
let client = PerpClient::new_base_mainnet(transport, signer, deployments)?;
// Required: initialize nonce and gas before trading
client.sync_nonce().await?;
client.refresh_gas().await?;
client.ensure_approval(U256::MAX).await?;
// Verify connection
let perp_id: alloy::primitives::B256 = "0xYOUR_PERP_ID".parse()?;
let (config, snapshot) = client.get_perp_snapshot(perp_id).await?;
println!("Mark price: {:.2}", snapshot.mark_price);
Ok(())
}Always call sync_nonce(), refresh_gas(), and ensure_approval() before your first transaction. These initialize the lock-free nonce tracker, fee cache, and USDC spending approval.
Endpoint Pools
The transport organizes endpoints into three pools: shared, read, and write. Every RPC request is automatically classified as a read (eth_call, eth_getBalance, etc.) or write (eth_sendRawTransaction) and routed to the appropriate pool.
Read request: read pool → shared pool (fallback)
Write request: write pool → shared pool (fallback)Each pool is an independent unit with its own endpoints, circuit breakers, latency tracking, and round-robin counter. If all endpoints in the dedicated pool are unhealthy (circuit breakers tripped), requests automatically fall back to the shared pool. When the dedicated pool recovers (circuit breaker probes succeed after the recovery timeout), requests route back to it.
let transport = HftTransport::new(
TransportConfig::builder()
.shared_endpoint("https://base.g.alchemy.com/v2/KEY") // writes + read fallback
.read_endpoint("https://base-rpc.publicnode.com") // dedicated reads (free)
.build()?,
)?;| Pool | Builder method | Purpose |
|---|---|---|
| Shared | .shared_endpoint() | Handles any request type, serves as fallback for both read and write pools |
| Read | .read_endpoint() | Dedicated to read operations. Use free/public RPCs here to reduce costs |
| Write | .write_endpoint() | Dedicated to write operations. Use reliable paid RPCs for transaction broadcast |
This design enables routing reads to free public RPCs while reserving paid endpoints for writes -- the primary mechanism for reducing RPC costs. Each endpoint only needs to appear in one pool (no duplication).
If you only use .shared_endpoint(), all requests go through the shared pool -- the same behavior as a single-pool setup. The read and write pools are optional.
Multi-Endpoint Transport
For production setups, configure multiple endpoints per pool with automatic failover:
use perpcity_sdk::transport::config::Strategy;
use std::time::Duration;
let transport = HftTransport::new(
TransportConfig::builder()
.shared_endpoint("https://primary-rpc.com")
.shared_endpoint("https://fallback-rpc.com")
.strategy(Strategy::LatencyBased)
.request_timeout(Duration::from_secs(2))
.build()?,
)?;Available strategies:
| Strategy | Description |
|---|---|
RoundRobin | Cycle through endpoints sequentially |
LatencyBased | Route to the lowest-latency healthy endpoint (default) |
Hedged { fan_out } | Fan out to N endpoints, take the fastest response |
Release Profile
For production builds, add to your Cargo.toml:
[profile.release]
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"Build Commands
cargo build # Debug build
cargo build --release # Optimized release build
cargo test # Run tests
cargo run --example quickstart # Run quickstart exampleNext Steps
- Browse the API Reference for all types, functions, and HFT infrastructure
- Check the examples for full trading workflows