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.pricefeedKeeper,
app.auctionKeeper, app.auctionKeeper,
) )
app.liquidKeeper = liquidkeeper.NewDefaultKeeper(
appCodec,
app.accountKeeper,
app.bankKeeper,
&app.stakingKeeper,
)
savingsKeeper := savingskeeper.NewKeeper( savingsKeeper := savingskeeper.NewKeeper(
appCodec, appCodec,
keys[savingstypes.StoreKey], keys[savingstypes.StoreKey],
savingsSubspace, savingsSubspace,
app.accountKeeper, app.accountKeeper,
app.bankKeeper, app.bankKeeper,
) app.liquidKeeper,
app.liquidKeeper = liquidkeeper.NewDefaultKeeper(
appCodec,
app.accountKeeper,
app.bankKeeper,
&app.stakingKeeper,
) )
earnKeeper := earnkeeper.NewKeeper( earnKeeper := earnkeeper.NewKeeper(
appCodec, appCodec,

View File

@ -1,9 +1,12 @@
package keeper_test package keeper_test
import ( import (
"fmt"
"strings" "strings"
sdk "github.com/cosmos/cosmos-sdk/types" 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" "github.com/tendermint/tendermint/crypto"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time" tmtime "github.com/tendermint/tendermint/types/time"
@ -13,6 +16,15 @@ import (
) )
func (suite *KeeperTestSuite) TestDeposit() { 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 { type args struct {
allowedDenoms []string allowedDenoms []string
depositor sdk.AccAddress depositor sdk.AccAddress
@ -67,6 +79,23 @@ func (suite *KeeperTestSuite) TestDeposit() {
contains: "", 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", "invalid deposit denom",
args{ args{
@ -84,6 +113,23 @@ func (suite *KeeperTestSuite) TestDeposit() {
contains: "invalid deposit denom", 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", "insufficient funds",
args{ args{
@ -119,14 +165,26 @@ func (suite *KeeperTestSuite) TestDeposit() {
types.Deposits{}, types.Deposits{},
) )
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
tApp.InitializeFromGenesisStates(authGS, tApp.InitializeFromGenesisStates(authGS,
app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)}, app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)},
app.GenesisState{stakingtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(stakingtypes.NewGenesisState(stakingParams, nil, nil))},
) )
keeper := tApp.GetSavingsKeeper() keeper := tApp.GetSavingsKeeper()
suite.app = tApp suite.app = tApp
suite.ctx = ctx suite.ctx = ctx
suite.keeper = keeper 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 // run the test
var err error var err error
for i := 0; i < tc.args.numberDeposits; i++ { for i := 0; i < tc.args.numberDeposits; i++ {

View File

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

View File

@ -8,9 +8,14 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time" tmtime "github.com/tendermint/tendermint/types/time"
"github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/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/app"
"github.com/kava-labs/kava/x/savings/keeper" "github.com/kava-labs/kava/x/savings/keeper"
"github.com/kava-labs/kava/x/savings/types" "github.com/kava-labs/kava/x/savings/types"
@ -39,6 +44,10 @@ func (suite *KeeperTestSuite) SetupTest() {
suite.ctx = ctx suite.ctx = ctx
suite.keeper = keeper suite.keeper = keeper
suite.addrs = addrs suite.addrs = addrs
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
suite.app.GetStakingKeeper().SetParams(suite.ctx, stakingParams)
} }
func (suite *KeeperTestSuite) TestGetSetDeleteDeposit() { func (suite *KeeperTestSuite) TestGetSetDeleteDeposit() {
@ -99,3 +108,87 @@ func (suite *KeeperTestSuite) getModuleAccountAtCtx(name string, ctx sdk.Context
func TestKeeperTestSuite(t *testing.T) { func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite)) 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 ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
liquidtypes "github.com/kava-labs/kava/x/liquid/types"
"github.com/kava-labs/kava/x/savings/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 { if supportedDenom == denom {
return true return true
} }
if supportedDenom == liquidtypes.DefaultDerivativeDenom {
if k.liquidKeeper.IsDerivativeDenom(ctx, denom) {
return true
}
}
} }
return false return false
} }

View File

@ -1,9 +1,12 @@
package keeper_test package keeper_test
import ( import (
"fmt"
"strings" "strings"
sdk "github.com/cosmos/cosmos-sdk/types" 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" "github.com/tendermint/tendermint/crypto"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time" tmtime "github.com/tendermint/tendermint/types/time"
@ -13,6 +16,14 @@ import (
) )
func (suite *KeeperTestSuite) TestWithdraw() { 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 { type args struct {
allowedDenoms []string allowedDenoms []string
depositor sdk.AccAddress depositor sdk.AccAddress
@ -52,6 +63,24 @@ func (suite *KeeperTestSuite) TestWithdraw() {
contains: "", 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", "valid: full withdraw",
args{ args{
@ -122,8 +151,12 @@ func (suite *KeeperTestSuite) TestWithdraw() {
types.Deposits{}, types.Deposits{},
) )
stakingParams := stakingtypes.DefaultParams()
stakingParams.BondDenom = "ukava"
tApp.InitializeFromGenesisStates(authGS, tApp.InitializeFromGenesisStates(authGS,
app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)}, app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)},
app.GenesisState{stakingtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(stakingtypes.NewGenesisState(stakingParams, nil, nil))},
) )
keeper := tApp.GetSavingsKeeper() keeper := tApp.GetSavingsKeeper()
suite.app = tApp suite.app = tApp
@ -131,6 +164,14 @@ func (suite *KeeperTestSuite) TestWithdraw() {
suite.keeper = keeper suite.keeper = keeper
bankKeeper := tApp.GetBankKeeper() 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) err := suite.keeper.Deposit(suite.ctx, tc.args.depositor, tc.args.depositAmount)
suite.Require().NoError(err) suite.Require().NoError(err)

View File

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