56555 sc critical user can avoid bad debt ratio scaling when claiming redeem leading to protocol insolvency

Submitted on Oct 17th 2025 at 15:47:28 UTC by @farismaulana for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #56555

  • Report Type: Smart Contract

  • Report severity: Critical

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

  • Impacts:

    • Theft of unclaimed yield

    • Protocol insolvency

Description

Brief/Intro

This issue stem in how the bad debt is calculated in Transmuter contract. in case of global bad debt happen, all redeem claim would get proportional amount of MYT. but user can avoid get the ratio amount and instead claimed full amount like there are no bad debt going on by sandwiching their redeem claim by MYT deposit and withdraw on the Alchemist contract, either by user own MYT holding or via flashloan.

Vulnerability Details

Take a look on how bad debt is calculated on transmuter contract:

        uint256 yieldTokenBalance = TokenUtils.safeBalanceOf(alchemist.myt(), address(this));
        // Avoid divide by 0
        uint256 denominator = alchemist.getTotalUnderlyingValue() + alchemist.convertYieldTokensToUnderlying(yieldTokenBalance) > 0 ? alchemist.getTotalUnderlyingValue() + alchemist.convertYieldTokensToUnderlying(yieldTokenBalance) : 1;
        uint256 badDebtRatio = alchemist.totalSyntheticsIssued() * 10**TokenUtils.expectDecimals(alchemist.underlyingToken()) / denominator;

        uint256 scaledTransmuted = amountTransmuted;

        if (badDebtRatio > 1e18) {
            scaledTransmuted = amountTransmuted * FIXED_POINT_SCALAR / badDebtRatio;
        }

when badDebtRatio is higher than 1e18 the custom logic would proportionally scale the transmuted amount.

but if we take a look on how denominator is calculated, it depends on two factor: transmuter holding of MYT and alchemist.getTotalUnderlyingValue

it is a bit harder to manipulate the transmuter holding of MYT, instead user can manipulate the alchemist.getTotalUnderlyingValue:

this is the root cause, because an user can easily influence how much _mytSharesDeposited inside AlchemistV3 contract via deposit or withdraw.

a malicious user can still get their full redeem amount at current global bad debt condition by atomically sandwich their claimRedemption call. more or less the attack path is like this:

  1. many users create redemption

  2. after sometime, market condition worsened so the protocol is having global bad debt ratio

  3. malicious user then call AlchemistV3::deposit , Transmuter::claimRedemption , AlchemistV3::withdraw possibly via flashloan or user own funds to temporarily getting out of the bad debt condition so the badDebtRatio > 1e18 is false

  4. after step 3, the protocol condition worsened, because one user claim amount does not get scaled

Impact Details

  1. malicious user can get more claim amount than intended in case of bad debt condition, it is effectively stealing from another user/making user claim less

  2. protocol condition prone to insolvency because some user can avoid the bad debt scaling

References

https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/Transmuter.sol#L217-L224 https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L1238

Proof of Concept

Proof of Concept

add this to src/test/AlchemistV3.t.sol :

run with forge test --mt test_inflatingMytDepositedToAvoidBadDebtRatioOnRedeem -vv :

it is shown that by doing the sandwich attack, malicious user can get redeem and get more MYT amount than how it is supposed to. in this specific PoC, it gains 888e18 more than intended.

Was this helpful?