Smart contract unable to operate due to lack of token funds
Protocol insolvency
Description
Brief/Intro
When a weakly collateralized account with earmarked debt is liquidated, the liquidation path calls _forceRepay(accountId, account.earmarked). This clears the account’s earmark but does not decrement the global cumulativeEarmarked. The next redeem() then consumes the stale global earmark, reducing totalDebt again, even though the earmarked amount was already consumed during liquidation.
Result: totalDebt drops below the sum of per-account debts, corrupting global accounting and enabling cross-account value leakage. Over time this can induce protocol insolvency and/or deplete available tokens.
Vulnerability Details
Root cause
_forceRepay() updates the account-level earmarked but does not decrement the global cumulativeEarmarked. In contrast, repay() reduces both account-level and global earmarks, keeping them in sync.
Excerpt (current pattern)
Reference (correct pattern in user repay path)
Impact Details
Protocol insolvency: Because redeem() acts on a stale global earmark, it can drive totalDebt below the sum of user debts (the system “believes” all debt is repaid while some users still owe). This breaks core solvency/accounting assumptions and can cascade into incorrect redemptions and collateral accounting.
Unable to operate due to lack of token funds: redeem() transfers MYT to the Transmuter based on the inflated global earmark. Over time this can deplete MYT from the Alchemist and cause subsequent redemptions/repays/withdrawals to fail or to behave unpredictably.
Risk Breakdown
Critical: The bug enables double application of the same earmark (once in liquidation via _forceRepay, again in redeem()), which understates totalDebt relative to actual user debts. Subsequent state transitions (redemptions, liquidations, withdrawals) operate on a corrupted ledger, leading to lossy, cross-account misallocation and potential insolvency.
Recommendation
Mirror the repay() logic inside _forceRepay() to keep global/account state consistent:
Proof of Concept
Proof of Concept
A Foundry test demonstrates the invariant break caused by the stale global earmark.