October 20, 2026
|
Exploit Postmortem
Cover image for "Thetanuts Finance's $105K Loss and How Olympix Would Have Prevented It"

Thetanuts Finance's $105K Loss and How Olympix Would Have Prevented It

Date: June 15, 2026

Chain: Ethereum

Loss: about $105K per Thetanuts, the USDC the attacker cashed out (PeckShield counts about $139K including option tokens)

Class: integer division rounding a mint's required deposit down to zero

At 13:53:59 UTC on June 15, 2026, a contract on Ethereum called mint() on an old Thetanuts Finance index token again and again inside a single transaction. The first mint created 0.000002 shares, and each one after that roughly doubled the size until a single call handed out more than 68,000 shares. Across the run the token issued about 153,192 new shares, and the transfers that were supposed to pay for them moved nothing.

The index token was TN-IDX-USDC-PUT, a legacy Thetanuts product that the team says it had migrated away from years earlier. The attacker walked away with 105,471.50 USDC, in line with the roughly $105K Thetanuts gave as the incident's total loss. It was possible because the mint function worked out each required deposit with integer division, and once the basket behind the token had been emptied, that division rounded every deposit down to nothing while the shares still got minted.

A Basket Token Left Running

Thetanuts Finance runs on-chain options vaults. Its Basic Vaults sell out-of-the-money options, and depositors earn the premium. TN-IDX-USDC-PUT sat one level above those vaults as an index token, a single share that stood for a slice of a basket of five option vault positions covering BTC, ETH, AVAX, BNB and MATIC.

A basket token like this has two core moves. Calling claim() burns your shares and pays out your proportional slice of each basket component. Calling mint() does the reverse, pulling in a proportional amount of each component from you and issuing new shares in return. The price of a new share is whatever slice of the basket it represents, so the amount you owe on mint is tied to how much the basket holds compared with how many shares already exist.

Thetanuts described the contract as deprecated and unrelated to any of its current products. It was still deployed, though, still callable by anyone, and still holding the basket positions behind its shares.

When the Math Rounds to Nothing

Solidity has no fractions. When a contract divides one whole number by another, it throws away the remainder, so 7 divided by 2 comes out as 3 and 1 divided by 2 comes out as 0. Most of the time the lost fraction is a rounding error measured in the smallest unit of a token, and it only turns into a real problem when the numbers involved get small enough that the whole answer is less than one unit.

SlowMist traced the root cause to exactly that: integer division in mint() made the deposit formula evaluate to zero, which let anyone mint shares for free. The DeFiHackLabs proof of concept reproduces the attack against a fork of mainnet and spells out the formula: the deposit owed for each component is the vault's basket balance times the requested amount, divided by totalSupply. After the attacker's claim, totalSupply sat at 3 wei with roughly 1 wei of backing left, so that division floored to zero for any mint smaller than the supply. DarkNavy's trace analysis adds that the mint path never rejected a call where a basket component rounded to zero, so mint() kept issuing new shares without restoring any backing.

The lesson for builders sits in that one missing check. Rounding down is the right default when a user is withdrawing, because the protocol keeps the dust. On a deposit it hands the dust to the user, and a function that issues shares has to make sure every share it creates arrives with something real behind it. A vending machine that drops a can whenever the coin slot reads zero cents works fine as long as nobody figures out how to make the slot read zero.

Free Shares, Doubling Every Call

The attacker's wallet, which Etherscan now labels ThetanutsFi Exploiter 1, was funded with 0.1 ETH from Tornado Cash at 13:36:35 UTC. Seventeen minutes later it sent the exploit transaction, 0xbba9...9fec, mined in block 25,323,329, which deployed a helper contract that in turn created the runner contract that did the work.

The runner opened with a flash loan of 153,054.6006 TN-IDX-USDC-PUT from an Aave-style lending pool that held the index shares. A flash loan lets anyone borrow a large amount with no collateral as long as it's repaid before the same transaction ends. The runner called claim() once with the whole loan, which burned the borrowed shares and paid out the five basket positions behind them, including 49,716 TN-CSCPv1-BTCUSD and 23,955 TN-CSCPv1-ETHUSD vault tokens. With the basket drained and the supply nearly gone, it called mint() over and over with doubling sizes and recreated 153,192.349709 shares.

Those free shares repaid the flash loan and its premium. The runner then redeemed the BTC and ETH option vault tokens it had claimed for 70,315.56 and 35,155.94 USDC, sent 105,471.50 USDC to a separate loot wallet, and swept the AVAX, BNB and MATIC vault tokens there as well. Every one of those transfers is visible in the exploit transaction on Etherscan.

Two Counts of the Damage

PeckShield's first alert said Thetanuts had been exploited for $2.1M and that about $2M in option tokens appeared to have been whitehatted. Its breakdown of what the exploiter kept came to about $139K: roughly $105K in USDC, swapped for about 60 ETH, plus about $34K in USDC-denominated option tokens still sitting in the wallet. Two days later, on June 17, the exploiter deposited 57 ETH into Tornado Cash, according to PeckShield.

Thetanuts first said its preliminary investigation showed "this is once again, a deprecated vault that we have migrated from years ago" with no relation to its current contracts or products. Its post-mortem on June 16 put total losses at about $105K. It said the attacker ran the same path against other exposed legacy vaults about an hour later and received only obsolete LP tokens with no realizable value, and it called any attempt to frame those actions as a white-hat rescue inaccurate. The team said the vaults dated from 2021, that it had left their residual balances in place out of respect for immutability, and that it would now retire the remaining outdated contracts and open a Merkle-based claims process for legitimate residual balances. The "once again" points to April 21, 2026, when Thetanuts reported an incident in a different legacy vault on Ethereum, a v1 test vault it said held no user funds.

How Olympix Catches a Mint That Costs Nothing

Olympix reads a contract the way an attacker does, as states, transitions and entry points, and it takes in the context around the code to work out the rules the system depends on but never writes down. For a basket token, the state that matters is the relationship between shares outstanding and the backing behind them, and claim() and mint() are the two public transitions that move it. A contract's state space looks endless, but the engine maps every path through those transitions and narrows them to the feasible ones that can touch that relationship.

Signals decide which of those paths get attacked first. In TN-IDX-USDC-PUT the signal is a division inside a share-issuing function whose divisor is the token's own supply, a number any caller can shrink through a public redeem, with the quotient fed straight into a transfer that never checks it's above zero. From there the engine pushes on that mint path in two ways. BugPoCer starts from the basket's accounting as a whole, infers what must stay true between shares and backing, and runs Counterexample-Guided Invariant Refutation across sequences of claims and mints until the rule gives way. BugScout starts at the division itself and runs Bounded Adversarial Verification, replaying known rounding and empty-vault strategies against it.

The invariant for this token fits in one sentence: minting a nonzero number of shares must always pull in a nonzero amount of every basket component. Breaking it takes two steps any caller can reach, a redeem that empties the basket followed by a mint that rounds to zero, and a flash loan supplies the starting shares. Olympix's output is that sequence as a counterexample, with the minimal conditions that trigger it and a generated Forge test that replays it, which is the June 15 transaction produced in development instead of on mainnet.

Because the engine runs automatically in development, at every merge and before release, a contract's last review doesn't have to be its only one. Olympix finds breaks by producing counterexamples, and here the counterexample is short enough to read in a minute.

Old Code Keeps Its Balance

TN-IDX-USDC-PUT outlived the product it belonged to, and the arithmetic inside it stayed exactly as exploitable as the day it was written for as long as the basket held value. Any function that issues shares should refuse to issue them for free, and that's a rule a tool can infer and test on every contract a team still has deployed, current or legacy. To see how Olympix maps the edge cases in your own share accounting and returns a runnable counterexample for any path that breaks it, book a technical walkthrough.

On-Chain References

Protocol: Thetanuts Finance (legacy TN-IDX-USDC-PUT index token)

Chain: Ethereum

Date: June 15, 2026, 13:53:59 UTC

Loss: about $105K per Thetanuts' post-mortem (105,471.50 USDC sent to the loot wallet); PeckShield counts about $139K including about $34K in option tokens

Attacker EOA (ThetanutsFi Exploiter 1): 0x3049...b41e

Helper contract: 0xA589...Ac91

Runner contract: 0x0f9d...492e

Loot wallet: 0xAf3a...2299

Vulnerable contract (TN-IDX-USDC-PUT): 0xC2C3...6Ac7

Flash loan pool: 0x2Ca7...ccdA

Funding tx (Tornado Cash, 13:36:35 UTC): 0xc787...5143

Exploit tx: 0xbba9...9fec

Block: 25,323,329

Sources

DarkNavy: Thetanuts Legacy Index Vault Zero-Cost Remint Exploit

SlowMist: Thetanuts Finance exploit alert

PeckShield: Thetanuts loss breakdown

PeckShield: Thetanuts exploiter Tornado Cash deposit

DeFiHackLabs: ThetanutsFi_exp.sol proof of concept

Thetanuts Finance: June 16 incident post-mortem

More from Olympix:

No items found.

Ready to Shift Security Assurance In-House? Talk to Our Security Experts Today.