# Liquidations can be DoSed by front running with repay()

AlchemistV3 liquidate() reverts when earmarked debt converts to 0 yield tokens, letting a borrower front-run with repay() to block their own liquidation.

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

**Target:** AlchemistV3 liquidate()/_forceRepay() conversion path
**Root cause:** _liquidate() calls _forceRepay(convertDebtTokensToYield(account.earmarked)); when earmarked debt is below the 1e12 decimal conversion factor, the conversion truncates to 0 and _forceRepay reverts on _checkArgument(amount > 0).
**Steps:**
1. Deploy Euler USDC-USDC-alUSD config where underlyingConversionFactor = 1e12 (debt alUSD 18 dec, underlying USDC 6 dec).
2. Borrower creates a Transmuter redemption so account.earmarked > 0, then waits several blocks.
3. Position becomes liquidatable after yield token supply/price shift.
4. Borrower front-runs liquidate() with `alchemist.repay(5037, tokenId)` reducing earmarked below 1e12.
5. Liquidator's `alchemist.liquidate(tokenId)` reverts with IllegalArgument because convertDebtTokensToYield(earmarked) == 0.
6. Repeat after each block/redeem to keep DoSing.
**How found:** Code review of _liquidate()/_forceRepay() flow: noticed _forceRepay reverts on amount==0 and traced that convertDebtTokensToYield truncates via integer division by underlyingConversionFactor (1e12 for 18->6 decimals), making small earmarked values round to zero.
**Exploitation:** Attacker monitors mempool for liquidate() on their position, submits repay() with a small amount sized so earmarked - convertYieldTokensToDebt(amount) < 1e12, causing _forceRepay to revert inside the victim's liquidation tx. Costs only ~5037 USDC per attempt; repeatable each block.
**Insight:** When a liquidation path calls a helper that reverts on zero, check whether any user-controllable state (like earmarked debt) can be driven below the decimal-conversion factor so integer division truncates to 0 — a cheap repay() can then brick liquidations.
