2022-07-20 22:57:56 +00:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-07-28 16:50:59 +00:00
|
|
|
"fmt"
|
2022-07-20 22:57:56 +00:00
|
|
|
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
|
2022-07-28 16:50:59 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/store/prefix"
|
2022-07-20 22:57:56 +00:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2022-07-28 16:50:59 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/types/query"
|
2022-07-20 22:57:56 +00:00
|
|
|
|
|
|
|
"github.com/kava-labs/kava/x/earn/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type queryServer struct {
|
|
|
|
keeper Keeper
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewQueryServerImpl creates a new server for handling gRPC queries.
|
|
|
|
func NewQueryServerImpl(k Keeper) types.QueryServer {
|
|
|
|
return &queryServer{keeper: k}
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.QueryServer = queryServer{}
|
|
|
|
|
|
|
|
// Params implements the gRPC service handler for querying x/earn parameters.
|
2022-07-28 16:50:59 +00:00
|
|
|
func (s queryServer) Params(
|
|
|
|
ctx context.Context,
|
|
|
|
req *types.QueryParamsRequest,
|
|
|
|
) (*types.QueryParamsResponse, error) {
|
2022-07-20 22:57:56 +00:00
|
|
|
if req == nil {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
params := s.keeper.GetParams(sdkCtx)
|
|
|
|
|
|
|
|
return &types.QueryParamsResponse{Params: params}, nil
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
|
|
|
// Vaults implements the gRPC service handler for querying x/earn vaults.
|
|
|
|
func (s queryServer) Vaults(
|
|
|
|
ctx context.Context,
|
|
|
|
req *types.QueryVaultsRequest,
|
|
|
|
) (*types.QueryVaultsResponse, error) {
|
|
|
|
if req == nil {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
|
|
|
|
vaults := []types.VaultResponse{}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
var vaultRecordsErr error
|
|
|
|
|
|
|
|
// Iterate over vault records instead of AllowedVaults to get all bkava-*
|
|
|
|
// vaults
|
|
|
|
s.keeper.IterateVaultRecords(sdkCtx, func(record types.VaultRecord) bool {
|
|
|
|
allowedVault, found := s.keeper.GetAllowedVault(sdkCtx, record.TotalShares.Denom)
|
2022-09-12 16:23:26 +00:00
|
|
|
if !found {
|
2022-09-12 17:50:35 +00:00
|
|
|
vaultRecordsErr = fmt.Errorf("vault record not found for vault record denom %s", record.TotalShares.Denom)
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
totalValue, err := s.keeper.GetVaultTotalValue(sdkCtx, record.TotalShares.Denom)
|
2022-07-28 16:50:59 +00:00
|
|
|
if err != nil {
|
2022-09-12 17:50:35 +00:00
|
|
|
vaultRecordsErr = err
|
|
|
|
// Stop iterating if error
|
|
|
|
return true
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vaults = append(vaults, types.VaultResponse{
|
2022-09-12 17:50:35 +00:00
|
|
|
Denom: record.TotalShares.Denom,
|
2022-09-12 17:29:35 +00:00
|
|
|
Strategies: allowedVault.Strategies,
|
|
|
|
IsPrivateVault: allowedVault.IsPrivateVault,
|
|
|
|
AllowedDepositors: addressSliceToStringSlice(allowedVault.AllowedDepositors),
|
2022-09-12 17:50:35 +00:00
|
|
|
TotalShares: record.TotalShares.Amount.String(),
|
2022-09-12 17:29:35 +00:00
|
|
|
TotalValue: totalValue.Amount,
|
2022-07-28 16:50:59 +00:00
|
|
|
})
|
2022-09-12 17:50:35 +00:00
|
|
|
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
if vaultRecordsErr != nil {
|
|
|
|
return nil, vaultRecordsErr
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
// Does not include vaults that have no deposits, only iterates over vault
|
|
|
|
// records which exists only for those with deposits.
|
2022-07-28 16:50:59 +00:00
|
|
|
return &types.QueryVaultsResponse{
|
|
|
|
Vaults: vaults,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
// Vaults implements the gRPC service handler for querying x/earn vaults.
|
|
|
|
func (s queryServer) Vault(
|
|
|
|
ctx context.Context,
|
|
|
|
req *types.QueryVaultRequest,
|
|
|
|
) (*types.QueryVaultResponse, error) {
|
|
|
|
if req == nil {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
|
|
|
|
if req.Denom == "" {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, "empty denom")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only 1 vault
|
|
|
|
allowedVault, found := s.keeper.GetAllowedVault(sdkCtx, req.Denom)
|
|
|
|
if !found {
|
|
|
|
return nil, status.Errorf(codes.NotFound, "vault not found with specified denom")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must be req.Denom and not allowedVault.Denom to get full "bkava" denom
|
|
|
|
vaultRecord, found := s.keeper.GetVaultRecord(sdkCtx, req.Denom)
|
|
|
|
if !found {
|
|
|
|
// No supply yet, no error just set it to zero
|
|
|
|
vaultRecord.TotalShares = types.NewVaultShare(req.Denom, sdk.ZeroDec())
|
|
|
|
}
|
|
|
|
|
|
|
|
totalValue, err := s.keeper.GetVaultTotalValue(sdkCtx, req.Denom)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
vault := types.VaultResponse{
|
|
|
|
// VaultRecord denom instead of AllowedVault.Denom for full bkava denom
|
|
|
|
Denom: vaultRecord.TotalShares.Denom,
|
|
|
|
Strategies: allowedVault.Strategies,
|
|
|
|
IsPrivateVault: allowedVault.IsPrivateVault,
|
|
|
|
AllowedDepositors: addressSliceToStringSlice(allowedVault.AllowedDepositors),
|
|
|
|
TotalShares: vaultRecord.TotalShares.Amount.String(),
|
|
|
|
TotalValue: totalValue.Amount,
|
|
|
|
}
|
|
|
|
|
|
|
|
return &types.QueryVaultResponse{
|
|
|
|
Vault: vault,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-07-28 16:50:59 +00:00
|
|
|
// Deposits implements the gRPC service handler for querying x/earn deposits.
|
|
|
|
func (s queryServer) Deposits(
|
|
|
|
ctx context.Context,
|
|
|
|
req *types.QueryDepositsRequest,
|
|
|
|
) (*types.QueryDepositsResponse, error) {
|
|
|
|
if req == nil {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
|
|
|
|
// 1. Specific account and specific vault
|
2022-09-12 17:50:35 +00:00
|
|
|
if req.Depositor != "" && req.Denom != "" {
|
2022-09-12 16:23:26 +00:00
|
|
|
return s.getAccountVaultDeposit(sdkCtx, req)
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// 2. All accounts, specific vault
|
2022-09-12 17:50:35 +00:00
|
|
|
if req.Depositor == "" && req.Denom != "" {
|
2022-09-12 16:23:26 +00:00
|
|
|
return s.getVaultAllDeposits(sdkCtx, req)
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// 3. Specific account, all vaults
|
2022-09-12 17:50:35 +00:00
|
|
|
if req.Depositor != "" && req.Denom == "" {
|
2022-09-12 16:23:26 +00:00
|
|
|
return s.getAccountAllDeposits(sdkCtx, req)
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// 4. All accounts, all vaults
|
|
|
|
return s.getAllDeposits(sdkCtx, req)
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// getAccountVaultDeposit returns deposits for a specific vault and a specific
|
|
|
|
// account
|
|
|
|
func (s queryServer) getAccountVaultDeposit(
|
|
|
|
ctx sdk.Context,
|
|
|
|
req *types.QueryDepositsRequest,
|
|
|
|
) (*types.QueryDepositsResponse, error) {
|
2022-09-12 17:50:35 +00:00
|
|
|
depositor, err := sdk.AccAddressFromBech32(req.Depositor)
|
2022-09-12 16:23:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, status.Error(codes.InvalidArgument, "Invalid address")
|
|
|
|
}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
shareRecord, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
2022-09-12 16:23:26 +00:00
|
|
|
if !found {
|
|
|
|
return nil, status.Error(codes.NotFound, "No deposit found for owner")
|
|
|
|
}
|
|
|
|
|
|
|
|
if shareRecord.Shares.AmountOf(req.Denom).IsZero() {
|
|
|
|
return nil, status.Error(codes.NotFound, fmt.Sprintf("No deposit for denom %s found for owner", req.Denom))
|
|
|
|
}
|
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
value, err := getAccountValue(ctx, s.keeper, depositor, shareRecord.Shares)
|
2022-09-12 16:23:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &types.QueryDepositsResponse{
|
|
|
|
Deposits: []types.DepositResponse{
|
|
|
|
{
|
2022-09-12 17:50:35 +00:00
|
|
|
Depositor: depositor.String(),
|
2022-09-12 16:23:26 +00:00
|
|
|
Shares: shareRecord.Shares,
|
|
|
|
Value: value,
|
2022-07-28 16:50:59 +00:00
|
|
|
},
|
2022-09-12 16:23:26 +00:00
|
|
|
},
|
|
|
|
Pagination: nil,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getVaultAllDeposits returns all deposits for a specific vault
|
|
|
|
func (s queryServer) getVaultAllDeposits(
|
|
|
|
ctx sdk.Context,
|
|
|
|
req *types.QueryDepositsRequest,
|
|
|
|
) (*types.QueryDepositsResponse, error) {
|
|
|
|
_, found := s.keeper.GetVaultRecord(ctx, req.Denom)
|
|
|
|
if !found {
|
|
|
|
return nil, status.Error(codes.NotFound, "Vault record for denom not found")
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
deposits := []types.DepositResponse{}
|
|
|
|
store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix)
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
pageRes, err := query.FilteredPaginate(
|
|
|
|
store,
|
|
|
|
req.Pagination,
|
|
|
|
func(key []byte, value []byte, accumulate bool) (bool, error) {
|
|
|
|
var record types.VaultShareRecord
|
|
|
|
err := s.keeper.cdc.Unmarshal(value, &record)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only those that have amount of requested denom
|
|
|
|
if record.Shares.AmountOf(req.Denom).IsZero() {
|
|
|
|
// inform paginate that there was no match on this key
|
|
|
|
return false, nil
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
if accumulate {
|
|
|
|
accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares)
|
2022-07-28 16:50:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// only add to results if paginate tells us to
|
|
|
|
deposits = append(deposits, types.DepositResponse{
|
|
|
|
Depositor: record.Depositor.String(),
|
|
|
|
Shares: record.Shares,
|
|
|
|
Value: accValue,
|
|
|
|
})
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// inform paginate that were was a match on this key
|
|
|
|
return true, nil
|
|
|
|
},
|
|
|
|
)
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
return &types.QueryDepositsResponse{
|
|
|
|
Deposits: deposits,
|
|
|
|
Pagination: pageRes,
|
|
|
|
}, nil
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// getAccountAllDeposits returns deposits for all vaults for a specific account
|
|
|
|
func (s queryServer) getAccountAllDeposits(
|
|
|
|
ctx sdk.Context,
|
|
|
|
req *types.QueryDepositsRequest,
|
|
|
|
) (*types.QueryDepositsResponse, error) {
|
2022-09-12 17:50:35 +00:00
|
|
|
depositor, err := sdk.AccAddressFromBech32(req.Depositor)
|
2022-09-12 16:23:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, status.Error(codes.InvalidArgument, "Invalid address")
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
deposits := []types.DepositResponse{}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
accountShare, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
2022-09-12 16:23:26 +00:00
|
|
|
if !found {
|
2022-09-12 17:50:35 +00:00
|
|
|
return nil, status.Error(codes.NotFound, "No deposit found for depositor")
|
2022-09-12 16:23:26 +00:00
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 17:50:35 +00:00
|
|
|
value, err := getAccountValue(ctx, s.keeper, depositor, accountShare.Shares)
|
2022-09-12 16:23:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
deposits = append(deposits, types.DepositResponse{
|
2022-09-12 17:50:35 +00:00
|
|
|
Depositor: depositor.String(),
|
2022-09-12 16:23:26 +00:00
|
|
|
Shares: accountShare.Shares,
|
|
|
|
Value: value,
|
|
|
|
})
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
return &types.QueryDepositsResponse{
|
|
|
|
Deposits: deposits,
|
|
|
|
Pagination: nil,
|
|
|
|
}, nil
|
|
|
|
}
|
2022-07-28 16:50:59 +00:00
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
// getAllDeposits returns all deposits for all vaults
|
|
|
|
func (s queryServer) getAllDeposits(
|
|
|
|
ctx sdk.Context,
|
|
|
|
req *types.QueryDepositsRequest,
|
|
|
|
) (*types.QueryDepositsResponse, error) {
|
2022-07-28 16:50:59 +00:00
|
|
|
deposits := []types.DepositResponse{}
|
2022-09-12 16:23:26 +00:00
|
|
|
store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix)
|
2022-07-28 16:50:59 +00:00
|
|
|
|
|
|
|
pageRes, err := query.Paginate(
|
|
|
|
store,
|
|
|
|
req.Pagination,
|
|
|
|
func(key []byte, value []byte) error {
|
|
|
|
var record types.VaultShareRecord
|
|
|
|
err := s.keeper.cdc.Unmarshal(value, &record)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares)
|
2022-07-28 16:50:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// only add to results if paginate tells us to
|
|
|
|
deposits = append(deposits, types.DepositResponse{
|
2022-09-12 16:23:26 +00:00
|
|
|
Depositor: record.Depositor.String(),
|
|
|
|
Shares: record.Shares,
|
|
|
|
Value: accValue,
|
2022-07-28 16:50:59 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &types.QueryDepositsResponse{
|
|
|
|
Deposits: deposits,
|
|
|
|
Pagination: pageRes,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getAccountValue(
|
|
|
|
ctx sdk.Context,
|
|
|
|
keeper Keeper,
|
|
|
|
account sdk.AccAddress,
|
2022-09-12 16:23:26 +00:00
|
|
|
shares types.VaultShares,
|
2022-07-28 16:50:59 +00:00
|
|
|
) (sdk.Coins, error) {
|
|
|
|
value := sdk.NewCoins()
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
for _, share := range shares {
|
|
|
|
accValue, err := keeper.GetVaultAccountValue(ctx, share.Denom, account)
|
2022-07-28 16:50:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-12 16:23:26 +00:00
|
|
|
value = value.Add(sdk.NewCoin(share.Denom, accValue.Amount))
|
2022-07-28 16:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return value, nil
|
|
|
|
}
|
2022-09-12 17:29:35 +00:00
|
|
|
|
|
|
|
func addressSliceToStringSlice(addresses []sdk.AccAddress) []string {
|
|
|
|
var strings []string
|
|
|
|
for _, address := range addresses {
|
|
|
|
strings = append(strings, address.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings
|
|
|
|
}
|