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: leverage + historacle #1654

Merged
merged 44 commits into from
Jan 1, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5dc0efe
add pump&dump tokens to mock oracle
toteki Dec 12, 2022
84065b5
value functions
toteki Dec 12, 2022
109a2ba
comments, renames
toteki Dec 12, 2022
d09eea6
function signature
toteki Dec 12, 2022
45eb372
no one outpizzas the hut
toteki Dec 12, 2022
db15b24
var
toteki Dec 12, 2022
5ebcae6
implement AssertBorrowerHealth
toteki Dec 12, 2022
c4a61db
implement restrictions - broken tests
toteki Dec 12, 2022
f481911
refactor MsgCollateralize
toteki Dec 12, 2022
37298a5
move some tests from Keeper to MsgServer due to liquidity checks bein…
toteki Dec 12, 2022
49fbedf
MaxSuply check moved
toteki Dec 12, 2022
7958af4
Merge branch 'main' into adam/hist
toteki Dec 15, 2022
588b011
use AvailableMedians return value
toteki Dec 15, 2022
0641e25
lint++
toteki Dec 15, 2022
899130c
Merge branch 'main' into adam/hist
adamewozniak Dec 20, 2022
107ae1f
previous tests fully refactored
toteki Dec 21, 2022
58f2273
fix case - new error precedence
toteki Dec 21, 2022
32cc4d8
reorder
toteki Dec 21, 2022
b44be59
test msg MaxWithdraw
toteki Dec 21, 2022
3ba51e6
add additional test case
toteki Dec 21, 2022
8001bcb
modify maxWithdraw and query to use minimum of historic and current max
toteki Dec 21, 2022
2529cb9
Merge branch 'main' into adam/hist
toteki Dec 21, 2022
9625469
Update x/leverage/keeper/borrows.go
toteki Dec 21, 2022
4e881b5
Merge branch 'main' into adam/hist
toteki Dec 22, 2022
046c835
Update x/leverage/keeper/oracle.go
toteki Dec 22, 2022
1dc6d21
lint++
toteki Dec 22, 2022
8c6522f
Merge branch 'main' into adam/hist
adamewozniak Dec 23, 2022
58ff6e6
Merge branch 'main' into adam/hist
adamewozniak Dec 27, 2022
034757a
historic withdraw test cases
toteki Dec 27, 2022
f9ce975
max withdraw scenarios - historic
toteki Dec 27, 2022
726d74a
historic decollateralize cases
toteki Dec 28, 2022
4b97faa
historic borrow cases
toteki Dec 28, 2022
8770428
helper funtion
toteki Dec 28, 2022
285885a
fail-safe on no historic prices - breaks tests
toteki Dec 28, 2022
3a4c499
Merge branch 'main' into adam/hist
toteki Dec 29, 2022
f151eb6
Update x/leverage/keeper/msg_server_test.go
toteki Dec 29, 2022
7385533
Update x/leverage/keeper/msg_server_test.go
toteki Dec 29, 2022
93cfa64
Update x/leverage/keeper/msg_server_test.go
toteki Dec 29, 2022
6421445
Update x/leverage/keeper/msg_server_test.go
toteki Dec 29, 2022
74dacb8
Update x/leverage/keeper/msg_server_test.go
toteki Dec 29, 2022
7eea870
fixed leverage simulations mock oracle
toteki Dec 30, 2022
f07af39
fix leverage CLI test mock oracle
toteki Dec 30, 2022
7d867cf
cl++
toteki Dec 30, 2022
d710901
Merge branch 'main' into adam/hist
toteki Jan 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 60 additions & 3 deletions x/leverage/keeper/borrows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,47 @@ import (
"github.com/umee-network/umee/v3/x/leverage/types"
)

// checkBorrowerHealth returns an error if a borrower is currently above their borrow limit,
// under either recent (historic median) or current prices. It returns an error if current
// prices cannot be calculated, but will use current prices (without returning an error)
// for any token whose historic prices cannot be calculated.
// This should be checked in msg_server.go at the end of any transaction which is restricted
// by borrow limits, i.e. Borrow, Decollateralize, Withdraw.
func (k Keeper) checkBorrowerHealth(ctx sdk.Context, borrowerAddr sdk.AccAddress) error {
borrowed := k.GetBorrowerBorrows(ctx, borrowerAddr)
collateral := k.GetBorrowerCollateral(ctx, borrowerAddr)

// Check using current prices
currentValue, err := k.TotalTokenValue(ctx, borrowed, false)
if err != nil {
return err
}
currentLimit, err := k.CalculateBorrowLimit(ctx, collateral, false)
if err != nil {
return err
}
if currentValue.GT(currentLimit) {
return types.ErrUndercollaterized.Wrapf(
"borrowed: %s, limit: %s (current prices)", currentValue, currentLimit)
}

// Check using historic prices
historicValue, err := k.TotalTokenValue(ctx, borrowed, true)
if err != nil {
return err
}
historicLimit, err := k.CalculateBorrowLimit(ctx, collateral, true)
if err != nil {
return err
}
if historicValue.GT(historicLimit) {
return types.ErrUndercollaterized.Wrapf(
"borrowed: %s, limit: %s (historic prices)", historicValue, historicLimit)
}
toteki marked this conversation as resolved.
Show resolved Hide resolved

return nil
}

// GetBorrow returns an sdk.Coin representing how much of a given denom a
// borrower currently owes.
func (k Keeper) GetBorrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, denom string) sdk.Coin {
Expand Down Expand Up @@ -72,7 +113,8 @@ func (k Keeper) SupplyUtilization(ctx sdk.Context, denom string) sdk.Dec {
// CalculateBorrowLimit uses the price oracle to determine the borrow limit (in USD) provided by
// collateral sdk.Coins, using each token's uToken exchange rate and collateral weight.
// An error is returned if any input coins are not uTokens or if value calculation fails.
func (k Keeper) CalculateBorrowLimit(ctx sdk.Context, collateral sdk.Coins) (sdk.Dec, error) {
// If the historic parameter is true, uses medians of recent prices instead of current prices.
func (k Keeper) CalculateBorrowLimit(ctx sdk.Context, collateral sdk.Coins, historic bool) (sdk.Dec, error) {
limit := sdk.ZeroDec()

for _, coin := range collateral {
Expand All @@ -90,7 +132,7 @@ func (k Keeper) CalculateBorrowLimit(ctx sdk.Context, collateral sdk.Coins) (sdk
// ignore blacklisted tokens
if !ts.Blacklist {
// get USD value of base assets
v, err := k.TokenValue(ctx, baseAsset)
v, err := k.TokenValue(ctx, baseAsset, historic)
toteki marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return sdk.ZeroDec(), err
}
Expand Down Expand Up @@ -125,7 +167,7 @@ func (k Keeper) CalculateLiquidationThreshold(ctx sdk.Context, collateral sdk.Co
// ignore blacklisted tokens
if !ts.Blacklist {
// get USD value of base assets
v, err := k.TokenValue(ctx, baseAsset)
v, err := k.TokenValue(ctx, baseAsset, false)
if err != nil {
return sdk.ZeroDec(), err
}
Expand All @@ -137,3 +179,18 @@ func (k Keeper) CalculateLiquidationThreshold(ctx sdk.Context, collateral sdk.Co

return totalThreshold, nil
}

// checkSupplyUtilization returns the appropriate error if a token denom's
// supply utilization has exceeded MaxSupplyUtilization
func (k Keeper) checkSupplyUtilization(ctx sdk.Context, denom string) error {
token, err := k.GetTokenSettings(ctx, denom)
if err != nil {
return err
}

utilization := k.SupplyUtilization(ctx, denom)
if utilization.GT(token.MaxSupplyUtilization) {
return types.ErrMaxSupplyUtilization.Wrap(utilization.String())
}
return nil
}
10 changes: 5 additions & 5 deletions x/leverage/keeper/borrows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,13 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() {
app, ctx, require := s.app, s.ctx, s.Require()

// Empty coins
borrowLimit, err := app.LeverageKeeper.CalculateBorrowLimit(ctx, sdk.NewCoins())
borrowLimit, err := app.LeverageKeeper.CalculateBorrowLimit(ctx, sdk.NewCoins(), false)
require.NoError(err)
require.Equal(sdk.ZeroDec(), borrowLimit)

// Unregistered asset
invalidCoins := sdk.NewCoins(coin("abcd", 1000))
_, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, invalidCoins)
_, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, invalidCoins, false)
require.ErrorIs(err, types.ErrNotUToken)

// Create collateral uTokens (1k u/umee)
Expand All @@ -222,7 +222,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() {
Mul(sdk.MustNewDecFromStr("0.25"))

// Check borrow limit vs. manually computed value
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, umeeCollateral)
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, umeeCollateral, false)
require.NoError(err)
require.Equal(expectedUmeeLimit, borrowLimit)

Expand All @@ -237,7 +237,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() {
Mul(sdk.MustNewDecFromStr("0.25"))

// Check borrow limit vs. manually computed value
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, atomCollateral)
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, atomCollateral, false)
require.NoError(err)
require.Equal(expectedAtomLimit, borrowLimit)

Expand All @@ -246,7 +246,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() {
combinedCollateral := umeeCollateral.Add(atomCollateral...)

// Check borrow limit vs. manually computed value
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, combinedCollateral)
borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, combinedCollateral, false)
require.NoError(err)
require.Equal(expectedCombinedLimit, borrowLimit)
}
2 changes: 1 addition & 1 deletion x/leverage/keeper/collateral.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (k Keeper) CalculateCollateralValue(ctx sdk.Context, collateral sdk.Coins)
}

// get USD value of base assets
v, err := k.TokenValue(ctx, baseAsset)
v, err := k.TokenValue(ctx, baseAsset, false)
if err != nil {
return sdk.ZeroDec(), err
}
Expand Down
19 changes: 14 additions & 5 deletions x/leverage/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (q Querier) MarketSummary(
}

// Oracle price in response will be nil if it is unavailable
if oraclePrice, _, oracleErr := q.Keeper.TokenDefaultDenomPrice(ctx, req.Denom); oracleErr == nil {
if oraclePrice, _, oracleErr := q.Keeper.TokenDefaultDenomPrice(ctx, req.Denom, false); oracleErr == nil {
resp.OraclePrice = &oraclePrice
}

Expand Down Expand Up @@ -199,19 +199,19 @@ func (q Querier) AccountSummary(
collateral := q.Keeper.GetBorrowerCollateral(ctx, addr)
borrowed := q.Keeper.GetBorrowerBorrows(ctx, addr)

suppliedValue, err := q.Keeper.TotalTokenValue(ctx, supplied)
suppliedValue, err := q.Keeper.TotalTokenValue(ctx, supplied, false)
if err != nil {
return nil, err
}
borrowedValue, err := q.Keeper.TotalTokenValue(ctx, borrowed)
borrowedValue, err := q.Keeper.TotalTokenValue(ctx, borrowed, false)
if err != nil {
return nil, err
}
collateralValue, err := q.Keeper.CalculateCollateralValue(ctx, collateral)
if err != nil {
return nil, err
}
borrowLimit, err := q.Keeper.CalculateBorrowLimit(ctx, collateral)
borrowLimit, err := q.Keeper.CalculateBorrowLimit(ctx, collateral, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -288,10 +288,19 @@ func (q Querier) MaxWithdraw(
return nil, err
}

uToken, err := q.Keeper.maxWithdraw(ctx, addr, req.Denom)
maxCurrentWithdraw, err := q.Keeper.maxWithdraw(ctx, addr, req.Denom, false)
if err != nil {
return nil, err
}
maxHistoricWithdraw, err := q.Keeper.maxWithdraw(ctx, addr, req.Denom, true)
if err != nil {
return nil, err
}

uToken := sdk.NewCoin(
maxCurrentWithdraw.Denom,
sdk.MinInt(maxCurrentWithdraw.Amount, maxHistoricWithdraw.Amount),
)

token, err := q.Keeper.ExchangeUToken(ctx, uToken)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion x/leverage/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (s *IntegrationTestSuite) TestQuerier_RegisteredTokens() {

resp, err := s.queryClient.RegisteredTokens(ctx.Context(), &types.QueryRegisteredTokens{})
require.NoError(err)
require.Len(resp.Registry, 3, "token registry length")
require.Len(resp.Registry, 5, "token registry length")
toteki marked this conversation as resolved.
Show resolved Hide resolved
}

func (s *IntegrationTestSuite) TestQuerier_Params() {
Expand Down
2 changes: 1 addition & 1 deletion x/leverage/keeper/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (k Keeper) GetEligibleLiquidationTargets(ctx sdk.Context) ([]sdk.AccAddress
collateral := k.GetBorrowerCollateral(ctx, addr)

// use oracle helper functions to find total borrowed value in USD
borrowValue, err := k.TotalTokenValue(ctx, borrowed)
borrowValue, err := k.TotalTokenValue(ctx, borrowed, false)
if err != nil {
return err
}
Expand Down
Loading