← Back to Circuit
Hyperliquid · Agent Infrastructure · Litepaper v0.1

Circuit

The risk sub-agent your bot can't argue past.

Circuit: Execution-Layer Circuit Breakers for Hyperliquid Trading Agents

Abstract

Automated trading agents on Hyperliquid currently enforce risk limits through system prompts and client-side logic—mechanisms that fail under computational pressure or adversarial conditions. Circuit moves risk validation to the execution layer by wrapping the Hyperliquid precompile with a HyperEVM policy contract. Every order is validated against declared constraints (position size, daily loss, leverage) before reaching the L1 order book. Violations revert on-chain. The result is a composable, auditable risk boundary that agents cannot override. We provide a TypeScript SDK that integrates as a drop-in replacement for existing Hyperliquid client libraries.

Motivation

The Problem: Prompt-Level Risk Mangement Does Not Hold

Trading agents on Hyperliquid are constrained by system prompts. A typical agent instruction might read:

You must not exceed a 5 BTC position. Daily losses beyond $50,000 trigger a halt. Never lever above 10x.

These rules are advisory. Under market stress—high volatility, latency spikes, or competitive pressure for alpha—agents routinely violate them. The agent sees an opportunity, the reasoning loop speeds up, the prompt constraint is deprioritized by the LLM's next-token prediction, and the order executes outside policy.

This is not a training problem. It is an architecture problem. Client-side rules lose enforcement power as soon as the environment becomes adversarial (market pressure, network latency, computational load).

Why This Matters for Capital Allocation

Risk management for algorithmic strategies traditionally lives in hard stops: circuit breakers, position limiters, and margin calls enforced by the exchange. Hyperliquid's perpetual market is decentralized. There is no central authority to enforce a halt.

Today's agent operators solve this by:

  1. Running agents on smaller accounts and monitoring logs manually
  2. Trusting that the agent's LLM reasoning will hold under pressure (it won't)
  3. Using crude on-chain limits on transaction frequency (coarse-grained, expensive)

None of these scale to agents managing significant capital. Risk operators need a primitive: a machine-checkable, cryptographically signed boundary that cannot be overridden by the agent's control loop.

The Execution-Layer Answer

Hyperliquid's architecture separates intent from execution. Agents submit orders as transactions to HyperEVM, which forwards them to the L1 order book. This separation creates a natural enforcement point.

Circuit inserts itself at that boundary. Before an order transaction executes, Circuit's policy contract validates it against declared constraints. If validation fails, the transaction reverts—the order never reaches the order book. If validation passes, the order executes atomically.

This changes the trust model. The agent is no longer responsible for enforcing its own constraints. The constraint is a contract invariant, checked and logged on-chain. The agent can be arbitrarily aggressive, arbitrarily pressured, or even compromised; it cannot spend more than its policy allows.

Background

Hyperliquid's Architecture

Hyperliquid operates as a decentralized perpetual exchange with two layers:

Agents interact with HyperEVM by calling the exchange precompile. The precompile is the sole trusted entry point for order submission. It executes synchronously and returns success/failure.

This architecture is documented in Hyperliquid's protocol specification. The exchange precompile accepts structured order data and validates basic format constraints (signature, account nonce, asset ID) before forwarding to the order book.

Related Work: Risk Limits in Decentralized Trading

The problem of enforcing risk limits in decentralized systems has been addressed in narrower contexts:

EIP-7716: Anti-MEV Transaction Ordering Precompile proposes a contract-level mechanism to enforce ordering constraints at the protocol layer. It establishes the principle that protocol-enforced constraints are stronger than client-side ones. Circuit applies the same principle to risk parameters.

Flash Loan Limitations and Balancer V2 Guarded Launches (Balancer's design docs) demonstrate how on-chain contracts can enforce invariants on balance changes. Circuit uses a similar pattern: compare state before and after an order, enforce policy invariants, revert if violated.

Market Microstructure Studies on Stop-Loss Execution (e.g., work by Brunnermeier and others) establish that client-side stops fail under stress and that externally enforced stops are necessary for risk management at scale. Circuit is the execution-layer equivalent.

Architecture

High-Level Component Diagram

┌─────────────────────────────────────────────────────────┐
│                    Hyperliquid L1                       │
│              (Order Book + Sequencing)                  │
└────────────────────┬────────────────────────────────────┘
                     ▲
                     │ (order submission)
                     │
┌────────────────────┴────────────────────────────────────┐
│                   HyperEVM Layer                        │
│  ┌──────────────────────────────────────────────────┐  │
│  │      PolicyVault Contract                        │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │ Validation Engine                          │  │  │
│  │  │  - Check position size                     │  │  │
│  │  │  - Check daily loss                        │  │  │
│  │  │  - Check leverage                          │  │  │
│  │  │  - Check asset whitelist                   │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  │  ┌────────────────────────────────────────────┐  │  │
│  │  │ State Trackers                             │  │  │
│  │  │  - Position accumulator (per asset)        │  │  │
│  │  │  - P&L accumulator (daily)                 │  │  │
│  │  │  - Policy metadata                         │  │  │
│  │  └────────────────────────────────────────────┘  │  │
│  └──────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────┘
     ▲
     │
     │ (TypeScript SDK)
     │
┌────┴──────────────────────────────────────────────────┐
│            Circuit SDK (Client Layer)                 │
│  ┌────────────────────────────────────────────────┐  │
│  │ Policy Manager                                 │  │
│  │  - Deploy policy contracts                    │  │
│  │  - Encode policy parameters                   │  │
│  │  - Track policy state                         │  │
│  └────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────┐  │
│  │ Order Router                                   │  │
│  │  - Intercept order calls                      │  │
│  │  - Route through PolicyVault                  │  │
│  │  - Emit local breach warnings                 │  │
│  └────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────┐  │
│  │ Monitoring & Analytics                         │  │
│  │  - Parse PolicyBreach events                  │  │
│  │  - Track utilization vs. limits               │  │
│  │  - Export metrics                             │  │
│  └────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────┘
     ▲
     │
┌────┴──────────────────────────────────────────────────┐
│      Agent (Autonomous or Manual)                     │
└───────────────────────────────────────────────────────┘

PolicyVault Contract

PolicyVault is a HyperEVM contract that wraps the Hyperliquid exchange precompile. It holds policy state and validates orders before forwarding them.

State Structure

State Variable Type Purpose
owner address Policy setter and withdrawal account
policy Policy struct Active risk parameters
positions mapping(asset => int128) Current net position per asset in lots
dailyPnL mapping(uint256 => int256) Cumulative P&L by calendar day (UTC)
lastOrderTimestamp uint256 Timestamp of most recent order for rate-limiting
policyVersion uint256 Nonce for policy updates
enabled bool Circuit breaker on/off switch

Policy Struct

struct Policy {
  uint256 maxPositionPerAsset;     // notional limit per asset
  int256 dailyLossLimit;           // max loss in USDC per UTC day
  uint8 maxLeverage;               // max leverage * 10 (e.g., 100 = 10x)
  uint256 allowlistedAssetsCount;
  mapping(bytes32 => bool) allowlistedAssets;  // asset IDs
  uint256 enabledAt;               // timestamp policy took effect
}

Core Methods

submitOrder(Order calldata order) external returns (bool success)

Entry point for agent order submission. The method:

  1. Validates order signature (delegate to exchange precompile)
  2. Applies policy checks (see validation rules below)
  3. If all checks pass, calls the exchange precompile to submit the order
  4. Updates state trackers (positions, P&L) based on order details
  5. Emits OrderApproved event
  6. Returns true

If any check fails, reverts with a descriptive reason code.

updatePolicy(Policy calldata newPolicy) external

Allows policy owner to update limits. New policy takes effect immediately for subsequent orders. Previous orders continue to use the policy version they were submitted under (stored in event logs for audit). Increments policyVersion.

withdrawLiquidity(uint256 amount) external onlyOwner

Transfers collateral out of the vault. Checked against current portfolio mark-to-market to prevent over-withdrawal (optional security measure).

Validation Rules

Before an order is forwarded to the exchange precompile, the following checks execute in sequence:

Check Condition Reverts If
Enabled policy.enabled == true Circuit is disabled
Signature Order signature is valid Signature verification fails
Asset Whitelist Asset ID in order is in allowlistedAssets Asset not allowed
Position Size Resulting position ≤ policy.maxPositionPerAsset Would exceed position limit
Daily Loss Cumulative P&L for today ≥ policy.dailyLossLimit Would exceed daily loss limit
Leverage Order leverage ≤ policy.maxLeverage / 10 Leverage too high
Nonce Order nonce increments monotonically Nonce reuse or replay

Position and P&L are recalculated on every order submission using mid-market prices fetched from Hyperliquid's price feed (accessible in HyperEVM context).

Circuit SDK (TypeScript)

The SDK provides a drop-in interface for existing Hyperliquid trading agents. It handles contract deployment, order routing, and monitoring.

Core Classes

PolicyDeployer

class PolicyDeployer {
  async deployPolicy(params: PolicyParams, signer: Signer): Promise<string>
  // Returns PolicyVault contract address
  
  async updatePolicy(vaultAddress: string, newParams: PolicyParams, signer: Signer): Promise<TransactionHash>
}

CircuitOrder

class CircuitOrder {
  constructor(vaultAddress: string, hyperliquidClient: HyperliquidClient)
  
  async placeOrder(order: OrderRequest): Promise<OrderResult>
  // Validates order client-side, routes through vault, returns result
  
  async cancel(orderId: string): Promise<TransactionHash>
  async modify(orderId: string, newSize: number): Promise<TransactionHash>
}

PolicyMonitor

class PolicyMonitor {
  constructor(vaultAddress: string, provider: HyperliquidProvider)
  
  async getUtilization(): Promise<Utilization>
  // Returns: { positionUtilization: Map<asset, fraction>, dailyLossUtilization: fraction }
  
  async watchBreaches(callback: (breach: PolicyBreach) => void): void
  // Subscribes to PolicyBreach events emitted by vault
}

Integration Patterns

For ElizaOS agents:

import { Circuit } from '@circuit/sdk';

const circuit = new Circuit({
  vaultAddress: '0x...',
  policy: {
    maxPositionPerAsset: 5e8,  // 5 BTC in sats
    dailyLossLimit: -50e6,     // -$50k USDC
    maxLeverage: 10,
  }
});

// Drop-in for HyperliquidClient.placeOrder()
agent.setOrderHandler(circuit.placeOrder.bind(circuit));

For custom trading bots:

import { CircuitOrder } from '@circuit/sdk';

const orderer = new CircuitOrder(vaultAddress, hyperliquidClient);

try {
  const result = await orderer.placeOrder({
    asset: 'BTC',
    isLong: true,
    sz: 2.5,
    leverage: 5,
    orderType: 'limit',
    limitPx: 95000
  });
  console.log('Order submitted:', result.txHash);
} catch (err) {
  if (err.code === 'POSITION_LIMIT_EXCEEDED') {
    console.log('Order blocked by circuit policy');
  }
}

Monitoring & Observability

PolicyMonitor emits structured logs:

[2025-07-15T14:32:10Z] ORDER_APPROVED
  vault: 0x...
  order_id: 12345
  asset: BTC
  side: long
  size: 2.5
  leverage: 5x
  new_position: 4.2 BTC
  position_utilization: 0.84

[2025-07-15T14:35:20Z] POLICY_BREACH
  vault: 0x...
  reason: POSITION_LIMIT_EXCEEDED
  attempted_position: 6.5 BTC
  limit: 5.0 BTC
  reverted_order_id: 12347

[2025-07-15T14:40:00Z] DAILY_LOSS_LIMIT_REACHED
  vault: 0x...
  cumulative_loss: -$50,018
  limit: -$50,000
  reset_at: 2025-07-16T00:00:00Z

Protocol / Mechanism

End-to-End Order Flow

The following sequence diagram shows a single order submission with policy validation:

Agent            Circuit SDK          PolicyVault         HyperEVM          L1 Order Book
  │                  │                     │                 │                  │
  ├─ placeOrder()───>│                     │                 │                  │
  │                  │                     │                 │                  │
  │                  ├─ Client-side ───────┤                 │                  │
  │                  │ signature            │                 │                  │
  │                  │ & nonce check        │                 │                  │
  │                  │                      │                 │                  │
  │                  ├─ Call submitOrder()─>│                 │                  │
  │                  │ with serialized      │                 │                  │
  │                  │ order                │                 │                  │
  │                  │                      │                 │                  │
  │                  │    ┌─────────────────┤                 │                  │
  │                  │    │ Policy checks:   │                 │                  │
  │                  │    │ 1. Enabled?      │                 │                  │
  │                  │    │ 2. Asset allowed?│                 │                  │
  │                  │    │ 3. Position OK?  │                 │                  │
  │                  │    │ 4. Loss OK?      │                 │                  │
  │                  │    │ 5. Leverage OK?  │                 │                  │
  │                  │    └─────────────────┤                 │                  │
  │                  │                      │                 │                  │
  │                  │   [PASS] ────────────├─ Call exchange()│                  │
  │                  │                      │ precompile ────>│                  │
  │                  │                      │                 ├─ Submit order ──>│
  │                  │                      │                 │ to book          │
  │                  │                      │                 │<─ Seq# & ack ────│
  │                  │                      │<─ return true ──│                  │
  │                  │                      │                 │                  │
  │                  │ ┌────────────────────┤                 │                  │
  │                  │ │ Update state:      │                 │                  │
  │                  │ │ - positions        │                 │                  │
  │                  │ │ - dailyPnL         │                 │                  │
  │                  │ │ - policyVersion    │                 │                  │
  │                  │ │                    │                 │                  │
  │                  │ │ Emit OrderApproved │                 │                  │
  │                  │ └────────────────────┤                 │                  │
  │                  │                      │                 │                  │
  │<─ result ────────│<─ return TxHash ─────│                 │                  │
  │                  │                      │                 │                  │

Detailed State Transitions

Scenario 1: Order Accepted

  1. Agent calls CircuitOrder.placeOrder(orderRequest)
  2. SDK serializes order and calls PolicyVault.submitOrder(order)
  3. PolicyVault runs validation checks (see table above)
  4. All checks pass
  5. PolicyVault calls exchange precompile with order data
  6. Exchange precompile submits order to L1 order book
  7. PolicyVault updates state:
    • positions[asset] += orderSize
    • If position crosses zero, record realized P&L
    • dailyPnL[today] -= loss (or += gain)
    • lastOrderTimestamp = now
    • policyVersion unchanged
  8. PolicyVault emits event OrderApproved(orderId, asset, size, position, tx)
  9. SDK returns { success: true, txHash, orderId }
  10. Agent receives confirmation and logs position update

Scenario 2: Policy Breach (Position Limit)

  1. Agent calls CircuitOrder.placeOrder(orderRequest) with size 3 BTC
  2. Current position is 2.5 BTC, limit is 5 BTC
  3. Resulting position would be 5.5 BTC
  4. PolicyVault.submitOrder() reaches position check, finds 5.5 > 5
  5. Transaction reverts with reason POSITION_LIMIT_EXCEEDED
  6. No state update occurs; nonce is not incremented
  7. SDK catches revert and emits local breach event
  8. Circuit SDK may emit alert: "Position limit would be exceeded. Current: 2.5 BTC, Limit: 5.0 BTC"
  9. Agent receives { success: false, reason: 'POSITION_LIMIT_EXCEEDED' } and can retry with smaller size

Scenario 3: Policy Update (Risk Reduction During Market Volatility)

  1. Risk operator calls PolicyDeployer.updatePolicy(vaultAddress, { maxLeverage: 5 })
  2. PolicyVault.updatePolicy() receives new policy struct
  3. policyVersion increments
  4. New policy becomes effective immediately for next order
  5. All in-flight orders submitted before the update use the previous policy version (if challenged later, can be audited)
  6. SDK monitors and logs: POLICY_UPDATED: version 1 -> 2

Constraint Interaction Matrix

How constraints interact when multiple limits are close to breached:

Scenario Position Limit Daily Loss Leverage Outcome
Order OK on all 20% util 10% util 6x/10x Approve
Order hits position 95% util 20% util 6x/10x Reject: position
Order hits loss first 50% util 98% util 5x/10x Reject: loss (checked before position)
Order OK, daily reset 30% util -$51k (limit -50k) 5x/10x Reject: loss limit already breached at UTC midnight boundary

Note: Validators check constraints in order: enabled > asset > position > loss > leverage. First failure halts and reverts.

Security Considerations

Threat Model 1: Compromised Agent Logic Attempting Constraint Bypass

Attack: An agent is infected with code that attempts to place orders outside declared policy by:

Mitigation:

Residual Risk: If an operator deploys Circuit but the agent is also configured to call the exchange precompile directly, the agent bypasses Circuit. This is an integration risk, not a protocol risk. Documentation must emphasize that Circuit must be the sole order submission path.

Threat Model 2: Oracle Manipulation Leading to Incorrect Position/PnL Calculation

Attack: An attacker manipulates Hyperliquid's mid-price oracle (used by PolicyVault to calculate current P&L and mark-to-market positions) to report false prices, causing:

Mitigation:

Residual Risk: If Hyperliquid L1's price consensus is compromised, so is Circuit. This is unavoidable without trusting an external oracle, which introduces its own risks.

Threat Model 3: Reentrancy During Order Submission

Attack: An adversary crafts a specially-designed order or uses a malicious contract to:

Mitigation:

Residual Risk: Low. Standard reentrancy guard mitigates this entirely.

Threat Model 4: Policy Parameter Underflow/Overflow

Attack: An operator sets policy parameters that cause arithmetic overflows or underflows:

Mitigation:

Residual Risk: Minimal if parameter validation is strict. Operators should be warned to set sane limits.

Threat Model 5: Time-of-Check to Time-of-Use (TOCTOU) Race Condition

Attack: Agent submits order O1 that is within policy at time T1. By time T2 (when order reaches the L1 order book and fills), market has moved, and the agent's realized P&L or position is now outside policy. The order fills anyway because the check happened before the fill.

Mitigation:

Residual Risk: By design. This is a property of async settlement, not a bug.

Threat Model 6: Policy Metadata Staleness

Attack: An operator updates policy, but Circuit SDK clients have cached the old policy version locally. Orders are still validated against the old policy.

Mitigation:

Residual Risk: Low, as long as SDK actively monitors versions or refetches on every order.

Open Questions

1. How Should Multi-Asset Correlation Risk Be Modeled?

The current position limits are per-asset. An agent trading both BTC and ETH can hold 5 BTC and 100 ETH simultaneously, even if their combined notional exposure to price risk is highly correlated. Should PolicyVault support:

Current design assumes agents manage correlation risk independently. This may be insufficient for agents managing large portfolios. An open question for future work is whether Circuit should expose a correlation-aware constraint engine, or whether that responsibility should remain with the agent.

2. How Should Funding Costs Be Attributed in Daily Loss Limits?

Daily loss limits are calculated as realized P&L: change in mark-to-market value. But agents on perpetuals incur funding costs (if position is long and funding is positive, the agent pays). These costs are not realized when the order is submitted; they accumulate over hours/days.

Should Circuit:

The current design does not include funding in loss limits. This could lead to an agent hitting realized loss limit but continuing to accrue funding costs, actually exceeding its risk budget.

3. Can PolicyVault Scale to Sub-Second Order Frequency Without Becoming a Bottleneck?

Hyperliquid's L1 processes orders at high frequency. If an agent submits orders every 100ms, does PolicyVault's state updates (reading positions, calculating new positions, updating nonces) become a limiting factor? Are there batching strategies or state root caching techniques that could allow Circuit to handle burst order submission without incurring per-order computational overhead?

4. How Should Policy Auditing Work Across Policy Upgrades?

When an operator upgrades the policy (e.g., reduces max leverage from 10x to 5x), in-flight orders may have been submitted under the old policy. Should Circuit:

Current design grandfathers in orders, but this could lead to a situation where an operator lowers leverage limits but existing high-leverage positions are not wound down. What is the right tradeoff between agent autonomy and risk operator control?

5. What Is the Appropriate Cadence for Daily Loss Limit Reset?

Currently, the daily loss limit resets at UTC midnight. But an agent trading globally across time zones may interpret "daily" differently. Should Circuit support:

Different reset mechanisms change the effective risk exposure. The open question is whether a one-size-fits-all UTC midnight reset is appropriate for global trading strategies.

References