Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CS Note 8.11: Trove Shares Exchange Rate Invariant #505

Open
bingen opened this issue Oct 9, 2024 · 0 comments
Open

CS Note 8.11: Trove Shares Exchange Rate Invariant #505

bingen opened this issue Oct 9, 2024 · 0 comments

Comments

@bingen
Copy link
Collaborator

bingen commented Oct 9, 2024

The function TroveManager._updateBatchShares() rounds down the shares removed from the batch when debt is decreased. Hence, any operations that decrease the debt level (i.e. redemptions, lowering the debt level of troves) reduce the amount of debt per debt share due to rounding.

We have not identified any strategy that could reduce the exchange rate below the initial exchange rate of one debt token per debt share.

If it was possible to bring the value of a debt share below one debt, it may be possible for further rounding issues to make debt shares very cheap (i.e., totalDebtShares >> totalBatchDebt). This could result in an overflow of the debt share calculation. For example, when the currentBatchDebtShares is 1e60 and the debtDecrease is 1e18, then the calculation to get the trove of a debt recordedDebt would overflow.

function _getLatestTroveDataFromBatch() internal view {
    Trove memory trove = Troves[_troveId];
    Batch memory batch = batches[_batchAddress];
    uint256 batchDebtShares = trove.batchDebtShares;
    uint256 totalDebtShares = batch.totalDebtShares;
    ...
    if (totalDebtShares > 0) {
        _latestTroveData.recordedDebt = _latestBatchData.recordedDebt * batchDebtShares / totalDebtShares;

This could prohibit certain positions from getting liquidated and be exploited to mint unbacked tokens to managers.

Additionally, full redemptions can leave some debt in the trove. For example, when total batch shares are 4000e18, debt is 4000e18 + 5, and the user has 2000e18 shares, the user debt is calculated as:

user.debt = batch.debt * user.shares / batch.shares = (4000e18 + 5) * 2000e18 / 4000e18 = 2000e18 + 2

However, redeeming that user debt burns:

user.shares = batch.shares * user.debt / batch.debt = 4000e18 * (2000e18 + 2) / (4000e18 + 5) = 2000e18 - 1

Thus, the user would end up with shares = 1 left after the redemption.

In conclusion, the rounding can potentially lead to unexpected behavior. If there is a way to trigger rounding that causes the ratio of debt:debtshares to go below 1, this could have catastrophic consequences. However, we have not been able to identify a transaction sequence that causes this.

It should be seen as a core protocol invariant that the debt:debtshare ratio is never allowed to go below 1.

Note on the audit process: At the beginning of the audit, it was already known to Liquity that fully redeemed troves can end up with a non-zero amount of debt shares. The GitHub issue related can be found here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant