On Ethereum at 2026-06-15 13:53:59 UTC, Thetanuts’ legacy TN-IDX-USDC-PUT index token at 0xC2C3AE0a7b405058558C9b4a63b373486CB86Ac7 was exploited through a low-backing mint accounting flaw. The attacker used runner 0x0f9daa9e0adced4e64578b2e131930dde54e492e and a flash loan of 153,054.600569 TN-IDX-USDC-PUT to redeem the basket once, then invoked mint(uint256) 37 times to recreate 153,192.349709 index tokens while every required basket-token transfer rounded to zero. Those reminted shares repaid the loan plus a 137.74914 token premium, while the redemption proceeds were retained by the attacker. The loot wallet 0xAf3a0FdBFB0e3127247B66a042310e09C32F2299 finished the transaction with 105,471.499078 USDC plus three residual vault tokens.

Root Cause

Vulnerable Contract

The vulnerable contract was the legacy Thetanuts index token TN-IDX-USDC-PUT at 0xC2C3AE0a7b405058558C9b4a63b373486CB86Ac7. No proxy hop appears in the execution trace for this contract. Only recovered Solidity is available, so the code-level explanation below is a medium-confidence approximation anchored to the on-chain call flow.

Vulnerable Function

The exploitable path is mint(uint256) with selector 0xa0712d68, reached immediately after a setup call to claim(uint256) with selector 0x379607f5. The single claim call strips the basket out of the index token, and the subsequent mint calls recreate index shares even though the trace shows the contract requesting zero units of every basket component from the attacker.

Vulnerable Code

// [pseudocode reconstructed from observed execution]
function claim(uint256 amount) external {
    uint256 supplyBefore = totalSupply;
    _burn(msg.sender, amount);

    for (uint256 i = 0; i < basket.length; ++i) {
        uint256 payout = basketBalance[i] * amount / supplyBefore;
        basket[i].transfer(msg.sender, payout);
    }
}

function mint(uint256 amount) external {
    uint256 supply = totalSupply;

    for (uint256 i = 0; i < basket.length; ++i) {
        uint256 required = basketBalance[i] * amount / supply;
        basket[i].transferFrom(msg.sender, address(this), required);
        // <-- VULNERABILITY: after claim() empties the basket and collapses supply,
        //     each required component rounds to zero but minting still continues
    }

    _mint(msg.sender, amount);
    // <-- VULNERABILITY: new index shares are created without restoring backing
}

Why It’s Vulnerable

Expected behavior: Minting new index shares must require the caller to restore the basket proportionally, and the contract should reject minting whenever a near-empty basket or near-empty share supply would make those per-component requirements meaningless.

Actual behavior: After the attacker redeemed almost the entire borrowed index-token position in one claim, the mint path still accepted 37 non-zero mint(uint256) calls even though each corresponding basket-token transferFrom amount was zero. The contract therefore recreated 153,192.349709 TN-IDX-USDC-PUT shares without receiving any of the five basket components back.

That gap breaks the core solvency invariant of the index token. The attacker first extracts the basket once, then remints enough unbacked index shares to repay the flash loan and keep the redeemed assets as profit.

Attack Execution

High-Level Flow

  1. The attacker deployed a bootstrap contract and runner, then used the runner to request a flash loan of 153,054.600569 TN-IDX-USDC-PUT.
  2. Inside the flash-loan callback, the runner called claim(uint256) once against the legacy index token and received the five basket vault tokens.
  3. The runner then called mint(uint256) 37 times, recreating 153,192.349709 TN-IDX-USDC-PUT while every basket-token pullback stayed at zero.
  4. The runner approved the lending pool, which pulled the reminted index tokens back as flash-loan repayment plus premium.
  5. The attacker redeemed two basket positions to USDC, swept the resulting USDC and the three remaining basket tokens to the loot wallet, and exited with profit.

Detailed Call Trace

The exploit begins with EOA 0x30498e4466789E534c72e03B52A16c978655b41e creating helper 0xA589C5342068B0c1fEfD44d3c95354427502Ac91, which then deploys runner 0x0f9daa9e0adced4e64578b2e131930dde54e492e and calls run(uint256). The runner calls the lending pool proxy 0x2Ca7641b841a79cC70220ce838D0B9f8197accdA, which routes through reserve token 0x075da7e9efea6813ab0b2680423df75150120d12 and transfers 153,054.600569 TN-IDX-USDC-PUT to the runner. In executeOperation(address[],uint256[],uint256[],address,bytes), the runner calls claim(153054600569) on 0xC2C3AE0a7b405058558C9b4a63b373486CB86Ac7. That single claim causes the index token to transfer out 49,716.431047 TN-CSCPv1-BTCUSD, 23,955.277333 TN-CSCPv1-ETHUSD, 6,378.688541 TN-CSCPv1-AVAXUSD, 17,186.382409 TN-CSCPv1-BNBUSD, and 10,028.704387 TN-CSCPv1-MATICUSD to the runner.

The runner then calls mint(uint256) 37 times. The mint sizes double from 0.000002 up to 68,719.476736, with a final mint of 15,753.396239, for a total of 153,192.349709 newly minted TN-IDX-USDC-PUT. Each mint triggers five transferFrom(address,address,uint256) calls into the basket token contracts, but every one of those calls carries an amount of zero, so no basket assets are returned to the index token before shares are minted. After the 37th mint, the runner approves the lending pool and the pool pulls 153,192.349709 TN-IDX-USDC-PUT back to reserve address 0x075da7e9efea6813ab0b2680423df75150120d12, covering the original flash loan plus a 137.74914 token premium.

With the loan repaid, the runner monetizes the redeemed basket. It calls initWithdraw(uint256) on 0x3ba337f3167ea35910e6979d5bc3b0aee60e7d59 for 49,716.431047 TN-CSCPv1-BTCUSD and receives 70,315.563951 USDC, then calls initWithdraw(uint256) on 0xe1c93de547cc85cbd568295f6cc322b1dbbcf8ae for 23,955.277333 TN-CSCPv1-ETHUSD and receives 35,155.935127 USDC. Finally, the runner transfers 105,471.499078 USDC to loot wallet 0xAf3a0FdBFB0e3127247B66a042310e09C32F2299 and separately sweeps the remaining AVAX, BNB, and MATIC vault tokens there.

Financial Impact

The loot wallet retained 105,471.499078 USDC, which is the confirmed stablecoin profit realized during this transaction. It also received three residual basket positions: 6,378.688541 TN-CSCPv1-AVAXUSD, 17,186.382409 TN-CSCPv1-BNBUSD, and 10,028.704387 TN-CSCPv1-MATICUSD. The realized USDC came from draining 70,315.563951 USDC from the BTC/USD satellite vault and 35,155.935127 USDC from the ETH/USD satellite vault. Separately, the index token lost all five basket-token positions transferred out during the initial claim, while the lending reserve recovered the borrowed index tokens plus the 137.74914 token premium.

Evidence

  • Transaction: 0xbba9f138fe39503bfd1aa62932dbd6ab35d37d23d48e4b7bf2988a9d5dc39fec on Ethereum, block 25,323,329, status success.
  • Attacker: 0x30498e4466789E534c72e03B52A16c978655b41e using runner 0x0f9daa9e0adced4e64578b2e131930dde54e492e.
  • Vulnerable contract: 0xC2C3AE0a7b405058558C9b4a63b373486CB86Ac7 (TN-IDX-USDC-PUT).
  • Impacted pool/token/protocol component: Thetanuts legacy index basket positions in the BTC/USD, ETH/USD, AVAX/USD, BNB/USD, and MATIC/USD satellite vaults.
  • Key on-chain fact: a single claim(uint256) burned 153,054.600569 TN-IDX-USDC-PUT and transferred out five basket-token positions worth 49,716.431047, 23,955.277333, 6,378.688541, 17,186.382409, and 10,028.704387 units respectively.
  • Key on-chain fact: 37 mint(uint256) calls then created 153,192.349709 new TN-IDX-USDC-PUT while every corresponding basket-token transferFrom amount was zero, and those reminted shares were used to repay the flash loan.

Remediation

Reject any mint where a non-zero share amount would require zero units of a basket component after rounding. Enforce minimum-supply and minimum-backing guards around both mint and redeem flows, and pause minting when the basket has been emptied or supply falls below a safe threshold. Recompute basket requirements with conservative rounding and assert that post-mint backing actually increases before new shares are issued. Add invariant tests that cover near-empty supply, basket exhaustion, and flash-loan-sized mint or redeem loops in a single transaction.