58143 sc low unused cap enforcement variables adjusted

Submitted on Oct 30th 2025 at 22:58:51 UTC by @w3llyc4de20Ik2nn1 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58143

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistAllocator.sol

  • Impacts:

    • Protocol insolvency

Description

Brief/Intro

The AlchemistAllocator calculates an adjusted cap by selecting the appropriate limit between absoluteCap, relativeCap, and daoTarget but fails to enforce it, allowing allocate and deallocate calls to bypass intended risk limits and pass unchecked amounts to the vault.

Vulnerability Details

Offer a detailed explanation of the vulnerability This issue affects both allocate and deallocate functions. Here's the full relevant code for each, highlighting the unused adjusted calculation: In the AlchemistAllocator.sol,

In allocate function:


function allocate(address adapter, uint256 amount) external { 
    require(msg.sender == admin || operators[msg.sender], "PD"); 
    bytes32 id = IMYTStrategy(adapter).adapterId(); 
    uint256 absoluteCap = vault.absoluteCap(id); 
    uint256 relativeCap = vault.relativeCap(id); 
    // FIXME get this from the StrategyClassificationProxy for the respective risk class 
    uint256 daoTarget = type(uint256).max; 
    uint256 adjusted = absoluteCap > relativeCap ? absoluteCap : relativeCap; 
    if (msg.sender != admin) { 
        // caller is operator 
        adjusted = adjusted > daoTarget ? adjusted : daoTarget; 
    } 
    // pass the old allocation to the adapter 
    bytes memory oldAllocation = abi.encode(vault.allocation(id)); 
    vault.allocate(adapter, oldAllocation, amount); 
} 

In deallocate function:

The variables absoluteCap, relativeCap, and daoTarget are fetched to compute adjusted, which is meant to represent a safe limit for the amount being allocated or deallocated based on strategy caps and targets; however, adjusted is calculated but never used or compared against amount, so the vault receives the full amount without any limit enforcement.

Impact Details

Without enforcing adjusted against amount, the contract allows unlimited allocations or deallocations, bypassing risk limits and potentially exposing the vault to excessive funds in unsafe strategies.

Add a check like require(amount <= adjusted, "Exceeds cap"); in allocate.

For deallocate, check require(amount <= vault.allocation(id), "Exceeds current allocation");

References

https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistAllocator.sol?utm_source=immunefi

Proof of Concept

Proof of Concept

The Bug is Crystal Clear:

Allocator fetches absoluteCap = 200 ether

Allocator fetches relativeCap = 0.10e18 (= 50 ether in assets)

Allocator calculates internally: adjusted = max(200, 50) = 200 ether

Allocator NEVER checks if amount (100e) <= adjusted (200e)

Allocator immediately calls vault.allocate(100 ether)

Vault enforces its own relativeCap and reverts with RelativeCapExceeded()

Why This Proves the Vulnerability:

The allocator's logic determined that absoluteCap (200e) should be the governing limit, NOT relativeCap (50e). This is what the adjusted calculation represents - picking the maximum of the two caps.

If the allocator had enforced its adjusted calculation:

It would check: require(100 <= 200) PASS

The allocation would be allowed from the allocator's perspective

But since the allocator ignores adjusted:

It blindly passes 100 ether to the vault

The vault independently enforces BOTH caps (not just the max)

The vault rejects because 100 > 50 (relative cap)

This makes the entire cap selection logic dead code with no purpose, proving the bug report is valid and definitively proves the unused adjusted variable vulnerability!

Was this helpful?