58275 sc high account rawlocked not clear even when debt is clear

Submitted on Oct 31st 2025 at 22:10:41 UTC by @emmac002 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58275

  • Report Type: Smart Contract

  • Report severity: High

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

  • Impacts:

    • Users lose funds

Description

Brief/Intro

A bug in the _subDebt function causes borrower rawLocked not clear even after full debt repayment (account.debt == 0) when minimumCollateralization/convertDebtTokensToYield is increased post-borrow. This occurs because _subDebt caps the toFree amount at _totalLocked but still computes lockedCollateral using the new higher ratio, leaving a residual in account.rawLocked.

Vulnerability Details

The vulnerability lies in the _subDebt function, which is called to reduce a position’s debt. The function correctly caps the amount of collateral to free (toFree) at the total locked amount (_totalLocked), but fails to adjust account.rawLocked when the required locked collateral exceeds _totalLocked due to a higher minimumCollateralizatio/convertDebtTokensToYield.

function _addDebt(uint256 tokenId, uint256 amount) internal {
    Account storage account = _accounts[tokenId];

    // Update collateral variables
    uint256 toLock = convertDebtTokensToYield(amount) * minimumCollateralization / FIXED_POINT_SCALAR;
    uint256 lockedCollateral = convertDebtTokensToYield(account.debt) * minimumCollateralization / FIXED_POINT_SCALAR;

    if (account.collateralBalance - lockedCollateral < toLock) revert Undercollateralized();

    account.rawLocked = lockedCollateral + toLock;
    _totalLocked += toLock;
    account.debt += amount;
    totalDebt += amount;
}
  1. User addDebt(100) and the minimumCollateralization = 1.5, toLock = 100 * 1.5 = 150, lockedCollateral = 0, account.rawLocked = 150, _totalLocked = 150, totalDebt = 100, account.debt = 100

  1. Admin increases minimumCollateralization or convertDebtTokensToYield increases, User subDebt(100) and the minimumCollateralization = 2, toFree = 100 * 2 = 200, lockedCollateral = 100 * 2 = 200, toFree (200) > _totalLocked (150), so toFree = 150, account.rawLocked = 200 - 150 = 50, account.debt = 100 - 100 = 0

  1. If in the _sync function _collateralWeight is not 0, then collateralToRemove will be incorrectly deducted from account.collateralBalance, even when the user has cleared the debt.

Impact Details

Any borrower may lose funds if account.rawLocked is not properly cleared

Solution

References

(https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol?utm_source=immunefi#L936-L947)

Proof of Concept

Proof of Concept

Test:

Output:

Was this helpful?