57129 sc high missing mytsharesdeposited decrement in liquidation functions causes permanent tvl inflation
Submitted on Oct 23rd 2025 at 17:51:48 UTC by @Max36935 for Audit Comp | Alchemix V3
Report ID: #57129
Report Type: Smart Contract
Report severity: High
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Protocol insolvency
Description
Brief/Intro
The AlchemistV3 contract fails to decrement the _mytSharesDeposited state variable when transferring MYT tokens out during liquidation operations, unlike all other token transfer functions (withdraw, burn, repay, claimRedemption) which properly maintain this accounting. This causes getTotalUnderlyingValue() to return permanently inflated values after each liquidation, affecting the alchemistCurrentCollateralization calculation used in critical liquidation decisions. The cumulative accounting error masks the protocol's true collateralization state, preventing the system from recognizing undercollateralization and triggering appropriate crisis responses, potentially enabling users to mint debt against phantom collateral and leading to protocol insolvency.
Vulnerability Details
Root Cause
The contract maintains _mytSharesDeposited (line 134 in AlchemistV3.sol) to track total deposited MYT (yield token) shares:
/// @dev Total yield tokens deposited
/// This is used to differentiate between tokens deposited into a CDP and balance of the contract
uint256 private _mytSharesDeposited;This variable is consistently updated across all token transfer operations: Token deposits increase the counter:
All standard token withdrawals decrease the counter:
However, liquidation functions break this pattern:
_doLiquidation() (lines 875-879) - Missing updates:
_forceRepay() (lines 774-779) - Missing updates:
_liquidate() repayment fee paths (lines 826, 840) - Missing updates:
How the Accounting Error Propagates The inflated _mytSharesDeposited directly affects TVL calculation:
This inflated TVL then feeds into the critical liquidation calculation at line 862:
The fourth parameter is alchemistCurrentCollateralization, calculated as (Total Protocol TVL / Total Protocol Debt) * 1e18. This ratio determines whether the protocol should enter emergency liquidation mode:
Impact Details
Primary Impact: Masked Undercollateralization The accounting error causes alchemistCurrentCollateralization to appear higher than reality, with the following consequences:
Failed Crisis Detection When the protocol becomes genuinely undercollateralized (TVL < Debt × globalMinimumCollateralization), the inflated TVL prevents the system from recognizing this state. The critical check at line 1257 in
calculateLiquidation():
This emergency mode should trigger full liquidations when the protocol is in crisis, but the inflated alchemistCurrentCollateralization prevents this, allowing normal operations to continue during actual insolvency.
Cumulative Degradation Each liquidation compounds the error:
First liquidation: TVL inflated by X tokens
Second liquidation: TVL inflated by X + Y tokens
After N liquidations: TVL inflated by sum of all liquidated amounts
No recovery mechanism exists to correct the discrepancy
Incorrect Liquidation Incentives The
calculateLiquidation()function uses the inflated collateralization ratio to determine liquidation amounts and fees. This can result in:
Undersized liquidations (liquidating less debt than needed)
Incorrect fee calculations
Delayed recognition of position health
Potential for Excessive Debt Minting Users can mint debt up to
minimumCollateralizationratio. If the protocol's actual TVL is lower than reported due to accumulated inflation, new debt minting could push the protocol into actual insolvency while appearing healthy.
References
AlchemistV3.sol: Main contract file
Line 134:
_mytSharesDepositedvariable declarationLines 852-890:
_doLiquidation()function (missing updates at lines 875, 879)Lines 735-780:
_forceRepay()function (missing updates at lines 774, 779)Lines 791-850:
_liquidate()function (missing updates at lines 826, 840)Line 1239:
_getTotalUnderlyingValue()function (uses inflated_mytSharesDeposited)Line 862:
calculateLiquidation()call using inflated TVL
Comparison functions that correctly update _mytSharesDeposited:
Line 410:
withdraw()- decrements properlyLine 485:
burn()- decrements properlyLine 541:
repay()- decrements properlyLine 638:
claimRedemption()- decrements properly
Recommendations
Fix 1: Add _mytSharesDeposited Decrements in _doLiquidation() Update the _doLiquidation() function to decrement _mytSharesDeposited when transferring MYT tokens out, matching the pattern used in all other withdrawal functions.
Fix 2: Add _mytSharesDeposited Decrements in _forceRepay() and _liquidate() Update both _forceRepay() (called during liquidation) and the repayment fee paths in _liquidate() to maintain consistent accounting. In _forceRepay():
These changes ensure that _mytSharesDeposited is consistently maintained across all functions that transfer MYT tokens out of the contract, aligning liquidation functions with the existing pattern used by withdraw(), burn(), repay(), and claimRedemption().
Proof of Concept
Proof of Concept
Create File
src/test/Audit_MytSharesNotDecrementedOnLiquidation.t.sol
Run Test
forge test --match-contract Audit_MytSharesNotDecrementedOnLiquidation -vvv
Was this helpful?