# DoS through ancounting errors: increment > total

Alchemist earmark accounting ignores yield tokens already in the Transmuter, so repay() causes WeightIncrement's increment>total invariant to revert, permanently bricking all core functions.

- Payout: Contest reward
- Severity: high
- Program: Alchemix
- Credited: Rikard Hjort
- Disclosed: 2025-07-15
- Write-up quality: 8/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_alchemix_may2025.pdf#3.1.12
- Library page: https://bugbountylibrary.com/r/cantina%3Aalchemix_may2025%3A3.1.12
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:dos

**Target:** AlchemistV3 / PositionDecay earmark accounting (Solidity)
**Root cause:** When computing the earmark increment, the Alchemist counts all matured Transmuter claims as its debt, ignoring yield tokens already sitting in the Transmuter from repay()/_forceRepay() or direct deposits. This makes increment exceed totalDebt - cumulativeEarmarked, violating WeightIncrement's require.
**Steps:**
1. Deposit collateral and mint debt: `alchemist.deposit(100e18, user, 0)` then `alchemist.mint(tokenId, 50e18, user)`.
2. Create a redemption in the Transmuter: `transmuterLogic.createRedemption(borrowedAmount)` to populate queryGraph.
3. Advance one block and repay a small amount: `alchemist.repay(1, tokenId)` — sends yield tokens to Transmuter, reduces totalDebt, does not change queryGraph.
4. Roll forward past timeToTransmute: `vm.roll(block.number + 5_256_000)`.
5. Call `transmuterLogic.claimRedemption(1)` — reverts with `WeightIncrement: increment > total`.
6. All core Alchemist calls (poke, withdraw, mint, repay, burn, liquidate, getCDP) now revert on _earmark().
**How found:** Code reading of _earmark()/WeightIncrement(): noticed the invariant `amount > totalDebt - cumulativeEarmarked` assumes all debt is repaid via burns, but repay()/_forceRepay() move yield tokens to the Transmuter without burning synthetics, so the Transmuter's own balance covers some claims — breaking the assumption.
**Exploitation:** No attacker capital needed; the flaw triggers organically via normal repay()/forceRepay() flows, especially after long gaps between earmarks or during bad-debt liquidation cascades. Once the invariant is violated, every core function except deposit reverts, permanently freezing the protocol.
**Escalation:** Bad-debt scenario with many large liquidations triggers _forceRepay() making large repayments, making the freeze more likely and more catastrophic.
**Insight:** When auditing earmark/accounting invariants, check whether every path that reduces a debt counter also reduces the corresponding claim counter; repay-without-burn paths silently desync the two and can violate monotonic invariants that brick the protocol.
