Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
improve error checking
  • Loading branch information
toteki committed Feb 15, 2023
commit e40ef37172cfc052d56369767abf74fcf8f76095
9 changes: 5 additions & 4 deletions x/leverage/keeper/borrows.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ func (k Keeper) assertBorrowerHealth(ctx sdk.Context, borrowerAddr sdk.AccAddres
}
limit, err := k.VisibleBorrowLimit(ctx, collateral)
if err != nil {
// This error is not a missing price
return err
}
if value.GT(limit) {
Expand Down Expand Up @@ -156,11 +155,13 @@ func (k Keeper) VisibleBorrowLimit(ctx sdk.Context, collateral sdk.Coins) (sdk.D
// get USD value of base assets using the chosen price mode
v, err := k.TokenValue(ctx, baseAsset, types.PriceModeLow)
if err == nil {
// if both spot and historic (if requeired) prices exist,
// add each collateral coin's weighted value to borrow limit
// if both spot and historic (if required) prices exist,
// add collateral coin's weighted value to borrow limit
limit = limit.Add(v.Mul(ts.CollateralWeight))
}

if nonOracleError(err) {
return sdk.ZeroDec(), err
}
}
}

Expand Down
16 changes: 6 additions & 10 deletions x/leverage/keeper/collateral.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,13 @@ func (k Keeper) VisibleCollateralValue(ctx sdk.Context, collateral sdk.Coins) (s

// get USD value of base assets
v, err := k.TokenValue(ctx, baseAsset, types.PriceModeSpot)
if err != nil {
k.Logger(ctx).Info(
"collateral value skipped",
"uToken", coin.String(),
"error", err.Error(),
)
continue
if err == nil {
// for coins that did not error, add their value to the total
total = total.Add(v)
}
if nonOracleError(err) {
return sdk.ZeroDec(), err
}

// for coins that did not error, add their value to the total
total = total.Add(v)
}

return total, nil
Expand Down
29 changes: 29 additions & 0 deletions x/leverage/keeper/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package keeper

import (
"strings"

"cosmossdk.io/errors"

"github.com/umee-network/umee/v4/util/decmath"
leveragetypes "github.com/umee-network/umee/v4/x/leverage/types"
oracletypes "github.com/umee-network/umee/v4/x/oracle/types"
)

// nonOracleError returns true if an error is non-nil
// and also not one of ErrEmptyList, ErrUnknownDenom, or ErrNoHistoricMedians
// which are errors which can result from missing prices
func nonOracleError(err error) bool {
if err == nil {
return false
}
// check typed errors
if errors.IsOf(err, leveragetypes.ErrNoHistoricMedians, oracletypes.ErrUnknownDenom) {
return false
}
// this error needs to be checked by string comparison
if strings.Contains(err.Error(), decmath.ErrEmptyList.Error()) {
return false
}
return true
}
38 changes: 38 additions & 0 deletions x/leverage/keeper/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package keeper

import (
"testing"

"github.com/stretchr/testify/require"

"cosmossdk.io/errors"

"github.com/umee-network/umee/v4/util/decmath"
leveragetypes "github.com/umee-network/umee/v4/x/leverage/types"
oracletypes "github.com/umee-network/umee/v4/x/oracle/types"
)

func TestErrorMatching(t *testing.T) {
// oracle errors
err1 := errors.Wrap(decmath.ErrEmptyList, "denom: UMEE")
err2 := oracletypes.ErrUnknownDenom.Wrap("UMEE")
err3 := leveragetypes.ErrNoHistoricMedians.Wrapf(
"requested %d, got %d",
16,
12,
)
// not oracle errors
err4 := leveragetypes.ErrBlacklisted
err5 := leveragetypes.ErrUToken
err6 := leveragetypes.ErrNotRegisteredToken
err7 := errors.New("foo", 1, "bar")

require.Equal(t, false, nonOracleError(nil))
require.Equal(t, false, nonOracleError(err1))
require.Equal(t, false, nonOracleError(err2))
require.Equal(t, false, nonOracleError(err3))
require.Equal(t, true, nonOracleError(err4))
require.Equal(t, true, nonOracleError(err5))
require.Equal(t, true, nonOracleError(err6))
require.Equal(t, true, nonOracleError(err7))
}
20 changes: 16 additions & 4 deletions x/leverage/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,22 @@ func (q Querier) AccountSummary(
borrowed := q.Keeper.GetBorrowerBorrows(ctx, addr)

// supplied value always uses spot prices, and skips supplied assets that are missing prices
suppliedValue := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeSpot)
suppliedValue, err := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeSpot)
if err != nil {
return nil, err
}

// borrowed value uses spot prices here, but leverage logic instead uses
// the higher of spot or historic prices for each borrowed token when comparing it
// to borrow limit. This line also skips borrowed assets that are missing prices.
borrowedValue := q.Keeper.VisibleTokenValue(ctx, borrowed, types.PriceModeSpot)
borrowedValue, err := q.Keeper.VisibleTokenValue(ctx, borrowed, types.PriceModeSpot)
if err != nil {
return nil, err
}

// collateral value always uses spot prices, and this line skips assets that are missing prices
collateralValue, err := q.Keeper.VisibleCollateralValue(ctx, collateral)
if err != nil {
// this error isn't a missing price - it would be non-uToken collateral
return nil, err
}

Expand All @@ -228,7 +233,6 @@ func (q Querier) AccountSummary(
// skips collateral tokens with missing oracle prices
borrowLimit, err := q.Keeper.VisibleBorrowLimit(ctx, collateral)
if err != nil {
// this error isn't a missing price - it would be non-uToken collateral
return nil, err
}

Expand Down Expand Up @@ -337,6 +341,10 @@ func (q Querier) MaxWithdraw(
maxUTokens = maxUTokens.Add(uToken)
maxTokens = maxTokens.Add(token)
}
// Non-price errors will cause the query itself to fail
if nonOracleError(err) {
return nil, err
}
}

return &types.QueryMaxWithdrawResponse{
Expand Down Expand Up @@ -388,6 +396,10 @@ func (q Querier) MaxBorrow(
if err == nil && maxBorrow.IsPositive() {
maxTokens = maxTokens.Add(maxBorrow)
}
// Non-price errors will cause the query itself to fail
if nonOracleError(err) {
return nil, err
}
}

return &types.QueryMaxBorrowResponse{
Expand Down
9 changes: 8 additions & 1 deletion x/leverage/keeper/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ func (k Keeper) GetEligibleLiquidationTargets(ctx sdk.Context) ([]sdk.AccAddress

// use oracle helper functions to find total borrowed value in USD
// skips denoms without prices
borrowValue := k.VisibleTokenValue(ctx, borrowed, types.PriceModeSpot)
borrowValue, err := k.VisibleTokenValue(ctx, borrowed, types.PriceModeSpot)
if err != nil {
return err
}

// compute liquidation threshold from enabled collateral
// in this case, we can't reasonably skip missing prices but can move on
Expand All @@ -180,6 +183,10 @@ func (k Keeper) GetEligibleLiquidationTargets(ctx sdk.Context) ([]sdk.AccAddress
// address is eligible for liquidation.
liquidationTargets = append(liquidationTargets, addr)
}
// Non-price errors will cause the query itself to fail
if nonOracleError(err) {
return err
}

return nil
}
Expand Down
5 changes: 4 additions & 1 deletion x/leverage/keeper/liquidate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ func (k Keeper) getLiquidationAmounts(

// calculate borrower health in USD values, using spot prices only (no historic)
// borrowed value will skip borrowed tokens with unknown oracle prices, treating them as zero value
borrowedValue := k.VisibleTokenValue(ctx, totalBorrowed, types.PriceModeSpot)
borrowedValue, err := k.VisibleTokenValue(ctx, totalBorrowed, types.PriceModeSpot)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err
}
collateralValue, err := k.CalculateCollateralValue(ctx, borrowerCollateral)
if err != nil {
return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, err
Expand Down
7 changes: 5 additions & 2 deletions x/leverage/keeper/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (k Keeper) TotalTokenValue(ctx sdk.Context, coins sdk.Coins, mode types.Pri

// VisibleTokenValue functions like TotalTokenValue, but interprets missing oracle prices
// as zero value instead of returning an error.
func (k Keeper) VisibleTokenValue(ctx sdk.Context, coins sdk.Coins, mode types.PriceMode) sdk.Dec {
func (k Keeper) VisibleTokenValue(ctx sdk.Context, coins sdk.Coins, mode types.PriceMode) (sdk.Dec, error) {
total := sdk.ZeroDec()

accepted := k.filterAcceptedCoins(ctx, coins)
Expand All @@ -131,9 +131,12 @@ func (k Keeper) VisibleTokenValue(ctx sdk.Context, coins sdk.Coins, mode types.P
if err == nil {
total = total.Add(v)
}
if nonOracleError(err) {
return sdk.ZeroDec(), err
}
}

return total
return total, nil
}

// TokenWithValue creates a token of a given denom with an given USD value.
Expand Down