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

x/leverage: Simplify Liquidate computation (RV #28-30) #898

Closed
toteki opened this issue May 12, 2022 · 0 comments · Fixed by #1118
Closed

x/leverage: Simplify Liquidate computation (RV #28-30) #898

toteki opened this issue May 12, 2022 · 0 comments · Fixed by #1118

Comments

@toteki
Copy link
Member

toteki commented May 12, 2022

Runtime Verification Audit Items 28-30

There are several simplifications that can be applied to the Liquidate function

  • Maximum allowed repayment check can be improved (28)
  • Liquidator minimum reward ratio check only needs price ratio and liquidation incentive (29)
  • General simplification (30)

Suggestion from item 30 to be considered

rewardTokenPrice, err := k.TokenPrice(ctx, desiredReward.Denom)
if err != nil {
  return sdk.ZeroInt(), sdk.ZeroInt(), err
}
repaymentTokenPrice, err := k.TokenPrice(ctx, desiredRepayment.Denom)
if err != nil {
  return sdk.ZeroInt(), sdk.ZeroInt(), err
}

if rewardTokenPrice.IsZero() || repaymentTokenPrice.IsZero() {
  return sdk.ZeroInt(), sdk.ZeroInt(), types.ErrInvalidOraclePrice
}
eXRateRepayToRewardDenom := repaymentTokenPrice.Quo(rewardTokenPrice)

// get reward-specific incentive and dynamic close factor
baseRewardDenom := desiredReward.Denom
liquidationIncentive, closeFactor, err := k.LiquidationParams(ctx, baseRewardDenom, borrowValue, liquidationLimit)
if err != nil {
  return sdk.ZeroInt(), sdk.ZeroInt(), err
}

if desiredReward.Amount.IsPositive() {
  //The actual reward ratio guaranteed the liquidation event is successful
  actualRewardRatio := eXRateRepayToRewardDenom.Mul(sdk.OneDec().Add(liquidationIncentive))
  // user-controlled minimum ratio of reward to repayment, expressed in base:base assets (not uTokens)
  minimumRewardRatio := desiredReward.Amount.ToDec().Quo(desiredRepayment.Amount.ToDec())

  // confirm if the end of liquidation reward ratio meets liquidator's expectation of minimum reward ratio
  if actualRewardRatio.LT(minimumRewardRatio) {
     return sdk.ZeroInt(), sdk.ZeroInt(), types.ErrLiquidationRewardRatio
  }
}

// actual repayment starts at desiredRepayment but can be lower due to limiting factors
repayment := desiredRepayment

// get liquidator's available balance of base asset to repay
liquidatorBalance := k.bankKeeper.SpendableCoins(ctx, liquidatorAddr).AmountOf(repayment.Denom)

// repayment cannot exceed liquidator's available balance
repayment.Amount = sdk.MinInt(repayment.Amount, liquidatorBalance)

// repayment cannot exceed borrower's borrowed amount of selected denom
repayment.Amount = sdk.MinInt(repayment.Amount, borrowed.AmountOf(repayment.Denom))

// repayment cannot exceed borrowed value * close factor
maxRepayAmount := borrowValue.Mul(closeFactor).Quo(repaymentTokenPrice).TruncateInt()
repayment.Amount = sdk.MinInt(repayment.Amount, maxRepayAmount)

//maximum allowed reward payable from borrower's collateral
eXRateTokentoUToken := k.DeriveExchangeRate(ctx, desiredReward.Denom)
rewardInUtoken := k.FromTokenToUTokenDenom(ctx, desiredReward.Denom)
maxPayableRewardInBaseToken := collateral.AmountOf(rewardInUtoken).ToDec().Mul(eXRateTokentoUToken)

//repayment amount is bounded by this maxPayableReward
repayment.Amount = sdk.MinInt(repayment.Amount,
  maxPayableRewardInBaseToken.Quo(sdk.OneDec().Add(liquidationIncentive)).Quo(eXRateRepayToRewardDenom).TruncateInt())

// final check for invalid liquidation (negative/zero value after reductions above)
if !repayment.Amount.IsPositive() {
  return sdk.ZeroInt(), sdk.ZeroInt(), sdkerrors.Wrap(types.ErrInvalidAsset, repayment.String())
}
// Given repay denom and amount, use oracle to find incentivised amount of
// rewardDenom's base asset.
baseReward := sdk.NewCoin(baseRewardDenom,
  repayment.Amount.ToDec().Mul(sdk.OneDec().Add(liquidationIncentive).Mul(eXRateRepayToRewardDenom)).TruncateInt())

// convert reward tokens back to uTokens
reward, err := k.ExchangeToken(ctx, baseReward)
if err != nil {
  return sdk.ZeroInt(), sdk.ZeroInt(), err
}

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

Successfully merging a pull request may close this issue.

1 participant