Docs

API Reference

Complete reference for the PerpCity Zig SDK -- context, trading, math, and HFT infrastructure.

Complete reference for the PerpCity Zig SDK.

PerpCityContext

The main client for interacting with the PerpCity protocol.

const sdk = @import("perpcity_sdk");

var ctx = sdk.context.PerpCityContext.init(
    allocator,
    "https://base-rpc-url.com",
    private_key,
    .{
        .perp_manager = perp_manager_addr,
        .usdc = usdc_addr,
    },
);
ctx.fixPointers();
defer ctx.deinit();

Methods

MethodReturnsDescription
init(allocator, rpc_url, private_key, deployments)PerpCityContextCreate context (call fixPointers() after)
fixPointers()voidFix internal pointer references
setupForTrading()!voidApprove max USDC spending
getPerpConfig(perp_id)!PerpConfigFetch and cache perp configuration
getPerpData(perp_id)!PerpDataFetch market data (mark price, bounds, fees)
getUserData(address, positions)!UserDataFetch user balance and position details
getOpenPositionData(perp_id, pos_id, is_long, is_maker)!OpenPositionDataFetch live position details
getPositionRawData(pos_id)!PositionRawDataFetch raw on-chain position data
deinit()voidFree all resources

Trading Functions

openTakerPosition

const position = try sdk.perp_manager.openTakerPosition(&ctx, perp_id, .{
    .is_long = true,
    .margin = 1000.0,
    .leverage = 10.0,
    .unspecified_amount_limit = 0,
});

Opens a leveraged long or short position. Returns OpenPosition.

openMakerPosition

const position = try sdk.perp_manager.openMakerPosition(&ctx, perp_id, .{
    .margin = 10000.0,
    .price_lower = 2900.0,
    .price_upper = 3100.0,
    .liquidity = 1000000,
    .max_amt0_in = 1000000,
    .max_amt1_in = 1000000,
});

Opens a liquidity provider position. Aligns price range to tick spacing automatically.

closePosition

const result = try sdk.perp_manager.closePosition(&ctx, perp_id, position_id, .{
    .min_amt0_out = 0,
    .min_amt1_out = 0,
    .max_amt1_in = 1000000,
});

adjustNotional / adjustMargin

_ = try sdk.perp_manager.adjustNotional(&ctx, .{
    .position_id = pos_id,
    .usd_delta = 5000,
    .perp_limit = 0,
});

_ = try sdk.perp_manager.adjustMargin(&ctx, .{
    .position_id = pos_id,
    .margin_delta = 1000,
});

createPerp

const perp_id = try sdk.perp_manager.createPerp(&ctx, .{
    .starting_price = 3000.0,
    .beacon = beacon_addr,
});

OpenPosition

Returned by openTakerPosition and openMakerPosition.

const live = try position.liveDetails();
const result = try position.closePosition(.{ ... });
_ = try position.adjustNotional(5000, 0);
_ = try position.adjustMargin(1000);
const hex = position.perpIdHex(); // [64]u8 hex string

Calculation Functions

Pure math -- no network required. Available via both perpcity_sdk and standalone math_root module.

Position Math

FunctionReturnsDescription
calculateEntryPrice(raw_data)f64Entry price from raw deltas
calculatePositionSize(raw_data)f64Position size in base asset
calculatePositionValue(raw_data, mark_price)f64Notional value at mark price
calculateLeverage(position_value, effective_margin)f64Current leverage ratio
calculateLiquidationPrice(raw_data, is_long)?f64Liquidation price

Conversions

FunctionReturnsDescription
priceToSqrtPriceX96(price)!u256Price to Uniswap V4 sqrtPriceX96
sqrtPriceX96ToPrice(sqrt)!f64sqrtPriceX96 to decimal price
priceToTick(price, round_down)!i32Price to Uniswap tick
tickToPrice(tick)f64Tick to price (1.0001^tick)
scale6Decimals(amount)!i128Scale to 6-decimal USDC format
scaleFrom6Decimals(value)f64Scale from 6-decimal format

Liquidity

FunctionReturnsDescription
getSqrtRatioAtTick(tick)!u256sqrtPriceX96 for a tick
estimateLiquidity(tick_lower, tick_upper, usd_scaled)!u256Liquidity for USD amount
calculateLiquidityForTargetRatio(margin_scaled, tick_lower, tick_upper, target_ratio)!u256Liquidity for target margin ratio

HFT Infrastructure

Nonce Manager

Lock-free atomic nonce acquisition for high-throughput trading.

var nonce_mgr = sdk.nonce.HftNonceManager.init(allocator, starting_nonce);
const nonce = nonce_mgr.acquireNonce(); // lock-free atomic
try nonce_mgr.trackSubmission(nonce, tx_hash);
nonce_mgr.confirmNonce(nonce);   // on success
nonce_mgr.releaseNonce(nonce);   // on failure
nonce_mgr.resync(on_chain_nonce);

Gas Cache

Pre-computed gas limits and EIP-1559 fee caching with urgency levels.

OperationGas Limit
APPROVE60,000
OPEN_TAKER_POS500,000
OPEN_MAKER_POS600,000
CLOSE_POSITION400,000
ADJUST_NOTIONAL350,000
ADJUST_MARGIN250,000

Urgency Levels: low, normal, high, critical -- each with escalating max fee multipliers.

var cache = sdk.gas.GasCache.init(.{});
cache.updateFromBlock(base_fee, now_ms);
const fees = cache.feesForUrgency(.critical, now_ms);

Multi-RPC Provider

Automatic failover across multiple RPC endpoints with health tracking.

var rpc = try sdk.multi_rpc.MultiRpcProvider.init(allocator, &.{
    "https://primary-rpc.com",
    "https://fallback-rpc.com",
});
const endpoint = rpc.selectEndpoint(now_ms);

Latency Tracker

Rolling window (1024 samples) with p50/p95/p99 percentiles.

var tracker = sdk.latency.LatencyTracker{};
tracker.recordSample(latency_ns);
const stats = tracker.getStats(); // .p50_ns, .p95_ns, .p99_ns

State Cache

Multi-layer TTL caching tuned for Base L2 block times.

var cache = sdk.state_cache.StateCache.init(allocator, .{
    .slow_ttl = 60,  // Fees, bounds (60s)
    .fast_ttl = 2,   // Mark prices (2s, ~1 Base L2 block)
});

Position Manager

Position tracking with stop-loss, take-profit, and trailing stop triggers.

var pm = sdk.position_manager.PositionManager.init(allocator);
try pm.track(.{
    .perp_id = perp_id,
    .position_id = pos_id,
    .is_long = true,
    .is_maker = false,
    .entry_price = 3000.0,
    .margin = 1000.0,
    .stop_loss = 2800.0,
    .take_profit = 3500.0,
    .trailing_stop_pct = 0.05,
});
const actions = pm.checkTriggers(current_price);

Types

Core

pub const Address = [20]u8;
pub const Bytes32 = [32]u8;

pub const PerpCityDeployments = struct {
    perp_manager: Address,
    usdc: Address,
    fees_module: ?Address = null,
    margin_ratios_module: ?Address = null,
    lockup_period_module: ?Address = null,
    sqrt_price_impact_limit_module: ?Address = null,
};

Market Data

pub const PerpData = struct {
    id: Bytes32, tick_spacing: i24, mark: f64,
    beacon: Address, bounds: Bounds, fees: Fees,
};
pub const Bounds = struct {
    min_margin: f64, min_taker_leverage: f64,
    max_taker_leverage: f64, liquidation_taker_ratio: f64,
};
pub const Fees = struct {
    creator_fee: f64, insurance_fee: f64,
    lp_fee: f64, liquidation_fee: f64,
};

Position

pub const OpenPositionData = struct {
    perp_id: Bytes32, position_id: u256,
    is_long: ?bool = null, is_maker: ?bool = null,
    live_details: LiveDetails,
};
pub const LiveDetails = struct {
    pnl: f64, funding_payment: f64,
    effective_margin: f64, is_liquidatable: bool,
};
pub const PositionRawData = struct {
    perp_id: Bytes32, position_id: u256, margin: f64,
    entry_perp_delta: i256, entry_usd_delta: i256,
    margin_ratios: MarginRatios,
};

Parameters

pub const OpenTakerPositionParams = struct {
    is_long: bool, margin: f64, leverage: f64,
    unspecified_amount_limit: u128,
};
pub const OpenMakerPositionParams = struct {
    margin: f64, price_lower: f64, price_upper: f64,
    liquidity: u128, max_amt0_in: u128, max_amt1_in: u128,
};
pub const ClosePositionParams = struct {
    min_amt0_out: u128, min_amt1_out: u128, max_amt1_in: u128,
};
pub const AdjustNotionalParams = struct {
    position_id: u256, usd_delta: i128, perp_limit: u128,
};
pub const AdjustMarginParams = struct {
    position_id: u256, margin_delta: i128,
};

Error Handling

const position = sdk.perp_manager.openTakerPosition(&ctx, perp_id, params) catch |err| {
    switch (err) {
        error.MarginMustBePositive => ...,
        error.TransactionReverted => ...,
        error.RpcError => ...,
        else => ...,
    }
};
ErrorDescription
MarginMustBePositiveMargin must be > 0
LeverageMustBePositiveLeverage must be > 0
InvalidPriceRangeLower price >= upper price
TransactionRevertedOn-chain transaction reverted
RpcErrorNetwork or RPC error
InsufficientFundsWallet has insufficient funds
ModuleAddressRequiredRequired module address not set