# Security

> Security for Hyperliquid applications — Solidity vulnerabilities on HyperEVM, API signing security, nonce safety, HYPE/USDC decimal traps, and the pre-deploy checklist.

- Skill: `dvcrn/security-3` (Agent Skill)
- Install (CLI): `npx skillmds@latest add dvcrn/security-3`
- Raw SKILL.md: https://api.skillmd.com/api/skills/dvcrn/security-3/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: dvcrn (https://skillmd.com/u/dvcrn)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/dvcrn/security-3

---


# Security on Hyperliquid

## What You Probably Got Wrong

**"HYPE has 18 decimals so math is the same as ETH."** HYPE has 18 decimals on HyperEVM, but USDC has 6. Mix them up and you're off by 1e12 — sending 1 trillion USDC instead of 1. This is the #1 bug in Hyperliquid dApps.

**"HyperEVM is just like Ethereum security-wise."** Almost. Same reentrancy, same precision loss, same access control issues. But also: Cancun opcodes without blobs, no MEV on HyperCore (so flash loan concerns are different), and HYPE as gas.

**"Signing an API request is always safe."** No. Always verify what you're signing. A malicious UI can get you to sign a different action than what's displayed. Verify the action payload before submitting.

---

## Critical Vulnerabilities (HyperEVM)

### 1. Token Decimal Mismatch

**USDC is 6 decimals. HYPE is 18 decimals.** Getting this wrong transfers absurd amounts.

```solidity
// ❌ WRONG — assumes 18 decimals for USDC
uint256 oneHundredUSDC = 100e18;  // Actually 100 trillion USDC

// ✅ CORRECT — check decimals dynamically
uint256 oneHundredUSDC = 100 * 10 ** IERC20Metadata(usdc).decimals();
// or if you know USDC is 6:
uint256 oneHundredUSDC = 100e6;   // $100 USDC
```

```solidity
// Decimal reference for HyperEVM
// HYPE: 18 decimals (native, like ETH)
// USDC: 6 decimals (always — same as Ethereum)
// WETH: 18 decimals
// HIP-1 tokens: varies (usually 6 or 8) — always check dynamically
```

**When mixing HYPE and USDC in calculations:**
```solidity
// Normalize to same decimal basis first
uint256 usdcNormalized = usdcAmount * 1e12;  // 6 → 18 decimals
uint256 total = hypeAmount + usdcNormalized;   // Now both 18 decimals
```

### 2. Reentrancy

Same as Ethereum. CEI pattern + ReentrancyGuard:

```solidity
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

function withdraw(uint256 amount) external nonReentrant {
    uint256 bal = balances[msg.sender];
    require(bal >= amount, "Insufficient balance");

    balances[msg.sender] = bal - amount;  // Effect BEFORE interaction

    (bool success,) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
}
```

### 3. Precision Loss

Solidity has no floats. Division truncates.

```solidity
// ❌ WRONG — truncates to 0
uint256 fivePercent = 5 / 100;

// ✅ CORRECT — basis points
uint256 FEE_BPS = 500; // 5%
uint256 fee = (amount * FEE_BPS) / 10_000;

// ✅ ALWAYS multiply before dividing
uint256 result = (a * c) / b;  // Not: a / b * c
```

### 4. SafeERC20

Always use SafeERC20 for token operations:

```solidity
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;

// ✅ CORRECT
token.safeTransfer(to, amount);
token.safeTransferFrom(from, to, amount);
token.safeApprove(spender, amount);

// ❌ WRONG — some tokens don't return bool
token.transfer(to, amount);
```

### 5. Oracle Safety

Don't use HyperSwap V2 spot prices as oracles — they can be manipulated in a single block.

For HyperEVM applications that need prices:

```solidity
// ❌ DANGEROUS — manipulable via a single large swap
function getPrice() internal view returns (uint256) {
    (uint112 reserve0, uint112 reserve1,) = hyperSwapPair.getReserves();
    return (reserve1 * 1e18) / reserve0;
}
```

For price data, prefer:
- HyperCore mark prices via the precompile or oracle feeds
- Time-weighted average prices (TWAP) over multiple blocks
- An external oracle if available

### 6. Access Control

Every state-changing function needs explicit access control:

```solidity
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

// For simple ownership
contract MyVault is Ownable {
    function setFee(uint256 newFee) external onlyOwner {
        fee = newFee;
    }
}

// For complex roles (admin, operator, keeper)
contract ComplexVault is AccessControl {
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

    function rebalance() external onlyRole(OPERATOR_ROLE) {
        // Only operators can rebalance
    }
}
```

### 7. HYPE Bridge Security

Interactions with `0x2222222222222222222222222222222222222222`:

```solidity
// Sending HYPE to the bridge address withdraws to HyperCore
// This is a state change — treat it like any external call

// ✅ SAFE pattern
function withdrawToHyperCore(address payable user, uint256 amount) external {
    require(msg.sender == user, "Not authorized");
    require(amount <= userBalances[user], "Insufficient balance");

    userBalances[user] -= amount;  // Update state BEFORE send

    (bool success,) = BRIDGE_ADDRESS.call{value: amount}("");
    require(success, "Bridge transfer failed");

    emit WithdrawnToHyperCore(user, amount);
}
```

---

## HyperCore API Security

### Signing Safety

Every HyperCore exchange action is an EIP-712 signed message. Security rules:

1. **Always verify what you're signing.** The SDK builds the message — review the action parameters before confirming.

2. **Verify nonces are correct.** Wrong nonce = order might be replayed or dropped.

3. **Use agent wallets for bots.** Never sign HyperCore actions with your main key in automated code.

4. **Don't expose signing keys.** Agent keys should have limited withdrawal permissions.

```python
# ✅ CORRECT: Verify order parameters before submitting
def place_safe_order(exchange, coin, is_buy, sz, px):
    # Sanity checks before signing
    assert sz > 0, "Zero size"
    assert px > 0, "Zero price"
    assert coin in APPROVED_COINS, f"Unknown coin: {coin}"
    assert sz <= MAX_ORDER_SIZE, f"Order too large: {sz}"

    # Log what we're about to sign
    print(f"Placing order: {coin} {'BUY' if is_buy else 'SELL'} {sz} @ {px}")

    return exchange.order(coin, is_buy, sz, px,
                          {"limit": {"tif": "Gtc"}})
```

### Nonce Replay Protection

HyperCore nonces protect against replay attacks. But:

- Don't expose your signed messages before submitting — they could be replayed with the same nonce
- Nonces are per-address — a compromised agent key can't replay mainnet nonces on testnet (different chainId in domain)

### Rate Limits and Error Handling

```python
import time
from functools import wraps

def with_retry(max_retries=3, delay=1.0):
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return fn(*args, **kwargs)
                except Exception as e:
                    if "rate limit" in str(e).lower() and attempt < max_retries - 1:
                        time.sleep(delay * (2 ** attempt))  # Exponential backoff
                    else:
                        raise
        return wrapper
    return decorator

@with_retry(max_retries=3)
def place_order_with_retry(exchange, coin, is_buy, sz, px):
    return exchange.order(coin, is_buy, sz, px, {"limit": {"tif": "Gtc"}})
```

---

## Pre-Deploy Security Checklist

Run through this for EVERY HyperEVM contract before deploying to production:

### HyperEVM Contracts

- [ ] **Token decimals** — no hardcoded `1e18` for USDC (6 decimals) or unknown tokens
- [ ] **Reentrancy** — CEI pattern + `nonReentrant` on all external-calling functions
- [ ] **Access control** — every admin function has explicit restrictions
- [ ] **SafeERC20** — all token operations use SafeERC20
- [ ] **Integer math** — multiply before divide, no precision loss in critical paths
- [ ] **Input validation** — zero address, zero amount, bounds checks
- [ ] **Events** — every state change emits an event
- [ ] **Oracle safety** — not using DEX spot prices for critical price decisions
- [ ] **HYPE bridge** — bridge calls are safe (CEI, guarded)
- [ ] **No infinite approvals** — approve exact amounts
- [ ] **Contract verified** — verified on https://explorer.hyperliquid.xyz after deploy
- [ ] **Ownership** — production contracts owned by multisig, not EOA

### HyperCore API Integration

- [ ] **Agent wallet** — bot uses agent key, not main wallet key
- [ ] **Key management** — no private keys in code/logs/git
- [ ] **Nonce handling** — using SDK or timestamp-based nonces
- [ ] **Input validation** — position size limits, price sanity checks
- [ ] **Error handling** — rate limits handled with backoff
- [ ] **Testnet tested** — full integration test on testnet before mainnet
- [ ] **Withdrawal limits** — agent wallet can't drain beyond approved amount

---

## HyperEVM-Specific Notes

**Cancun opcodes available:** `TSTORE`, `TLOAD` (transient storage) work on HyperEVM.

**No blobs:** `BLOBHASH`, `BLOBBASEFEE` opcodes are NOT available despite Cancun compatibility. Don't use them.

**Priority fees are burned:** You can't use priority fee manipulation for MEV. Bots don't bid gas wars on HyperEVM like they do on Ethereum.

**Block time ~1-2s:** Contracts relying on block.timestamp should account for faster block production. Minimum deadlines should be longer than 1 block.

```solidity
// ✅ CORRECT: Use timestamps in seconds, not block numbers
// 5 minute deadline
uint256 deadline = block.timestamp + 5 * 60;

// ❌ RISKY: Block number arithmetic depends on block time
// "100 blocks" is only ~100-200 seconds on HyperEVM, not 20 minutes
```

---

## Automated Security Tools

```bash
# Static analysis
slither .                      # Common vulnerability detection
mythril analyze src/MyContract.sol  # Symbolic execution

# Foundry fuzzing
forge test --fuzz-runs 10000   # Fuzz all parameterized tests

# Gas optimization
forge test --gas-report
```

Run slither before any mainnet deployment. No high/medium findings unaddressed.

