58683 sc critical there is an issue in earmarked debt eeduction in the repay can causes a permanent fund freeze

Submitted on Nov 4th 2025 at 01:45:21 UTC by @XDZIBECX for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58683

  • Report Type: Smart Contract

  • Report severity: Critical

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

  • Impacts:

    • Permanent freezing of funds

Description

Brief/Intro

there is an issue in the repay function that is removes all earmarked debt when a user repays, regardless of the repayment amount. and this is creates an asymmetry where users lose their protection during redemptions, so their collateral is reduced globally based on system-wide redemption weights, but their debt reduction depends on their local earmarked amount. When a user repays 50% of their debt, the function is incorrectly wipes 100% of their earmarked protection instead of 50%. Subsequent redemptions then reduce the user's collateral without proportional debt reduction, and this is not correct because this is create a positions with collateralization ratios below the minimum threshold. and this positions become a permanently frozen due an underflows and that is prevent withdrawals, and liquidations, or any recovery operations, so as reuslt his is gone be a permanent loss of user funds.

Vulnerability Details

The vulnerability exists in the repay() function here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L521C9-L523C46

// Repay debt from earmarked amount of debt first
uint256 earmarkToRemove = credit > account.earmarked ? account.earmarked : credit;
account.earmarked -= earmarkToRemove;

so the flaw is that When credit < account.earmarked, the contract is removes min(credit, earmarked) from the earmarked. and this is means if a user repays 50% of their debt, but earmarked equals or exceeds that amount, then the full repayment amount is removed from earmarked, not a proportional share.

the burn() function it's correctly protects earmarked debt ---> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L470 from here it's prevents burning earmarked debt entirely, but the repay() removes it disproportionately. and this inconsistency is a bug, and this is not an intended behavior.

as known that the Earmarked debt represents a user's proportional share of debt that will be redeemed via the Transmuter. the fucntion _sync()uses rhe earmarked amounts to calculate the debt reduction during the redemption from here --> https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L1075C7-L1084C1 :

so If the account.earmarked = 0, then the redeemedTotal ≈ 0, this is mean there is no debt reduction occurs during the redemptions.

the collateral is ALWAYS reduced and hti is based on global weights here https://github.com/alchemix-finance/v3-poc/blob/a192ab313c81ba3ab621d9ca1ee000110fbdd1e9/src/AlchemistV3.sol#L1217C7-L1219C80

this is creates the asymmetry that collateral reduced globally, debt reduced locally based on earmarked.

Impact Details

i chose this as impact Permanent freezing of funds i alerdy test this and is occur check and run please the test is show all result

so this impact can happen when users repay their debt, then the contract incorrectly removes all their earmarked protection instead of reducing it proportionally. as an example, if a user repays 50% of their debt, the contract should keep 50% of their earmarked protection but instead is removes 100% of it. and this earmarked protection is crucial because it determines how much debt gets reduced when redemptions happen. Without it, users lose collateral during the redemptions but their debt stays the same, and this making them owe more than they have. The position then breaks mathematically the contract's internal accounting becomes impossible (it calculates that locked collateral should be 27.78 ETH when the user only has 20.36 ETH), and this is cause all operations to fail with arithmetic errors. The user cannot withdraw, cannot repay more, and cannot be liquidated. Their funds are permanently stuck. In a real scenario with 100 ETH deposited, approximately 20-30 ETH (worth $40,000-$60,000) becomes permanently frozen. and this is happens automatically during normal use any user who repays debt while the Transmuter has active positions will trigger this with no way to recover their fund this need to be fixed

References

check the vulnerbaility details i use all them in the pargraph

Proof of Concept

Proof of Concept

copy past this test in the AlchemistV3.t.sol and run it

  • the result :

Was this helpful?