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 6 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
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";
}

// AccountsSummary queries USD values representing an account's total positions and borrowing limits. It requires oracle prices to return successfully.
rpc AccountsSummary(QueryAccountsSummary)
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
returns (QueryAccountsSummaryResponse) {
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 {
];
}

// QueryAccountsSummary defines the request structure for the AccountsSummary gRPC service handler.
message QueryAccountsSummary {
// 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;
}

// QueryAccountsSummaryResponse defines the response structure for the AccountsSummary gRPC service handler.
message QueryAccountsSummaryResponse {
repeated AccountSummary accounts_summary = 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(),
QueryAccountsSummary(),

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
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
QueryLiquidationTargets(),
QueryBadDebts(),
QueryMaxWithdraw(),
Expand Down Expand Up @@ -206,6 +207,39 @@
return cmd
}

// QueryAccountsSummary creates a Cobra command to query the USD
// values representing an all account's positions and borrowing limits.
gsk967 marked this conversation as resolved.
Show resolved Hide resolved
func QueryAccountsSummary() *cobra.Command {
cmd := &cobra.Command{
Use: "accounts-summary",
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.QueryAccountsSummary{
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.AccountsSummary(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, "accounts-summary")

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
142 changes: 142 additions & 0 deletions x/leverage/keeper/accounts_summary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
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/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 18 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L18

Added line #L18 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 26 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L26

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L30

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L34

Added line #L34 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 41 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L41

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L45

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L49

Added line #L49 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 68 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L68

Added line #L68 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 81 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L81

Added line #L81 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
}

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L95-L97

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L99

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

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

View check run for this annotation

Codecov / codecov/patch

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

Added lines #L101 - L102 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 108 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L104-L108

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L110-L112

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L114-L118

Added lines #L114 - L118 were not covered by tests
})

return &types.QueryAccountsSummaryResponse{AccountsSummary: accounts, Pagination: pageRes}, err

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L121

Added line #L121 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 130 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L130

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

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

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L133

Added line #L133 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 139 in x/leverage/keeper/accounts_summary.go

View check run for this annotation

Codecov / codecov/patch

x/leverage/keeper/accounts_summary.go#L139

Added line #L139 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