diff --git a/x/hard/client/cli/query.go b/x/hard/client/cli/query.go index 24c65a19..cbf3a9d6 100644 --- a/x/hard/client/cli/query.go +++ b/x/hard/client/cli/query.go @@ -39,6 +39,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { queryParamsCmd(queryRoute, cdc), queryModAccountsCmd(queryRoute, cdc), queryDepositsCmd(queryRoute, cdc), + queryDepositCmd(queryRoute, cdc), queryClaimsCmd(queryRoute, cdc), queryBorrowsCmd(queryRoute, cdc), queryBorrowCmd(queryRoute, cdc), @@ -147,7 +148,7 @@ func queryDepositsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { page := viper.GetInt(flags.FlagPage) limit := viper.GetInt(flags.FlagLimit) - params := types.NewQueryDepositParams(page, limit, depositDenom, owner) + params := types.NewQueryDepositsParams(page, limit, depositDenom, owner) bz, err := cdc.MarshalJSON(params) if err != nil { return err @@ -373,3 +374,50 @@ func queryBorrowCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd.Flags().String(flagOwner, "", "filter for borrows by owner address") return cmd } + +func queryDepositCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "deposit", + Short: "query outstanding deposit balance for a user", + Long: strings.TrimSpace(`query outstanding deposit balance for a user: + Example: + $ kvcli q hard deposit --owner kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny`, + ), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + var owner sdk.AccAddress + + ownerBech := viper.GetString(flagOwner) + if len(ownerBech) != 0 { + borrowOwner, err := sdk.AccAddressFromBech32(ownerBech) + if err != nil { + return err + } + owner = borrowOwner + } + + params := types.NewQueryDepositParams(owner) + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetDeposit) + res, height, err := cliCtx.QueryWithData(route, bz) + if err != nil { + return err + } + cliCtx = cliCtx.WithHeight(height) + + var balance sdk.Coins + if err := cdc.UnmarshalJSON(res, &balance); err != nil { + return fmt.Errorf("failed to unmarshal borrow balance: %w", err) + } + return cliCtx.PrintOutput(balance) + }, + } + cmd.Flags().String(flagOwner, "", "filter for borrows by owner address") + return cmd +} diff --git a/x/hard/client/rest/query.go b/x/hard/client/rest/query.go index 92729f9a..bd99b2f0 100644 --- a/x/hard/client/rest/query.go +++ b/x/hard/client/rest/query.go @@ -73,7 +73,7 @@ func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } } - params := types.NewQueryDepositParams(page, limit, depositDenom, depositOwner) + params := types.NewQueryDepositsParams(page, limit, depositDenom, depositOwner) bz, err := cliCtx.Codec.MarshalJSON(params) if err != nil { diff --git a/x/hard/keeper/deposit.go b/x/hard/keeper/deposit.go index 99f56151..91f6d0a7 100644 --- a/x/hard/keeper/deposit.go +++ b/x/hard/keeper/deposit.go @@ -161,3 +161,36 @@ func (k Keeper) DecrementSuppliedCoins(ctx sdk.Context, coins sdk.Coins) error { k.SetSuppliedCoins(ctx, updatedSuppliedCoins) return nil } + +// GetSupplyBalance gets the user's total supply balance (supply balance + pending interest) +func (k Keeper) GetSupplyBalance(ctx sdk.Context, depositor sdk.AccAddress) sdk.Coins { + supplyBalance := sdk.Coins{} + deposit, found := k.GetDeposit(ctx, depositor) + if !found { + return supplyBalance + } + + totalNewInterest := sdk.Coins{} + for _, coin := range deposit.Amount { + interestFactorValue, foundInterestFactorValue := k.GetSupplyInterestFactor(ctx, coin.Denom) + if foundInterestFactorValue { + // Locate the interest factor by coin denom in the user's list of interest factors + foundAtIndex := -1 + for i := range deposit.Index { + if deposit.Index[i].Denom == coin.Denom { + foundAtIndex = i + break + } + } + // Calculate interest owed by user for this asset + if foundAtIndex != -1 { + storedAmount := sdk.NewDecFromInt(deposit.Amount.AmountOf(coin.Denom)) + userLastInterestFactor := deposit.Index[foundAtIndex].Value + coinInterest := (storedAmount.Quo(userLastInterestFactor).Mul(interestFactorValue)).Sub(storedAmount) + totalNewInterest = totalNewInterest.Add(sdk.NewCoin(coin.Denom, coinInterest.TruncateInt())) + } + } + } + + return deposit.Amount.Add(totalNewInterest...) +} diff --git a/x/hard/keeper/querier.go b/x/hard/keeper/querier.go index a9c5445a..2293b6ef 100644 --- a/x/hard/keeper/querier.go +++ b/x/hard/keeper/querier.go @@ -22,6 +22,8 @@ func NewQuerier(k Keeper) sdk.Querier { return queryGetModAccounts(ctx, req, k) case types.QueryGetDeposits: return queryGetDeposits(ctx, req, k) + case types.QueryGetDeposit: + return queryGetDeposit(ctx, req, k) case types.QueryGetClaims: return queryGetClaims(ctx, req, k) case types.QueryGetBorrows: @@ -79,7 +81,7 @@ func queryGetModAccounts(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryDepositParams + var params types.QueryDepositsParams err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) @@ -239,7 +241,6 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e } func queryGetBorrows(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { - var params types.QueryBorrowsParams err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { @@ -315,3 +316,23 @@ func queryGetBorrow(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e return bz, nil } + +func queryGetDeposit(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) { + var params types.QueryDepositParams + err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + var supplyBalance sdk.Coins + if len(params.Owner) > 0 { + supplyBalance = k.GetSupplyBalance(ctx, params.Owner) + } + + bz, err := codec.MarshalJSONIndent(types.ModuleCdc, supplyBalance) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} diff --git a/x/hard/types/querier.go b/x/hard/types/querier.go index 1b7fb4ef..6a4f0b3c 100644 --- a/x/hard/types/querier.go +++ b/x/hard/types/querier.go @@ -9,6 +9,7 @@ const ( QueryGetParams = "params" QueryGetModuleAccounts = "accounts" QueryGetDeposits = "deposits" + QueryGetDeposit = "deposit" QueryGetClaims = "claims" QueryGetBorrows = "borrows" QueryGetBorrowed = "borrowed" @@ -17,15 +18,27 @@ const ( // QueryDepositParams is the params for a filtered deposit query type QueryDepositParams struct { + Owner sdk.AccAddress `json:"owner" yaml:"owner"` +} + +// NewQueryDepositParams creates a new QueryDepositParams +func NewQueryDepositParams(owner sdk.AccAddress) QueryDepositParams { + return QueryDepositParams{ + Owner: owner, + } +} + +// QueryDepositsParams is the params for a filtered deposit query +type QueryDepositsParams struct { Page int `json:"page" yaml:"page"` Limit int `json:"limit" yaml:"limit"` DepositDenom string `json:"deposit_denom" yaml:"deposit_denom"` Owner sdk.AccAddress `json:"owner" yaml:"owner"` } -// NewQueryDepositParams creates a new QueryDepositParams -func NewQueryDepositParams(page, limit int, depositDenom string, owner sdk.AccAddress) QueryDepositParams { - return QueryDepositParams{ +// NewQueryDepositsParams creates a new QueryDepositsParams +func NewQueryDepositsParams(page, limit int, depositDenom string, owner sdk.AccAddress) QueryDepositsParams { + return QueryDepositsParams{ Page: page, Limit: limit, DepositDenom: depositDenom,