mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 07:45: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,
|
||||
app.accountKeeper,
|
||||
app.bankKeeper,
|
||||
app.liquidKeeper,
|
||||
&hardKeeper,
|
||||
&savingsKeeper,
|
||||
)
|
||||
|
@ -170,7 +170,7 @@ func (suite *depositTestSuite) TestDeposit_PrivateVault() {
|
||||
|
||||
func (suite *depositTestSuite) TestDeposit_bKava() {
|
||||
vaultDenom := "bkava"
|
||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
||||
coinDenom := testutil.TestBkavaDenoms[0]
|
||||
|
||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||
|
@ -3,13 +3,12 @@ package keeper
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/prefix"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
|
||||
"github.com/kava-labs/kava/x/earn/types"
|
||||
)
|
||||
@ -51,6 +50,14 @@ func (s queryServer) Vaults(
|
||||
|
||||
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{}
|
||||
|
||||
var vaultRecordsErr error
|
||||
@ -58,9 +65,16 @@ func (s queryServer) Vaults(
|
||||
// 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)
|
||||
// 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 {
|
||||
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)
|
||||
@ -79,6 +93,9 @@ func (s queryServer) Vaults(
|
||||
TotalValue: totalValue.Amount,
|
||||
})
|
||||
|
||||
// Mark this allowed vault as visited
|
||||
visitedMap[allowedVaultDenom] = true
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
@ -86,6 +103,30 @@ func (s queryServer) Vaults(
|
||||
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
|
||||
// records which exists only for those with deposits.
|
||||
return &types.QueryVaultsResponse{
|
||||
@ -114,6 +155,11 @@ func (s queryServer) Vault(
|
||||
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
|
||||
vaultRecord, found := s.keeper.GetVaultRecord(sdkCtx, req.Denom)
|
||||
if !found {
|
||||
@ -141,6 +187,54 @@ func (s queryServer) Vault(
|
||||
}, 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.
|
||||
func (s queryServer) Deposits(
|
||||
ctx context.Context,
|
||||
@ -150,30 +244,29 @@ func (s queryServer) Deposits(
|
||||
return nil, status.Errorf(codes.InvalidArgument, "empty request")
|
||||
}
|
||||
|
||||
if req.Depositor == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "depositor is required")
|
||||
}
|
||||
|
||||
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
||||
|
||||
// 1. Specific account and specific vault
|
||||
if req.Depositor != "" && req.Denom != "" {
|
||||
return s.getAccountVaultDeposit(sdkCtx, req)
|
||||
// bkava aggregate total
|
||||
if req.Denom == "bkava" {
|
||||
return s.getOneAccountBkavaVaultDeposit(sdkCtx, req)
|
||||
}
|
||||
|
||||
// 2. All accounts, specific vault
|
||||
if req.Depositor == "" && req.Denom != "" {
|
||||
return s.getVaultAllDeposits(sdkCtx, req)
|
||||
// specific vault
|
||||
if req.Denom != "" {
|
||||
return s.getOneAccountOneVaultDeposit(sdkCtx, req)
|
||||
}
|
||||
|
||||
// 3. Specific account, all vaults
|
||||
if req.Depositor != "" && req.Denom == "" {
|
||||
return s.getAccountAllDeposits(sdkCtx, req)
|
||||
// all vaults
|
||||
return s.getOneAccountAllDeposits(sdkCtx, req)
|
||||
}
|
||||
|
||||
// 4. All accounts, all vaults
|
||||
return s.getAllDeposits(sdkCtx, req)
|
||||
}
|
||||
|
||||
// getAccountVaultDeposit returns deposits for a specific vault and a specific
|
||||
// getOneAccountOneVaultDeposit returns deposits for a specific vault and a specific
|
||||
// account
|
||||
func (s queryServer) getAccountVaultDeposit(
|
||||
func (s queryServer) getOneAccountOneVaultDeposit(
|
||||
ctx sdk.Context,
|
||||
req *types.QueryDepositsRequest,
|
||||
) (*types.QueryDepositsResponse, error) {
|
||||
@ -184,90 +277,95 @@ func (s queryServer) getAccountVaultDeposit(
|
||||
|
||||
shareRecord, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
||||
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{
|
||||
Deposits: []types.DepositResponse{
|
||||
{
|
||||
Depositor: depositor.String(),
|
||||
Shares: shareRecord.Shares,
|
||||
Value: value,
|
||||
// Zero shares and zero value for no deposits
|
||||
Shares: types.NewVaultShares(types.NewVaultShare(req.Denom, sdk.ZeroDec())),
|
||||
Value: sdk.NewCoins(sdk.NewCoin(req.Denom, sdk.ZeroInt())),
|
||||
},
|
||||
},
|
||||
Pagination: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getVaultAllDeposits returns all deposits for a specific vault
|
||||
func (s queryServer) getVaultAllDeposits(
|
||||
// Only requesting the value of the specified denom
|
||||
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,
|
||||
req *types.QueryDepositsRequest,
|
||||
) (*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 {
|
||||
return nil, status.Error(codes.NotFound, "Vault record for denom not found")
|
||||
}
|
||||
|
||||
deposits := []types.DepositResponse{}
|
||||
store := prefix.NewStore(ctx.KVStore(s.keeper.key), types.VaultShareRecordKeyPrefix)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return &types.QueryDepositsResponse{
|
||||
Deposits: []types.DepositResponse{
|
||||
{
|
||||
Depositor: depositor.String(),
|
||||
// Zero shares and zero value for no deposits
|
||||
Shares: types.NewVaultShares(types.NewVaultShare(req.Denom, sdk.ZeroDec())),
|
||||
Value: sdk.NewCoins(sdk.NewCoin(req.Denom, sdk.ZeroInt())),
|
||||
},
|
||||
)
|
||||
},
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.QueryDepositsResponse{
|
||||
Deposits: deposits,
|
||||
Pagination: pageRes,
|
||||
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(stakedValue),
|
||||
},
|
||||
},
|
||||
Pagination: nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getAccountAllDeposits returns deposits for all vaults for a specific account
|
||||
func (s queryServer) getAccountAllDeposits(
|
||||
// getOneAccountAllDeposits returns deposits for all vaults for a specific account
|
||||
func (s queryServer) getOneAccountAllDeposits(
|
||||
ctx sdk.Context,
|
||||
req *types.QueryDepositsRequest,
|
||||
) (*types.QueryDepositsResponse, error) {
|
||||
@ -280,10 +378,13 @@ func (s queryServer) getAccountAllDeposits(
|
||||
|
||||
accountShare, found := s.keeper.GetVaultShareRecord(ctx, depositor)
|
||||
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 {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
@ -300,51 +401,9 @@ func (s queryServer) getAccountAllDeposits(
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getAllDeposits returns all deposits for all vaults
|
||||
func (s queryServer) getAllDeposits(
|
||||
ctx sdk.Context,
|
||||
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(
|
||||
// getAccountTotalValue returns the total value for all vaults for a specific
|
||||
// account based on their shares.
|
||||
func getAccountTotalValue(
|
||||
ctx sdk.Context,
|
||||
keeper Keeper,
|
||||
account sdk.AccAddress,
|
||||
|
@ -5,14 +5,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
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/status"
|
||||
|
||||
"github.com/kava-labs/kava/x/earn/keeper"
|
||||
"github.com/kava-labs/kava/x/earn/testutil"
|
||||
"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 {
|
||||
@ -81,13 +86,32 @@ func (suite *grpcQueryTestSuite) TestVaults_ZeroSupply() {
|
||||
suite.Run("all", func() {
|
||||
res, err := suite.queryClient.Vaults(context.Background(), types.NewQueryVaultsRequest())
|
||||
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() {
|
||||
vaultDenom := "usdx"
|
||||
vault2Denom := "bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
||||
vault2Denom := testutil.TestBkavaDenoms[0]
|
||||
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 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() {
|
||||
_, err := suite.queryClient.Vault(context.Background(), types.NewQueryVaultRequest("usdx"))
|
||||
suite.Require().Error(err)
|
||||
@ -141,7 +219,7 @@ func (suite *grpcQueryTestSuite) TestVault_NotFound() {
|
||||
func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||
vault1Denom := "usdx"
|
||||
vault2Denom := "busd"
|
||||
vault3Denom := "bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
||||
vault3Denom := testutil.TestBkavaDenoms[0]
|
||||
|
||||
// Add vaults
|
||||
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(vault3Denom, 1000),
|
||||
)
|
||||
|
||||
deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100)
|
||||
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 200)
|
||||
deposit3Amount := sdk.NewInt64Coin(vault3Denom, 200)
|
||||
@ -163,7 +242,7 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||
|
||||
// Deposit into each vault from each account - 4 total deposits
|
||||
// Acc 1: usdx + busd
|
||||
// Acc 2: usdx + usdc
|
||||
// Acc 2: usdx + bkava
|
||||
err := suite.Keeper.Deposit(suite.Ctx, acc1, deposit1Amount, types.STRATEGY_TYPE_HARD)
|
||||
suite.Require().NoError(err)
|
||||
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)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.Run("1) 1 vault for 1 account", func() {
|
||||
suite.Run("specific vault", func() {
|
||||
// Query all deposits for account 1
|
||||
res, err := suite.queryClient.Deposits(
|
||||
context.Background(),
|
||||
@ -186,12 +265,12 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||
[]types.DepositResponse{
|
||||
{
|
||||
Depositor: acc1.String(),
|
||||
// Still includes all deposits
|
||||
// Only includes specified deposit shares
|
||||
Shares: types.NewVaultShares(
|
||||
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,
|
||||
@ -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(
|
||||
context.Background(),
|
||||
types.NewQueryDepositsRequest(acc1.String(), "notavaliddenom", nil),
|
||||
)
|
||||
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
|
||||
res, err := suite.queryClient.Deposits(
|
||||
context.Background(),
|
||||
@ -231,55 +335,35 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||
res.Deposits,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
suite.Run("2) all accounts, specific vault", func() {
|
||||
// Query all deposits for vault 3
|
||||
func (suite *grpcQueryTestSuite) TestDeposits_NoDeposits() {
|
||||
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(
|
||||
context.Background(),
|
||||
types.NewQueryDepositsRequest("", vault3Denom, nil),
|
||||
types.NewQueryDepositsRequest(acc1.String(), vault1Denom, nil),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
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(
|
||||
[]types.DepositResponse{
|
||||
{
|
||||
Depositor: acc1.String(),
|
||||
Shares: types.NewVaultShares(
|
||||
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
||||
types.NewVaultShare(deposit2Amount.Denom, deposit2Amount.Amount.ToDec()),
|
||||
),
|
||||
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),
|
||||
// Zero shares and zero value
|
||||
Shares: nil,
|
||||
Value: nil,
|
||||
},
|
||||
},
|
||||
res.Deposits,
|
||||
@ -287,15 +371,25 @@ func (suite *grpcQueryTestSuite) TestDeposits() {
|
||||
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(
|
||||
context.Background(),
|
||||
types.NewQueryDepositsRequest("", "usdx", nil),
|
||||
)
|
||||
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() {
|
||||
@ -314,9 +408,89 @@ func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() {
|
||||
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"
|
||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
||||
coinDenom := testutil.TestBkavaDenoms[0]
|
||||
|
||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||
@ -356,3 +530,132 @@ func (suite *grpcQueryTestSuite) TestVault_bKava() {
|
||||
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
|
||||
accountKeeper types.AccountKeeper
|
||||
bankKeeper types.BankKeeper
|
||||
liquidKeeper types.LiquidKeeper
|
||||
|
||||
// Keepers used for strategies
|
||||
hardKeeper types.HardKeeper
|
||||
@ -29,6 +30,7 @@ func NewKeeper(
|
||||
paramstore paramtypes.Subspace,
|
||||
accountKeeper types.AccountKeeper,
|
||||
bankKeeper types.BankKeeper,
|
||||
liquidKeeper types.LiquidKeeper,
|
||||
hardKeeper types.HardKeeper,
|
||||
savingsKeeper types.SavingsKeeper,
|
||||
) Keeper {
|
||||
@ -42,6 +44,7 @@ func NewKeeper(
|
||||
paramSubspace: paramstore,
|
||||
accountKeeper: accountKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
liquidKeeper: liquidKeeper,
|
||||
hardKeeper: hardKeeper,
|
||||
savingsKeeper: savingsKeeper,
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Partial() {
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_bKava() {
|
||||
vaultDenom := "bkava"
|
||||
coinDenom := vaultDenom + "-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l"
|
||||
coinDenom := testutil.TestBkavaDenoms[0]
|
||||
|
||||
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
||||
|
@ -28,6 +28,12 @@ import (
|
||||
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
|
||||
type Suite struct {
|
||||
suite.Suite
|
||||
@ -146,7 +152,9 @@ func (suite *Suite) SetupTest() {
|
||||
savingstypes.NewParams(
|
||||
[]string{
|
||||
"ukava",
|
||||
"bkava-kavavaloper16xyempempp92x9hyzz9wrgf94r6j9h5f2w4n2l",
|
||||
TestBkavaDenoms[0],
|
||||
TestBkavaDenoms[1],
|
||||
TestBkavaDenoms[2],
|
||||
},
|
||||
),
|
||||
nil,
|
||||
|
@ -25,6 +25,11 @@ type BankKeeper interface {
|
||||
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.
|
||||
type HardKeeper interface {
|
||||
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
|
||||
}
|
||||
|
||||
// GetKavaForDerivatives returns the total amount of the provided derivatives
|
||||
// in Kava accounting for the specific share prices.
|
||||
func (k Keeper) GetKavaForDerivatives(ctx sdk.Context, coins sdk.Coins) (sdk.Int, error) {
|
||||
totalKava := sdk.ZeroInt()
|
||||
// GetStakedTokensForDerivatives returns the total value of the provided derivatives
|
||||
// in staked tokens, accounting for the specific share prices.
|
||||
func (k Keeper) GetStakedTokensForDerivatives(ctx sdk.Context, coins sdk.Coins) (sdk.Coin, error) {
|
||||
total := sdk.ZeroInt()
|
||||
|
||||
for _, coin := range coins {
|
||||
valAddr, err := types.ParseLiquidStakingTokenDenom(coin.Denom)
|
||||
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)
|
||||
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
|
||||
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 {
|
||||
|
@ -379,7 +379,7 @@ func (suite *KeeperTestSuite) TestIsDerivativeDenom() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetKavaForDerivatives() {
|
||||
func (suite *KeeperTestSuite) TestGetStakedTokensForDerivatives() {
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||
valAccAddr1, delegator, valAccAddr2, valAccAddr3 := addrs[0], addrs[1], addrs[2], addrs[3]
|
||||
valAddr1 := sdk.ValAddress(valAccAddr1)
|
||||
@ -458,13 +458,13 @@ func (suite *KeeperTestSuite) TestGetKavaForDerivatives() {
|
||||
|
||||
for _, tc := range testCases {
|
||||
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 {
|
||||
suite.Require().Error(err)
|
||||
} else {
|
||||
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