Perp CityDocs

PerpCity Rust SDK

High-performance Rust SDK for HFT bots and latency-sensitive trading on PerpCity perpetual futures.

The PerpCity Rust SDK is a high-performance Rust library for the PerpCity perpetual futures protocol on Arbitrum. Built for HFT bots and latency-sensitive trading systems with lock-free nonce management, read/write endpoint splitting, Multicall3 batching, cached gas estimation, and block-pinned maker-equity previews.

Why Rust?

  • Zero-cost abstractions -- Async/await with Tokio, no runtime overhead
  • Lock-free nonce management -- AtomicU64 for O(1) nonce acquisition on the hot path
  • Read/write endpoint splitting -- Route reads to free public RPCs, writes to paid endpoints, with automatic circuit-breaker failover
  • Multicall3 batching -- Fetch N balances or a full market snapshot in a single RPC call (1 CU instead of N)
  • Universal pre-flight simulation -- Every send is simulated before broadcast; would-be reverts decode into typed errors instead of burning gas on-chain
  • Local swap simulation -- Quote taker trades against a block-pinned order book with contract-exact integer math, no RPC on the quote path
  • Maker equity previews -- Batched, block-pinned settle previews for open maker positions, validated against a real on-chain liquidation
  • Human-readable API -- f64 prices and margins, SDK handles on-chain 6-decimal conversion

Architecture

LayerDescriptionNetwork Required
Pure MathPrice/tick conversions, position calculations, liquidity math, swap simulation, maker settle mathNo
HFT InfrastructureNonce management, multi-RPC failover, fee/gas caching, latency tracking, tx pipelineNo
Contract InteractionPerpClient (one per market), trading functions, liquidations, Multicall3 batchingYes

Each market is its own Perp contract, identified by its address -- a PerpClient is constructed per market and its methods need no market parameter.

Quick Start

use alloy::primitives::U256;
use alloy::signers::local::PrivateKeySigner;
use perpcity_sdk::prelude::*;

#[tokio::main]
async fn main() -> perpcity_sdk::Result<()> {
    let signer: PrivateKeySigner = "0xYOUR_PRIVATE_KEY".parse()?;

    let transport = HftTransport::new(
        TransportConfig::builder()
            .shared_endpoint("https://arb-mainnet.g.alchemy.com/v2/KEY")
            .read_endpoint("https://arbitrum-one-rpc.publicnode.com")
            .build()?,
    )?;

    let deployments = Deployments {
        perp: "0xYOUR_PERP".parse()?,
        usdc: ARBITRUM_USDC,
        pool_manager: ARBITRUM_POOL_MANAGER,
    };

    let client = PerpClient::new_arbitrum(transport, signer, deployments)?;
    client.sync_nonce().await?;
    client.refresh_gas().await?;
    client.ensure_approval(U256::MAX).await?;

    // Fetch config + live market data in 2 multicalls
    let (config, snapshot) = client.get_perp_snapshot().await?;
    println!("Mark: {:.2}, Index: {:.2}", snapshot.mark_price, snapshot.index_price);

    // Open a long: 100 USDC margin, +1 perp of exposure
    let result = client.open_taker(&OpenTakerParams {
        margin: 100.0,
        perp_delta: 1.0,     // positive = long, negative = short
        amt1_limit: 0,       // slippage limit, 0 = no limit
    }, Urgency::Normal).await?;

    println!("Position opened: {}", result.pos_id);

    // Close it (pass the position's current perp delta)
    let closed = client
        .close_taker(result.pos_id, result.perp_delta, Urgency::Normal)
        .await?;
    println!("Closed: perp {:+.6}, usd {:+.2}", closed.perp_delta, closed.usd_delta);

    Ok(())
}

Guides

Resources