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: adding new accounts summary query #2527

Merged
merged 9 commits into from
May 17, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
- [2472](https://github.com/umee-network/umee/pull/2472) un-wire the `crisis` module from umee app.
- [2500](https://github.com/umee-network/umee/pull/2500) (x/leverage): add Rewards Auction fees and `params.rewards_auction_factor`.
- [2506](https://github.com/umee-network/umee/pull/2506) (ics20): support leverage/MsgRepay in Memo
- [2527](https://github.com/umee-network/umee/pull/2527) (x/leverage):add `accounts_summary` grpc-web api and cli query to leverage module.

### Improvements

Expand Down
2 changes: 2 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,9 @@ func New(
app.LeverageKeeper = leveragekeeper.NewKeeper(
appCodec,
keys[leveragetypes.ModuleName],
keys[authtypes.StoreKey],
app.BankKeeper,
app.AccountKeeper,
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
app.OracleKeeper,
app.UGovKeeperB.EmergencyGroup,
cast.ToBool(appOpts.Get(leveragetypes.FlagEnableLiquidatorQuery)),
Expand Down
26 changes: 26 additions & 0 deletions proto/umee/leverage/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "umee/leverage/v1/genesis.proto";
import "umee/leverage/v1/leverage.proto";
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/base/query/v1beta1/pagination.proto";

option go_package = "github.com/umee-network/umee/v6/x/leverage/types";

Expand Down Expand Up @@ -54,6 +55,12 @@ service Query {
option (google.api.http).get = "/umee/leverage/v1/account_summary";
}

// AccountSummaries queries USD values representing an account's total positions and borrowing limits. It requires oracle prices to return successfully.
rpc AccountSummaries(QueryAccountSummaries)
returns (QueryAccountSummariesResponse) {
option (google.api.http).get = "/umee/leverage/v1/accounts_summary";
}

// LiquidationTargets queries a list of all borrower account addresses eligible for liquidation.
rpc LiquidationTargets(QueryLiquidationTargets)
returns (QueryLiquidationTargetsResponse) {
Expand Down Expand Up @@ -341,6 +348,25 @@ message QueryAccountSummaryResponse {
];
}

// QueryAccountSummaries defines the request structure for the AccountSummaries gRPC service handler.
message QueryAccountSummaries {
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

// AccountSummary is holds account_summary with address.
message AccountSummary {
string address = 1;
QueryAccountSummaryResponse account_summary = 2;
}

// QueryAccountSummariesResponse defines the response structure for the AccountSummaries gRPC service handler.
message QueryAccountSummariesResponse {
repeated AccountSummary account_summaries = 1;
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryLiquidationTargets defines the request structure for the LiquidationTargets gRPC service handler.
message QueryLiquidationTargets {}

Expand Down
34 changes: 34 additions & 0 deletions x/leverage/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
QueryMarketSummary(),
QueryAccountBalances(),
QueryAccountSummary(),
QueryAccountSummaries(),

Check warning on line 37 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L37

Added line #L37 was not covered by tests
QueryLiquidationTargets(),
QueryBadDebts(),
QueryMaxWithdraw(),
Expand Down Expand Up @@ -206,6 +207,39 @@
return cmd
}

// QueryAccountSummaries creates a Cobra command to query the USD
// values representing an all account's positions and borrowing limits.
func QueryAccountSummaries() *cobra.Command {
cmd := &cobra.Command{
Use: "account-summaries",
Args: cobra.NoArgs,
Short: "Query the position USD values and borrowing limits for an all accounts.",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err

Check warning on line 220 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L212-L220

Added lines #L212 - L220 were not covered by tests
}

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err

Check warning on line 225 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L223-L225

Added lines #L223 - L225 were not covered by tests
}

queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryAccountSummaries{
Pagination: pageReq,

Check warning on line 230 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L228-L230

Added lines #L228 - L230 were not covered by tests
}
resp, err := queryClient.AccountSummaries(cmd.Context(), req)
return cli.PrintOrErr(resp, err, clientCtx)

Check warning on line 233 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L232-L233

Added lines #L232 - L233 were not covered by tests
},
}

flags.AddQueryFlagsToCmd(cmd)
flags.AddPaginationFlagsToCmd(cmd, "account-summaries")

Check warning on line 238 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L237-L238

Added lines #L237 - L238 were not covered by tests

return cmd

Check warning on line 240 in x/leverage/client/cli/query.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/client/cli/query.go#L240

Added line #L240 was not covered by tests
}

// QueryLiquidationTargets creates a Cobra command to query for
// all eligible liquidation targets.
func QueryLiquidationTargets() *cobra.Command {
Expand Down
153 changes: 153 additions & 0 deletions x/leverage/keeper/accounts_summary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package keeper

import (
"context"

"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/umee-network/umee/v6/util/coin"
"github.com/umee-network/umee/v6/x/leverage/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (q Querier) accountSummary(ctx sdk.Context, addr sdk.AccAddress) (*types.QueryAccountSummaryResponse, error) {
supplied, err := q.GetAllSupplied(ctx, addr)
if err != nil {
return nil, err

Check warning on line 19 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L19

Added line #L19 was not covered by tests
}
collateral := q.GetBorrowerCollateral(ctx, addr)
borrowed := q.GetBorrowerBorrows(ctx, addr)

// the following price calculations use the most recent prices if spot prices are missing
lastSuppliedValue, err := q.VisibleTokenValue(ctx, supplied, types.PriceModeQuery)
if err != nil {
return nil, err

Check warning on line 27 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L27

Added line #L27 was not covered by tests
}
lastBorrowedValue, err := q.VisibleTokenValue(ctx, borrowed, types.PriceModeQuery)
if err != nil {
return nil, err

Check warning on line 31 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L31

Added line #L31 was not covered by tests
}
lastCollateralValue, err := q.VisibleCollateralValue(ctx, collateral, types.PriceModeQuery)
if err != nil {
return nil, err

Check warning on line 35 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L35

Added line #L35 was not covered by tests
}

// 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.VisibleTokenValue(ctx, supplied, types.PriceModeQueryLow)
if err != nil {
return nil, err

Check warning on line 42 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L42

Added line #L42 was not covered by tests
}
collateralValue, err := q.VisibleCollateralValue(ctx, collateral, types.PriceModeQueryLow)
if err != nil {
return nil, err

Check warning on line 46 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L46

Added line #L46 was not covered by tests
}
borrowedValue, err := q.VisibleTokenValue(ctx, borrowed, types.PriceModeQueryHigh)
if err != nil {
return nil, err

Check warning on line 50 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L50

Added line #L50 was not covered by tests
}

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

// values computed from position use the same prices found in leverage logic:
// using the lower of spot or historic prices for each collateral token
// and the higher of spot or historic prices for each borrowed token
// skips collateral tokens with missing prices, but errors on borrow tokens missing prices
// (for oracle errors only the relevant response fields will be left nil)
ap, err := q.GetAccountPosition(ctx, addr, false)
if nonOracleError(err) {
return nil, err

Check warning on line 69 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L69

Added line #L69 was not covered by tests
}
if err == nil {
// 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.
// skips borrowed tokens with missing prices, but errors on collateral missing prices
// (for oracle errors only the relevant response fields will be left nil)
ap, err = q.GetAccountPosition(ctx, addr, true)
if nonOracleError(err) {
return nil, err

Check warning on line 82 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L82

Added line #L82 was not covered by tests
}
if err == nil {
// on missing collateral price, liquidation threshold is nil
liquidationThreshold := ap.Limit()
resp.LiquidationThreshold = &liquidationThreshold
}

return resp, nil
}

// AccountSummaries implements types.QueryServer.
func (q Querier) AccountSummaries(goCtx context.Context, req *types.QueryAccountSummaries) (
*types.QueryAccountSummariesResponse, error,
) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")

Check warning on line 98 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L96-L98

Added lines #L96 - L98 were not covered by tests
}
ctx := sdk.UnwrapSDKContext(goCtx)

Check warning on line 100 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L100

Added line #L100 was not covered by tests
// get the all accounts
store := ctx.KVStore(q.akStoreKey)
accountsStore := prefix.NewStore(store, authtypes.AddressStoreKeyPrefix)

Check warning on line 103 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L102-L103

Added lines #L102 - L103 were not covered by tests

var accounts []*types.AccountSummary
pageRes, err := query.Paginate(accountsStore, req.Pagination, func(key, value []byte) error {
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
acc, err := q.authKeeper.UnmarshalAccount(value)
if err != nil {
return err

Check warning on line 109 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L105-L109

Added lines #L105 - L109 were not covered by tests
}
balance := q.bankKeeper.GetAllBalances(ctx, acc.GetAddress())
hasUToken := false
for _, c := range balance {
if coin.HasUTokenPrefix(c.Denom) {
hasUToken = true
break

Check warning on line 116 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L111-L116

Added lines #L111 - L116 were not covered by tests
}
}
if hasUToken {
accSummary, err := q.accountSummary(ctx, acc.GetAddress())
if err != nil {
return err

Check warning on line 122 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L119-L122

Added lines #L119 - L122 were not covered by tests
}
accounts = append(accounts, &types.AccountSummary{
Address: acc.GetAddress().String(),
AccountSummary: accSummary,
})

Check warning on line 127 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L124-L127

Added lines #L124 - L127 were not covered by tests
}
return nil

Check warning on line 129 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L129

Added line #L129 was not covered by tests
})

return &types.QueryAccountSummariesResponse{AccountSummaries: accounts, Pagination: pageRes}, err

Check warning on line 132 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L132

Added line #L132 was not covered by tests
}

// AccountSummary implements types.QueryServer.
func (q Querier) AccountSummary(
goCtx context.Context,
req *types.QueryAccountSummary,
) (*types.QueryAccountSummaryResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")

Check warning on line 141 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L141

Added line #L141 was not covered by tests
}
if req.Address == "" {
return nil, status.Error(codes.InvalidArgument, "empty address")

Check warning on line 144 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L144

Added line #L144 was not covered by tests
}

ctx := sdk.UnwrapSDKContext(goCtx)
addr, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, err

Check warning on line 150 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L150

Added line #L150 was not covered by tests
}
return q.accountSummary(ctx, addr)
}
94 changes: 0 additions & 94 deletions x/leverage/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,100 +241,6 @@ func (q Querier) AccountBalances(
}, nil
}

func (q Querier) AccountSummary(
goCtx context.Context,
req *types.QueryAccountSummary,
) (*types.QueryAccountSummaryResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if req.Address == "" {
return nil, status.Error(codes.InvalidArgument, "empty address")
}

ctx := sdk.UnwrapSDKContext(goCtx)

addr, err := sdk.AccAddressFromBech32(req.Address)
if err != nil {
return nil, err
}

supplied, err := q.GetAllSupplied(ctx, addr)
if err != nil {
return nil, err
}
collateral := q.GetBorrowerCollateral(ctx, addr)
borrowed := q.GetBorrowerBorrows(ctx, addr)

// the following price calculations use the most recent prices if spot prices are missing
lastSuppliedValue, err := q.VisibleTokenValue(ctx, supplied, types.PriceModeQuery)
if err != nil {
return nil, err
}
lastBorrowedValue, err := q.VisibleTokenValue(ctx, borrowed, types.PriceModeQuery)
if err != nil {
return nil, err
}
lastCollateralValue, err := q.VisibleCollateralValue(ctx, collateral, types.PriceModeQuery)
if err != nil {
return nil, err
}

// 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.VisibleTokenValue(ctx, supplied, types.PriceModeQueryLow)
if err != nil {
return nil, err
}
collateralValue, err := q.VisibleCollateralValue(ctx, collateral, types.PriceModeQueryLow)
if err != nil {
return nil, err
}
borrowedValue, err := q.VisibleTokenValue(ctx, borrowed, types.PriceModeQueryHigh)
if err != nil {
return nil, err
}

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

// values computed from position use the same prices found in leverage logic:
// using the lower of spot or historic prices for each collateral token
// and the higher of spot or historic prices for each borrowed token
// skips collateral tokens with missing prices, but errors on borrow tokens missing prices
// (for oracle errors only the relevant response fields will be left nil)
ap, err := q.GetAccountPosition(ctx, addr, false)
if nonOracleError(err) {
return nil, err
}
if err == nil {
// 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.
// skips borrowed tokens with missing prices, but errors on collateral missing prices
// (for oracle errors only the relevant response fields will be left nil)
ap, err = q.GetAccountPosition(ctx, addr, true)
if nonOracleError(err) {
return nil, err
}
if err == nil {
// on missing collateral price, liquidation threshold is nil
liquidationThreshold := ap.Limit()
resp.LiquidationThreshold = &liquidationThreshold
}

return resp, nil
}

func (q Querier) LiquidationTargets(
goCtx context.Context,
req *types.QueryLiquidationTargets,
Expand Down
5 changes: 5 additions & 0 deletions x/leverage/keeper/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"

"github.com/umee-network/umee/v6/tests/accs"
"github.com/umee-network/umee/v6/x/leverage/types"
Expand All @@ -22,14 +23,18 @@ type TestKeeper struct {
func NewTestKeeper(
cdc codec.Codec,
storeKey storetypes.StoreKey,
akStoreKey storetypes.StoreKey,
bk types.BankKeeper,
ak authkeeper.AccountKeeper,
ok types.OracleKeeper,
enableLiquidatorQuery bool,
) (Keeper, TestKeeper) {
k := NewKeeper(
cdc,
storeKey,
akStoreKey,
bk,
ak,
ok,
ugovmocks.NewSimpleEmergencyGroupBuilder(),
enableLiquidatorQuery,
Expand Down
Loading
Loading