56363 sc high mytsharesdeposited not correctly updated in all cases leading to incorrect protocol collateralization and reduced liquidation incentives
Submitted on Oct 15th 2025 at 04:19:12 UTC by @silencedogood for Audit Comp | Alchemix V3
Report ID: #56363
Report Type: Smart Contract
Report severity: High
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
_mytSharesDeposited is not updated in all cases correctly in AlchemistV3, when MYT funds are transferred out. This leads to an overestimation of the current protocol collateralization, as well as reduced liquidation fees.
Vulnerability Details
_mytSharesDeposited is intended to differentiate between tokens deposited into a CDP and balance of the contract. However during functions _forceRepay(), _liquidate() and _doLiquidation(), MYT tokens may be transferred out of the contract but not accounted for by reducing _mytSharesDeposited accordingly. This leads to _mytSharesDeposited being artificially high.
An artificially high _mytSharesDeposited value leads to an overestimation of the protocol's current collateralization and an artificially high value for getTotalUnderlyingValue(). In partial liquidations and in cases where alchemistCurrentCollateralization is slightly higher than alchemistMinimumCollateralizatio, this overestimation can lead to liquidations incorrectly skipping setting outsourcedFee to non-zero. Thus leading to a reduce fee and incentive for partial liquidations:
function calculateLiquidation(
uint256 collateral,
uint256 debt,
uint256 targetCollateralization,
uint256 alchemistCurrentCollateralization,
uint256 alchemistMinimumCollateralization,
uint256 feeBps
) public pure returns (uint256 grossCollateralToSeize, uint256 debtToBurn, uint256 fee, uint256 outsourcedFee) {
if (debt >= collateral) {
outsourcedFee = (debt * feeBps) / BPS;
// fully liquidate debt if debt is greater than collateral
return (collateral, debt, 0, outsourcedFee);
}
if (alchemistCurrentCollateralization < alchemistMinimumCollateralization) {
outsourcedFee = (debt * feeBps) / BPS;
// fully liquidate debt in high ltv global environment
return (debt, debt, 0, outsourcedFee);
}
// fee is taken from surplus = collateral - debt
uint256 surplus = collateral > debt ? collateral - debt : 0;
fee = (surplus * feeBps) / BPS;
// collateral remaining for margin‐restore calc
uint256 adjCollat = collateral - fee;
// compute m*d (both plain units)
uint256 md = (targetCollateralization * debt) / FIXED_POINT_SCALAR;
// if md <= adjCollat, nothing to liquidate
if (md <= adjCollat) {
return (0, 0, fee, 0);
}
// numerator = md - adjCollat
uint256 num = md - adjCollat;
// denom = m - 1 => (targetCollateralization - FIXED_POINT_SCALAR)/FIXED_POINT_SCALAR
uint256 denom = targetCollateralization - FIXED_POINT_SCALAR;
// debtToBurn = (num * FIXED_POINT_SCALAR) / denom
debtToBurn = (num * FIXED_POINT_SCALAR) / denom;
// gross collateral seize = net + fee
grossCollateralToSeize = debtToBurn + fee;
}https://github.com/alchemix-finance/v3-poc/blob/b2e2aba046c36ff5e1db6f40f399e93cd2bdaad0/src/AlchemistV3.sol#L1244-L1291
Impact Details
Overestimating protocol MYT shares leads to an overestimation of protocol health, which is systemically dangerous. Furthermore, during partial liquidations, this overestimation can lead to the liquidator's outsourcedFee being skipped entirely, leading to loss of incentive for liquidators to act during high global LTV situations.
In my following PoC, two users deposit the same amount of tokens, but user 2 is fully liquidated, leading to:
No
outsourcedFeefor partial liquidation of user 1, when there should have beenReported protocol current collateralization appearing 2x bigger than it should, because the full liquidation transferred collateral tokens out but didn't reduce tracked balance. Thus protocol collateral massively overstated
References
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol?utm_source=immunefi#L1244-L1291
Proof of Concept
Proof of Concept
Paste the following unit test into src/test/AlchemistV3.t.sol and run:
Was this helpful?