Skip to content

Commit

Permalink
feat: recent price mode support (#2267)
Browse files Browse the repository at this point in the history
* price mode support

* cl#

* fix tests

* change query behavior

* update test

* add emulated price outage to mock oracle

* test++

* test++

* adjust price behavior for account summary query

* message behavior tests

* lint

* comment

* market summary uses price mdoe query

* fix oracle behavior (stop clearing prices)

* tests++

* tests++

* warning++
  • Loading branch information
toteki authored Oct 3, 2023
1 parent ce876d2 commit 33624ed
Show file tree
Hide file tree
Showing 18 changed files with 642 additions and 161 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@ Ref: https://keepachangelog.com/en/1.0.0/

## v6.1.0-beta1 - 2023-09-29

### API-Breaking

- [2267](https://github.com/umee-network/umee/pull/2267) `BorrowLimit` field in QueryAccountSummaryResponse can be nil on missing borrow price (behavior now matches `LiquidationThreshold` field)

### Improvements

- [2261](https://github.com/umee-network/umee/pull/2261) Use go 1.21
- [2263](https://github.com/umee-network/umee/pull/2263) Add spot price fields to account summary, and ensure all other fields use leverage logic prices.
- [2267](https://github.com/umee-network/umee/pull/2267) Leverage transactions accept spot prices up to 3 minutes old, and leverage queries use most recent spot price when required.
- [2263](https://github.com/umee-network/umee/pull/2263) Add spot price fields to account summary.
- [2270](https://github.com/umee-network/umee/pull/2270) Increase free oracle tx limit to 200k gas.

### Features
Expand Down
15 changes: 9 additions & 6 deletions proto/umee/leverage/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -286,28 +286,31 @@ message QueryAccountSummaryResponse {
// Borrow Limit is the maximum Borrowed Value the account is allowed to reach through direct borrowing.
// The lower of spot or historic price for each collateral token is used when calculating borrow limits.
// Computation skips collateral which is missing an oracle price, potentially resulting in a lower borrow
// limit than if prices were all available.
// limit than if prices were all available. Will be null if an oracle price required for computation is
// missing.
string borrow_limit = 4 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
(gogoproto.nullable) = true
];
// Liquidation Threshold is the Borrowed Value at which the account becomes eligible for liquidation.
// Will be null if an oracle price required for computation is missing.
// Computation skips borrows which are missing an oracle price, potentially resulting in a lower borrow
// limit than if prices were all available. Will be null if an oracle price required for computation is
// missing.
string liquidation_threshold = 5 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = true
];
// Spot Supplied Value is supplied value but always uses spot prices.
// Spot Supplied Value is supplied value but always uses the most recent available spot prices.
string spot_supplied_value = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// Spot Collateral Value is collateral value but always uses spot prices.
// Spot Collateral Value is collateral value but always uses the most recent available spot prices.
string spot_collateral_value = 7 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// Spot Borrowed Value is borrowed value but always uses spot prices.
// Spot Borrowed Value is borrowed value but always uses the most recent available spot prices.
string spot_borrowed_value = 8 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
Expand Down
147 changes: 134 additions & 13 deletions swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -175,30 +175,38 @@ paths:
Computation skips collateral which is missing an oracle price,
potentially resulting in a lower borrow
limit than if prices were all available.
limit than if prices were all available. Will be null if an
oracle price required for computation is
missing.
liquidation_threshold:
type: string
description: >-
Liquidation Threshold is the Borrowed Value at which the
account becomes eligible for liquidation.
Will be null if an oracle price required for computation is
Computation skips borrows which are missing an oracle price,
potentially resulting in a lower borrow
limit than if prices were all available. Will be null if an
oracle price required for computation is
missing.
spot_supplied_value:
type: string
description: >-
Spot Supplied Value is supplied value but always uses spot
prices.
Spot Supplied Value is supplied value but always uses the most
recent available spot prices.
spot_collateral_value:
type: string
description: >-
Spot Collateral Value is collateral value but always uses spot
prices.
Spot Collateral Value is collateral value but always uses the
most recent available spot prices.
spot_borrowed_value:
type: string
description: >-
Spot Borrowed Value is borrowed value but always uses spot
prices.
Spot Borrowed Value is borrowed value but always uses the most
recent available spot prices.
description: >-
QueryAccountSummaryResponse defines the response structure for the
AccountSummary gRPC service handler.
Expand Down Expand Up @@ -1809,6 +1817,71 @@ paths:
type: string
tags:
- Query
/umee/oracle/v1/denoms/exg_rates_timestamp:
get:
summary: >-
ExgRatesWithTimestamp returns exchange rates of all denoms with
timestamp,
or, if specified, returns a single denom
operationId: ExgRatesWithTimestamp
responses:
'200':
description: A successful response.
schema:
type: object
properties:
exg_rates:
type: array
items:
type: object
properties:
denom:
type: string
rate:
type: string
timestamp:
type: string
format: date-time
title: DenomExchangeRate stores exchange rate with timestamp
title: >-
exchange_rates defines a list of the exchange rate for all
whitelisted
denoms with timestamp
description: |-
QueryExgRatesWithTimestampResponse is response type for the
Query/ExchangeRatesWithTimestamp RPC method.
default:
description: An unexpected error response.
schema:
type: object
properties:
error:
type: string
code:
type: integer
format: int32
message:
type: string
details:
type: array
items:
type: object
properties:
type_url:
type: string
value:
type: string
format: byte
parameters:
- name: denom
description: denom defines the denomination to query for.
in: query
required: false
type: string
tags:
- Query
/umee/oracle/v1/params:
get:
summary: Params queries all parameters.
Expand Down Expand Up @@ -4757,23 +4830,38 @@ definitions:
Computation skips collateral which is missing an oracle price,
potentially resulting in a lower borrow
limit than if prices were all available.
limit than if prices were all available. Will be null if an oracle
price required for computation is
missing.
liquidation_threshold:
type: string
description: >-
Liquidation Threshold is the Borrowed Value at which the account
becomes eligible for liquidation.
Will be null if an oracle price required for computation is missing.
Computation skips borrows which are missing an oracle price,
potentially resulting in a lower borrow
limit than if prices were all available. Will be null if an oracle
price required for computation is
missing.
spot_supplied_value:
type: string
description: Spot Supplied Value is supplied value but always uses spot prices.
description: >-
Spot Supplied Value is supplied value but always uses the most recent
available spot prices.
spot_collateral_value:
type: string
description: Spot Collateral Value is collateral value but always uses spot prices.
description: >-
Spot Collateral Value is collateral value but always uses the most
recent available spot prices.
spot_borrowed_value:
type: string
description: Spot Borrowed Value is borrowed value but always uses spot prices.
description: >-
Spot Borrowed Value is borrowed value but always uses the most recent
available spot prices.
description: >-
QueryAccountSummaryResponse defines the response structure for the
AccountSummary gRPC service handler.
Expand Down Expand Up @@ -5897,6 +5985,17 @@ definitions:
type: integer
format: int64
title: Denom - the object to hold configurations of each denom
umee.oracle.v1.DenomExchangeRate:
type: object
properties:
denom:
type: string
rate:
type: string
timestamp:
type: string
format: date-time
title: DenomExchangeRate stores exchange rate with timestamp
umee.oracle.v1.ExchangeRateTuple:
type: object
properties:
Expand Down Expand Up @@ -6152,6 +6251,28 @@ definitions:
description: |-
QueryExchangeRatesResponse is response type for the
Query/ExchangeRates RPC method.
umee.oracle.v1.QueryExgRatesWithTimestampResponse:
type: object
properties:
exg_rates:
type: array
items:
type: object
properties:
denom:
type: string
rate:
type: string
timestamp:
type: string
format: date-time
title: DenomExchangeRate stores exchange rate with timestamp
title: |-
exchange_rates defines a list of the exchange rate for all whitelisted
denoms with timestamp
description: |-
QueryExgRatesWithTimestampResponse is response type for the
Query/ExchangeRatesWithTimestamp RPC method.
umee.oracle.v1.QueryFeederDelegationResponse:
type: object
properties:
Expand Down
3 changes: 2 additions & 1 deletion x/leverage/client/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ func (s *IntegrationTests) TestLeverageScenario() {
}

lt1 := sdk.MustNewDecFromStr("0.0089034946")
bl1 := sdk.MustNewDecFromStr("0.0085610525")

nonzeroQueries := []itestsuite.TestQuery{
{
Expand Down Expand Up @@ -337,7 +338,7 @@ func (s *IntegrationTests) TestLeverageScenario() {
BorrowedValue: sdk.MustNewDecFromStr("0.00858671"),
SpotBorrowedValue: sdk.MustNewDecFromStr("0.00858671"),
// (1001 / 1000000) * 34.21 * 0.25 = 0.0085610525
BorrowLimit: sdk.MustNewDecFromStr("0.0085610525"),
BorrowLimit: &bl1,
// (1001 / 1000000) * 0.26 * 34.21 = 0.008903494600000000
LiquidationThreshold: &lt1,
},
Expand Down
1 change: 1 addition & 0 deletions x/leverage/keeper/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func nonOracleError(err error) bool {
if errors.IsOf(err,
leveragetypes.ErrInvalidOraclePrice,
leveragetypes.ErrNoHistoricMedians,
leveragetypes.ErrExpiredOraclePrice,
oracletypes.ErrUnknownDenom,
) {
return false
Expand Down
43 changes: 27 additions & 16 deletions x/leverage/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,9 @@ func (q Querier) MarketSummary(
AvailableCollateralize: availableCollateralize,
}

// Oracle prices in response will be nil if it is unavailable
oraclePrice, _, oracleErr := q.Keeper.TokenPrice(ctx, req.Denom, types.PriceModeSpot)
// Oracle price in response will be nil if the oracle module has no price at all, but will instead
// show the most recent price if one existed.
oraclePrice, _, oracleErr := q.Keeper.TokenPrice(ctx, req.Denom, types.PriceModeQuery)
if oracleErr == nil {
resp.OraclePrice = &oraclePrice
} else {
Expand Down Expand Up @@ -237,32 +238,42 @@ func (q Querier) AccountSummary(
collateral := q.Keeper.GetBorrowerCollateral(ctx, addr)
borrowed := q.Keeper.GetBorrowerBorrows(ctx, addr)

// the following spot price calculations skip assets missing prices, but otherwise always
// use the most up to date prices
spotSuppliedValue, err := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeSpot)
// the following price calculations use the most recent prices if spot prices are missing
lastSuppliedValue, err := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeQuery)
if err != nil {
return nil, err
}
spotBorrowedValue, err := q.Keeper.VisibleTokenValue(ctx, borrowed, types.PriceModeSpot)
lastBorrowedValue, err := q.Keeper.VisibleTokenValue(ctx, borrowed, types.PriceModeQuery)
if err != nil {
return nil, err
}
spotCollateralValue, err := q.Keeper.VisibleCollateralValue(ctx, collateral, types.PriceModeSpot)
lastCollateralValue, err := q.Keeper.VisibleCollateralValue(ctx, collateral, types.PriceModeQuery)
if err != nil {
return nil, err
}

// this supplied value uses leverage-like prices: the lower of spot or historic price for supplied tokens
suppliedValue, err := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeLow)
// these use leverage-like prices: the lower of spot or historic price for supplied tokens and higher for borrowed.
// unlike transactions, this query will use expired prices instead of skipping them.
suppliedValue, err := q.Keeper.VisibleTokenValue(ctx, supplied, types.PriceModeQueryLow)
if err != nil {
return nil, err
}
collateralValue, err := q.Keeper.VisibleCollateralValue(ctx, collateral, types.PriceModeQueryLow)
if err != nil {
return nil, err
}
borrowedValue, err := q.Keeper.VisibleTokenValue(ctx, borrowed, types.PriceModeQueryHigh)
if err != nil {
return nil, err
}

resp := &types.QueryAccountSummaryResponse{
SuppliedValue: suppliedValue,
SpotSuppliedValue: spotSuppliedValue,
SpotCollateralValue: spotCollateralValue,
SpotBorrowedValue: spotBorrowedValue,
CollateralValue: collateralValue,
BorrowedValue: borrowedValue,
SpotSuppliedValue: lastSuppliedValue,
SpotCollateralValue: lastCollateralValue,
SpotBorrowedValue: lastBorrowedValue,
}

// values computed from position use the same prices found in leverage logic:
Expand All @@ -275,9 +286,9 @@ func (q Querier) AccountSummary(
return nil, err
}
if err == nil {
resp.BorrowedValue = ap.BorrowedValue()
resp.CollateralValue = ap.CollateralValue()
resp.BorrowLimit = ap.Limit()
// on missing borrow price, borrow limit is nil
borrowLimit := ap.Limit()
resp.BorrowLimit = &borrowLimit
}

// liquidation threshold shown here as it is used in leverage logic: using spot prices.
Expand All @@ -288,7 +299,7 @@ func (q Querier) AccountSummary(
return nil, err
}
if err == nil {
// on an error here, simply skip setting the response field
// on missing collateral price, liquidation threshold is nil
liquidationThreshold := ap.Limit()
resp.LiquidationThreshold = &liquidationThreshold
}
Expand Down
Loading

0 comments on commit 33624ed

Please sign in to comment.