Add liquid bkava support to savings (#1309)

* Add savings support for bkava deposits

* Update savings tests with valid validator

* Add invalid bkava deposit test

* Remove test logs

* Add bkava withdraw test
This commit is contained in:
Derrick Lee 2022-09-23 09:38:45 -07:00 committed by GitHub
parent 651de460ca
commit 50fdebe657
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 214 additions and 7 deletions

View File

@ -584,18 +584,19 @@ func NewApp(
app.pricefeedKeeper,
app.auctionKeeper,
)
app.liquidKeeper = liquidkeeper.NewDefaultKeeper(
appCodec,
app.accountKeeper,
app.bankKeeper,
&app.stakingKeeper,
)
savingsKeeper := savingskeeper.NewKeeper(
appCodec,
keys[savingstypes.StoreKey],
savingsSubspace,
app.accountKeeper,
app.bankKeeper,
)
app.liquidKeeper = liquidkeeper.NewDefaultKeeper(
appCodec,
app.accountKeeper,
app.bankKeeper,
&app.stakingKeeper,
app.liquidKeeper,
)
earnKeeper := earnkeeper.NewKeeper(
appCodec,

View File

@ -1,9 +1,12 @@
package keeper_test
import (
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/tendermint/tendermint/crypto"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
@ -13,6 +16,15 @@ import (
)
func (suite *KeeperTestSuite) TestDeposit() {
_, addrs := app.GeneratePrivKeyAddressPairs(5)
valAccAddr, delegator := addrs[0], addrs[1]
valAddr := sdk.ValAddress(valAccAddr)
initialBalance := sdk.NewInt(1e9)
bkavaDenom := fmt.Sprintf("bkava-%s", valAddr.String())
invalidBkavaDenom := fmt.Sprintf("bkava-%s", sdk.ValAddress(addrs[2]).String())
type args struct {
allowedDenoms []string
depositor sdk.AccAddress
@ -67,6 +79,23 @@ func (suite *KeeperTestSuite) TestDeposit() {
contains: "",
},
},
{
"valid bkava",
args{
allowedDenoms: []string{"bnb", "btcb", "ukava", "bkava"},
depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
initialDepositorBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
depositAmount: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
numberDeposits: 1,
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(900)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
expectedDepositCoins: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
},
errArgs{
expectPass: true,
contains: "",
},
},
{
"invalid deposit denom",
args{
@ -84,6 +113,23 @@ func (suite *KeeperTestSuite) TestDeposit() {
contains: "invalid deposit denom",
},
},
{
"invalid bkava",
args{
allowedDenoms: []string{"bnb", "btcb", "ukava", "bkava"},
depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
initialDepositorBalance: sdk.NewCoins(sdk.NewCoin(invalidBkavaDenom, sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
depositAmount: sdk.NewCoins(sdk.NewCoin(invalidBkavaDenom, sdk.NewInt(100))),
numberDeposits: 1,
expectedAccountBalance: sdk.Coins{},
expectedModAccountBalance: sdk.Coins{},
expectedDepositCoins: sdk.Coins{},
},
errArgs{
expectPass: false,
contains: "invalid deposit denom",
},
},
{
"insufficient funds",
args{
@ -119,14 +165,26 @@ func (suite *KeeperTestSuite) TestDeposit() {
types.Deposits{},
)
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
tApp.InitializeFromGenesisStates(authGS,
app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)},
app.GenesisState{stakingtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(stakingtypes.NewGenesisState(stakingParams, nil, nil))},
)
keeper := tApp.GetSavingsKeeper()
suite.app = tApp
suite.ctx = ctx
suite.keeper = keeper
// Create validator and delegate for bkava
suite.CreateAccountWithAddress(valAccAddr, cs(c("ukava", 100e10)))
suite.CreateAccountWithAddress(delegator, cs(c("ukava", 100e10)))
suite.CreateNewUnbondedValidator(valAddr, initialBalance)
suite.CreateDelegation(valAddr, delegator, initialBalance)
staking.EndBlocker(suite.ctx, suite.app.GetStakingKeeper())
// run the test
var err error
for i := 0; i < tc.args.numberDeposits; i++ {

View File

@ -19,13 +19,14 @@ type Keeper struct {
paramSubspace paramtypes.Subspace
accountKeeper types.AccountKeeper
bankKeeper types.BankKeeper
liquidKeeper types.LiquidKeeper
hooks types.SavingsHooks
}
// NewKeeper returns a new keeper for the savings module.
func NewKeeper(
cdc codec.Codec, key sdk.StoreKey, paramstore paramtypes.Subspace,
ak types.AccountKeeper, bk types.BankKeeper,
ak types.AccountKeeper, bk types.BankKeeper, lk types.LiquidKeeper,
) Keeper {
if !paramstore.HasKeyTable() {
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
@ -37,6 +38,7 @@ func NewKeeper(
paramSubspace: paramstore,
accountKeeper: ak,
bankKeeper: bk,
liquidKeeper: lk,
hooks: nil,
}
}

View File

@ -8,9 +8,14 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
"github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/savings/keeper"
"github.com/kava-labs/kava/x/savings/types"
@ -39,6 +44,10 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.ctx = ctx
suite.keeper = keeper
suite.addrs = addrs
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
suite.app.GetStakingKeeper().SetParams(suite.ctx, stakingParams)
}
func (suite *KeeperTestSuite) TestGetSetDeleteDeposit() {
@ -99,3 +108,87 @@ func (suite *KeeperTestSuite) getModuleAccountAtCtx(name string, ctx sdk.Context
func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}
// CreateAccount creates a new account from the provided balance and address
func (suite *KeeperTestSuite) CreateAccountWithAddress(addr sdk.AccAddress, initialBalance sdk.Coins) authtypes.AccountI {
ak := suite.app.GetAccountKeeper()
acc := ak.NewAccountWithAddress(suite.ctx, addr)
ak.SetAccount(suite.ctx, acc)
err := simapp.FundAccount(suite.app.GetBankKeeper(), suite.ctx, acc.GetAddress(), initialBalance)
suite.Require().NoError(err)
return acc
}
// CreateVestingAccount creates a new vesting account. `vestingBalance` should be a fraction of `initialBalance`.
func (suite *KeeperTestSuite) CreateVestingAccountWithAddress(addr sdk.AccAddress, initialBalance sdk.Coins, vestingBalance sdk.Coins) authtypes.AccountI {
if vestingBalance.IsAnyGT(initialBalance) {
panic("vesting balance must be less than initial balance")
}
acc := suite.CreateAccountWithAddress(addr, initialBalance)
bacc := acc.(*authtypes.BaseAccount)
periods := vestingtypes.Periods{
vestingtypes.Period{
Length: 31556952,
Amount: vestingBalance,
},
}
vacc := vestingtypes.NewPeriodicVestingAccount(bacc, vestingBalance, suite.ctx.BlockTime().Unix(), periods)
suite.app.GetAccountKeeper().SetAccount(suite.ctx, vacc)
return vacc
}
func (suite *KeeperTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error {
msg, err := stakingtypes.NewMsgCreateValidator(
address,
ed25519.GenPrivKey().PubKey(),
selfDelegation,
stakingtypes.Description{},
stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
sdk.NewInt(1e6),
)
if err != nil {
return err
}
msgServer := stakingkeeper.NewMsgServerImpl(suite.app.GetStakingKeeper())
_, err = msgServer.CreateValidator(sdk.WrapSDKContext(suite.ctx), msg)
return err
}
// CreateNewUnbondedValidator creates a new validator in the staking module.
// New validators are unbonded until the end blocker is run.
func (suite *KeeperTestSuite) CreateNewUnbondedValidator(addr sdk.ValAddress, selfDelegation sdk.Int) stakingtypes.Validator {
// Create a validator
err := suite.deliverMsgCreateValidator(suite.ctx, addr, sdk.NewCoin("ukava", selfDelegation))
suite.Require().NoError(err)
// New validators are created in an unbonded state. Note if the end blocker is run later this validator could become bonded.
validator, found := suite.app.GetStakingKeeper().GetValidator(suite.ctx, addr)
suite.Require().True(found)
return validator
}
// CreateDelegation delegates tokens to a validator.
func (suite *KeeperTestSuite) CreateDelegation(valAddr sdk.ValAddress, delegator sdk.AccAddress, amount sdk.Int) sdk.Dec {
sk := suite.app.GetStakingKeeper()
stakingDenom := sk.BondDenom(suite.ctx)
msg := stakingtypes.NewMsgDelegate(
delegator,
valAddr,
sdk.NewCoin(stakingDenom, amount),
)
msgServer := stakingkeeper.NewMsgServerImpl(sk)
_, err := msgServer.Delegate(sdk.WrapSDKContext(suite.ctx), msg)
suite.Require().NoError(err)
del, found := sk.GetDelegation(suite.ctx, delegator, valAddr)
suite.Require().True(found)
return del.Shares
}

View File

@ -3,6 +3,7 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
"github.com/kava-labs/kava/x/savings/types"
)
@ -25,6 +26,13 @@ func (k Keeper) IsDenomSupported(ctx sdk.Context, denom string) bool {
if supportedDenom == denom {
return true
}
if supportedDenom == liquidtypes.DefaultDerivativeDenom {
if k.liquidKeeper.IsDerivativeDenom(ctx, denom) {
return true
}
}
}
return false
}

View File

@ -1,9 +1,12 @@
package keeper_test
import (
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/tendermint/tendermint/crypto"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
@ -13,6 +16,14 @@ import (
)
func (suite *KeeperTestSuite) TestWithdraw() {
_, addrs := app.GeneratePrivKeyAddressPairs(5)
valAccAddr, delegator := addrs[0], addrs[1]
valAddr := sdk.ValAddress(valAccAddr)
initialBalance := sdk.NewInt(1e9)
bkavaDenom := fmt.Sprintf("bkava-%s", valAddr.String())
type args struct {
allowedDenoms []string
depositor sdk.AccAddress
@ -52,6 +63,24 @@ func (suite *KeeperTestSuite) TestWithdraw() {
contains: "",
},
},
{
"valid: partial bkava",
args{
allowedDenoms: []string{"bnb", "btcb", "ukava", "bkava"},
depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
initialDepositorBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
depositAmount: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(200))),
withdrawAmount: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(900)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
expectedDepositCoins: sdk.NewCoins(sdk.NewCoin(bkavaDenom, sdk.NewInt(100))),
},
errArgs{
expectPass: true,
expectDelete: false,
contains: "",
},
},
{
"valid: full withdraw",
args{
@ -122,8 +151,12 @@ func (suite *KeeperTestSuite) TestWithdraw() {
types.Deposits{},
)
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
tApp.InitializeFromGenesisStates(authGS,
app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)},
app.GenesisState{stakingtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(stakingtypes.NewGenesisState(stakingParams, nil, nil))},
)
keeper := tApp.GetSavingsKeeper()
suite.app = tApp
@ -131,6 +164,14 @@ func (suite *KeeperTestSuite) TestWithdraw() {
suite.keeper = keeper
bankKeeper := tApp.GetBankKeeper()
// Create validator and delegate for bkava
suite.CreateAccountWithAddress(valAccAddr, cs(c("ukava", 100e10)))
suite.CreateAccountWithAddress(delegator, cs(c("ukava", 100e10)))
suite.CreateNewUnbondedValidator(valAddr, initialBalance)
suite.CreateDelegation(valAddr, delegator, initialBalance)
staking.EndBlocker(suite.ctx, suite.app.GetStakingKeeper())
err := suite.keeper.Deposit(suite.ctx, tc.args.depositor, tc.args.depositAmount)
suite.Require().NoError(err)

View File

@ -31,3 +31,7 @@ type SavingsHooks interface {
AfterSavingsDepositCreated(ctx sdk.Context, deposit Deposit)
BeforeSavingsDepositModified(ctx sdk.Context, deposit Deposit, incomingDenoms []string)
}
type LiquidKeeper interface {
IsDerivativeDenom(ctx sdk.Context, denom string) bool
}