mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
Add specific /vaults/bkava
and /deposits
query handler to get aggregated bkava amounts (#1293)
* Use custom aggregate handler for querying 'bkava' vault * Add 3rd bkava vault * Add special kava deposit handlers * Separate bkava logic to parent deposits handler * Rename single vault/account queries * Remove all deposits queries * Include empty vaults in /vaults query * Respond with empty values when querying account deposits with no deposits * return ukava value in bkava vault queries * remove refernce to specific staked token denom * return ukava value in bkava deposit queries Co-authored-by: rhuairahrighairigh <ruaridh.odonnell@gmail.com>
This commit is contained in:
parent
ed116b24ba
commit
9fb64b1f11
@ -615,6 +615,7 @@ func NewApp(
|
|||||||
earnSubspace,
|
earnSubspace,
|
||||||
app.accountKeeper,
|
app.accountKeeper,
|
||||||
app.bankKeeper,
|
app.bankKeeper,
|
||||||
|
app.liquidKeeper,
|
||||||
&hardKeeper,
|
&hardKeeper,
|
||||||
&savingsKeeper,
|
&savingsKeeper,
|
||||||
)
|
)
|
||||||
|
@ -170,7 +170,7 @@ func (suite *depositTestSuite) TestDeposit_PrivateVault() {
|
|||||||
|
|
||||||
func (suite *depositTestSuite) TestDeposit_bKava() {
|
func (suite *depositTestSuite) TestDeposit_bKava() {
|
||||||
vaultDenom := "bkava"
|
vaultDenom := "bkava"
|
||||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
coinDenom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||||
|
@ -3,13 +3,12 @@ package keeper
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/types/query"
|
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/earn/types"
|
"github.com/kava-labs/kava/x/earn/types"
|
||||||
)
|
)
|
||||||
@ -51,6 +50,14 @@ func (s queryServer) Vaults(
|
|||||||
|
|
||||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||||
|
|
||||||
|
allowedVaults := s.keeper.GetAllowedVaults(sdkCtx)
|
||||||
|
allowedVaultsMap := make(map[string]types.AllowedVault)
|
||||||
|
visitedMap := make(map[string]bool)
|
||||||
|
for _, av := range allowedVaults {
|
||||||
|
allowedVaultsMap[av.Denom] = av
|
||||||
|
visitedMap[av.Denom] = false
|
||||||
|
}
|
||||||
|
|
||||||
vaults := []types.VaultResponse{}
|
vaults := []types.VaultResponse{}
|
||||||
|
|
||||||
var vaultRecordsErr error
|
var vaultRecordsErr error
|
||||||
@ -58,9 +65,16 @@ func (s queryServer) Vaults(
|
|||||||
// Iterate over vault records instead of AllowedVaults to get all bkava-*
|
// Iterate over vault records instead of AllowedVaults to get all bkava-*
|
||||||
// vaults
|
// vaults
|
||||||
s.keeper.IterateVaultRecords(sdkCtx, func(record types.VaultRecord) bool {
|
s.keeper.IterateVaultRecords(sdkCtx, func(record types.VaultRecord) bool {
|
||||||
allowedVault, found := s.keeper.GetAllowedVault(sdkCtx, record.TotalShares.Denom)
|
// Check if bkava, use allowed vault
|
||||||
|
allowedVaultDenom := record.TotalShares.Denom
|
||||||
|
if strings.HasPrefix(record.TotalShares.Denom, bkavaPrefix) {
|
||||||
|
allowedVaultDenom = bkavaDenom
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedVault, found := allowedVaultsMap[allowedVaultDenom]
|
||||||
if !found {
|
if !found {
|
||||||
vaultRecordsErr = fmt.Errorf("vault record not found for vault record denom %s", record.TotalShares.Denom)
|
vaultRecordsErr = fmt.Errorf("vault record not found for vault record denom %s", record.TotalShares.Denom)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
totalValue, err := s.keeper.GetVaultTotalValue(sdkCtx, record.TotalShares.Denom)
|
totalValue, err := s.keeper.GetVaultTotalValue(sdkCtx, record.TotalShares.Denom)
|
||||||
@ -79,6 +93,9 @@ func (s queryServer) Vaults(
|
|||||||
TotalValue: totalValue.Amount,
|
TotalValue: totalValue.Amount,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Mark this allowed vault as visited
|
||||||
|
visitedMap[allowedVaultDenom] = true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -86,6 +103,30 @@ func (s queryServer) Vaults(
|
|||||||
return nil, vaultRecordsErr
|
return nil, vaultRecordsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the allowed vaults that have not been visited yet
|
||||||
|
// These are always empty vaults, as the vault would have been visited
|
||||||
|
// earlier if there are any deposits
|
||||||
|
for denom, visited := range visitedMap {
|
||||||
|
if visited {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedVault, found := allowedVaultsMap[denom]
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("vault record not found for vault record denom %s", denom)
|
||||||
|
}
|
||||||
|
|
||||||
|
vaults = append(vaults, types.VaultResponse{
|
||||||
|
Denom: denom,
|
||||||
|
Strategies: allowedVault.Strategies,
|
||||||
|
IsPrivateVault: allowedVault.IsPrivateVault,
|
||||||
|
AllowedDepositors: addressSliceToStringSlice(allowedVault.AllowedDepositors),
|
||||||
|
// No shares, no value
|
||||||
|
TotalShares: sdk.ZeroDec().String(),
|
||||||
|
TotalValue: sdk.ZeroInt(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Does not include vaults that have no deposits, only iterates over vault
|
// Does not include vaults that have no deposits, only iterates over vault
|
||||||
// records which exists only for those with deposits.
|
// records which exists only for those with deposits.
|
||||||
return &types.QueryVaultsResponse{
|
return &types.QueryVaultsResponse{
|
||||||
@ -114,6 +155,11 @@ func (s queryServer) Vault(
|
|||||||
return nil, status.Errorf(codes.NotFound, "vault not found with specified denom")
|
return nil, status.Errorf(codes.NotFound, "vault not found with specified denom")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle bkava separately to get total of **all** bkava vaults
|
||||||
|
if req.Denom == "bkava" {
|
||||||
|
return s.getAggregateBkavaVault(sdkCtx, allowedVault)
|
||||||
|
}
|
||||||
|
|
||||||
// Must be req.Denom and not allowedVault.Denom to get full "bkava" denom
|
// Must be req.Denom and not allowedVault.Denom to get full "bkava" denom
|
||||||
vaultRecord, found := s.keeper.GetVaultRecord(sdkCtx, req.Denom)
|
vaultRecord, found := s.keeper.GetVaultRecord(sdkCtx, req.Denom)
|
||||||
if !found {
|
if !found {
|
||||||
@ -141,6 +187,54 @@ func (s queryServer) Vault(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getAggregateBkavaVault returns a VaultResponse of the total of all bkava
|
||||||
|
// vaults.
|
||||||
|
func (s queryServer) getAggregateBkavaVault(
|
||||||
|
ctx sdk.Context,
|
||||||
|
allowedVault types.AllowedVault,
|
||||||
|
) (*types.QueryVaultResponse, error) {
|
||||||
|
allBkava := sdk.NewCoins()
|
||||||
|
|
||||||
|
var iterErr error
|
||||||
|
s.keeper.IterateVaultRecords(ctx, func(record types.VaultRecord) (stop bool) {
|
||||||
|
// Skip non bkava vaults
|
||||||
|
if !strings.HasPrefix(record.TotalShares.Denom, "bkava") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
vaultValue, err := s.keeper.GetVaultTotalValue(ctx, record.TotalShares.Denom)
|
||||||
|
if err != nil {
|
||||||
|
iterErr = err
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
allBkava = allBkava.Add(vaultValue)
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if iterErr != nil {
|
||||||
|
return nil, iterErr
|
||||||
|
}
|
||||||
|
|
||||||
|
vaultValue, err := s.keeper.liquidKeeper.GetStakedTokensForDerivatives(ctx, allBkava)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.QueryVaultResponse{
|
||||||
|
Vault: types.VaultResponse{
|
||||||
|
Denom: "bkava",
|
||||||
|
Strategies: allowedVault.Strategies,
|
||||||
|
IsPrivateVault: allowedVault.IsPrivateVault,
|
||||||
|
AllowedDepositors: addressSliceToStringSlice(allowedVault.AllowedDepositors),
|
||||||
|
// Empty for shares, as adding up all shares is not useful information
|
||||||
|
TotalShares: "0",
|
||||||
|
TotalValue: vaultValue.Amount,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Deposits implements the gRPC service handler for querying x/earn deposits.
|
// Deposits implements the gRPC service handler for querying x/earn deposits.
|
||||||
func (s queryServer) Deposits(
|
func (s queryServer) Deposits(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -150,30 +244,29 @@ func (s queryServer) Deposits(
|
|||||||
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if req.Depositor == "" {
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "depositor is required")
|
||||||
|
}
|
||||||
|
|
||||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||||
|
|
||||||
// 1. Specific account and specific vault
|
// bkava aggregate total
|
||||||
if req.Depositor != "" && req.Denom != "" {
|
if req.Denom == "bkava" {
|
||||||
return s.getAccountVaultDeposit(sdkCtx, req)
|
return s.getOneAccountBkavaVaultDeposit(sdkCtx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. All accounts, specific vault
|
// specific vault
|
||||||
if req.Depositor == "" && req.Denom != "" {
|
if req.Denom != "" {
|
||||||
return s.getVaultAllDeposits(sdkCtx, req)
|
return s.getOneAccountOneVaultDeposit(sdkCtx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Specific account, all vaults
|
// all vaults
|
||||||
if req.Depositor != "" && req.Denom == "" {
|
return s.getOneAccountAllDeposits(sdkCtx, req)
|
||||||
return s.getAccountAllDeposits(sdkCtx, req)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. All accounts, all vaults
|
// getOneAccountOneVaultDeposit returns deposits for a specific vault and a specific
|
||||||
return s.getAllDeposits(sdkCtx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAccountVaultDeposit returns deposits for a specific vault and a specific
|
|
||||||
// account
|
// account
|
||||||
func (s queryServer) getAccountVaultDeposit(
|
func (s queryServer) getOneAccountOneVaultDeposit(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
req *types.QueryDepositsRequest,
|
req *types.QueryDepositsRequest,
|
||||||
) (*types.QueryDepositsResponse, error) {
|
) (*types.QueryDepositsResponse, error) {
|
||||||
@ -184,90 +277,95 @@ func (s queryServer) getAccountVaultDeposit(
|
|||||||
|
|
||||||
shareRecord, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
shareRecord, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
||||||
if !found {
|
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := getAccountValue(ctx, s.keeper, depositor, shareRecord.Shares)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.QueryDepositsResponse{
|
return &types.QueryDepositsResponse{
|
||||||
Deposits: []types.DepositResponse{
|
Deposits: []types.DepositResponse{
|
||||||
{
|
{
|
||||||
Depositor: depositor.String(),
|
Depositor: depositor.String(),
|
||||||
Shares: shareRecord.Shares,
|
// Zero shares and zero value for no deposits
|
||||||
Value: value,
|
Shares: types.NewVaultShares(types.NewVaultShare(req.Denom, sdk.ZeroDec())),
|
||||||
|
Value: sdk.NewCoins(sdk.NewCoin(req.Denom, sdk.ZeroInt())),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Pagination: nil,
|
Pagination: nil,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVaultAllDeposits returns all deposits for a specific vault
|
// Only requesting the value of the specified denom
|
||||||
func (s queryServer) getVaultAllDeposits(
|
value, err := s.keeper.GetVaultAccountValue(ctx, req.Denom, depositor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.NotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.QueryDepositsResponse{
|
||||||
|
Deposits: []types.DepositResponse{
|
||||||
|
{
|
||||||
|
Depositor: depositor.String(),
|
||||||
|
// Only respond with requested denom shares
|
||||||
|
Shares: types.NewVaultShares(
|
||||||
|
types.NewVaultShare(req.Denom, shareRecord.Shares.AmountOf(req.Denom)),
|
||||||
|
),
|
||||||
|
Value: sdk.NewCoins(value),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pagination: nil,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOneAccountBkavaVaultDeposit returns deposits for the aggregated bkava vault
|
||||||
|
// and a specific account
|
||||||
|
func (s queryServer) getOneAccountBkavaVaultDeposit(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
req *types.QueryDepositsRequest,
|
req *types.QueryDepositsRequest,
|
||||||
) (*types.QueryDepositsResponse, error) {
|
) (*types.QueryDepositsResponse, error) {
|
||||||
_, found := s.keeper.GetVaultRecord(ctx, req.Denom)
|
depositor, err := sdk.AccAddressFromBech32(req.Depositor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "Invalid address")
|
||||||
|
}
|
||||||
|
|
||||||
|
shareRecord, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, status.Error(codes.NotFound, "Vault record for denom not found")
|
return &types.QueryDepositsResponse{
|
||||||
}
|
Deposits: []types.DepositResponse{
|
||||||
|
{
|
||||||
deposits := []types.DepositResponse{}
|
Depositor: depositor.String(),
|
||||||
store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix)
|
// Zero shares and zero value for no deposits
|
||||||
|
Shares: types.NewVaultShares(types.NewVaultShare(req.Denom, sdk.ZeroDec())),
|
||||||
pageRes, err := query.FilteredPaginate(
|
Value: sdk.NewCoins(sdk.NewCoin(req.Denom, sdk.ZeroInt())),
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
if accumulate {
|
|
||||||
accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add to results if paginate tells us to
|
|
||||||
deposits = append(deposits, types.DepositResponse{
|
|
||||||
Depositor: record.Depositor.String(),
|
|
||||||
Shares: record.Shares,
|
|
||||||
Value: accValue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// inform paginate that were was a match on this key
|
|
||||||
return true, nil
|
|
||||||
},
|
},
|
||||||
)
|
},
|
||||||
|
Pagination: nil,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all account deposit values to add up bkava
|
||||||
|
totalAccountValue, err := getAccountTotalValue(ctx, s.keeper, depositor, shareRecord.Shares)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use account value with only the aggregate bkava converted to underlying staked tokens
|
||||||
|
stakedValue, err := s.keeper.liquidKeeper.GetStakedTokensForDerivatives(ctx, totalAccountValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.QueryDepositsResponse{
|
return &types.QueryDepositsResponse{
|
||||||
Deposits: deposits,
|
Deposits: []types.DepositResponse{
|
||||||
Pagination: pageRes,
|
{
|
||||||
|
Depositor: depositor.String(),
|
||||||
|
// Only respond with requested denom shares
|
||||||
|
Shares: types.NewVaultShares(
|
||||||
|
types.NewVaultShare(req.Denom, shareRecord.Shares.AmountOf(req.Denom)),
|
||||||
|
),
|
||||||
|
Value: sdk.NewCoins(stakedValue),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pagination: nil,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAccountAllDeposits returns deposits for all vaults for a specific account
|
// getOneAccountAllDeposits returns deposits for all vaults for a specific account
|
||||||
func (s queryServer) getAccountAllDeposits(
|
func (s queryServer) getOneAccountAllDeposits(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
req *types.QueryDepositsRequest,
|
req *types.QueryDepositsRequest,
|
||||||
) (*types.QueryDepositsResponse, error) {
|
) (*types.QueryDepositsResponse, error) {
|
||||||
@ -280,10 +378,13 @@ func (s queryServer) getAccountAllDeposits(
|
|||||||
|
|
||||||
accountShare, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
accountShare, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
||||||
if !found {
|
if !found {
|
||||||
return nil, status.Error(codes.NotFound, "No deposit found for depositor")
|
return &types.QueryDepositsResponse{
|
||||||
|
Deposits: []types.DepositResponse{},
|
||||||
|
Pagination: nil,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := getAccountValue(ctx, s.keeper, depositor, accountShare.Shares)
|
value, err := getAccountTotalValue(ctx, s.keeper, depositor, accountShare.Shares)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||||
}
|
}
|
||||||
@ -300,51 +401,9 @@ func (s queryServer) getAccountAllDeposits(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllDeposits returns all deposits for all vaults
|
// getAccountTotalValue returns the total value for all vaults for a specific
|
||||||
func (s queryServer) getAllDeposits(
|
// account based on their shares.
|
||||||
ctx sdk.Context,
|
func getAccountTotalValue(
|
||||||
req *types.QueryDepositsRequest,
|
|
||||||
) (*types.QueryDepositsResponse, error) {
|
|
||||||
deposits := []types.DepositResponse{}
|
|
||||||
store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
accValue, err := getAccountValue(ctx, s.keeper, record.Depositor, record.Shares)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// only add to results if paginate tells us to
|
|
||||||
deposits = append(deposits, types.DepositResponse{
|
|
||||||
Depositor: record.Depositor.String(),
|
|
||||||
Shares: record.Shares,
|
|
||||||
Value: accValue,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &types.QueryDepositsResponse{
|
|
||||||
Deposits: deposits,
|
|
||||||
Pagination: pageRes,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAccountValue(
|
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
keeper Keeper,
|
keeper Keeper,
|
||||||
account sdk.AccAddress,
|
account sdk.AccAddress,
|
||||||
|
@ -5,14 +5,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
|
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||||
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/earn/keeper"
|
"github.com/kava-labs/kava/x/earn/keeper"
|
||||||
"github.com/kava-labs/kava/x/earn/testutil"
|
"github.com/kava-labs/kava/x/earn/testutil"
|
||||||
"github.com/kava-labs/kava/x/earn/types"
|
"github.com/kava-labs/kava/x/earn/types"
|
||||||
"github.com/stretchr/testify/suite"
|
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type grpcQueryTestSuite struct {
|
type grpcQueryTestSuite struct {
|
||||||
@ -81,13 +86,32 @@ func (suite *grpcQueryTestSuite) TestVaults_ZeroSupply() {
|
|||||||
suite.Run("all", func() {
|
suite.Run("all", func() {
|
||||||
res, err := suite.queryClient.Vaults(context.Background(), types.NewQueryVaultsRequest())
|
res, err := suite.queryClient.Vaults(context.Background(), types.NewQueryVaultsRequest())
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Require().Empty(res.Vaults)
|
suite.Require().ElementsMatch([]types.VaultResponse{
|
||||||
|
{
|
||||||
|
Denom: "usdx",
|
||||||
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
TotalShares: sdk.ZeroDec().String(),
|
||||||
|
TotalValue: sdk.ZeroInt(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Denom: "busd",
|
||||||
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
TotalShares: sdk.ZeroDec().String(),
|
||||||
|
TotalValue: sdk.ZeroInt(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res.Vaults,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *grpcQueryTestSuite) TestVaults_WithSupply() {
|
func (suite *grpcQueryTestSuite) TestVaults_WithSupply() {
|
||||||
vaultDenom := "usdx"
|
vaultDenom := "usdx"
|
||||||
vault2Denom := "bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
vault2Denom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||||
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 100)
|
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 100)
|
||||||
@ -132,6 +156,60 @@ func (suite *grpcQueryTestSuite) TestVaults_WithSupply() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *grpcQueryTestSuite) TestVaults_MixedSupply() {
|
||||||
|
vaultDenom := "usdx"
|
||||||
|
vault2Denom := "busd"
|
||||||
|
vault3Denom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
|
depositAmount := sdk.NewInt64Coin(vault3Denom, 100)
|
||||||
|
|
||||||
|
suite.CreateVault(vaultDenom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
||||||
|
suite.CreateVault(vault2Denom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
||||||
|
suite.CreateVault("bkava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil)
|
||||||
|
|
||||||
|
acc := suite.CreateAccount(sdk.NewCoins(
|
||||||
|
sdk.NewInt64Coin(vaultDenom, 1000),
|
||||||
|
sdk.NewInt64Coin(vault2Denom, 1000),
|
||||||
|
sdk.NewInt64Coin(vault3Denom, 1000),
|
||||||
|
), 0)
|
||||||
|
|
||||||
|
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
res, err := suite.queryClient.Vaults(context.Background(), types.NewQueryVaultsRequest())
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Len(res.Vaults, 3)
|
||||||
|
suite.Require().ElementsMatch(
|
||||||
|
[]types.VaultResponse{
|
||||||
|
{
|
||||||
|
Denom: vaultDenom,
|
||||||
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
TotalShares: sdk.ZeroDec().String(),
|
||||||
|
TotalValue: sdk.ZeroInt(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Denom: vault2Denom,
|
||||||
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
TotalShares: sdk.ZeroDec().String(),
|
||||||
|
TotalValue: sdk.ZeroInt(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Denom: vault3Denom,
|
||||||
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_SAVINGS},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: nil,
|
||||||
|
TotalShares: depositAmount.Amount.ToDec().String(),
|
||||||
|
TotalValue: depositAmount.Amount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res.Vaults,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *grpcQueryTestSuite) TestVault_NotFound() {
|
func (suite *grpcQueryTestSuite) TestVault_NotFound() {
|
||||||
_, err := suite.queryClient.Vault(context.Background(), types.NewQueryVaultRequest("usdx"))
|
_, err := suite.queryClient.Vault(context.Background(), types.NewQueryVaultRequest("usdx"))
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
@ -141,7 +219,7 @@ func (suite *grpcQueryTestSuite) TestVault_NotFound() {
|
|||||||
func (suite *grpcQueryTestSuite) TestDeposits() {
|
func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||||
vault1Denom := "usdx"
|
vault1Denom := "usdx"
|
||||||
vault2Denom := "busd"
|
vault2Denom := "busd"
|
||||||
vault3Denom := "bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
vault3Denom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
// Add vaults
|
// Add vaults
|
||||||
suite.CreateVault(vault1Denom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
suite.CreateVault(vault1Denom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
||||||
@ -153,6 +231,7 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
sdk.NewInt64Coin(vault2Denom, 1000),
|
sdk.NewInt64Coin(vault2Denom, 1000),
|
||||||
sdk.NewInt64Coin(vault3Denom, 1000),
|
sdk.NewInt64Coin(vault3Denom, 1000),
|
||||||
)
|
)
|
||||||
|
|
||||||
deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100)
|
deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100)
|
||||||
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 200)
|
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 200)
|
||||||
deposit3Amount := sdk.NewInt64Coin(vault3Denom, 200)
|
deposit3Amount := sdk.NewInt64Coin(vault3Denom, 200)
|
||||||
@ -163,7 +242,7 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
|
|
||||||
// Deposit into each vault from each account - 4 total deposits
|
// Deposit into each vault from each account - 4 total deposits
|
||||||
// Acc 1: usdx + busd
|
// Acc 1: usdx + busd
|
||||||
// Acc 2: usdx + usdc
|
// Acc 2: usdx + bkava
|
||||||
err := suite.Keeper.Deposit(suite.Ctx, acc1, deposit1Amount, types.STRATEGY_TYPE_HARD)
|
err := suite.Keeper.Deposit(suite.Ctx, acc1, deposit1Amount, types.STRATEGY_TYPE_HARD)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
err = suite.Keeper.Deposit(suite.Ctx, acc1, deposit2Amount, types.STRATEGY_TYPE_HARD)
|
err = suite.Keeper.Deposit(suite.Ctx, acc1, deposit2Amount, types.STRATEGY_TYPE_HARD)
|
||||||
@ -174,7 +253,7 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
err = suite.Keeper.Deposit(suite.Ctx, acc2, deposit3Amount, types.STRATEGY_TYPE_SAVINGS)
|
err = suite.Keeper.Deposit(suite.Ctx, acc2, deposit3Amount, types.STRATEGY_TYPE_SAVINGS)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
suite.Run("1) 1 vault for 1 account", func() {
|
suite.Run("specific vault", func() {
|
||||||
// Query all deposits for account 1
|
// Query all deposits for account 1
|
||||||
res, err := suite.queryClient.Deposits(
|
res, err := suite.queryClient.Deposits(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@ -186,12 +265,12 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
[]types.DepositResponse{
|
[]types.DepositResponse{
|
||||||
{
|
{
|
||||||
Depositor: acc1.String(),
|
Depositor: acc1.String(),
|
||||||
// Still includes all deposits
|
// Only includes specified deposit shares
|
||||||
Shares: types.NewVaultShares(
|
Shares: types.NewVaultShares(
|
||||||
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
||||||
types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()),
|
|
||||||
),
|
),
|
||||||
Value: sdk.NewCoins(deposit1Amount, deposit2Amount),
|
// Only the specified vault denom value
|
||||||
|
Value: sdk.NewCoins(deposit1Amount),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res.Deposits,
|
res.Deposits,
|
||||||
@ -200,16 +279,41 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
suite.Run("1) invalid vault for 1 account", func() {
|
suite.Run("specific bkava vault", func() {
|
||||||
|
res, err := suite.queryClient.Deposits(
|
||||||
|
context.Background(),
|
||||||
|
types.NewQueryDepositsRequest(acc2.String(), vault3Denom, nil),
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Len(res.Deposits, 1)
|
||||||
|
suite.Require().ElementsMatchf(
|
||||||
|
[]types.DepositResponse{
|
||||||
|
{
|
||||||
|
Depositor: acc2.String(),
|
||||||
|
// Only includes specified deposit shares
|
||||||
|
Shares: types.NewVaultShares(
|
||||||
|
types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()),
|
||||||
|
),
|
||||||
|
// Only the specified vault denom value
|
||||||
|
Value: sdk.NewCoins(deposit3Amount),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res.Deposits,
|
||||||
|
"deposits should match, got %v",
|
||||||
|
res.Deposits,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.Run("invalid vault", func() {
|
||||||
_, err := suite.queryClient.Deposits(
|
_, err := suite.queryClient.Deposits(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
types.NewQueryDepositsRequest(acc1.String(), "notavaliddenom", nil),
|
types.NewQueryDepositsRequest(acc1.String(), "notavaliddenom", nil),
|
||||||
)
|
)
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
suite.Require().ErrorIs(err, status.Errorf(codes.NotFound, "No deposit for denom notavaliddenom found for owner"))
|
suite.Require().ErrorIs(err, status.Errorf(codes.NotFound, "vault for notavaliddenom not found"))
|
||||||
})
|
})
|
||||||
|
|
||||||
suite.Run("3) all vaults for 1 account", func() {
|
suite.Run("all vaults", func() {
|
||||||
// Query all deposits for account 1
|
// Query all deposits for account 1
|
||||||
res, err := suite.queryClient.Deposits(
|
res, err := suite.queryClient.Deposits(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
@ -231,55 +335,35 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
res.Deposits,
|
res.Deposits,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
suite.Run("2) all accounts, specific vault", func() {
|
func (suite *grpcQueryTestSuite) TestDeposits_NoDeposits() {
|
||||||
// Query all deposits for vault 3
|
vault1Denom := "usdx"
|
||||||
|
vault2Denom := "busd"
|
||||||
|
|
||||||
|
// Add vaults
|
||||||
|
suite.CreateVault(vault1Denom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
||||||
|
suite.CreateVault(vault2Denom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
||||||
|
suite.CreateVault("bkava", types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS}, false, nil)
|
||||||
|
|
||||||
|
// Accounts
|
||||||
|
acc1 := suite.CreateAccount(sdk.NewCoins(), 0).GetAddress()
|
||||||
|
|
||||||
|
suite.Run("specific vault", func() {
|
||||||
|
// Query all deposits for account 1
|
||||||
res, err := suite.queryClient.Deposits(
|
res, err := suite.queryClient.Deposits(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
types.NewQueryDepositsRequest("", vault3Denom, nil),
|
types.NewQueryDepositsRequest(acc1.String(), vault1Denom, nil),
|
||||||
)
|
)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Require().Len(res.Deposits, 1)
|
suite.Require().Len(res.Deposits, 1)
|
||||||
suite.Require().ElementsMatch(
|
|
||||||
[]types.DepositResponse{
|
|
||||||
{
|
|
||||||
Depositor: acc2.String(),
|
|
||||||
Shares: types.NewVaultShares(
|
|
||||||
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
|
||||||
types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()),
|
|
||||||
),
|
|
||||||
Value: sdk.NewCoins(deposit1Amount, deposit3Amount),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
res.Deposits,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
suite.Run("4) all vaults and all accounts", func() {
|
|
||||||
// Query all deposits for all vaults
|
|
||||||
res, err := suite.queryClient.Deposits(
|
|
||||||
context.Background(),
|
|
||||||
types.NewQueryDepositsRequest("", "", nil),
|
|
||||||
)
|
|
||||||
suite.Require().NoError(err)
|
|
||||||
suite.Require().Len(res.Deposits, 2)
|
|
||||||
suite.Require().ElementsMatchf(
|
suite.Require().ElementsMatchf(
|
||||||
[]types.DepositResponse{
|
[]types.DepositResponse{
|
||||||
{
|
{
|
||||||
Depositor: acc1.String(),
|
Depositor: acc1.String(),
|
||||||
Shares: types.NewVaultShares(
|
// Zero shares and zero value
|
||||||
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
Shares: nil,
|
||||||
types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()),
|
Value: nil,
|
||||||
),
|
|
||||||
Value: sdk.NewCoins(deposit1Amount, deposit2Amount),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Depositor: acc2.String(),
|
|
||||||
Shares: types.NewVaultShares(
|
|
||||||
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
|
||||||
types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()),
|
|
||||||
),
|
|
||||||
Value: sdk.NewCoins(deposit1Amount, deposit3Amount),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
res.Deposits,
|
res.Deposits,
|
||||||
@ -287,15 +371,25 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
|||||||
res.Deposits,
|
res.Deposits,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
suite.Run("all vaults", func() {
|
||||||
|
// Query all deposits for account 1
|
||||||
|
res, err := suite.queryClient.Deposits(
|
||||||
|
context.Background(),
|
||||||
|
types.NewQueryDepositsRequest(acc1.String(), "", nil),
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Empty(res.Deposits)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *grpcQueryTestSuite) TestDeposits_NotFound() {
|
func (suite *grpcQueryTestSuite) TestDeposits_NoDepositor() {
|
||||||
_, err := suite.queryClient.Deposits(
|
_, err := suite.queryClient.Deposits(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
types.NewQueryDepositsRequest("", "usdx", nil),
|
types.NewQueryDepositsRequest("", "usdx", nil),
|
||||||
)
|
)
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
suite.Require().ErrorIs(err, status.Error(codes.NotFound, "Vault record for denom not found"))
|
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "depositor is required"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() {
|
func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() {
|
||||||
@ -314,9 +408,89 @@ func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() {
|
|||||||
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "Invalid address"))
|
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "Invalid address"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *grpcQueryTestSuite) TestVault_bKava() {
|
func (suite *grpcQueryTestSuite) TestDeposits_bKava() {
|
||||||
|
// vault denom is only "bkava" which has it's own special handler
|
||||||
|
suite.CreateVault(
|
||||||
|
"bkava",
|
||||||
|
types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS},
|
||||||
|
false,
|
||||||
|
[]sdk.AccAddress{},
|
||||||
|
)
|
||||||
|
|
||||||
|
address1, derivatives1, _ := suite.createAccountWithDerivatives(testutil.TestBkavaDenoms[0], sdk.NewInt(1e9))
|
||||||
|
address2, derivatives2, _ := suite.createAccountWithDerivatives(testutil.TestBkavaDenoms[1], sdk.NewInt(1e9))
|
||||||
|
|
||||||
|
// Slash the last validator to reduce the value of it's derivatives to test bkava to underlying token conversion.
|
||||||
|
// First call end block to bond validator to enable slashing.
|
||||||
|
staking.EndBlocker(suite.Ctx, suite.App.GetStakingKeeper())
|
||||||
|
err := suite.slashValidator(sdk.ValAddress(address2), sdk.MustNewDecFromStr("0.5"))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.Run("no deposits", func() {
|
||||||
|
// Query all deposits for account 1
|
||||||
|
res, err := suite.queryClient.Deposits(
|
||||||
|
context.Background(),
|
||||||
|
types.NewQueryDepositsRequest(address1.String(), "bkava", nil),
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Len(res.Deposits, 1)
|
||||||
|
suite.Require().ElementsMatchf(
|
||||||
|
[]types.DepositResponse{
|
||||||
|
{
|
||||||
|
Depositor: address1.String(),
|
||||||
|
// Zero shares for "bkava" aggregate
|
||||||
|
Shares: nil,
|
||||||
|
// Only the specified vault denom value
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res.Deposits,
|
||||||
|
"deposits should match, got %v",
|
||||||
|
res.Deposits,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
err = suite.Keeper.Deposit(suite.Ctx, address1, derivatives1, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
err = suite.BankKeeper.SendCoins(suite.Ctx, address2, address1, sdk.NewCoins(derivatives2))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
err = suite.Keeper.Deposit(suite.Ctx, address1, derivatives2, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
suite.Run("multiple deposits", func() {
|
||||||
|
// Query all deposits for account 1
|
||||||
|
res, err := suite.queryClient.Deposits(
|
||||||
|
context.Background(),
|
||||||
|
types.NewQueryDepositsRequest(address1.String(), "bkava", nil),
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Len(res.Deposits, 1)
|
||||||
|
// first validator isn't slashed, so bkava units equal to underlying staked tokens
|
||||||
|
// last validator slashed 50% so derivatives are worth half
|
||||||
|
expectedValue := derivatives1.Amount.Add(derivatives2.Amount.QuoRaw(2))
|
||||||
|
suite.Require().ElementsMatchf(
|
||||||
|
[]types.DepositResponse{
|
||||||
|
{
|
||||||
|
Depositor: address1.String(),
|
||||||
|
// Zero shares for "bkava" aggregate
|
||||||
|
Shares: nil,
|
||||||
|
// Value returned in units of staked token
|
||||||
|
Value: sdk.NewCoins(
|
||||||
|
sdk.NewCoin(suite.bondDenom(), expectedValue),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res.Deposits,
|
||||||
|
"deposits should match, got %v",
|
||||||
|
res.Deposits,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *grpcQueryTestSuite) TestVault_bKava_Single() {
|
||||||
vaultDenom := "bkava"
|
vaultDenom := "bkava"
|
||||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
coinDenom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||||
@ -356,3 +530,132 @@ func (suite *grpcQueryTestSuite) TestVault_bKava() {
|
|||||||
res.Vault,
|
res.Vault,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *grpcQueryTestSuite) TestVault_bKava_Aggregate() {
|
||||||
|
vaultDenom := "bkava"
|
||||||
|
|
||||||
|
address1, derivatives1, _ := suite.createAccountWithDerivatives(testutil.TestBkavaDenoms[0], sdk.NewInt(1e9))
|
||||||
|
address2, derivatives2, _ := suite.createAccountWithDerivatives(testutil.TestBkavaDenoms[1], sdk.NewInt(1e9))
|
||||||
|
address3, derivatives3, _ := suite.createAccountWithDerivatives(testutil.TestBkavaDenoms[2], sdk.NewInt(1e9))
|
||||||
|
// Slash the last validator to reduce the value of it's derivatives to test bkava to underlying token conversion.
|
||||||
|
// First call end block to bond validator to enable slashing.
|
||||||
|
staking.EndBlocker(suite.Ctx, suite.App.GetStakingKeeper())
|
||||||
|
err := suite.slashValidator(sdk.ValAddress(address3), sdk.MustNewDecFromStr("0.5"))
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
// vault denom is only "bkava" which has it's own special handler
|
||||||
|
suite.CreateVault(
|
||||||
|
vaultDenom,
|
||||||
|
types.StrategyTypes{types.STRATEGY_TYPE_SAVINGS},
|
||||||
|
false,
|
||||||
|
[]sdk.AccAddress{},
|
||||||
|
)
|
||||||
|
|
||||||
|
err = suite.Keeper.Deposit(suite.Ctx, address1, derivatives1, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
err = suite.Keeper.Deposit(suite.Ctx, address2, derivatives2, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
err = suite.Keeper.Deposit(suite.Ctx, address3, derivatives3, types.STRATEGY_TYPE_SAVINGS)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
// Query "bkava" to get aggregate amount
|
||||||
|
res, err := suite.queryClient.Vault(
|
||||||
|
context.Background(),
|
||||||
|
types.NewQueryVaultRequest(vaultDenom),
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
// first two validators are not slashed, so bkava units equal to underlying staked tokens
|
||||||
|
expectedValue := derivatives1.Amount.Add(derivatives2.Amount)
|
||||||
|
// last validator slashed 50% so derivatives are worth half
|
||||||
|
expectedValue = expectedValue.Add(derivatives2.Amount.QuoRaw(2))
|
||||||
|
suite.Require().Equal(
|
||||||
|
types.VaultResponse{
|
||||||
|
Denom: vaultDenom,
|
||||||
|
Strategies: types.StrategyTypes{
|
||||||
|
types.STRATEGY_TYPE_SAVINGS,
|
||||||
|
},
|
||||||
|
IsPrivateVault: false,
|
||||||
|
AllowedDepositors: []string(nil),
|
||||||
|
// No shares for aggregate
|
||||||
|
TotalShares: "0",
|
||||||
|
TotalValue: expectedValue,
|
||||||
|
},
|
||||||
|
res.Vault,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createUnbondedValidator creates an unbonded validator with the given amount of self-delegation.
|
||||||
|
func (suite *grpcQueryTestSuite) createUnbondedValidator(address sdk.ValAddress, selfDelegation sdk.Coin, minSelfDelegation sdk.Int) error {
|
||||||
|
msg, err := stakingtypes.NewMsgCreateValidator(
|
||||||
|
address,
|
||||||
|
ed25519.GenPrivKey().PubKey(),
|
||||||
|
selfDelegation,
|
||||||
|
stakingtypes.Description{},
|
||||||
|
stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||||
|
minSelfDelegation,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgServer := stakingkeeper.NewMsgServerImpl(suite.App.GetStakingKeeper())
|
||||||
|
_, err = msgServer.CreateValidator(sdk.WrapSDKContext(suite.Ctx), msg)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// createAccountWithDerivatives creates an account with the given amount and denom of derivative token.
|
||||||
|
// Internally, it creates a validator account and mints derivatives from the validator's self delegation.
|
||||||
|
func (suite *grpcQueryTestSuite) createAccountWithDerivatives(denom string, amount sdk.Int) (sdk.AccAddress, sdk.Coin, sdk.Coins) {
|
||||||
|
valAddress, err := liquidtypes.ParseLiquidStakingTokenDenom(denom)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
address := sdk.AccAddress(valAddress)
|
||||||
|
|
||||||
|
remainingSelfDelegation := sdk.NewInt(1e6)
|
||||||
|
selfDelegation := sdk.NewCoin(
|
||||||
|
suite.bondDenom(),
|
||||||
|
amount.Add(remainingSelfDelegation),
|
||||||
|
)
|
||||||
|
|
||||||
|
suite.NewAccountFromAddr(address, sdk.NewCoins(selfDelegation))
|
||||||
|
|
||||||
|
err = suite.createUnbondedValidator(valAddress, selfDelegation, remainingSelfDelegation)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
toConvert := sdk.NewCoin(suite.bondDenom(), amount)
|
||||||
|
derivatives, err := suite.App.GetLiquidKeeper().MintDerivative(suite.Ctx,
|
||||||
|
address,
|
||||||
|
valAddress,
|
||||||
|
toConvert,
|
||||||
|
)
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
fullBalance := suite.BankKeeper.GetAllBalances(suite.Ctx, address)
|
||||||
|
|
||||||
|
return address, derivatives, fullBalance
|
||||||
|
}
|
||||||
|
|
||||||
|
// slashValidator slashes the validator with the given address by the given percentage.
|
||||||
|
func (suite *grpcQueryTestSuite) slashValidator(address sdk.ValAddress, slashFraction sdk.Dec) error {
|
||||||
|
stakingKeeper := suite.App.GetStakingKeeper()
|
||||||
|
|
||||||
|
validator, found := stakingKeeper.GetValidator(suite.Ctx, address)
|
||||||
|
suite.Require().True(found)
|
||||||
|
consAddr, err := validator.GetConsAddr()
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
// Assume infraction was at current height. Note unbonding delegations and redelegations are only slashed if created after
|
||||||
|
// the infraction height so none will be slashed.
|
||||||
|
infractionHeight := suite.Ctx.BlockHeight()
|
||||||
|
|
||||||
|
power := stakingKeeper.TokensToConsensusPower(suite.Ctx, validator.GetTokens())
|
||||||
|
|
||||||
|
stakingKeeper.Slash(suite.Ctx, consAddr, infractionHeight, power, slashFraction)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bondDenom fetches the staking denom from the staking module.
|
||||||
|
func (suite *grpcQueryTestSuite) bondDenom() string {
|
||||||
|
return suite.App.GetStakingKeeper().BondDenom(suite.Ctx)
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ type Keeper struct {
|
|||||||
hooks types.EarnHooks
|
hooks types.EarnHooks
|
||||||
accountKeeper types.AccountKeeper
|
accountKeeper types.AccountKeeper
|
||||||
bankKeeper types.BankKeeper
|
bankKeeper types.BankKeeper
|
||||||
|
liquidKeeper types.LiquidKeeper
|
||||||
|
|
||||||
// Keepers used for strategies
|
// Keepers used for strategies
|
||||||
hardKeeper types.HardKeeper
|
hardKeeper types.HardKeeper
|
||||||
@ -29,6 +30,7 @@ func NewKeeper(
|
|||||||
paramstore paramtypes.Subspace,
|
paramstore paramtypes.Subspace,
|
||||||
accountKeeper types.AccountKeeper,
|
accountKeeper types.AccountKeeper,
|
||||||
bankKeeper types.BankKeeper,
|
bankKeeper types.BankKeeper,
|
||||||
|
liquidKeeper types.LiquidKeeper,
|
||||||
hardKeeper types.HardKeeper,
|
hardKeeper types.HardKeeper,
|
||||||
savingsKeeper types.SavingsKeeper,
|
savingsKeeper types.SavingsKeeper,
|
||||||
) Keeper {
|
) Keeper {
|
||||||
@ -42,6 +44,7 @@ func NewKeeper(
|
|||||||
paramSubspace: paramstore,
|
paramSubspace: paramstore,
|
||||||
accountKeeper: accountKeeper,
|
accountKeeper: accountKeeper,
|
||||||
bankKeeper: bankKeeper,
|
bankKeeper: bankKeeper,
|
||||||
|
liquidKeeper: liquidKeeper,
|
||||||
hardKeeper: hardKeeper,
|
hardKeeper: hardKeeper,
|
||||||
savingsKeeper: savingsKeeper,
|
savingsKeeper: savingsKeeper,
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Partial() {
|
|||||||
|
|
||||||
func (suite *withdrawTestSuite) TestWithdraw_bKava() {
|
func (suite *withdrawTestSuite) TestWithdraw_bKava() {
|
||||||
vaultDenom := "bkava"
|
vaultDenom := "bkava"
|
||||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
coinDenom := testutil.TestBkavaDenoms[0]
|
||||||
|
|
||||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||||
|
@ -28,6 +28,12 @@ import (
|
|||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var TestBkavaDenoms = []string{
|
||||||
|
"bkava-kavavaloper15gqc744d05xacn4n6w2furuads9fu4pqn6zxlu",
|
||||||
|
"bkava-kavavaloper15qdefkmwswysgg4qxgqpqr35k3m49pkx8yhpte",
|
||||||
|
"bkava-kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42",
|
||||||
|
}
|
||||||
|
|
||||||
// Suite implements a test suite for the earn module integration tests
|
// Suite implements a test suite for the earn module integration tests
|
||||||
type Suite struct {
|
type Suite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
@ -146,7 +152,9 @@ func (suite *Suite) SetupTest() {
|
|||||||
savingstypes.NewParams(
|
savingstypes.NewParams(
|
||||||
[]string{
|
[]string{
|
||||||
"ukava",
|
"ukava",
|
||||||
"bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l",
|
TestBkavaDenoms[0],
|
||||||
|
TestBkavaDenoms[1],
|
||||||
|
TestBkavaDenoms[2],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
nil,
|
nil,
|
||||||
|
@ -25,6 +25,11 @@ type BankKeeper interface {
|
|||||||
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LiquidKeeper defines the expected interface needed for derivative to staked token conversions.
|
||||||
|
type LiquidKeeper interface {
|
||||||
|
GetStakedTokensForDerivatives(ctx sdk.Context, derivatives sdk.Coins) (sdk.Coin, error)
|
||||||
|
}
|
||||||
|
|
||||||
// HardKeeper defines the expected interface needed for the hard strategy.
|
// HardKeeper defines the expected interface needed for the hard strategy.
|
||||||
type HardKeeper interface {
|
type HardKeeper interface {
|
||||||
Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
|
Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
|
||||||
|
@ -111,28 +111,29 @@ func (k Keeper) IsDerivativeDenom(ctx sdk.Context, denom string) bool {
|
|||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKavaForDerivatives returns the total amount of the provided derivatives
|
// GetStakedTokensForDerivatives returns the total value of the provided derivatives
|
||||||
// in Kava accounting for the specific share prices.
|
// in staked tokens, accounting for the specific share prices.
|
||||||
func (k Keeper) GetKavaForDerivatives(ctx sdk.Context, coins sdk.Coins) (sdk.Int, error) {
|
func (k Keeper) GetStakedTokensForDerivatives(ctx sdk.Context, coins sdk.Coins) (sdk.Coin, error) {
|
||||||
totalKava := sdk.ZeroInt()
|
total := sdk.ZeroInt()
|
||||||
|
|
||||||
for _, coin := range coins {
|
for _, coin := range coins {
|
||||||
valAddr, err := types.ParseLiquidStakingTokenDenom(coin.Denom)
|
valAddr, err := types.ParseLiquidStakingTokenDenom(coin.Denom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.Int{}, fmt.Errorf("invalid derivative denom: %w", err)
|
return sdk.Coin{}, fmt.Errorf("invalid derivative denom: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
validator, found := k.stakingKeeper.GetValidator(ctx, valAddr)
|
validator, found := k.stakingKeeper.GetValidator(ctx, valAddr)
|
||||||
if !found {
|
if !found {
|
||||||
return sdk.Int{}, fmt.Errorf("invalid derivative denom %s: validator not found", coin.Denom)
|
return sdk.Coin{}, fmt.Errorf("invalid derivative denom %s: validator not found", coin.Denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
// bkava is 1:1 to delegation shares
|
// bkava is 1:1 to delegation shares
|
||||||
valTokens := validator.TokensFromSharesTruncated(coin.Amount.ToDec())
|
valTokens := validator.TokensFromSharesTruncated(coin.Amount.ToDec())
|
||||||
totalKava = totalKava.Add(valTokens.TruncateInt())
|
total = total.Add(valTokens.TruncateInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
return totalKava, nil
|
totalCoin := sdk.NewCoin(k.stakingKeeper.BondDenom(ctx), total)
|
||||||
|
return totalCoin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) mintCoins(ctx sdk.Context, receiver sdk.AccAddress, amount sdk.Coins) error {
|
func (k Keeper) mintCoins(ctx sdk.Context, receiver sdk.AccAddress, amount sdk.Coins) error {
|
||||||
|
@ -379,7 +379,7 @@ func (suite *KeeperTestSuite) TestIsDerivativeDenom() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *KeeperTestSuite) TestGetKavaForDerivatives() {
|
func (suite *KeeperTestSuite) TestGetStakedTokensForDerivatives() {
|
||||||
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||||
valAccAddr1, delegator, valAccAddr2, valAccAddr3 := addrs[0], addrs[1], addrs[2], addrs[3]
|
valAccAddr1, delegator, valAccAddr2, valAccAddr3 := addrs[0], addrs[1], addrs[2], addrs[3]
|
||||||
valAddr1 := sdk.ValAddress(valAccAddr1)
|
valAddr1 := sdk.ValAddress(valAccAddr1)
|
||||||
@ -458,13 +458,13 @@ func (suite *KeeperTestSuite) TestGetKavaForDerivatives() {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
suite.Run(tc.name, func() {
|
suite.Run(tc.name, func() {
|
||||||
kavaAmount, err := suite.Keeper.GetKavaForDerivatives(suite.Ctx, tc.derivatives)
|
kavaAmount, err := suite.Keeper.GetStakedTokensForDerivatives(suite.Ctx, tc.derivatives)
|
||||||
|
|
||||||
if tc.err != nil {
|
if tc.err != nil {
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
} else {
|
} else {
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
suite.Require().Equal(tc.wantKavaAmount, kavaAmount)
|
suite.Require().Equal(suite.NewBondCoin(tc.wantKavaAmount), kavaAmount)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user