hats-finance / StakeWise-0xd91cd6ed6c9a112fdc112b1a3c66e47697f522cd

Liquid staking protocol for Ethereum
Other
0 stars 0 forks source link

Staker are incentivised to self-liquidate instead of burning osETH when LTV >= 100% #112

Open hats-bug-reporter[bot] opened 1 year ago

hats-bug-reporter[bot] commented 1 year ago

Github username: @milotruck Submission hash (on-chain): 0xa2b2b9cfac0e8a5b2bb559c1ee365019a2d990b149d46c7711f76267701e2b49 Severity: medium

Description:

Bug Description

In VaultOsToken.sol, when liquidateOsToken() is called to liquidate a staker, the following occurs:

  1. The amount of assets liquidated is calculated based on osTokenShares:

VaultOsToken.sol#L187-L193

    if (isLiquidation) {
      receivedAssets = Math.mulDiv(
        _osToken.convertToAssets(osTokenShares),
        liqBonusPercent,
        _maxPercent
      );
    } else {
  1. osTokenShares is subtracted from the staker's osETH position:

VaultOsToken.sol#L224-L226

    // update osToken position
    position.shares -= SafeCast.toUint128(osTokenShares);
    _positions[owner] = position;
  1. A corresponding amount of shares is burned from the user's vault shares:

VaultOsToken.sol#L228

    uint256 sharesToBurn = convertToShares(receivedAssets);

VaultOsToken.sol#L235-L236

    // burn owner shares
    _burnShares(owner, sharesToBurn);
  1. Transfer the liquidated amount to the caller:

VaultOsToken.sol#L247-L248

    // transfer assets to the receiver
    _transferVaultAssets(receiver, receivedAssets);

As seen from above, the amount of assets liquidated is determined using the osETH exchange rate. Additionally, since the entire liquidated amount and premium is transferred to the caller, the liquidation penalty is only reflected in position.shares.

This becomes an issue if:

  1. A position's LTV is greater than 100%
  2. The staker wants to fully exit his position

as he is incentivised to fully liquidate himself instead of burning osETH using burnOsToken(). This way, the staker only needs to use a portion of his osETH balance to withdraw all his staked ETH.

Attack Scenario

For convenience, we assume that:

Assume that Alice has the following osETH position in a vault:

The vault experiences a loss of 4 ETH:

Alice wants to fully exit her position and withdraw all her staked ETH. If she calls burnOsToken() to burn all her shares and redeem() afterwards:

However, if she liquidates her own position, she gets to keep a portion of her osETH:

Although position.shares is non-zero, Alice has managed to withdraw all her staked ETH by self-liquidating. By doing so, she also gained an additional 0.8e18 osETH.

Impact

By self-liquidating, stakers can gain additional osETH when exiting positions with greater than 100% LTV ratios. The additional osETH is accrued as bad debt for the protocol, which could accumulate and destabilize the osETH peg in the long term.

Recommended Mitigation

Consider charging an additional liquidation fee, which is taken from the staker and given to the protocol. This would discourage self-liquidation as the staker does not get all his staked ETH in return when liquidating his own position.

tsudmi commented 1 year ago

Duplicate of https://github.com/hats-finance/StakeWise-0xd91cd6ed6c9a112fdc112b1a3c66e47697f522cd/issues/109

MiloTruck commented 1 year ago

Hi @tsudmi, while this issue has a similar impact to #109, it is fundamentally different.

109 points out how the liquidation premium incentivizes self-liquidation, while this issue demonstrates how the liquidation formula itself encourages self-liquidation, even if the liquidation premium is set to 0%.

You can see this in the attack scenarios given: the example in #109 uses 100% LTV with 1% liquidation premium, while this issue uses 103% LTV with 0% liquidation premium.

tsudmi commented 1 year ago

Hi @MiloTruck , I think the situation here is similar. We won't remove the liquidation premium, so it will be above 0%. The liquidations will most probably be executed by MEV bots.