Reproduced Exploit
Hyperhyper — option premium is priced on the short input period
_computeMaturityDate rounds expiry up to the last second of the day, so a 7-day input lasts longer than 604800 seconds. _calculatePremium still calls optionPrices with the input period.
Chain
Other
Category
logic
Date
Mar 2025
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 57738-h-01-option-premium-calculation-underprices-options-pashov-a. Standalone Foundry PoC and full write-up: 57738-h-01-option-premium-calculation-underprices-options-pashov-a_exp in the
evm-hack-registrymirror.
Vulnerability classes: vuln/logic/price-calculation · vuln/logic/fee-calculation · vuln/logic/incorrect-calculation
Reproduction: a self-contained Foundry PoC that compiles and runs offline with only
forge-std— no fork and no RPC. Trace: output.txt. PoC: test/57738-h-01-option-premium-calculation-underprices-options-pashov-a_exp.sol.
Key info#
| Impact | HIGH — the buyer pays 7 ether and the seller is short 999976851851851851 wei |
| Protocol | Hyperhyper Strategy._calculatePremium |
| Vulnerable code | optionPrices(period, ...) instead of the rounded maturity |
| Bug class | Premium uses a shorter time than the option actually lives |
| Finding | Pashov Audit Group — Hyperhyper, 30 March 2025 · H-01 · id 57738 |
| Report | Hyperhyper-security-review_2025-03-30.md |
| Source | AuditVault finding |
| Status | Reproduced with the audited maturity rounding and a linear time value |
| Compiler | solc 0.8.27, cancun, via IR |
TL;DR#
_computeMaturityDate rounds expiry up to the last second of the day, so a 7-day input lasts longer than 604800 seconds. _calculatePremium still calls optionPrices with the input period.
On the replay timestamp the charged premium is 7 ether (365 ether * 7 days / 365 days). The fair premium, using the rounded maturity, is 7999976851851851851 wei. The seller who hedges at the fair value is short 999976851851851851 wei.
Pricing optionPrices with maturity - block.timestamp makes charged equal fair. The short is 0.
The vulnerable code#
function _computeMaturityDate(uint256 period) public view returns (uint256 maturityTimestamp) {
maturityTimestamp = block.timestamp + period;
maturityTimestamp -= (maturityTimestamp % 1 days);
maturityTimestamp += 1 days - 1;
}
premium = bs.optionPrices(period, notional);
The time value in this reduction is notional * time / 365 days, so the only difference between the two quotes is the time argument. The report's Black-Scholes numbers show the same direction: a 7-day call quoted at 8963759233891000000 against a maturity quote of 11675104193168500000.
Root cause#
The option's lifetime and the time passed into the pricer are different values. Maturity is stored rounded up. The premium is not.
Preconditions#
periodis 7 days.block.timestamp + periodis not already the last second of a day, so the round-up adds time. It does on this block.
Attack walkthrough#
- Read
actualDuration(7 days). It is greater than 604800. buychargesoptionPrices(period)which is 7 ether, and pulls that from the buyer.- The seller pays the buyer the fair hedge,
optionPrices(actualDuration). - The buyer's balance rises by the gap. The seller's reserve falls by the same gap.
Control: StrategyFixed.buy passes actualDuration(period) into optionPrices. Charged equals fair. Both balances are unchanged apart from the round trip. The short is 0.
Diagrams#
Impact#
The buyer pays less than the time they receive. Here that discount is 999976851851851851 wei on a 7 ether quote, and the seller is short exactly that amount. The fixed quote charges the fair premium and the short is 0.
Remediation#
Pass maturity - block.timestamp into optionPrices, not the raw period argument.
Sources#
Taxonomy#
severity/highvuln/logic/price-calculation·vuln/logic/fee-calculation·vuln/logic/incorrect-calculation
Sources & further analysis#
Reproductions & code
- Standalone PoC + full trace: 57738-h-01-option-premium-calculation-underprices-options-pashov-a_exp (evm-hack-registry mirror).
- AuditVault finding: 57738-h-01-option-premium-calculation-underprices-options-pashov-a.
Alerts & third-party analyses
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.