Calculated from recorded token losses using historical USD prices at the incident time.
0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc0xd63b340f6e9cccf0c997c83c8d036fa53b113546Ethereum0x6f3f35a268b3af45331471eabf3f9881b601f5aaEthereum0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8Ethereum0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640Ethereum0x99ac8ca7087fa4a2a1fb6357269965a2014abc35EthereumIn Ethereum mainnet block 12955063, EOA 0xf9e3d08196f76f5078882d98941b71c0884bea52 sent a single transaction (0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc) to adversary-orchestrator contract 0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6. That transaction atomically borrowed from Aave V2, traded through several Uniswap V3 pools, interacted with SorbettoFragola vaults, collected fees, and withdrew liquidity, finishing with exactly 5,387,085.448419 USDC at the adversary EOA while paying gas only in ETH.
No smart-contract invariant or access control check was violated. Instead, the root cause is an ACT MEV opportunity: permissionless Aave V2, Uniswap V3, and SorbettoFragola contracts allow an unprivileged searcher to route flash liquidity and fee-collection operations such that SorbettoFragola depositors and Uniswap LPs lose value while protocol-level invariants remain satisfied.
SorbettoFragola vaults are ERC20Permit vaults that manage concentrated Uniswap V3 liquidity. Users deposit token0 and token1 to mint vault shares, and withdraw by burning shares to reclaim a proportional share of the underlying Uniswap position and idle balances. The vault also exposes collectFees so users can claim accumulated Uniswap trading fees according to per-share accounting.
In the exploited configuration, the relevant SorbettoFragola vaults are:
0xd63b340f6e9cccf0c997c83c8d036fa53b113546 – SorbettoFragola USDC/WETH vault.0x6f3f35a268b3af45331471eabf3f9881b601f5aa – another SorbettoFragola USDC/WETH vault.The collected Sorbetto source (Contract.sol under the Sorbetto artifact tree) shows that deposits and fee collections are permissionless and that per-share accounting is handled by global and per-user variables:
/// @inheritdoc ISorbettoFragola
function deposit(
uint256 amount0Desired,
uint256 amount1Desired
)
external
payable
override
nonReentrant
checkDeviation
updateVault(msg.sender)
returns (uint256 shares, uint256 amount0, uint256 amount1)
{
require(amount0Desired > 0 && amount1Desired > 0, "ANV");
...
_mint(msg.sender, shares);
emit Deposit(msg.sender, shares, amount0, amount1);
}
function collectFees(uint256 amount0, uint256 amount1) external nonReentrant updateVault(msg.sender) {
UserInfo storage user = userInfo[msg.sender];
require(user.token0Rewards >= amount0, "A0R");
require(user.token1Rewards >= amount1, "A1R");
...
user.token0Rewards = user.token0Rewards.sub(amount0);
user.token1Rewards = user.token1Rewards.sub(amount1);
emit RewardPaid(msg.sender, amount0, amount1);
}
There is no access control beyond standard reentrancy and deviation checks; any address that has accrued fees or holds shares can call these functions. Global variables such as accruedProtocolFees0, accruedProtocolFees1, token0PerShareStored, and token1PerShareStored track fee accrual per share, while userInfo maintains each account’s reward debt and balances.
The adversary uses an orchestrator contract at 0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6. Heimdall decompilation for its main entry point (0x6466c309) clearly hard-codes the controlling EOA and routes through standard DeFi components:
/// @custom:selector 0x6466c309
/// @custom:signature Unresolved_6466c309(uint256 arg0, uint256 arg1) public
function Unresolved_6466c309(uint256 arg0, uint256 arg1) public {
...
require(address(msg.sender) == 0xf9e3d08196f76f5078882d98941b71c0884bea52);
...
var_c = 0xdac17f958d2ee523a2206206994597c13d831ec7; // USDT
var_d = 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2; // WETH
...
var_f = 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48; // USDC
var_g = 0x6b175474e89094c44da98b954eedeac495271d0f; // DAI
var_h = 0x1f9840a85d5af5bf1d1762f925bdaddc4201f984; // UNI
...
require(address(0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9).code.length); // Aave V2 lending pool
(bool success, bytes memory ret0) =
address(0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9).Unresolved_ab9c4b5d(var_q); // flash loan entry
...
}
This function requires the call to originate from 0xf9e3...ea52 and then orchestrates Aave flash loans, Uniswap V3 swaps, and SorbettoFragola interactions. There is no privileged dependency beyond control of this EOA and the deployed orchestrator contract.
The key liquidity venues and vaults involved in the exploit are:
0x8ad599c3a0ff1de082011efddc58f1908eb6e6d80x88e6a0c2ddd26feeb64f039a2c41296fcb3f56400x99ac8ca7087fa4a2a1fb6357269965a2014abc350x7d2768de32b0b80b7a3454c06bdac94a69ddc7a90xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48The pre-state σ_B (state immediately before block 12955063) is reconstructed from:
0xcd7dae...70fc.0xd63b... and 0x6f3f... and the adversary cluster, as captured in the data-collector artifacts.This incident is not a smart-contract bug but an ACT MEV opportunity. All core protocol invariants for Aave V2, Uniswap V3, and SorbettoFragola remain satisfied throughout the transaction:
The economic vulnerability is that these invariants, combined with:
allow an unprivileged adversary to construct a single standard transaction which extracts a large amount of USDC from SorbettoFragola vaults and Uniswap LPs. Because the strategy uses only public interfaces, public traces, and permissionless contracts, it constitutes an ACT opportunity that any sufficiently equipped searcher can realize from σ_B.
SorbettoFragola’s design maintains several key invariants:
token0PerShareStored and token1PerShareStored variables.userInfo entry tracks token0Rewards, token1Rewards, and the per-share values they have already been credited for.collectFees only transfers amounts the user has actually accrued (A0R/A1R checks) and then decrements user.token*Rewards by the amount paid.State-diff artifacts for the vaults over the seed transaction (state_diff_sorbetto_0xd63b... and state_diff_sorbetto_0x6f3f...) show:
accruedProtocolFees*, usersFees*, token*PerShareStored all increase modestly, reflecting fee accrual over time.userInfo entries for adversary-linked addresses (orchestrator, helper EOAs) are updated consistently with fee accrual and collection.These observations confirm that SorbettoFragola behaves as designed. There is no arithmetic overflow, unchecked mint, or unauthorized withdrawal; instead, the adversary leverages the intended, but economically fragile, fee and share accounting.
The orchestrator function 0x6466c309 is callable only by EOA 0xf9e3...ea52. The collected call tree for the seed transaction (sorbetto_call_tree_summary.json) shows the following high-level sequence:
{
"chainid": 1,
"txhash": "0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc",
"calls": [
{
"from": "0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6",
"to": "0xd63b340f6e9cccf0c997c83c8d036fa53b113546",
"decoded": { "function": "deposit", "args": [...] }
},
{
"from": "0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6",
"to": "0xd63b340f6e9cccf0c997c83c8d036fa53b113546",
"decoded": { "function": "withdraw", "args": [...] }
},
{
"from": "0x576cf5f8ba98e1643a2c93103881d8356c3550cf",
"to": "0x6f3f35a268b3af45331471eabf3f9881b601f5aa",
"decoded": { "function": "collectFees", "args": [...] }
}
]
}
Together with the full call tree and trace, this shows that:
0xd63b... to temporarily take a large share of the vault’s position, interacting with the underlying Uniswap V3 pool through mint and swap callbacks.0x8ad599..., 0x88e6a0..., and 0x99ac8c... to move prices and fees.0x576c...50cf calls collectFees on vault 0x6f3f..., realizing accumulated fees for an adversary-controlled address.Throughout this sequence:
The net effect is that the adversary’s path exploits price and fee path-dependence, along with temporarily concentrated vault positions, to concentrate losses on Sorbetto depositors and Uniswap LPs.
The opportunity satisfies the ACT criteria:
0 to 5,387,085,448,419 units (5,387,085.448419 USDC) while no negative USDC deltas appear for the adversary EOA, orchestrator, or funder. This yields a deterministic positive USDC outcome given σ_B.Accordingly, the root cause is the existence of this economically harmful yet protocol-compliant route, not an implementation bug in SorbettoFragola or the integrated protocols.
The adversary cluster comprises:
0xf9e3d08196f76f5078882d98941b71c0884bea52 – Sender of the seed transaction and recipient of the 5,387,085.448419 USDC profit (USDC delta in balance_diff.json).0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6 – Called by the EOA in the seed transaction; decompiled code includes require(msg.sender == 0xf9e3...) and sequences all Aave, Uniswap, and Sorbetto interactions.0x3a9d90ed069021057d9d11e78f142f2c4267934a – Deployer of the orchestrator and funder of the adversary EOA.0x576cf5f8ba98e1643a2c93103881d8356c3550cf – Calls collectFees on SorbettoFragola vault 0x6f3f... during the seed transaction.0xd282f740bb0ff5d9e0a861df024fcbd3c0bd0dc8 – Transfers its token balances (WETH, WBTC, USDC, DAI, UNI) into the orchestrated flow, providing additional liquidity under adversary control.Adversary contract deployment
0xb15912667e8d66e11a56c85a69018b024aa6b1a9a411b4b958cb07e2d3484f52 at block 12954674 deploys orchestrator 0xdfb6...eade6 from EOA 0x3a9d...934a.0xf9e3...ea52.Adversary EOA funding
0xba5967be2d11eded4bd5fec5e4f8263e9253b736425d8991f4517ff926487389 at block 12954903 sends 9.803712915 ETH from 0x3a9d...934a to 0xf9e3...ea52.Single-tx MEV execution and profit realization
0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc at block 12955063 is sent from 0xf9e3...ea52 to 0xdfb6...eade6.0x8ad599..., 0x88e6a0..., 0x99ac8c....0xd63b....collectFees calls on SorbettoFragola vault 0x6f3f... via EOA 0x576c...50cf.0xf9e3...ea52 and corresponding losses at Sorbetto vaults and Uniswap pools.The seed transaction’s USDC balance diff (balance_diff.json for token 0xa0b8...6eb48) shows:
0xd63b340f6e9cccf0c997c83c8d036fa53b113546: -41,675.197889 USDC.0x6f3f35a268b3af45331471eabf3f9881b601f5aa: -585.480343 USDC.0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8: -3,738,923.233314 USDC.0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640: -711,267.681118 USDC.0x99ac8ca7087fa4a2a1fb6357269965a2014abc35: -921,633.855755 USDC.The sum of these losses is exactly 5,414,085.448419 USDC. The same diff records:
0xf9e3...ea52: +5,387,085.448419 USDC.0xbcca60bb61934080951369a648fb03df4f96263c: +27,000.000000 USDC.An excerpt of the USDC balance diff illustrates these movements:
{
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"holder": "0xd63b340f6e9cccf0c997c83c8d036fa53b113546",
"delta": "-41675197889"
},
{
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"holder": "0x6f3f35a268b3af45331471eabf3f9881b601f5aa",
"delta": "-585480343"
},
{
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"holder": "0xf9e3d08196f76f5078882d98941b71c0884bea52",
"delta": "5387085448419"
}
Here, raw deltas are in USDC’s 6-decimal units. The adversary’s USDC gain and the Aave-related increase together exactly equal the combined losses of the Sorbetto vaults and Uniswap pools, confirming conservation of total USDC and pinpointing where value leaves the victims and arrives.
Native balance deltas show that:
0xf9e3...ea52 pays 0.49648356 ETH in gas to block producer 0x99c85bb64564d9ef9a99621301f22c9993cb89e3.0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc (Ethereum mainnet block 12955063).0xd63b340f6e9cccf0c997c83c8d036fa53b113546 and 0x6f3f35a268b3af45331471eabf3f9881b601f5aa (Contract.sol and associated artifacts).0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6.