pre-audit triage. — public before mainnet · 0 crit, 0 high, 1 medium fixed, all lows acknowledged or fixed. external audits pending.
critical
high
medium
low
info
USDC and a vanilla $HELIX revert on failure so the immediate deploy is safe. But the engine is positioned as general-purpose — a future market backed by a non-reverting ERC20 (USDT-style) would silently swallow failed transfers and corrupt internal accounting. Remediation: added _safeTransfer + _safeTransferFrom wrappers in HelixPerps.sol L266-280 that decode optional bool returns and revert on either failure mode. All 10 sites refactored; 34/34 forge tests still passing.
Standard guidance is "multiply before divide" to preserve precision. In our case WAD / 1e6 = 1e12 is exact (both powers of ten, no truncation) so result is identical to amount * WAD / 1e6. The bonus-emissions math already does multiplication first. No code change. Documented so external auditors don't re-flag.
Slither flags "contract has payable but no withdraw." The receive() exists specifically to revert as a defensive guard against accidental ETH transfers. No ether can accumulate. No code change.
Market existence is gated by markets[id].id != bytes32(0). Since id == keccak256(symbol) for valid markets, a zero-hash collision is computationally impossible. No code change.
Bootstrap window relies on block.timestamp < bootstrapEndTs. A miner could nudge timestamps a few seconds. Remediation: bootstrap end set with 1-block grace (HelixPerps.sol L189) so a single block of miner clock-skew can't accidentally tip the window. Multi-block manipulation requires miner economic incentive larger than the 2× emission delta — out of scope.
🔒 reentrancy on settle
Every external function that touches state uses OpenZeppelin's nonReentrant. The hook's beforeSwap path is checks-effects-interactions; liquidate() sweeps before any token transfer.
🎯 oracle manipulation
There is no oracle. Mark price = AMM pool price after the swap completes. To manipulate the mark you have to move the same pool everyone arbs against — impact equals normal swap impact, no advantage.
🌪 sandwich on perp open
Sandwiching a perp open requires moving the pool. The bot pays the same slippage as a normal trader, then HELIX uses the post-swap tick as the mark. Sandwich profit = 0.
🧊 funding rate spike
FUNDING_CLAMP_PP1E8 = 5000 caps per-block funding accrual at ±0.005%/hr (≈ ±43.8% APR). Even at 100% one-sided OI, the rate cannot exceed that. Manipulation surface is bounded.
💀 HLP drawdown spiral
The 8% drawdown gate (HLP_DRAWDOWN_GATE_BPS = 800) pauses withdrawals once HLP falls 8% from peak. Stops a panic-run loop where deficit → withdrawals → realized losses → more deficit.
👤 admin / governance attack
None exists. Contract has no Ownable, no proxy, no upgrade path. Every parameter is a public constant. There is nothing for governance to compromise because there is no governance.
🐛 math invariant break
34/34 forge tests assert: hlpAssets ≥ 0, sum(margin) == totalCollateral, fundingPaid + fundingReceived == 0 across every fuzz run.
🔨 DoS / griefing
Per-block work bounded: SWEEP_MAX_PER_SWAP = 8 liquidations per hook call. New positions can't push the queue above the cap. Gas spike on a single swap is bounded.
// payout tiers
/brand/security-pubkey.asc (TBA post-deploy) · response within 24h