What this is: self-run static analysis with the public Slither tool. What this isn't: a formal independent audit. No external audit firm has reviewed Helm. Treat the contract as research-grade pending audit.
Tool: slither-analyzer v0.11.5
Solidity: 0.8.28
Scope: contracts/src/ (Helm.sol, ManualMetricOracle.sol, interfaces/)
Date: 2026-05-13
Total detectors run: 101
Total findings: 5 (all informational severity)
| # | Detector | Location | Severity | Action |
|---|---|---|---|---|
| 1 | timestamp |
proposeIssue line 173 |
Informational | No fix needed — timing window comparisons are intentional |
| 2 | timestamp |
bet line 220 |
Informational | No fix needed — same |
| 3 | timestamp |
decide line 239 |
Informational | No fix needed — same |
| 4 | timestamp |
resolve line 281 |
Informational | No fix needed — same |
| 5 | low-level-calls |
claim line 341 |
Informational | No fix needed — required for native USDC transfer on Arc |
Helm's futarchy mechanism requires four distinct time windows
(decideAt, resolveAt, block.timestamp < x, block.timestamp >= x).
Slither flags any use of block.timestamp for comparisons because miners
can manipulate timestamps by up to ~15 seconds. For Helm specifically:
- The smallest meaningful interval is
resolveAt - decideAt, which the contract requires to be strictly positive (line 173). Real deployments would set this to minutes-to-hours. - A 15-second miner manipulation is dwarfed by the protocol's natural granularity. No exploit possible.
This is the standard, accepted pattern for any contract with timed phases.
(bool ok,) = msg.sender.call{value: payout}("");
if (!ok) revert TransferFailed();On Arc, USDC is the native gas asset — so transferring USDC means a
native-value call. Using transfer is not recommended (2300-gas limit
is a known anti-pattern),
and high-level payable(addr).send() has the same issue. The pattern shown
is the OpenZeppelin-recommended approach.
claim() is protected by ReentrancyGuard.nonReentrant. The call to the
recipient happens after all storage updates (checks-effects-interactions).
No reentrancy or unexpected-state risk.
- Reentrancy: zero matches.
- Arithmetic overflow / underflow: zero matches (Solidity 0.8.28 native overflow checks + no
uncheckedarithmetic except the safeissueCount++). - Tx.origin authorization: zero matches (msg.sender used throughout).
- Storage layout collisions: no upgrade proxy, so N/A.
- Unsafe delegatecall: zero matches (no delegatecalls).
- Unprotected selfdestruct: zero matches (no selfdestruct).
- Unbounded loops: zero matches.
cd contracts
pip install slither-analyzer # tested with 0.11.5
solc-select install 0.8.28
solc-select use 0.8.28
slither src/ --solc-remaps "openzeppelin-contracts/=lib/openzeppelin-contracts/" --filter-paths "lib|test"Slither only catches automatable issues. It misses:
- Economic incentive analysis (parimutuel payout edge cases — covered by 31 forge tests including
test_claim_chosenBranch_*andtest_claim_proRataBetweenMultipleWinners) - Game-theoretic attacks (whale buying near
decideAt— acknowledged in README's Honest Limits) - Cross-contract invariants (e.g., oracle trust —
ManualMetricOracleis trivially trusted, also documented in README)
A real audit by an audit firm (Trail of Bits, ChainSecurity, OpenZeppelin, etc.) would cost $10K-$50K and take 2-6 weeks. This is not a substitute. It is the best self-served evidence we can offer pre-funding.