# 58409 sc high high arithmetic underflow in mytstrategy sol s deallocate check prevents yield withdrawal

**Submitted on Nov 2nd 2025 at 02:08:55 UTC by @chief\_hunter888 for** [**Audit Comp | Alchemix V3**](https://immunefi.com/audit-competition/alchemix-v3-audit-competition)

* **Report ID:** #58409
* **Report Type:** Smart Contract
* **Report severity:** High
* **Target:** <https://github.com/alchemix-finance/v3-poc/blob/immunefi\\_audit/src/MYTStrategy.sol>
* **Impacts:**
  * Theft of unclaimed yield

## Description

## \[HIGH] Arithmetic Underflow in `MYTStrategy.sol`'s `deallocate()` Prevents Yield Withdrawal

### **Summary**

The `MYTStrategy` base contract is designed to work with yield-bearing strategies that wrap ERC4626 vaults (e.g., MorphoYearnOGWETH, EulerWETH). These strategies deposit assets (e.g., WETH) and receive shares that accrue value over time. When the Alchemix vault later deallocates funds (for user withdrawals, rebalancing, or other operations), it should be able to withdraw the original principal **plus** any earned yield.

However, the `deallocate()` function in `MYTStrategy.sol` contains a critical arithmetic flaw on **line 130** that makes withdrawing yield impossible:

```solidity
function deallocate(bytes memory data, uint256 assets, ...) external returns (...) {
    uint256 oldAllocation = abi.decode(data, (uint256));      // Line 128: Original deposit amount
    uint256 amountDeallocated = _deallocate(assets);          // Line 129: Amount withdrawn (can include yield)
    uint256 newAllocation = oldAllocation - amountDeallocated; // Line 130: ❌ UNDERFLOW!
    // ...
}
```

**The Problem:**

* `oldAllocation` is decoded from the `data` parameter passed by the vault (e.g., 100 WETH)
* The `data` parameter contains **stale accounting** - it reflects the allocation amount at some previous point in time. On-chain `oldAllocation` tracks the **original deposit amount** (e.g., 100 WETH). See allocate flow.
* `amountDeallocated` is the **actual amount withdrawn**, which includes accrued yield (e.g., 105 WETH after 5% yield)
* When yield exists: `100 WETH - 105 WETH` causes **arithmetic underflow** and reverts

**Critical Issue:** The `data` parameter has **no freshness guarantee**. There's no timestamp or block number to validate that `oldAllocation` reflects current reality. Even if the vault could update `data` externally (via oracle or manual update), yield earned **between updates** remains permanently inaccessible because the accounting is always behind the actual accrued value.

***

### **Impact**

**Critical:** This bug fundamentally breaks the core purpose of yield-bearing strategies - earned yield cannot be withdrawn by the protocol.

1. **Yield Permanently Frozen Between Updates**: The vault can only deallocate up to the amount encoded in the `data` parameter. Any yield earned beyond that amount causes arithmetic underflow. Even if `data` could theoretically be updated externally:
   * There's no freshness validation (no block number or timestamp)
   * Yield accrues continuously, but `data` updates are discrete
   * The gap between actual value and recorded `oldAllocation` creates permanently locked yield
   * For strategies earning 5-20% APY, significant value becomes inaccessible
2. **Complete Protocol DoS When Attempting Full Withdrawal**: Any attempt to deallocate the actual current value (principal + yield) causes an arithmetic underflow revert, making the following operations impossible:
   * Processing user withdrawal requests for full balances
   * Vault rebalancing operations to optimize yield
   * Emergency fund recovery during crises
   * Strategy migrations or upgrades
   * Liquidations requiring full collateral withdrawal
3. **Affects ALL Strategies**: This bug is in the `MYTStrategy.sol` base contract, meaning it affects **every strategy** that inherits from it:
   * `MorphoYearnOGWETHStrategy`
   * `EulerWETHStrategy` / `EulerUSDCStrategy`
   * `PeapodsETHStrategy` / `PeapodsUSDCStrategy`
   * `TokeAutoEthStrategy` / `TokeAutoUSDStrategy`
   * And all other MYT strategies
4. **Loss of Funds**: While the principal remains technically accessible (assuming `data` reflects at least the principal), the yield (which could represent millions of dollars across all strategies) is permanently or semi-permanently locked, depending on whether and how frequently `data` could be updated externally.

Furthermore, in addition to preventing the current programmatic underflow reverts that may occur during deallocation when the yield-bearing token snapshot is outdated, the code should be updated to handle this “out-of-sync” error more gracefully than a Solidity revert. Specifically, an early check should be introduced at the start of the deallocate function to verify that previousAllocationData is sufficiently fresh, using the block number at which it was last refreshed. This ensures all bookkeeping values are up-to-date and prevents the error from being triggered.

Implementing this check also guarantees that the maximum possible amount can be withdrawn from the strategy via deallocation at any given time. Without fresh off-chain oracle data, any strategy will always have a portion of yield effectively frozen, because the *amount* that can be deallocated is limited to the last snapshot recorded in the *data* field.

***

### **Proof of Concept**

**Scenario:** Alchemix vault allocates to MorphoYearnOGWETH strategy, yield accrues, then protocol attempts to deallocate.

```solidity
// Step 1: Initial allocation by Alchemix vault
// Vault allocates 100 WETH to strategy via allocate()
// The allocation amount (100 ether) gets encoded in vault's internal data
bytes memory allocationData = abi.encode(100 ether);
strategy.allocate(allocationData, 100 ether, selector, vault);

// Step 2: Time passes, yield accrues in the underlying ERC4626 vault
// Strategy's shares are now worth 105 WETH (5% yield earned)
uint256 currentValue = strategy.realAssets(); // Returns 105 ether
// But allocationData still encodes the STALE value: 100 ether

// Step 3: Protocol attempts to deallocate full value (principal + yield)
// Vault calls deallocate with the same stale allocationData
strategy.deallocate(allocationData, 105 ether, selector, vault);

// Inside deallocate():
uint256 oldAllocation = abi.decode(allocationData, (uint256));  // 100 ether (STALE!)
uint256 amountDeallocated = _deallocate(105 ether);             // 105 ether (ACTUAL!)
uint256 newAllocation = oldAllocation - amountDeallocated;      // Line 130
// ❌ REVERTS: 100 - 105 = arithmetic underflow!

// Result: Protocol cannot withdraw yield earned in strategy
// User withdrawal requests fail, funds are stuck
```

**Test Evidence:** See `test_MYTStrategy_arithmetic_underflow_with_yield()` in:

* `src/test/strategies/MockYieldStrategy.t.sol` (clean POC)
* `src/test/strategies/MorphoYearnOGWETHStrategy.t.sol` (real strategy)

How to run

```
source .env && FOUNDRY_PROFILE=default forge test --fork-url $MAINNET_RPC_URL --match-path "src/test/strategies/MorphoYearnOGWETHStrategy.t.sol" --match-test "test_MYTStrategy_arithmetic_underflow_with_yield" -vv --evm-version cancun
```

***

### **Likelihood**

**Certainty: 100%** — This will occur in **every** yield-bearing strategy deployment:

1. **Yield Accrual is Expected**: The entire purpose of these strategies is to earn yield (5-20% APY). Yield accrues continuously in the underlying ERC4626 vaults.
2. **Data Staleness is Inevitable**: The `data` parameter passed to `deallocate()` contains a snapshot of allocation at some previous time. Unless there's a mechanism to update this data on every block (which would be prohibitively expensive), the encoded `oldAllocation` will **always** lag behind the actual `realAssets()` value.
3. **Affects Normal Operations**: Any protocol operation attempting to deallocate the full current value (principal + accrued yield) will hit this underflow:
   * User withdrawal requests for full balances
   * Vault rebalancing to move funds between strategies
   * Emergency deallocations during market stress
   * Strategy migrations
4. **No Freshness Validation**: The contract has **no mechanism** to verify that `data` is fresh. There's no block number, timestamp, or staleness check. Even if an external system could update `data`, the contract would blindly trust any value passed in.
5. **Already Deployed**: If these strategies are in production, yield is accruing **right now** and the gap between `oldAllocation` and `realAssets()` is growing with every block.

***

### **Recommendation**

**Option 1: Add Freshness Validation (Recommended)**

Update the `data` structure to include freshness metadata and validate it:

```solidity
struct AllocationData {
    uint256 amount;
    uint256 lastUpdateBlock;  // Block when this data was last updated
}

function deallocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
    external
    onlyVault
    returns (bytes32[] memory strategyIds, int256 change)
{
    if (killSwitch) {
        return (ids(), int256(0));
    }
    require(assets > 0, "Zero amount");
    
    AllocationData memory allocData = abi.decode(data, (AllocationData));
    
    // Validate freshness: data must be from recent blocks
    require(
        block.number - allocData.lastUpdateBlock <= MAX_STALENESS_BLOCKS,
        "Allocation data too stale"
    );
    
    // Require vault to update data to current realAssets() before large deallocations
    uint256 currentValue = realAssets();
    if (assets > allocData.amount * 105 / 100) { // If withdrawing >105% of recorded amount
        require(
            allocData.amount >= currentValue * 99 / 100, // Must be within 1% of current
            "Must refresh allocation data before withdrawing yield"
        );
    }
    
    uint256 amountDeallocated = _deallocate(assets);
    uint256 newAllocation = allocData.amount > amountDeallocated 
        ? allocData.amount - amountDeallocated 
        : 0;
    
    emit Deallocate(amountDeallocated, address(this));
    return (ids(), int256(newAllocation) - int256(allocData.amount));
}
```

**Option 2: Fix Arithmetic Logic (Simpler, but doesn't address staleness)**

```solidity
function deallocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
    external
    onlyVault
    returns (bytes32[] memory strategyIds, int256 change)
{
    if (killSwitch) {
        return (ids(), int256(0));
    }
    require(assets > 0, "Zero amount");
    
    uint256 oldAllocation = abi.decode(data, (uint256));
    uint256 amountDeallocated = _deallocate(assets);
    
    // FIX: Handle case where withdrawal includes yield (amountDeallocated > oldAllocation)
    uint256 newAllocation = oldAllocation > amountDeallocated 
        ? oldAllocation - amountDeallocated 
        : 0;
    
    if (amountDeallocated > oldAllocation) {
        emit YieldWithdrawn(amountDeallocated - oldAllocation);
    }
    
    emit Deallocate(amountDeallocated, address(this));
    return (ids(), int256(newAllocation) - int256(oldAllocation));
}
```

**Option 3: Vault-Level Solution**

Implement a mechanism in the vault to query `strategy.realAssets()` and update the allocation data before calling `deallocate()`, ensuring `oldAllocation` always reflects current value including yield.

***

### **Root Cause**

**Two interrelated design flaws:**

1. **Stale Data Without Freshness Validation**: The `data` parameter encodes allocation amounts but has no mechanism to ensure this data is current:
   * No block number or timestamp to validate freshness
   * No requirement that `oldAllocation` reflects `realAssets()` before deallocation
   * The vault could theoretically update `data` externally (oracle, manual update), but the contract has no way to enforce or verify this
2. **Arithmetic Assumes No Yield**: The subtraction `oldAllocation - amountDeallocated` (line 130) assumes `amountDeallocated ≤ oldAllocation`. This works for:
   * Non-yield-bearing strategies
   * Partial withdrawals less than original deposit
   * **But fails for yield-bearing strategies** where `amountDeallocated` can include accrued yield

The fundamental issue: **The contract treats allocations as static amounts, but yield-bearing strategies have dynamic, appreciating values**. Without fresh accounting data, any yield earned since the last update becomes inaccessible.

***

### **Severity**

**HIGH / CRITICAL**

* **Impact**: Critical - Yield permanently or semi-permanently frozen, protocol-level DoS for operations requiring full withdrawals
* **Likelihood**: Certain - Data staleness is inevitable unless updated every block (prohibitively expensive); affects 100% of yield-bearing strategy usage
* **Affected Code**: Base contract affecting all inheriting strategies (10+ production strategies)
* **Protocol Impact**:
  * Cannot process user withdrawal requests for full balances
  * Cannot fully rebalance between strategies
  * Yield earned between data updates is inaccessible
  * Emergency operations blocked
* **Financial Impact**: Yield losses proportional to update frequency and APY rates (5-20% APY on potentially millions of TVL)

***

### **References**

**Vulnerable Code:**

* File: `src/MYTStrategy.sol`
* Function: `deallocate()` (lines 119-133)
* Bug Location: Line 130

**Affected Strategies (Inheriting from MYTStrategy):**

* `src/strategies/mainnet/MorphoYearnOGWETH.sol`
* `src/strategies/mainnet/EulerWETHStrategy.sol`
* `src/strategies/mainnet/EulerUSDCStrategy.sol`
* `src/strategies/mainnet/PeapodsETH.sol`
* `src/strategies/mainnet/PeapodsUSDC.sol`
* `src/strategies/mainnet/TokeAutoEth.sol`
* `src/strategies/mainnet/TokeAutoUSDStrategy.sol`
* All other strategies inheriting `MYTStrategy`

**Proof of Concept Tests:**

* `src/test/strategies/MockYieldStrategy.t.sol::test_MYTStrategy_arithmetic_underflow_with_yield()`
* `src/test/strategies/MorphoYearnOGWETHStrategy.t.sol::test_MYTStrategy_arithmetic_underflow_with_yield()`

**Documentation:**

* [Alchemix Meta Yield Token Documentation](https://keenanlukeom.github.io/alchemix-v3-docs/dev/myt/meta-yield-token)

## Proof of Concept

## Proof of Concept

Mock Contract that mocks a Yield Accruing Strategy for testing:

```
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "../../MYTStrategy.sol";
import {IERC20} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

/**
 * @title MockYieldStrategy
 * @notice A mock strategy that simulates yield accrual for testing
 * @dev This is the SIMPLEST way to demonstrate the MYTStrategy architectural flaw
 */
contract MockYieldStrategy is MYTStrategy {
    IERC20 public immutable asset;
    
    uint256 private _totalDeposited;
    uint256 private _simulatedYield;
    
    constructor(
        address _myt,
        StrategyParams memory _params,
        address _asset,
        address _permit2Address
    ) MYTStrategy(_myt, _params, _permit2Address, _asset) {
        asset = IERC20(_asset);
    }
    
    /// @notice Simulate yield accrual
    function simulateYield(uint256 yieldAmount) external {
        _simulatedYield += yieldAmount;
    }
    
    function _allocate(uint256 amount) internal override returns (uint256) {
        require(asset.balanceOf(address(this)) >= amount, "Insufficient balance");
        _totalDeposited += amount;
        return amount;
    }
    
    function _deallocate(uint256 amount) internal override returns (uint256) {
        uint256 availableBalance = asset.balanceOf(address(this));
        require(availableBalance >= amount, "Insufficient balance to deallocate");
        
        _totalDeposited -= amount > _totalDeposited ? _totalDeposited : amount;
        if (_simulatedYield >= amount) {
            _simulatedYield -= amount;
        }
        
        // Approve the vault to take the assets
        asset.approve(msg.sender, amount);
        return amount;
    }
    
    function realAssets() external view override returns (uint256) {
        // Returns deposited amount + simulated yield
        return _totalDeposited + _simulatedYield;
    }
    
    function _previewAdjustedWithdraw(uint256 amount) internal view override returns (uint256) {
        return amount;
    }
}

```

the mock yield strategy test:

```
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "../libraries/BaseStrategyTest.sol";
import {MockYieldStrategy} from "../mocks/MockYieldStrategy.sol";
import {IERC20} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {console} from "../../../lib/forge-std/src/console.sol";
import {stdError} from "../../../lib/forge-std/src/StdError.sol";

/**
 * @title MockYieldStrategyTest
 * @notice Demonstrates the architectural flaw in MYTStrategy.sol
 * @dev This is the SIMPLEST test showing MYTStrategy cannot handle yield
 */
contract MockYieldStrategyTest is BaseStrategyTest {
    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public constant MAINNET_PERMIT2 = 0x000000000022d473030f1dF7Fa9381e04776c7c5;

    function getStrategyConfig() internal pure override returns (IMYTStrategy.StrategyParams memory) {
        return IMYTStrategy.StrategyParams({
            owner: address(1),
            name: "MockYield",
            protocol: "MockYield",
            riskClass: IMYTStrategy.RiskClass.LOW,
            cap: 10_000e18,
            globalCap: 1e18,
            estimatedYield: 100e18,
            additionalIncentives: false,
            slippageBPS: 1
        });
    }

    function getTestConfig() internal pure override returns (TestConfig memory) {
        return TestConfig({
            vaultAsset: WETH,
            vaultInitialDeposit: 1000e18,
            absoluteCap: 10_000e18,
            relativeCap: 1e18,
            decimals: 18
        });
    }

    function createStrategy(address _vault, IMYTStrategy.StrategyParams memory params) internal override returns (address) {
        return address(new MockYieldStrategy(_vault, params, WETH, MAINNET_PERMIT2));
    }

    function getForkBlockNumber() internal pure override returns (uint256) {
        return 22_089_302;
    }

    function getRpcUrl() internal view override returns (string memory) {
        return vm.envString("MAINNET_RPC_URL");
    }
    
    /**
     * @notice THE DEFINITIVE TEST: Shows MYTStrategy.deallocate() arithmetic underflows with yield
     * @dev This is line 130 in MYTStrategy.sol: newAllocation = oldAllocation - amountDeallocated
     */
    function test_MYTStrategy_arithmetic_underflow_with_yield() public {
        vm.startPrank(vault);
        
        console.log("\n=== THE CORE BUG: MYTStrategy Cannot Handle Yield ===\n");
        
        // Step 1: Allocate 100 WETH
        uint256 allocAmount = 100 ether;
        deal(WETH, strategy, allocAmount);
        
        bytes memory prevAlloc = abi.encode(0);
        IMYTStrategy(strategy).allocate(prevAlloc, allocAmount, "", address(vault));
        
        uint256 initialAssets = IMYTStrategy(strategy).realAssets();
        console.log("Step 1: Allocated 100 WETH");
        console.log("  realAssets =", initialAssets / 1e18, "WETH");
        
        // Step 2: Simulate 5 WETH of yield (5%)
        uint256 yieldAmount = 5 ether;
        deal(WETH, strategy, IERC20(WETH).balanceOf(strategy) + yieldAmount);
        MockYieldStrategy(strategy).simulateYield(yieldAmount);
        
        uint256 assetsWithYield = IMYTStrategy(strategy).realAssets();
        console.log("\nStep 2: Yield earned");
        console.log("  realAssets =", assetsWithYield / 1e18, "WETH");
        console.log("  yield =", (assetsWithYield - initialAssets) / 1e18, "WETH");
        
        // Step 3: Try to withdraw all assets (principal + yield)
        console.log("\nStep 3: Attempting to withdraw", assetsWithYield / 1e18, "WETH (principal + yield)");
        console.log("  oldAllocation tracked in vault = 100 WETH");
        console.log("  amountDeallocated will be = 105 WETH");
        console.log("\n  MYTStrategy.sol line 130:");
        console.log("    newAllocation = oldAllocation - amountDeallocated");
        console.log("    newAllocation = 100 - 105");
        console.log("    = ARITHMETIC UNDERFLOW!");
        
        prevAlloc = abi.encode(allocAmount);
        vm.expectRevert(stdError.arithmeticError);
        IMYTStrategy(strategy).deallocate(prevAlloc, assetsWithYield, "", address(vault));
        
        console.log("\n=== RESULT: Yield is PERMANENTLY FROZEN ===");
        console.log("Users can never withdraw more than their original deposit!");
        
        vm.stopPrank();
    }
    
    /**
     * @notice Shows that even attempting to withdraw principal + partial yield fails
     */
    function test_partial_yield_withdrawal_fails() public {
        vm.startPrank(vault);
        
        // Allocate 100 WETH
        uint256 allocAmount = 100 ether;
        deal(WETH, strategy, allocAmount);
        IMYTStrategy(strategy).allocate(abi.encode(0), allocAmount, "", address(vault));
        
        // Simulate 10 WETH yield (10%)
        deal(WETH, strategy, IERC20(WETH).balanceOf(strategy) + 10 ether);
        MockYieldStrategy(strategy).simulateYield(10 ether);
        
        console.log("\nTrying to withdraw principal + 1 WETH yield = 101 WETH");
        console.log("oldAllocation = 100 WETH");
        console.log("This will underflow: 100 - 101 = underflow!");
        
        // Try to withdraw even just 1 wei more than allocation
        vm.expectRevert(stdError.arithmeticError);
        IMYTStrategy(strategy).deallocate(
            abi.encode(allocAmount),
            allocAmount + 1 ether, // Just 1 WETH of yield
            "",
            address(vault)
        );
        
        vm.stopPrank();
    }
}

```

Just 2 tests to add to `MorphoYearnOGWETHStrategy.sol` to illustrate the limitation in the withdrawal cycle

```
    /**
     * @notice Test showing MYTStrategy base contract deallocate arithmetic underflow with yield
     * @dev Demonstrates arithmetic underflow when trying to withdraw principal + yield
     * @dev This is a separate bug from the _deallocate implementation bug!
     */
    function test_MYTStrategy_deallocate_arithmetic_underflow_with_yield() public {
        vm.startPrank(vault);
        
        // Step 1: Allocate 100 WETH
        deal(WETH, strategy, 100 ether);
        bytes memory prevAlloc = abi.encode(0);
        IMYTStrategy(strategy).allocate(prevAlloc, 100 ether, "", address(vault));
        
        uint256 initialAssets = IMYTStrategy(strategy).realAssets();
        console.log("\n=== INITIAL STATE ===");
        console.log("Allocated: 100 WETH");
        console.log("Real assets:", initialAssets / 1e18, "WETH");
        
        // Step 2: Simulate yield accrual
        vm.warp(block.timestamp + 90 days);
        _simulateYieldPercent(500); // 5% yield
        
        uint256 assetsWithYield = IMYTStrategy(strategy).realAssets();
        console.log("\n=== AFTER YIELD ACCRUAL ===");
        console.log("Real assets:", assetsWithYield / 1e18, "WETH");
        console.log("Yield earned:", (assetsWithYield - initialAssets) / 1e18, "WETH");
        
        // Step 3: Try to withdraw ALL assets (principal + yield)
        console.log("\n=== ATTEMPTING TO WITHDRAW PRINCIPAL + YIELD ===");
        console.log("Trying to withdraw:", assetsWithYield / 1e18, "WETH");
        console.log("Original allocation tracked: 100 WETH");
        
        prevAlloc = abi.encode(100 ether);
        
        // THIS WILL CAUSE ARITHMETIC UNDERFLOW IN MYTStrategy.sol line 130:
        // uint256 newAllocation = oldAllocation - amountDeallocated;
        // Because: 100 ether - 105 ether = underflow!
        console.log("\n=== BUG IN MYTStrategy.sol ===");
        console.log("Line 130: newAllocation = oldAllocation - amountDeallocated");
        console.log("           newAllocation = 100 WETH - 105 WETH");
        console.log("           UNDERFLOW! (uint256 can't be negative)");
        
        vm.expectRevert(stdError.arithmeticError); // Arithmetic underflow
        IMYTStrategy(strategy).deallocate(prevAlloc, assetsWithYield, "", address(vault));
        
        console.log("\n=== RESULT ===");
        console.log("MYTStrategy.deallocate() doesn't handle yield!");
        console.log("Cannot withdraw more than originally allocated");
        console.log("Yield is permanently stuck!");
        
        vm.stopPrank();
    }

 /**
     * @notice Test showing MYTStrategy base contract handles yield and data in sync
     */
    function test_MYTStrategy_deallocate_with_yield_and_data_in_sync() public {
        vm.startPrank(vault);
        
        // Step 1: Allocate 100 WETH
        deal(WETH, strategy, 100 ether);
        bytes memory prevAlloc = abi.encode(0);
        IMYTStrategy(strategy).allocate(prevAlloc, 100 ether, "", address(vault));
        
        uint256 initialAssets = IMYTStrategy(strategy).realAssets();
        console.log("\n=== INITIAL STATE ===");
        console.log("Allocated: 100 WETH");
        console.log("Real assets:", initialAssets / 1e18, "WETH");
        
        // Step 2: Simulate yield accrual
        vm.warp(block.timestamp + 90 days);
        _simulateYieldPercent(500); // 5% yield
        
        uint256 assetsWithYield = IMYTStrategy(strategy).realAssets();
        console.log("\n=== AFTER YIELD ACCRUAL ===");
        console.log("Real assets:", assetsWithYield / 1e18, "WETH");
        console.log("Yield earned:", (assetsWithYield - initialAssets) / 1e18, "WETH");
        
        // Step 3: Try to withdraw ALL assets (principal + yield)
        console.log("\n=== ATTEMPTING TO WITHDRAW PRINCIPAL + YIELD ===");
        console.log("Trying to withdraw:", assetsWithYield / 1e18, "WETH");
        console.log("Original allocation tracked: 100 WETH");
        
        prevAlloc = abi.encode(assetsWithYield);
        IMYTStrategy(strategy).deallocate(prevAlloc, assetsWithYield, "", 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/58409-sc-high-high-arithmetic-underflow-in-mytstrategy-sol-s-deallocate-check-prevents-yield-withdra.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.
