mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 00:35:18 +00:00
4c879bc4fb
* Add earn deposits query param to return individual vaults value as ukava * Add value_in_staked_tokens param handler to specific vault query * Use separate ukava coins when querying deposits in ukava Avoid usd of sdk.Coins.Add() as those will aggregate ukava amounts
766 lines
25 KiB
Go
766 lines
25 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"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/app"
|
|
"github.com/kava-labs/kava/x/earn/keeper"
|
|
"github.com/kava-labs/kava/x/earn/testutil"
|
|
"github.com/kava-labs/kava/x/earn/types"
|
|
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
|
|
)
|
|
|
|
type grpcQueryTestSuite struct {
|
|
testutil.Suite
|
|
|
|
queryClient types.QueryClient
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) SetupTest() {
|
|
suite.Suite.SetupTest()
|
|
suite.Keeper.SetParams(suite.Ctx, types.DefaultParams())
|
|
|
|
queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry())
|
|
types.RegisterQueryServer(queryHelper, keeper.NewQueryServerImpl(suite.Keeper))
|
|
|
|
suite.queryClient = types.NewQueryClient(queryHelper)
|
|
}
|
|
|
|
func TestGrpcQueryTestSuite(t *testing.T) {
|
|
suite.Run(t, new(grpcQueryTestSuite))
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) TestQueryParams() {
|
|
vaultDenom := "usdx"
|
|
|
|
res, err := suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
|
|
suite.Require().NoError(err)
|
|
// ElementsMatch instead of Equal because AllowedVaults{} != AllowedVaults(nil)
|
|
suite.Require().ElementsMatch(types.DefaultParams().AllowedVaults, res.Params.AllowedVaults)
|
|
|
|
// Add vault to params
|
|
suite.CreateVault(vaultDenom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
|
|
|
// Query again for added vault
|
|
res, err = suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(
|
|
types.AllowedVaults{
|
|
types.NewAllowedVault(vaultDenom, types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil),
|
|
},
|
|
res.Params.AllowedVaults,
|
|
)
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) TestVaults_ZeroSupply() {
|
|
// Add vaults
|
|
suite.CreateVault("usdx", types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
|
suite.CreateVault("busd", types.StrategyTypes{types.STRATEGY_TYPE_HARD}, false, nil)
|
|
|
|
suite.Run("single", func() {
|
|
res, err := suite.queryClient.Vault(context.Background(), types.NewQueryVaultRequest("usdx"))
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(
|
|
types.VaultResponse{
|
|
Denom: "usdx",
|
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
|
IsPrivateVault: false,
|
|
AllowedDepositors: nil,
|
|
TotalShares: sdk.NewDec(0).String(),
|
|
TotalValue: sdk.NewInt(0),
|
|
},
|
|
res.Vault,
|
|
)
|
|
})
|
|
|
|
suite.Run("all", func() {
|
|
res, err := suite.queryClient.Vaults(context.Background(), types.NewQueryVaultsRequest())
|
|
suite.Require().NoError(err)
|
|
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 := testutil.TestBkavaDenoms[0]
|
|
|
|
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
|
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 100)
|
|
|
|
suite.CreateVault(vaultDenom, 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),
|
|
), 0)
|
|
|
|
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount, types.STRATEGY_TYPE_HARD)
|
|
suite.Require().NoError(err)
|
|
|
|
err = suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), deposit2Amount, 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, 2)
|
|
suite.Require().ElementsMatch(
|
|
[]types.VaultResponse{
|
|
{
|
|
Denom: vaultDenom,
|
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_HARD},
|
|
IsPrivateVault: false,
|
|
AllowedDepositors: nil,
|
|
TotalShares: depositAmount.Amount.ToDec().String(),
|
|
TotalValue: depositAmount.Amount,
|
|
},
|
|
{
|
|
Denom: vault2Denom,
|
|
Strategies: []types.StrategyType{types.STRATEGY_TYPE_SAVINGS},
|
|
IsPrivateVault: false,
|
|
AllowedDepositors: nil,
|
|
TotalShares: deposit2Amount.Amount.ToDec().String(),
|
|
TotalValue: deposit2Amount.Amount,
|
|
},
|
|
},
|
|
res.Vaults,
|
|
)
|
|
}
|
|
|
|
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)
|
|
suite.Require().ErrorIs(err, status.Errorf(codes.NotFound, "vault not found with specified denom"))
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) TestDeposits() {
|
|
// Validator setup for bkava
|
|
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
|
valAccAddr1, valAccAddr2, delegator := addrs[0], addrs[1], addrs[2]
|
|
valAddr1 := sdk.ValAddress(valAccAddr1)
|
|
valAddr2 := sdk.ValAddress(valAccAddr2)
|
|
|
|
vault1Denom := "usdx"
|
|
vault2Denom := "busd"
|
|
vault3Denom := fmt.Sprintf("bkava-%s", valAddr1.String())
|
|
vault4Denom := fmt.Sprintf("bkava-%s", valAddr2.String())
|
|
|
|
initialUkavaBalance := sdk.NewInt(1e9)
|
|
startBalance := sdk.NewCoins(
|
|
sdk.NewCoin("ukava", initialUkavaBalance),
|
|
sdk.NewInt64Coin(vault1Denom, 1000),
|
|
sdk.NewInt64Coin(vault2Denom, 1000),
|
|
// Bkava isn't actually minted via x/liquid
|
|
sdk.NewInt64Coin(vault3Denom, 1000),
|
|
sdk.NewInt64Coin(vault4Denom, 1000),
|
|
)
|
|
|
|
delegateAmount := sdk.NewInt(100e6)
|
|
|
|
suite.App.FundAccount(suite.Ctx, valAccAddr1, startBalance)
|
|
suite.App.FundAccount(suite.Ctx, valAccAddr2, startBalance)
|
|
suite.App.FundAccount(suite.Ctx, delegator, startBalance)
|
|
|
|
suite.CreateNewUnbondedValidator(valAddr1, initialUkavaBalance)
|
|
suite.CreateNewUnbondedValidator(valAddr2, initialUkavaBalance)
|
|
suite.CreateDelegation(valAddr1, delegator, delegateAmount)
|
|
suite.CreateDelegation(valAddr2, delegator, delegateAmount)
|
|
|
|
staking.EndBlocker(suite.Ctx, suite.App.GetStakingKeeper())
|
|
|
|
savingsParams := suite.SavingsKeeper.GetParams(suite.Ctx)
|
|
savingsParams.SupportedDenoms = append(savingsParams.SupportedDenoms, "bkava")
|
|
suite.SavingsKeeper.SetParams(suite.Ctx, savingsParams)
|
|
|
|
// 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)
|
|
|
|
deposit1Amount := sdk.NewInt64Coin(vault1Denom, 100)
|
|
deposit2Amount := sdk.NewInt64Coin(vault2Denom, 200)
|
|
deposit3Amount := sdk.NewInt64Coin(vault3Denom, 200)
|
|
deposit4Amount := sdk.NewInt64Coin(vault4Denom, 300)
|
|
|
|
// Accounts
|
|
acc1 := suite.CreateAccount(startBalance, 0).GetAddress()
|
|
acc2 := delegator
|
|
|
|
// Deposit into each vault from each account - 4 total deposits
|
|
// Acc 1: usdx + busd
|
|
// Acc 2: usdx + bkava-1 + bkava-2
|
|
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)
|
|
suite.Require().NoError(err)
|
|
|
|
err = suite.Keeper.Deposit(suite.Ctx, acc2, deposit1Amount, types.STRATEGY_TYPE_HARD)
|
|
suite.Require().NoError(err)
|
|
err = suite.Keeper.Deposit(suite.Ctx, acc2, deposit3Amount, types.STRATEGY_TYPE_SAVINGS)
|
|
suite.Require().NoError(err)
|
|
err = suite.Keeper.Deposit(suite.Ctx, acc2, deposit4Amount, types.STRATEGY_TYPE_SAVINGS)
|
|
suite.Require().NoError(err)
|
|
|
|
suite.Run("specific vault", func() {
|
|
// Query all deposits for account 1
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc1.String(), vault1Denom, false, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Len(res.Deposits, 1)
|
|
suite.Require().ElementsMatchf(
|
|
[]types.DepositResponse{
|
|
{
|
|
Depositor: acc1.String(),
|
|
// Only includes specified deposit shares
|
|
Shares: types.NewVaultShares(
|
|
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
|
),
|
|
// Only the specified vault denom value
|
|
Value: sdk.NewCoins(deposit1Amount),
|
|
},
|
|
},
|
|
res.Deposits,
|
|
"deposits should match, got %v",
|
|
res.Deposits,
|
|
)
|
|
})
|
|
|
|
suite.Run("specific bkava vault", func() {
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc2.String(), vault3Denom, false, 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("specific bkava vault in staked tokens", func() {
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc2.String(), vault3Denom, true, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Len(res.Deposits, 1)
|
|
suite.Require().Equal(
|
|
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(
|
|
sdk.NewCoin("ukava", deposit3Amount.Amount),
|
|
),
|
|
},
|
|
res.Deposits[0],
|
|
)
|
|
})
|
|
|
|
suite.Run("invalid vault", func() {
|
|
_, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc1.String(), "notavaliddenom", false, nil),
|
|
)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, status.Errorf(codes.NotFound, "vault for notavaliddenom not found"))
|
|
})
|
|
|
|
suite.Run("all vaults", func() {
|
|
// Query all deposits for account 1
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc1.String(), "", false, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Len(res.Deposits, 1)
|
|
suite.Require().ElementsMatch(
|
|
[]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),
|
|
},
|
|
},
|
|
res.Deposits,
|
|
)
|
|
})
|
|
|
|
suite.Run("all vaults value in staked tokens", func() {
|
|
// Query all deposits for account 1 with value in staked tokens
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc2.String(), "", true, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Len(res.Deposits, 1)
|
|
suite.Require().Equal(
|
|
types.DepositResponse{
|
|
Depositor: acc2.String(),
|
|
Shares: types.VaultShares{
|
|
types.NewVaultShare(deposit4Amount.Denom, deposit4Amount.Amount.ToDec()),
|
|
types.NewVaultShare(deposit3Amount.Denom, deposit3Amount.Amount.ToDec()),
|
|
types.NewVaultShare(deposit1Amount.Denom, deposit1Amount.Amount.ToDec()),
|
|
},
|
|
Value: sdk.Coins{
|
|
sdk.NewCoin("ukava", deposit4Amount.Amount),
|
|
sdk.NewCoin("ukava", deposit3Amount.Amount),
|
|
// Deposit 1 is last as it's the same order as vault shares
|
|
deposit1Amount,
|
|
},
|
|
},
|
|
res.Deposits[0],
|
|
)
|
|
for i := range res.Deposits[0].Shares {
|
|
suite.Equal(
|
|
res.Deposits[0].Shares[i].Amount,
|
|
res.Deposits[0].Value[i].Amount.ToDec(),
|
|
"order of deposit value should match shares",
|
|
)
|
|
}
|
|
})
|
|
}
|
|
|
|
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(acc1.String(), vault1Denom, false, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Len(res.Deposits, 1)
|
|
suite.Require().ElementsMatchf(
|
|
[]types.DepositResponse{
|
|
{
|
|
Depositor: acc1.String(),
|
|
// Zero shares and zero value
|
|
Shares: nil,
|
|
Value: nil,
|
|
},
|
|
},
|
|
res.Deposits,
|
|
"deposits should match, got %v",
|
|
res.Deposits,
|
|
)
|
|
})
|
|
|
|
suite.Run("all vaults", func() {
|
|
// Query all deposits for account 1
|
|
res, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest(acc1.String(), "", false, nil),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Empty(res.Deposits)
|
|
})
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) TestDeposits_NoDepositor() {
|
|
_, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest("", "usdx", false, nil),
|
|
)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "depositor is required"))
|
|
}
|
|
|
|
func (suite *grpcQueryTestSuite) TestDeposits_InvalidAddress() {
|
|
_, err := suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest("asdf", "usdx", false, nil),
|
|
)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "Invalid address"))
|
|
|
|
_, err = suite.queryClient.Deposits(
|
|
context.Background(),
|
|
types.NewQueryDepositsRequest("asdf", "", false, nil),
|
|
)
|
|
suite.Require().Error(err)
|
|
suite.Require().ErrorIs(err, status.Error(codes.InvalidArgument, "Invalid address"))
|
|
}
|
|
|
|
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{},
|
|
)
|
|
|
|
suite.CreateVault(
|
|
"ukava",
|
|
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))
|
|
|
|
err := suite.App.FundAccount(suite.Ctx, address1, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1e9))))
|
|
suite.Require().NoError(err)
|
|
|
|
// 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", false, 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)
|
|
|
|
err = suite.Keeper.Deposit(suite.Ctx, address1, sdk.NewInt64Coin("ukava", 1e6), 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", false, 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
|
|
// Excludes non-bkava deposits
|
|
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 := testutil.TestBkavaDenoms[0]
|
|
|
|
startBalance := sdk.NewInt64Coin(coinDenom, 1000)
|
|
depositAmount := sdk.NewInt64Coin(coinDenom, 100)
|
|
|
|
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
|
|
|
|
// 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, acc1.GetAddress(), depositAmount, types.STRATEGY_TYPE_SAVINGS)
|
|
suite.Require().NoError(
|
|
err,
|
|
"should be able to deposit bkava derivative denom in bkava vault",
|
|
)
|
|
|
|
res, err := suite.queryClient.Vault(
|
|
context.Background(),
|
|
types.NewQueryVaultRequest(coinDenom),
|
|
)
|
|
suite.Require().NoError(err)
|
|
suite.Require().Equal(
|
|
types.VaultResponse{
|
|
Denom: coinDenom,
|
|
Strategies: types.StrategyTypes{
|
|
types.STRATEGY_TYPE_SAVINGS,
|
|
},
|
|
IsPrivateVault: false,
|
|
AllowedDepositors: []string(nil),
|
|
TotalShares: "100.000000000000000000",
|
|
TotalValue: sdk.NewInt(100),
|
|
},
|
|
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)
|
|
}
|