This is a lower bound: only assets with reliable historical USD prices are counted, so the actual loss may be higher.
0x60cf091cd3f50420d50fd7f707414d0df4751c58Optimism0xe3b81318b1b6776f0877c3770afddff97b9f5fe5Optimism0xf7b5965f5c117eb1b5450187c9dcfccc3c317e8eOptimismTwo attacker-crafted Optimism transactions exploited Sonne Finance immediately after the soVELO market was listed. The attacker first used an open timelock executor role to execute governance actions, then minted only 2 soVELO and directly donated 2552964259704665837527 VELO to the soVELO contract. Sonne later accepted the donation-inflated exchange rate as valid collateral and let the attacker borrow real protocol assets in tx 0x9312ae377d7ebdf3c7c3a86f80514878deb5df51aad38b6191d55db53e42b7f0.
The root cause was not oracle manipulation or privileged access. It was Compound-style collateral accounting that trusted the raw underlying balance of the cToken contract. In soVELO, direct VELO transfers increased totalCash without minting proportional cTokens, and Comptroller liquidity checks converted that inflated exchange rate into borrow power.
Sonne on Optimism uses Compound-style cTokens with a Unitroller/Comptroller architecture. A market's collateral value is derived from three ingredients: the holder's cToken balance, the market collateral factor, and the cToken exchange rate multiplied by the oracle price.
The relevant cToken path is:
uint256 totalCash = getCashPrior();
uint256 exchangeRate = (totalCash + totalBorrows - totalReserves) * 1e18 / totalSupply;
In CErc20, getCashPrior() is just the underlying token balance of the cToken contract:
function getCashPrior() virtual override internal view returns (uint) {
EIP20Interface token = EIP20Interface(underlying);
return token.balanceOf(address(this));
}
That matters because a plain ERC20 transfer can increase totalCash without going through . Once such a market is collateral-enabled and priced, Comptroller uses the stored exchange rate when computing account liquidity.
mint()The vulnerable condition was enabling collateral on a near-empty soVELO market whose exchange rate could be inflated through direct VELO donation. exchangeRateStoredInternal() treated all VELO held by the soVELO contract as legitimate market cash, regardless of whether the VELO arrived via mint() or a direct transfer. That created a mismatch between cToken share ownership and underlying assets.
Comptroller then consumed the inflated exchange rate inside getHypotheticalAccountLiquidityInternal():
vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa});
vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice);
vars.sumCollateral = mul_ScalarTruncateAddUInt(vars.tokensToDenom, vars.cTokenBalance, vars.sumCollateral);
Because the attacker held only 2 soVELO, the inflated exchange rate made those 2 shares appear to represent the donated VELO. Once the timelock-enabled collateral factor was set to 0.35e18, the fabricated collateral value became real borrow capacity. The exploit is therefore a protocol accounting failure: collateral valuation trusted raw cToken cash balances in a donation-sensitive market.
At block 120062493, Sonne's timelock 0x37ff10390f22fabdc2137e428a6e6965960d60b6 exposed EXECUTOR_ROLE to address(0), so queued governance operations were executable by any unprivileged actor. Auditor verification confirms the open executor role and a live non-zero oracle price for soVELO.
The first attacker transaction, 0x45c0ccfd3ca1b4a937feebcb0f5a166c409c9e403070808835d41da40732db96, executed the listing-related timelock actions and established the market configuration. The attacker path then minted only 2 soVELO while sending 2552964259704665837527 VELO directly into the soVELO contract. The tx balance diff shows the attacker helper lost exactly that VELO amount and soVELO gained the same amount, while soVELO share supply credited only 2 cTokens.
The code-level breakpoint is the combination of CErc20.getCashPrior() and CToken.exchangeRateStoredInternal(). Because getCashPrior() reads the raw VELO balance of the cToken contract, the direct donation caused exchangeRateStored() to jump to 1276482129852332918763500000000000000000 while total supply remained tiny. That result is confirmed in the on-chain verification artifact.
The second attacker transaction, 0x9312ae377d7ebdf3c7c3a86f80514878deb5df51aad38b6191d55db53e42b7f0, executed the separate timelock action that set the soVELO collateral factor to 0.35e18, then borrowed against the inflated collateral state. Comptroller's liquidity path multiplied the attacker's cToken balance by collateral factor, exchange rate, and oracle price, treating the donated VELO as legitimate collateral backing.
The resulting extraction was real, not accounting-only. The tx balance diff shows soVELO's VELO balance falling from 2552964259704665837527 to 200000000000000000001, a depletion of 2352964259704665837526 VELO. The same tx credited attacker-linked addresses with 724276902167 USDC and 999946408013195540 wei. This closes the loop from listing, to inflation, to borrow realization, to asset exit.
The adversary cluster used two EOAs and multiple helper contracts, but no privileged keys or private artifacts were required.
0x5d0d99e9886581ff8fcb01f35804317f5ed80bbb sent tx 0x45c0... to helper 0xa78aefd483ce3919c0ad55c8a2e5c97cbac1caf8.0x60cf091cd3f50420d50fd7f707414d0df4751c58 at oracle 0x22c7e5ce392bc951f63b68a8020b121a8e1c0fea.2 soVELO, then transferred 2552964259704665837527 VELO directly into soVELO 0xe3b81318b1b6776f0877c3770afddff97b9f5fe5, inflating the exchange rate.0xae4a7cde7c99fb98b0d5fa414aa40f0300531f43 then sent tx 0x9312... to helper 0x02fa2625825917e9b1f8346a465de1bbc150c5b9.0x9312... show the timelock execution that set collateral factor 0.35e18, followed by borrow-side activity involving soVELO and soWETH and transfers through helper 0xa16388a6210545b27f669d5189648c1722300b8b.The exploit remained ACT because the decisive enabler was the public timelock executor role. The helper contracts only automated execution; they did not supply exclusive capability.
The exploit converted fabricated soVELO collateral value into borrowable protocol assets. The measured losses and gains captured in the evidence are:
2352964259704665837526 wei-token units drained from soVELO (2352.964259704665837526 VELO)724276902167 base units gained by attacker-linked helper (724276.902167 USDC)999946408013195540 wei gained by the attacker EOAThe directly affected protocol components were Unitroller 0x60cf091cd3f50420d50fd7f707414d0df4751c58, soVELO 0xe3b81318b1b6776f0877c3770afddff97b9f5fe5, and the borrow-side market soWETH 0xf7b5965f5c117eb1b5450187c9dcfccc3c317e8e.
0x45c0ccfd3ca1b4a937feebcb0f5a166c409c9e403070808835d41da40732db960x9312ae377d7ebdf3c7c3a86f80514878deb5df51aad38b6191d55db53e42b7f00x37ff10390f22fabdc2137e428a6e6965960d60b60x60cf091cd3f50420d50fd7f707414d0df4751c580xe8ff1489227fa74f77e49c688903e69e1583c03f0xe3b81318b1b6776f0877c3770afddff97b9f5fe50xf7b5965f5c117eb1b5450187c9dcfccc3c317e8e0x22c7e5ce392bc951f63b68a8020b121a8e1c0fea0x45c0... and 0x9312..., plus the structured receipt and trace artifacts for both transactions