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: recent price mode support #2267

Merged
merged 22 commits into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
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
toteki marked this conversation as resolved.
Show resolved Hide resolved
];
// 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,
toteki marked this conversation as resolved.
Show resolved Hide resolved
) {
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
Loading