mars-protocol/contracts

rounding error during full liquidations

Closed this issue · 0 comments

background

currently, a full liquidation (i.e. 100% of a collateral is liquidated) works as follows (over-simpliefied, in pseudo-code):

  1. calculate the user's total collateral amount available to be liquidated:
user_collateral_total_amount = scaled_amount_to_underlying_amount(user_collateral_total_scaled_amount)
  1. calculate the amount of debt to pay as
debt_amount_to_pay = min(sent_debt_asset_amount, close_factor * user_debt_total_amount)
  1. calculate the amount of collateral to liquidate as
collateral_amount_to_liquidate = debt_amount_to_pay * debt_price * (1 + liquidation_bonus) / collateral_price
  1. this is a full liquidation, meaning the collateral amount to liquidate is bigger than the user's total available collateral amount. adjust the amount to liquidate:
collateral_amount_to_liquidate = min(collateral_amount_to_liquidate, user_collateral_total_amount)
  1. calculate the scaled amount to liquidate:
collateral_scaled_amount_to_liquidate = underlying_amount_to_scaled_amount(collateral_amount_to_liquidate)
  1. transfer the scaled amount from the user's account to the liquidator's:
user_collateral_total_scaled_amount -= collateral_scaled_amount_to_liquidate
liquidator_collateral_total_scaled_amount += collateral_scaled_amount_to_liquidate

the problem

the calculations in step (1) and (5) both use floor, i.e. truncate decimal places. this means the adjusted collateral_amount_to_liquidate is necessarily smaller than user_collateral_total_scaled_amount. that is, the user will still have some "dust" collateral left after the "full" liquidation.

this is noted in the relevant unit test:

https://github.com/mars-protocol/outposts/blob/3f28cde2cfd112507799b6867f4fa93504284574/contracts/mars-red-bank/src/testing/test_liquidate.rs#L631-L632

the solution

steps 1–3 unchanged.

revert the order of steps 4 and 5:

  1. calculate the scaled amount to liquidate:
collateral_scaled_amount_to_liquidate = underlying_amount_to_scaled_amount(collateral_amount_to_liquidate)
  1. compare the scaled collateral amount to liquidate with the user's available scaled collateral amount:
collateral_scaled_amount_to_liquidate = min(collateral_scaled_amount_to_liquidate, user_collateral_total_scaled_amount)

step 6 unchanged.

this solves the issue.