# AlchemistV3._liquidate fee amount is not normalized for underlying token

Liquidation feeBonus computed in debtToken decimals but paid in underlyingToken, so when debtToken has more decimals the liquidator drains the entire fee vault.

- Payout: Contest reward
- Severity: high
- Program: Alchemix
- Credited: ZoA
- Disclosed: 2025-07-15
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_alchemix_may2025.pdf#3.1.4
- Library page: https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.4
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:solidity technique:source-review vuln:accounting-error vuln:liquidation vuln:precision-loss

**Target:** AlchemistV3._liquidate / alchemistFeeVault
**Root cause:** feeBonus = debtToBurn * liquidatorFee / BPS is denominated in debtToken decimals but is paid out as underlyingToken from alchemistFeeVault without normalization, so a higher-decimal debtToken inflates the payout.
**Steps:**
1. Set fakeUnderlyingToken decimals to 6 while debtToken stays 18 decimals.
2. Deposit collateral and mint debt to create an undercollateralized CDP (tokenIdFor0xBeef).
3. Inflate yield token supply by 40% to push the position underwater.
4. Call `alchemist.liquidate(tokenIdFor0xBeef)` from a liquidator.
5. Observe `feeInUnderlying` equals the entire vault balance (10000e18) instead of the expected ~5.4e18 normalized fee.
**How found:** Code reading of _liquidate: noticed feeBonus derived from debtToBurn (debtToken units) but transferred as underlyingToken, then wrote a Foundry test with mismatched decimals to confirm the vault-draining payout.
**Exploitation:** Liquidator triggers liquidation of an underwater CDP; because feeBonus is in debtToken decimals (18) but paid in 6-decimal underlying, the payout exceeds the intended fee and is capped only by vaultBalance, draining the fee vault to the liquidator.
**Insight:** When a protocol mixes debtToken and underlyingToken units, check every fee/bonus/transfer path for missing normalizeDebtTokensToUnderlying; mismatched decimals turn a small fee into a full-vault drain.
