Crypto exploits

SetProtocol exploit analysis

Set Protocol's RebalancingSetTokenV3 drained for 5.08 ETH on Ethereum when `actualizeFee()` recomputed `unitShares` with `divCeil` against an unchanged balance, widening the issue/redeem gap by one unit.

David

Security ·

Reported loss
5.083725683631735476 ETH to the attacker, per DeFiHackLabs
Occurred
Logged

Logged as “2026-10-06: Actualizefee Is Permissionless And Recomputes Unitshares With A Divceil Even When It Mints Zero Fee Rounding The Backing Collateral Per Share Up By One With No Fee Actually Taken So Issuing The Rebalancing Set Before The Bump And Redeeming The Same Quantity After Returns More Collateral Than Was Deposited The 25044 Eth Issueredeem Size Supplied Fee Free By A Uniswap V4 Native Eth Flash Accounting Loan on SetProtocol”.

Analysis2 min read

RebalancingSetTokenV3 stores unitShares, the amount of the backing currentSet token held per naturalUnit of rebalancing-set supply. Issuance sets it with a floor division, so the stored value is rounded down at every issue. actualizeFee() then unconditionally overwrites it with a ceil re-derivation, even when the fee paid is zero. The re-derivation is calculateNewUnitShares() = currentSetAmount.mul(naturalUnit).divCeil(totalSupply()).

divCeil rounds up. With no fee minted, totalSupply and the vault's currentSet balance are unchanged, yet the ceil re-derivation bumps the stored floor value up by one. In the live transaction, FeeActualization(profitFee: 0, streamingFee: 0) and IncentiveFeePaid(..., newUnitShares: 4928) confirm a fee of zero still moved the unit share from 4927 to 4928.

Core.issue and Core.redeem are permissionless and read the live unitShares. Issuing the rebalancing set costs 4927 currentSet per naturalUnit; redeeming the same quantity after actualizeFee pays out 4928 per naturalUnit. The one-unit gap per naturalUnit, multiplied by the issued quantity, is pure profit. The gain scales linearly with the working capital, so the attacker raises about 25,044 ETH for one atomic block.

That capital is borrowed fee-free from the Uniswap v4 PoolManager through flash accounting. The attacker enters a PoolManager.unlock callback and uses take(native ETH) and settle{value} to draw and repay native ETH inside the unlock, with no swap and no fee. Inside the same callback, the attacker deposits the ETH as WETH, approves the TransferProxy, and runs issue(3, baseSet), then issue(rSet, ...). The rebalancing-set issue uses the floor value 4927. The attacker calls rebalancingSet.actualizeFee(), which bumps unitShares to 4928 with zero fee minted. A redeem(rSet, ...) against the new value releases more baseSet than was paid in. Withdraw the base set to WETH, repay the flash capital, and the surplus stays as ETH in the attacker's EOA.

DeFiHackLabs records a loss of 5.083725683631735476 ETH to the attacker (reconstructed, 0.011% over the 5.083148662083682304 ETH taken on-chain) on Ethereum. The collateral backing Set Protocol's RebalancingSetTokenV3 was skimmed through the per-share rounding surplus, with the ~25,044 ETH issue/redeem working capital borrowed and repaid in the same transaction via Uniswap v4 zero-fee flash accounting. The on-chain attack transaction is 0xaca5f83ff3a206209f3d213c33d04e6febe345f6ad52ef476bd1c0ee1505ba65, mined on 2026-10-06.

Two changes would have stopped this. actualizeFee() should not run when the fee minted is zero, and the stored value should be derived from the same arithmetic on issue and on the zero-fee recompute, so a no-op recompute cannot move the rate.

The second Set Protocol incident on the same day drained a different RebalancingSetTokenV3 vault (ETHMACOAPY) using the same actualizeFee() divCeil floor, amplified by a different shape (a residual-collateral donation that widened the rounding edge from one unit to eleven, instead of a single huge issue). The root cause is the same; the surrounding flow is different.

Sources read (3)

Other exploits

Query every incident yourself — free, no signup

Updated 11 Nov 2026. Figures are as reported by the sources listed above and are not independently verified.