Security for Hyperliquid applications — Solidity vulnerabilities on HyperEVM, API signing security, nonce safety, HYPE/USDC decimal traps, and the pre-deploy checklist.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add dvcrn/openclaw-skills-marketplace --skill security --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Security?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/dvcrn-security)More formats (shields.io, HTML) on the badges page.
---
name: security
description: Security for Hyperliquid applications — Solidity vulnerabilities on HyperEVM, API signing security, nonce safety, HYPE/USDC decimal traps, and the pre-deploy checklist.
---
# 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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!