56672 sc high inconsistent myt share accounting leads to under liquidation and solvency risk
Submitted on Oct 19th 2025 at 08:15:09 UTC by @yesofcourse for Audit Comp | Alchemix V3
Report ID: #56672
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
Protocol insolvency
Description
Brief/Intro
AlchemistV3._forceRepay transfers MYT (vault shares) out of the Alchemist during repay-only liquidations but does not decrement the internal _mytSharesDeposited counter used to compute TVL.
As a result, getTotalUnderlyingValue() overstates collateral, the global collateralization ratio is inflated, and the system can skip emergency full-liquidation when it should trigger - creating persistent under-liquidation and elevated insolvency risk.
Vulnerability Details
The protocol tracks TVL via
_mytSharesDeposited:function _getTotalUnderlyingValue() internal view returns (uint256) { return convertYieldTokensToUnderlying(_mytSharesDeposited); }In
_forceRepay, when earmarked debt is repaid using a user’s MYT collateral, the Alchemist sends MYT out (to the Transmuter and optionally the fee receiver) but does not decrease_mytSharesDeposited.
Elsewhere in the codebase, when MYT leaves the Alchemist, _mytSharesDeposited is lowered to keep TVL honest; this function breaks that invariant.
Affected snippet (inside _forceRepay):
Because TVL is read from
_mytSharesDeposited, not from the live MYT balance, every_forceRepayoutflow leaves reported TVL stale/too high.
Why this matters
The liquidation controller uses TVL to compute the Alchemist’s global collateralization and decide whether to take the emergency full-liquidation branch:
If _getTotalUnderlyingValue() is overstated, alchemistCurrentCollateralization is inflated and the emergency branch can fail to trigger when the true (economic) ratio is already below the minimum. Positions that should be fully liquidated are only partially delevered or not delevered at all.
Scenario PoC
Set fees to zero for clarity.
Deposit MYT, mint debt, create and mature a redemption (to push the repay-only path).
Force a price drop so
_forceRepaysends MYT out to the Transmuter.Observe:
vault.balanceOf(Alchemist)drops (real MYT left).getTotalUnderlyingValue()(derived from_mytSharesDeposited) does not drop accordingly (remains stale).The computed global collateralization is too high vs. reality.
The coded testForceRepay_MissingMYTOutflow_DeSync() demonstrates exactly this: real MYT outflow occurs, but reported TVL is unchanged within tight tolerance.
Impact Details
Primary impact: Protocol insolvency (systemic). By overstating TVL, the protocol underestimates risk and skips emergency full-liquidations that should occur. Under-liquidation lets unhealthy positions linger, allowing bad debt to accumulate, and reduces liquidator compensation (“outsourced fee”), weakening incentives.
Secondary impacts:
Temporary freezing of funds. As the true asset base shrinks while TVL appears healthy, later withdrawals/redemptions can revert once the real shortfall surfaces.
Contract fails to deliver promised safety behavior. The system’s liquidation guarantees rely on accurate TVL; misreporting breaks those guarantees.
Concrete loss mechanics (example):
A repay-only liquidation transfers 100 MYT shares out (to Transmuter + fees).
Real MYT holdings drop by 100 shares; reported TVL is unchanged.
The global collateralization ratio derived from TVL stays artificially high and emergency liquidation is not triggered.
Repeating this across multiple undercollateralized accounts can compound the TVL gap, increasing bad debt and leading to withdrawal failures; in the worst case, overall insolvency.
References
Affected file:
src/AlchemistV3.sol—_forceRepayoutflows without_mytSharesDepositeddecrement: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol#L764-L775TVL source:
_getTotalUnderlyingValue()uses_mytSharesDeposited: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol#L1238-L1241
Proof of Concept
Proof of Concept
Paste the following test in AlchemistV3.t.sol and run with forge test --match-test testForceRepay_MissingMYTOutflow_DeSync:
Was this helpful?