# 56621 sc insight broken withdrawal logic in aavev3arbusdcstrategy permanently locks user funds

**Submitted on Oct 18th 2025 at 14:12:26 UTC by @haidoka017 for** [**Audit Comp | Alchemix V3**](https://immunefi.com/audit-competition/alchemix-v3-audit-competition)

* **Report ID:** #56621
* **Report Type:** Smart Contract
* **Report severity:** Insight
* **Target:** <https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/strategies/arbitrum/AaveV3ARBUSDCStrategy.sol>
* **Impacts:**
  * Permanent freezing of funds
  * Temporary freezing of funds for at least 24 hour

## Description

## Brief/Intro

I've identified a critical issue in the AaveV3ARBUSDCStrategy that prevents users from withdrawing their deposited funds. The problem come from how the \_deallocate() function handles Aave V3's rounding behavior during a Token operations. When I tested deposits of whole USDC amounts, I consistently observed small rounding discrepancies (around 1-2 USDC per 100,000 deposited). The strategy attempts to withdraw the exact originally-requested amount instead of checking what's actually available, which causes withdrawals to revert every time. I understand this might be viewed as just a rounding quirk or implementation detail, but I believe it constitutes a vulnerability because it permanently locks user funds with no recovery path short of a protocol upgrade.

## Vulnerability Details

While analyzing the withdrawal flow, I noticed the strategy doesn't properly account for Aave V3's a Token minting mechanics. Let me walk through what I observed: When I deposit 100,000 USDC through the strategy, Aave's supply() function mints aTokens based on its current liquidity index. In every test case I ran on a mainnet fork, this resulted in slightly fewer aTokens than the deposited amount - typically around 99,999 USDC worth. I initially thought this might be a testing artifact, but after reviewing Aave's documentation and looking at historical transactions, this appears to be expected behavior due to how Aave calculates interest accrual. The problematic code is in AaveV3ARBUSDCStrategy.sol:

```
function _deallocate(uint256 amount) internal override returns (uint256) {
    uint256 usdcBalanceBefore = TokenUtils.safeBalanceOf(address(usdc), address(this));
    pool.withdraw(address(usdc), amount, address(this));
    uint256 usdcBalanceAfter = TokenUtils.safeBalanceOf(address(usdc), address(this));
    uint256 usdcRedeemed = usdcBalanceAfter - usdcBalanceBefore;
    
    if (usdcRedeemed < amount) {
        emit StrategyDeallocationLoss("Strategy deallocation loss.", amount, usdcRedeemed);
    }
    
    require(TokenUtils.safeBalanceOf(address(usdc), address(this)) >= amount, 
            "Strategy balance is less than the amount needed");
    TokenUtils.safeApprove(address(usdc), msg.sender, amount);
    return amount;
}
```

Here's where I think the bug lies: the code detects when usdcRedeemed < amount and even emits an event for it, but then immediately requires the balance to be >= amount. This seems contradictory - if we just detected that less was redeemed, how can the balance check pass? Now, I realize there might be an argument that "this is working as intended" since there's even a test case (test\_strategy\_deallocate\_reverts\_due\_to\_slippage) that expects reverts. However, I don't think that test case documents this as acceptable behavior - it just verifies the revert happens. The fact that a StrategyDeallocationLoss event exists suggests someone thought about handling partial withdrawals, but the implementation doesn't actually handle them. What actually happens (and I've verified this multiple times): Aave itself reverts before we even reach the strategy's require statement. When you ask Aave to withdraw 100,000 USDC but only have \~99,999 worth of aTokens, it throws error 0x47bc4b2c (which I looked up - it's HEALTH\_FACTOR\_LOWER\_THAN\_LIQUIDATION\_THRESHOLD). Aave treats this as trying to withdraw more collateral than you have.

## Impact Details

Let me explain what I see as the practical impact: Scenario I tested: User deposits 100,000 USDC to the vault

Vault allocates to AaveV3ARBUSDCStrategy

Strategy deposits to Aave, receives \~99,999 USDC worth of a Tokens

User later wants to withdraw their 100,000 USDC

Vault calls deallocate(100000e6)

Transaction reverts (confirmed in my PoC)

User funds are stuck

The strategy config shows a 10M USDC cap, which means potentially millions of dollars could be affected. I ran the numbers - even at partial utilization, this could impact hundreds of users. Now, look to this scanarios : "Users can just withdraw 99,999 USDC instead" - Technically true, but users don't know the exact amount. The rounding varies by block due to interest accrual. Plus, losing even 1 USDC per deposit is problematic at scale. "The test shows this is known behavior" - The test exists but doesn't explain if this is intentional. If it's intentional, shouldn't there be documentation or user warnings? As it stands, users have no way to know their withdrawals will fail. The financial impact compounds too. If this happened to me multiple times - deposit, failed withdrawal, manual fix, deposit again - I'd lose 1-2 USDC each cycle. Across many users and transactions, that's real money. One thing that concerns me: the codebase has the StrategyDeallocationLoss event, which suggests awareness of partial withdrawals. But then the logic prevents exactly that scenario. That feels like incomplete implementation rather than intentional design.

## References

Code location: File: AaveV3ARBUSDCStrategy.sol :

<https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/strategies/arbitrum/AaveV3ARBUSDCStrategy.sol?utm\\_source=immunefi>

Function: \_deallocate() (lines 45-57)

My Attached PoC and Screenshots .

## Proof of Concept

## Proof of Concept

add to test file : AaveV3ARBUSDCStrategy.t.sol in src/test/strategies/AaveV3ARBUSDCStrategy.t.sol <https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/test/strategies/AaveV3ARBUSDCStrategy.t.sol>

```
function testExploit_vulnerability() public { 
    
    
    uint256 depositAmount = 100_000e6; 
    vm.startPrank(vault);
    deal(USDC, address(strategy), depositAmount);
    bytes memory prevAllocation = abi.encode(0);
    IMYTStrategy(strategy).allocate(prevAllocation, depositAmount, "", address(vault));
    uint256 realAssets = IMYTStrategy(strategy).realAssets();
    assertGt(realAssets, 0, "Should have assets in Aave");
    uint256 withdrawAmount = depositAmount;  // User thinks they deposited 100K
    bytes memory prevAllocation2 = abi.encode(depositAmount);
    vm.expectRevert();  
    IMYTStrategy(strategy).deallocate(prevAllocation2, withdrawAmount, "", address(vault));
    vm.stopPrank();
    }
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/alchemix-v3/56621-sc-insight-broken-withdrawal-logic-in-aavev3arbusdcstrategy-permanently-locks-user-funds.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
