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

feat: split collateralWeight into collateralWeight and liquidationThreshold #627

Merged
merged 14 commits into from
Mar 9, 2022
1 change: 1 addition & 0 deletions app/beta/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func IntegrationTestNetworkConfig() network.Config {
Exponent: 6,
ReserveFactor: sdk.MustNewDecFromStr("0.100000000000000000"),
CollateralWeight: sdk.MustNewDecFromStr("0.050000000000000000"),
LiquidationThreshold: sdk.MustNewDecFromStr("0.050000000000000000"),
BaseBorrowRate: sdk.MustNewDecFromStr("0.020000000000000000"),
KinkBorrowRate: sdk.MustNewDecFromStr("0.200000000000000000"),
MaxBorrowRate: sdk.MustNewDecFromStr("1.50000000000000000"),
Expand Down
23 changes: 16 additions & 7 deletions proto/umee/leverage/v1beta1/leverage.proto
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,19 @@ message Token {
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"collateral_weight\""
];

// The liquidation_threshold defines what amount of the total value of the asset
// can contribute to a user's liquidation threshold (above which they become
// eligible for liquidation).
string liquidation_threshold = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"liquidation_threshold\""
];

// The base_borrow_rate defines the base interest rate for borrowing this
// asset.
string base_borrow_rate = 4 [
string base_borrow_rate = 5 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"base_borrow_rate\""
Expand All @@ -67,38 +76,38 @@ message Token {
// The kink_borrow_rate defines the interest rate for borrowing this
// asset when utilization is at the 'kink' utilization value as defined
// on the utilization:interest graph.
string kink_borrow_rate = 5 [
string kink_borrow_rate = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"kink_borrow_rate\""
];

// The max_borrow_rate defines the interest rate for borrowing this
// asset (seen when utilization is 100%).
string max_borrow_rate = 6 [
string max_borrow_rate = 7 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"max_borrow_rate\""
];

// The kink_utilization_rate defines the borrow utilization rate for this
// asset where the 'kink' on the utilization:interest graph occurs.
string kink_utilization_rate = 7 [
string kink_utilization_rate = 8 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"kink_utilization_rate\""
];

// The liquidation_incentive determines the portion of bonus collateral of
// a token type liquidators receive as a liquidation reward.
string liquidation_incentive = 8 [
string liquidation_incentive = 9 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"liquidation_incentive\""
];

// The symbol_denom and exponent are solely used to update the oracle's accept
// list of allowed tokens.
string symbol_denom = 9 [(gogoproto.moretags) = "yaml:\"symbol_denom\""];
uint32 exponent = 10 [(gogoproto.moretags) = "yaml:\"exponent\""];
string symbol_denom = 10 [(gogoproto.moretags) = "yaml:\"symbol_denom\""];
uint32 exponent = 11 [(gogoproto.moretags) = "yaml:\"exponent\""];
}
4 changes: 3 additions & 1 deletion starport.ci.beta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ genesis:
exponent: 6
reserve_factor: "0.100000000000000000"
collateral_weight: "0.050000000000000000"
liquidation_threshold: "0.050000000000000000"
base_borrow_rate: "0.020000000000000000"
kink_borrow_rate: "0.200000000000000000"
max_borrow_rate: "1.50000000000000000"
Expand All @@ -23,7 +24,8 @@ genesis:
symbol_denom: "ATOM"
exponent: 6
reserve_factor: "0.100000000000000000"
collateral_weight: "0.050000000000000000"
collateral_weight: "0.550000000000000000"
liquidation_threshold: "0.600000000000000000"
base_borrow_rate: "0.020000000000000000"
kink_borrow_rate: "0.200000000000000000"
max_borrow_rate: "1.50000000000000000"
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/e2e_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ func (s *IntegrationTestSuite) initGenesis() {
Exponent: 6,
ReserveFactor: sdk.MustNewDecFromStr("0.100000000000000000"),
CollateralWeight: sdk.MustNewDecFromStr("0.050000000000000000"),
LiquidationThreshold: sdk.MustNewDecFromStr("0.050000000000000000"),
BaseBorrowRate: sdk.MustNewDecFromStr("0.020000000000000000"),
KinkBorrowRate: sdk.MustNewDecFromStr("0.200000000000000000"),
MaxBorrowRate: sdk.MustNewDecFromStr("1.50000000000000000"),
Expand Down
1 change: 1 addition & 0 deletions x/leverage/client/cli/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestParseUpdateRegistryProposal(t *testing.T) {
"base_denom": "uumee",
"reserve_factor": "0.1",
"collateral_weight": "0.05",
"liquidation_threshold": "0.05",
"base_borrow_rate": "0.02",
"kink_borrow_rate": "0.2",
"max_borrow_rate": "1.5",
Expand Down
1 change: 1 addition & 0 deletions x/leverage/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Where proposal.json contains:
"base_denom": "uumee",
"reserve_factor": "0.1",
"collateral_weight": "0.05",
"liquidation_threshold": "0.05",
"base_borrow_rate": "0.02",
"kink_borrow_rate": "0.2",
"max_borrow_rate": "1.5",
Expand Down
5 changes: 3 additions & 2 deletions x/leverage/client/tests/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func (s *IntegrationTestSuite) TestQueryAllRegisteredTokens() {
Exponent: 6,
ReserveFactor: sdk.MustNewDecFromStr("0.1"),
CollateralWeight: sdk.MustNewDecFromStr("0.05"),
LiquidationThreshold: sdk.MustNewDecFromStr("0.05"),
BaseBorrowRate: sdk.MustNewDecFromStr("0.02"),
KinkBorrowRate: sdk.MustNewDecFromStr("0.2"),
MaxBorrowRate: sdk.MustNewDecFromStr("1.5"),
Expand Down Expand Up @@ -1663,9 +1664,9 @@ func (s *IntegrationTestSuite) TestCmdLiquidate() {

runTestQueries(s, noTargetsQuery)
runTestTransactions(s, setupCommands)
updateCollateralWeight(s, "uumee", sdk.MustNewDecFromStr("0.01")) // lower to allow liquidation
updateLiquidationThreshold(s, "uumee", sdk.MustNewDecFromStr("0.01")) // lower to allow liquidation
runTestQueries(s, oneTargetQuery)
runTestTransactions(s, testCases)
updateCollateralWeight(s, "uumee", sdk.MustNewDecFromStr("0.05")) // reset to original
updateLiquidationThreshold(s, "uumee", sdk.MustNewDecFromStr("0.05")) // reset to original
runTestTransactions(s, cleanupCommands)
}
12 changes: 6 additions & 6 deletions x/leverage/client/tests/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ func (s *IntegrationTestSuite) UpdateRegistry(
)
}

// updateCollateralWeight modifies the collateral weight of a registered token identified by baseDenom.
func updateCollateralWeight(s *IntegrationTestSuite, baseDenom string, collateralWeight sdk.Dec) {
// updateLiquidationThreshold modifies the liquidation threshold of a registered token identified by baseDenom.
func updateLiquidationThreshold(s *IntegrationTestSuite, baseDenom string, liquidationThreshold sdk.Dec) {
val := s.network.Validators[0]
clientCtx := s.network.Validators[0].ClientCtx

Expand All @@ -156,11 +156,11 @@ func updateCollateralWeight(s *IntegrationTestSuite, baseDenom string, collatera
resp := &types.QueryRegisteredTokensResponse{}
s.Require().NoError(clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp), out.String())

// Replace the collateral weight of the selected token with the new value
// Replace the liquidation threshold of the selected token with the new value
newTokens := resp.GetRegistry()
for i := range newTokens {
if newTokens[i].BaseDenom == baseDenom {
newTokens[i].CollateralWeight = collateralWeight
newTokens[i].LiquidationThreshold = liquidationThreshold
}
}

Expand All @@ -171,8 +171,8 @@ func updateCollateralWeight(s *IntegrationTestSuite, baseDenom string, collatera
s.UpdateRegistry(
clientCtx,
types.NewUpdateRegistryProposal(
fmt.Sprintf("collateral weight update - %d", proposalCounter),
"update collateral weight to "+collateralWeight.String(),
fmt.Sprintf("liquidation threshold update - %d", proposalCounter),
"update liquidation threshold to "+liquidationThreshold.String(),
newTokens,
),
sdk.NewCoins(sdk.NewCoin(app.BondDenom, govtypes.DefaultMinDepositTokens)),
Expand Down
32 changes: 32 additions & 0 deletions x/leverage/keeper/borrows.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ func (k Keeper) CalculateBorrowLimit(ctx sdk.Context, collateral sdk.Coins) (sdk
return limit, nil
}

// CalculateLiquidationThreshold uses the price oracle to determine the liquidation threshold
// (in USD) provided by collateral sdk.Coins, using each token's uToken exchange rate and
// liquidation threshold. An error is returned if any input coins are not uTokens or if value
// calculation fails.
func (k Keeper) CalculateLiquidationThreshold(ctx sdk.Context, collateral sdk.Coins) (sdk.Dec, error) {
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
threshold := sdk.ZeroDec()

for _, coin := range collateral {
// convert uToken collateral to base assets
baseAsset, err := k.ExchangeUToken(ctx, coin)
if err != nil {
return sdk.ZeroDec(), err
}

// get USD value of base assets
value, err := k.TokenValue(ctx, baseAsset)
if err != nil {
return sdk.ZeroDec(), err
}

weight, err := k.GetLiquidationThreshold(ctx, baseAsset.Denom)
if err != nil {
return sdk.ZeroDec(), err
}

// add each collateral threshold value to borrow limit
toteki marked this conversation as resolved.
Show resolved Hide resolved
threshold = threshold.Add(value.Mul(weight))
}

return threshold, nil
}

// setBadDebtAddress sets or deletes an address in a denom's list of addresses with unpaid bad debt.
func (k Keeper) setBadDebtAddress(ctx sdk.Context, addr sdk.AccAddress, denom string, hasDebt bool) error {
if err := sdk.ValidateDenom(denom); err != nil {
Expand Down
1 change: 1 addition & 0 deletions x/leverage/keeper/borrows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() {
BaseDenom: umeeDenom,
ReserveFactor: sdk.MustNewDecFromStr("0"),
CollateralWeight: sdk.MustNewDecFromStr("1"),
LiquidationThreshold: sdk.MustNewDecFromStr("1"),
BaseBorrowRate: sdk.MustNewDecFromStr("0"),
KinkBorrowRate: sdk.MustNewDecFromStr("0"),
MaxBorrowRate: sdk.MustNewDecFromStr("0"),
Expand Down
10 changes: 5 additions & 5 deletions x/leverage/keeper/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,15 @@ func (k Keeper) GetEligibleLiquidationTargets(ctx sdk.Context) ([]sdk.AccAddress
return err
}

// use collateral weights to compute borrow limit from enabled collateral
borrowLimit, err := k.CalculateBorrowLimit(ctx, collateral)
// compute liquidation threshold from enabled collateral
liquidationThreshold, err := k.CalculateLiquidationThreshold(ctx, collateral)
if err != nil {
return err
}

// if the borrowLimit is smaller then the borrowValue
// the address is eligible to liquidation
if borrowLimit.LT(borrowValue) {
// if liquidation threshold is smaller than borrowed value
// then the address is eligible for liquidation
toteki marked this conversation as resolved.
Show resolved Hide resolved
if liquidationThreshold.LT(borrowValue) {
liquidationTargets = append(liquidationTargets, addr)
}

Expand Down
8 changes: 4 additions & 4 deletions x/leverage/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,20 +399,20 @@ func (k Keeper) LiquidateBorrow(
return sdk.ZeroInt(), sdk.ZeroInt(), err
}

// use collateral weights to compute borrow limit from enabled collateral
borrowLimit, err := k.CalculateBorrowLimit(ctx, collateral)
// compute liquidation threshold from enabled collateral
liquidationThreshold, err := k.CalculateLiquidationThreshold(ctx, collateral)
if err != nil {
return sdk.ZeroInt(), sdk.ZeroInt(), err
}

// confirm borrower's eligibility for liquidation
if borrowLimit.GTE(borrowValue) {
if liquidationThreshold.GTE(borrowValue) {
return sdk.ZeroInt(), sdk.ZeroInt(), sdkerrors.Wrap(types.ErrLiquidationIneligible, borrowerAddr.String())
}

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