VIS-2 / taobank-04-24

0 stars 0 forks source link

Minimal borrowed amounts can grief protocol, leading to underwater debt #29

Open DanailYordanov opened 4 months ago

DanailYordanov commented 4 months ago

Impact

Severity: Medium Likelihood: Medium

Context

StabilityPool::_computeRewardsPerUnitStaked()

Description

While calculating the stableCoinLossNumerator on L833, lastStableCoinLossErrorOffset is subtracted due to the fact that the result of stableCoinLossPerUnitStaked is rounded up, but lastStableCoinLossErrorOffset being scaled to 1e18 precision can lead to underflows for vaults with low borrowed amount, thus making the vault non-liquidatable and yielding underwater debt for the protocol. This opens the door for malicious users to grief the protocol by opening really small positions.

/* code */

if (_debtToOffset == _totalStableCoinDeposits) {
    stableCoinLossPerUnitStaked = DECIMAL_PRECISION; // When the Pool depletes to 0, so does each deposit
    lastStableCoinLossErrorOffset = 0;
} else {
    uint256 stableCoinLossNumerator = _debtToOffset *
@>      DECIMAL_PRECISION - lastStableCoinLossErrorOffset; // @audit underflow
    /*
     * Add 1 to make error in quotient positive. We want "slightly too much" StableCoin loss,
     * which ensures the error in any given compoundedStableCoinDeposit favors the Stability Pool.
     */
    stableCoinLossPerUnitStaked =
        stableCoinLossNumerator /
        _totalStableCoinDeposits +
        1;
    lastStableCoinLossErrorOffset =
        stableCoinLossPerUnitStaked *
        _totalStableCoinDeposits -
        stableCoinLossNumerator;
}

/* code */

Recommendation

Consider enforcing a minimum borrowable amount in VaultFactory::borrow()

function borrow(
    address _vault,
    uint256 _amount,
    address _to
) external onlyVaultOwnerOrOperator(_vault) {
    require(containsVault(_vault), 'vault-not-found');
    require(_to != address(0x0), 'to-is-0');
+   require(_amount >= MIN_BORROWABLE_AMOUNT, 'below-minimum-amount');

/ * code */
}

PoC

To verify the issue, follow this guide and include this test in the codebase. The test demonstrates the impossibility of liquidating a vault with small debt.