Risk Controls
Understand how Atract's automated risk management protects vault capital.
Overview
Atract implements multi-layered risk controls to protect investor capital when traders use vault funding. These automated systems monitor every trade and can pause trading if risk limits are breached.
Risk Control Architecture
Two-Layer Protection
Layer 1: Smart Contract (On-Chain)
Enforces drawdown limits
Pauses vault when limits breached
Cannot be bypassed or disabled
Layer 2: Oracle System (Real-time Monitoring)
Monitors Hyperliquid vault balance
Checks balance every 60 seconds
Triggers smart contract actions
Result: Vault capital is protected through continuous monitoring and automatic enforcement.
Drawdown Limits
Max Drawdown
What it is: The maximum percentage loss from the vault's peak balance.
How it works:
Peak Balance: $100,000
Max Drawdown: 20%
Pause Trigger: $80,000When the vault is paused:
Vault.paused() returns true
No new trades or position increases allowed
Closing positions is still allowed
Recovery: If balance improves above the limit (due to market recovery or partial closes), the vault remains paused until manually unpaused by the trader.
Daily Drawdown
What it is: The maximum percentage loss within a single 24-hour period.
How it works:
Balance at Start of Day: $100,000
Daily Drawdown: 5%
Pause Trigger: $95,000Reset: Daily drawdown resets every 24 hours at midnight UTC.
Purpose: Prevents a single catastrophic day from wiping out capital.
Combined Example
Vault Parameters:
Initial Balance: $100,000
Max Drawdown: 20%
Daily Drawdown: 5%
Scenario 1: Slow decline
Day 1: $98,000 (2% down) - OK
Day 2: $96,000 (2% more) - OK
Day 3: $94,000 (2% more) - OK
...eventually...
Day 10: $80,000 (20% total) - PAUSED (max drawdown hit)
Scenario 2: Bad day
Start: $100,000
After trades: $94,500 (5.5% down) - PAUSED (daily drawdown hit)Oracle Balance Sync
How It Works
The oracle system continuously monitors vaults on Hyperliquid:
Poll Hyperliquid: Every 60 seconds
Fetch account value from
getAccountState(vaultAddress)Convert to USDC equivalent
Update On-Chain: Write to vault contract
Call
vault.updateBalance(newBalance)Update
lastBalanceUpdatetimestamp
Check Limits: Smart contract checks
Is balance below max drawdown threshold?
Is balance below daily drawdown threshold?
If yes to either:
vault.pause()is called automatically
Result: Risk limits are enforced within ~60 seconds of breach.
Balance Freshness
Critical for safety:
Atract checks balance age before every vault trade:
Hard Block (>10 minutes):
Trade is rejected immediately
Error: "Balance data too stale"
Reason: Cannot trust old data for risk decisions
Warning (5-10 minutes):
Trade allowed but warning logged
May indicate oracle issues
Fresh (<5 minutes):
Trade proceeds normally
Why this matters: If the oracle stops updating, we cannot know the true vault balance. Trading with stale data could violate risk limits unknowingly.
Vault Pause Behavior
What Happens When Paused
Automatically:
Vault status changes to PAUSED
Vault.paused()returns trueEvent emitted:
VaultPaused(reason)
Allowed Actions:
View positions
Close positions (reduce-only orders)
Withdraw remaining balance (if vault is closed)
Blocked Actions:
Opening new positions
Increasing existing positions
Any trade that adds risk
Unpause Mechanism
Option 1: Balance Recovery If market moves in your favor and balance recovers above the limit:
Vault remains paused (safety feature)
Trader must manually unpause via vault dashboard
Investor can verify recovery before trading resumes
Option 2: Manual Unpause Trader can request unpause:
Only if balance is above thresholds
Investor may be notified (future feature)
Option 3: Vault Closure If recovery is unlikely:
Close all positions
Trigger vault closure
Distribute remaining capital per profit-sharing agreement
Pre-Trade Checks
Before every vault trade, Atract performs:
1. Vault Status Check
const vaultRecord = await db.vault.findUnique({ address });
if (vaultRecord.status !== 'ACTIVE') {
reject('Vault not active');
}2. Contract Deployment Check
const code = await provider.getCode(vaultAddress);
if (code === '0x') {
reject('Contract not deployed');
}3. On-Chain State Check
const paused = await vaultContract.paused();
const closed = await vaultContract.closed();
const deadline = await vaultContract.dealDeadline();
if (paused || closed || Date.now() > deadline) {
reject('Vault cannot accept trades');
}4. Balance Freshness Check
const ageSeconds = now - vaultRecord.lastBalanceUpdate;
if (ageSeconds > 600) { // 10 minutes
reject('Balance too stale');
}5. Hyperliquid Balance Check
const hlState = await HLClient.getAccountState(vaultAddress);
const availableBalance = hlState.marginSummary.accountValue;
if (orderSize > availableBalance * 0.8) {
reject('Insufficient balance');
}All checks must pass before order is submitted to Hyperliquid.
Post-Trade Verification
After trade execution, Atract checks for race conditions:
Race Condition Detection
What's the risk: Vault could be paused/closed between the pre-check and trade execution.
How we detect:
// After trade executes
const postPaused = await vaultContract.paused();
if (postPaused && !prePaused) {
// Vault was paused during execution
logger.error('RACE_CONDITION_DETECTED', { orderId, vault });
}What happens:
Trade cannot be reversed (already executed on Hyperliquid)
Event logged for transparency
Investor/trader notified
Future trades blocked
Why this matters: Oracle update might pause vault at the exact moment trade is executing. Detection ensures everyone knows what happened.
Deal Deadline
Time-Based Protection
Every vault has a deal deadline:
Proposal Duration: 30 days
Start: 2025-01-01
Deal Deadline: 2025-01-31After deadline:
Vault automatically becomes eligible for closure
No new trades allowed
Parties must settle and close vault
Purpose:
Ensures vaults don't run indefinitely
Forces periodic settlement
Protects both investors and traders
Deadline Checks
Checked on every trade:
const dealDeadline = await vaultContract.dealDeadline();
if (Date.now() > dealDeadline) {
reject('Deal deadline passed - vault must be closed');
}Emergency Procedures
Trader Actions
If vault is paused:
Check drawdown status in vault dashboard
Evaluate options:
Wait for market recovery
Close positions to lock in losses
Request unpause if balance recovered
Communicate with investor
If oracle fails:
Cannot trade (freshness check fails)
Contact admin to check oracle status
Wait for oracle recovery
Investor Protections
Monitoring:
Real-time vault status updates
Email notifications for pauses (future)
Access to full trade audit trail
Actions:
Cannot directly pause/unpause (only trader can)
Can trigger vault closure if deadline passed
Can view all positions and PnL in real-time
Admin Interventions
Oracle Maintenance:
Restart oracle service if stopped
Verify blockchain connectivity
Check for syncing issues
Emergency Shutdown:
Not implemented (vaults are non-custodial)
Would require smart contract upgrade
Best Practices for Traders
Stay Within Limits
Know your limits before trading:
Max Drawdown: 20%
Current Drawdown: 15%
Buffer Remaining: 5%Risk Management:
Keep buffer of 5-10% below limits
Don't trade all the way to the edge
Use stop-losses to prevent auto-pause
Monitor Balance Freshness
Before large trades:
Check
lastBalanceUpdatetimestampEnsure update within last 2-3 minutes
If stale, wait for next oracle update
Best Practice: Refresh vault status every few minutes during active trading.
Communicate with Investors
Transparency builds trust:
Notify investor if approaching limits
Explain strategy if paused and recovering
Provide updates on position management
Recovery Strategies
If vault is paused:
Option A: Close and Accept Loss
Close all positions
Lock in realized loss
Close vault and distribute remaining capital
Option B: Wait for Recovery
Keep positions open
Hope market reverses
Risk: Could get worse (liquidation)
Option C: Partial Close
Close losing positions
Keep winners running
Reduce overall risk while maintaining some exposure
Monitoring Dashboard
Vault Status Panel:
Current balance (updated every 60s)
Current drawdown (% from peak)
Daily drawdown (% from day start)
Time to deal deadline
Last oracle update time
Pause status
Alerts:
Warning: Within 5% of limit
Critical: Within 2% of limit
Paused: Limit breached
Technical Details
Smart Contract Functions
Pause/Unpause:
function pause() external onlyOracle {
paused = true;
emit VaultPaused(block.timestamp);
}
function unpause() external onlyTrader {
require(!closed, "Vault is closed");
require(hyperliquidBalance >= pauseThreshold, "Still below limit");
paused = false;
emit VaultUnpaused(block.timestamp);
}Balance Update:
function updateBalance(uint256 newBalance) external onlyOracle {
uint256 oldBalance = hyperliquidBalance;
hyperliquidBalance = newBalance;
lastBalanceUpdate = block.timestamp;
// Check max drawdown
if (newBalance < peakBalance * (100 - maxDrawdown) / 100) {
pause();
}
// Check daily drawdown
if (newBalance < dayStartBalance * (100 - dailyDrawdown) / 100) {
pause();
}
// Update peak if new high
if (newBalance > peakBalance) {
peakBalance = newBalance;
}
emit BalanceUpdated(oldBalance, newBalance);
}Next Steps
Vault Lifecycle - Understand full vault flow
Position Management - Managing positions safely
Creating Proposals - Investor guide
Related Pages
Last updated