Reproduced Exploit

Tokemak: streaming fee on total assets pays the sink 99999999999999999999

_calculateSharesToMintFeeCollection prices the streaming fee from totalAssets. The sink is minted 111111111111111111111 shares and redeems 99999999999999999999 assets. A fee on the 100 of profit pays 10.

Jul 2024•Other•logic•2 min read

Chain

Other

Category

logic

Date

Jul 2024

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

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.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 53524-toke-14-incorrect-streaming-fee-share-calculation-hexens-non. Standalone Foundry PoC and full write-up: 53524-toke-14-incorrect-streaming-fee-share-calculation-hexens-non_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/fee-calculation · vuln/defi/fee-manipulation

Key info#

ProtocolTokemak
FindingHexens TOKE-14, AuditVault 53524
Vulnerable codeFeeVault._mintStreamingFee
HarmA 10 percent fee on 1000 assets, of which only 100 is profit, mints shares that redeem for 99999999999999999999. The profit-based fee redeems for 10.
Chainlocal synthetic

TL;DR#

_calculateSharesToMintFeeCollection prices the streaming fee from totalAssets. The sink is minted 111111111111111111111 shares and redeems 99999999999999999999 assets. A fee on the 100 of profit pays 10.

The vulnerable code#

SOLIDITY
        // @> VULN: fee assets are totalAssets times the fee bps, not the profit
        uint256 feeAssets = assets * feeBps / FEE_DIVISOR;

Root cause#

The share formula uses the whole asset balance. The streaming fee is supposed to apply to profit only.

Preconditions#

The vault has 1000 assets and 1000 shares. Profit inside the total is 100. The fee is 1000 basis points.

Attack walkthrough#

  1. Load 1000 assets and 1000 shares. See output.txt.
  2. Charge the streaming fee.
  3. The vulnerable branch mints 111111111111111111111 shares.
  4. Those shares redeem for 99999999999999999999 assets.
  5. The sink's token balance is that amount.
  6. The fixed branch, which uses profit times the fee, pays 10.

Diagrams#

flowchart TD A["1000 assets and 100 profit"] --> B["Fee uses total assets"] B["Fee uses total assets"] --> C["Sink is minted 111111111111111111111 shares"] C["Sink is minted 111111111111111111111 shares"] --> D["Redeem pays 99999999999999999999"] D["Redeem pays 99999999999999999999"] --> E["Fair fee would pay 10"]

Impact#

LPs give up about 90 more assets than the 10 the profit fee would take. The fixed mulDiv on profit pays exactly 10.

Remediation#

Mint profit * feeBps * supply / (totalAssets * FEE_DIVISOR - profit * feeBps), rounding up.

How to reproduce#

BASH
cd 53524-toke-14-incorrect-streaming-fee-share-calculation-hexens-non_exp
forge test -vv

Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

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.