All incidents

SorbettoFragola Aave/Uniswap route arbitrage extracts USDC via fee collection

Share
Aug 03, 2021 22:53 UTCMEVGain: 5,414,085.45 USDCManually checked1 exploit txWindow: Atomic
Estimated Impact
5,414,085.45 USDC
Label
MEV
Exploit Tx
1
Addresses
5
Attack Window
Atomic
Aug 03, 2021 22:53 UTC → Aug 03, 2021 22:53 UTC

Exploit Transactions

TX 1Ethereum
0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc
Aug 03, 2021 22:53 UTCExplorer

Victim Addresses

0xd63b340f6e9cccf0c997c83c8d036fa53b113546Ethereum
0x6f3f35a268b3af45331471eabf3f9881b601f5aaEthereum
0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8Ethereum
0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640Ethereum
0x99ac8ca7087fa4a2a1fb6357269965a2014abc35Ethereum

Loss Breakdown

5,414,085.45USDC

Similar Incidents

Root Cause Analysis

SorbettoFragola Aave/Uniswap route arbitrage extracts USDC via fee collection

1. Incident Overview TL;DR

In 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.

2. Key Background

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:

  • Uniswap V3 USDC/ETH pools:
    • 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8
    • 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640
    • 0x99ac8ca7087fa4a2a1fb6357269965a2014abc35
  • Aave V2 lending pool: 0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9
  • USDC token contract: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48

The pre-state σ_B (state immediately before block 12955063) is reconstructed from:

  • Seed transaction metadata and balance diffs for 0xcd7dae...70fc.
  • Sorbetto-focused call tree for that transaction.
  • Pre-exploit tx history of Sorbetto vaults 0xd63b... and 0x6f3f... and the adversary cluster, as captured in the data-collector artifacts.

3. Vulnerability Analysis & Root Cause Summary

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:

  • Aave V2 flash loans are fully repaid within the transaction.
  • Uniswap V3 pools maintain their pricing and fee-accrual mechanics.
  • SorbettoFragola distributes Uniswap trading fees over ERC20 share supply using per-share accounting and user reward debts, with no unauthorized minting or burning of vault shares.

The economic vulnerability is that these invariants, combined with:

  • Permissionless access to deposit, withdraw, and collect fees,
  • Large amounts of public flash liquidity on Aave V2, and
  • Deep, path-dependent liquidity and fee dynamics across multiple Uniswap V3 pools,

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.

4. Detailed Root Cause Analysis

Invariants and behavior of SorbettoFragola

SorbettoFragola’s design maintains several key invariants:

  • Vault shares represent a pro-rata claim on the underlying Uniswap position and idle balances.
  • Collected Uniswap fees are recorded in global token0PerShareStored and token1PerShareStored variables.
  • Each user’s 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.
  • Total supply of vault shares remains consistent with the expected mint/burn behavior for the deposit/withdraw operations observed in the call traces.

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.

Orchestrated flash-loan route

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:

  • The orchestrator borrows assets via Aave V2 flash loans.
  • It deposits into SorbettoFragola vault 0xd63b... to temporarily take a large share of the vault’s position, interacting with the underlying Uniswap V3 pool through mint and swap callbacks.
  • It then uses multi-hop swaps across Uniswap pools 0x8ad599..., 0x88e6a0..., and 0x99ac8c... to move prices and fees.
  • Helper EOA 0x576c...50cf calls collectFees on vault 0x6f3f..., realizing accumulated fees for an adversary-controlled address.
  • Finally, the orchestrator withdraws from Sorbetto, repays all flash loans, and returns control to the EOA with a large USDC surplus.

Throughout this sequence:

  • Aave V2 enforces full repayment of the flash loans.
  • Uniswap V3 pools enforce their swap and fee rules.
  • SorbettoFragola enforces its per-share fee distribution and reward-debt checks.

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.

Why this is an ACT MEV opportunity

The opportunity satisfies the ACT criteria:

  • Permissionless feasibility: The orchestrator uses only public Aave V2, Uniswap V3, SorbettoFragola, and ERC20 interfaces. Any EOA that deploys an equivalent orchestrator can submit a comparable transaction from σ_B with standard gas pricing.
  • No privileged roles or hidden data: All contracts are publicly deployed and verified; the route depends only on on-chain state, not on privileged governance actions or off-chain secrets.
  • Deterministic profit predicate: From the balance diff for USDC in the seed transaction, the adversary’s USDC balance increases from 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.

5. Adversary Flow Analysis

Adversary accounts and roles

The adversary cluster comprises:

  • EOA 0xf9e3d08196f76f5078882d98941b71c0884bea52 – Sender of the seed transaction and recipient of the 5,387,085.448419 USDC profit (USDC delta in balance_diff.json).
  • Orchestrator contract 0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6 – Called by the EOA in the seed transaction; decompiled code includes require(msg.sender == 0xf9e3...) and sequences all Aave, Uniswap, and Sorbetto interactions.
  • EOA 0x3a9d90ed069021057d9d11e78f142f2c4267934a – Deployer of the orchestrator and funder of the adversary EOA.
  • Helper EOA 0x576cf5f8ba98e1643a2c93103881d8356c3550cf – Calls collectFees on SorbettoFragola vault 0x6f3f... during the seed transaction.
  • Helper contract 0xd282f740bb0ff5d9e0a861df024fcbd3c0bd0dc8 – Transfers its token balances (WETH, WBTC, USDC, DAI, UNI) into the orchestrated flow, providing additional liquidity under adversary control.

Lifecycle stages

  1. Adversary contract deployment

    • Tx 0xb15912667e8d66e11a56c85a69018b024aa6b1a9a411b4b958cb07e2d3484f52 at block 12954674 deploys orchestrator 0xdfb6...eade6 from EOA 0x3a9d...934a.
    • Etherscan txlist and the decompiled Solidity confirm that this contract implements the MEV route and restricts its main entry to 0xf9e3...ea52.
  2. Adversary EOA funding

    • Tx 0xba5967be2d11eded4bd5fec5e4f8263e9253b736425d8991f4517ff926487389 at block 12954903 sends 9.803712915 ETH from 0x3a9d...934a to 0xf9e3...ea52.
    • This ETH funds the seed transaction’s gas and provides margin where needed; the bulk of trading notional still comes from Aave flash loans and on-chain liquidity.
  3. Single-tx MEV execution and profit realization

    • Tx 0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc at block 12955063 is sent from 0xf9e3...ea52 to 0xdfb6...eade6.
    • The call tree shows:
      • Flash loans from Aave V2.
      • Swaps across Uniswap V3 pools 0x8ad599..., 0x88e6a0..., 0x99ac8c....
      • Deposits and withdrawals on SorbettoFragola vault 0xd63b....
      • collectFees calls on SorbettoFragola vault 0x6f3f... via EOA 0x576c...50cf.
    • All flash loans are repaid; the net result is a large USDC surplus at 0xf9e3...ea52 and corresponding losses at Sorbetto vaults and Uniswap pools.

6. Impact & Losses

The seed transaction’s USDC balance diff (balance_diff.json for token 0xa0b8...6eb48) shows:

  • SorbettoFragola vault 0xd63b340f6e9cccf0c997c83c8d036fa53b113546: -41,675.197889 USDC.
  • SorbettoFragola vault 0x6f3f35a268b3af45331471eabf3f9881b601f5aa: -585.480343 USDC.
  • Uniswap V3 pool 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8: -3,738,923.233314 USDC.
  • Uniswap V3 pool 0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640: -711,267.681118 USDC.
  • Uniswap V3 pool 0x99ac8ca7087fa4a2a1fb6357269965a2014abc35: -921,633.855755 USDC.

The sum of these losses is exactly 5,414,085.448419 USDC. The same diff records:

  • Adversary EOA 0xf9e3...ea52: +5,387,085.448419 USDC.
  • Aave-related holder 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.
  • This ETH gas cost is external to the USDC profit computation; in the ACT model used here, USDC is the reference asset and the gas cost is tracked separately.

7. References

  • Seed transaction metadata, trace, and balance diff for 0xcd7dae143a4c0223349c16237ce4cd7696b1638d116a72755231ede872ab70fc (Ethereum mainnet block 12955063).
  • SorbettoFragola source code for vaults 0xd63b340f6e9cccf0c997c83c8d036fa53b113546 and 0x6f3f35a268b3af45331471eabf3f9881b601f5aa (Contract.sol and associated artifacts).
  • Sorbetto-focused call tree summary and state-diff artifacts for the seed transaction, including address-annotated userInfo and fee accounting.
  • Heimdall decompile and tx history for orchestrator contract 0xdfb6fab7f4bc9512d5620e679e90d1c91c4eade6.
  • Etherscan-style txlists for adversary-related EOAs and helper contracts around blocks 12954000–12956063.