Implement Hard strategy for Earn vaults (#1278)

* Simplify strategies to lend and savings

* Add hard and savings keepers

* Add ctx to strategy interface, fill in lend strategy

* Rename lend strategy to hard

* Fix hard deposit query, fix withdraw bank send

* Fix misleading borrow instead of withdraw for hard

* Remove liquidateall strategy method

* Withdraw tests

* Add hard gs to testutil suite

* Update withdraw tests with working hard strategy, clean strategy interface methods

* Check allowed denom for strategy

* Update GetVaultTotalValue doc note

* Update error wrap message for unsupported denom

* Remove unnecessary viewvault keeper

* Withdraw amount from account value, not supplied value

* Test value > supplied withdraw

* Use dec when dividing for withdrawAmountPercent

* Use the correct store prefix for vault shares

* Update swap references to earn

* Simplify vault shares, use a single share for all coins per address
This commit is contained in:
Derrick Lee 2022-07-28 09:39:57 -07:00 committed by GitHub
parent ae181604ff
commit 88d4868316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 813 additions and 336 deletions

View File

@ -624,6 +624,8 @@ func NewApp(
earnSubspace, earnSubspace,
app.accountKeeper, app.accountKeeper,
app.bankKeeper, app.bankKeeper,
hardKeeper,
savingsKeeper,
) )
// create committee keeper with router // create committee keeper with router
@ -996,9 +998,11 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
func (app *App) loadBlockedMaccAddrs() map[string]bool { func (app *App) loadBlockedMaccAddrs() map[string]bool {
modAccAddrs := app.ModuleAccountAddrs() modAccAddrs := app.ModuleAccountAddrs()
kavadistMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName) kavadistMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName)
earnMaccAddr := app.accountKeeper.GetModuleAddress(earntypes.ModuleName)
for addr := range modAccAddrs { for addr := range modAccAddrs {
// Set the kavadist module account address as unblocked // Set the kavadist and earn module account address as unblocked
if addr == kavadistMaccAddr.String() { if addr == kavadistMaccAddr.String() || addr == earnMaccAddr.String() {
modAccAddrs[addr] = false modAccAddrs[addr] = false
} }
} }

View File

@ -2752,9 +2752,8 @@ Msg defines the committee Msg service
| Name | Number | Description | | Name | Number | Description |
| ---- | ------ | ----------- | | ---- | ------ | ----------- |
| STRATEGY_TYPE_UNKNOWN | 0 | | | STRATEGY_TYPE_UNKNOWN | 0 | |
| STRATEGY_TYPE_KAVA_STAKERS | 1 | | | STRATEGY_TYPE_HARD | 1 | |
| STRATEGY_TYPE_STABLECOIN_STAKERS | 2 | USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord | | STRATEGY_TYPE_SAVINGS | 2 | |
| STRATEGY_TYPE_KAVA_FOUNDATION | 3 | |
<!-- end enums --> <!-- end enums -->
@ -2809,13 +2808,13 @@ vault.
<a name="kava.earn.v1beta1.VaultShareRecord"></a> <a name="kava.earn.v1beta1.VaultShareRecord"></a>
### VaultShareRecord ### VaultShareRecord
VaultShareRecord defines the shares owned by a depositor and vault. VaultShareRecord defines the vault shares owned by a depositor.
| Field | Type | Label | Description | | Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- | | ----- | ---- | ----- | ----------- |
| `depositor` | [bytes](#bytes) | | depositor represents the owner of the shares | | `depositor` | [bytes](#bytes) | | Depositor represents the owner of the shares |
| `amount_supplied` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount_supplied represents the total amount a depositor has supplied to the vault. The vault is determined by the coin denom. | | `amount_supplied` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | AmountSupplied represents the total amount a depositor has supplied to the vault. The vault is determined by the coin denom. |
@ -2872,7 +2871,7 @@ VaultShareRecord defines the shares owned by a depositor and vault.
<a name="kava.earn.v1beta1.GenesisState"></a> <a name="kava.earn.v1beta1.GenesisState"></a>
### GenesisState ### GenesisState
GenesisState defines the swap module's genesis state. GenesisState defines the earn module's genesis state.
| Field | Type | Label | Description | | Field | Type | Label | Description |

View File

@ -7,7 +7,7 @@ import "kava/earn/v1beta1/vault.proto";
import "kava/earn/v1beta1/params.proto"; import "kava/earn/v1beta1/params.proto";
import "gogoproto/gogo.proto"; import "gogoproto/gogo.proto";
// GenesisState defines the swap module's genesis state. // GenesisState defines the earn module's genesis state.
message GenesisState { message GenesisState {
// params defines all the paramaters related to earn // params defines all the paramaters related to earn
Params params = 1 [(gogoproto.nullable) = false]; Params params = 1 [(gogoproto.nullable) = false];

View File

@ -11,8 +11,6 @@ enum StrategyType {
option (gogoproto.goproto_enum_prefix) = false; option (gogoproto.goproto_enum_prefix) = false;
STRATEGY_TYPE_UNKNOWN = 0; STRATEGY_TYPE_UNKNOWN = 0;
STRATEGY_TYPE_KAVA_STAKERS = 1; STRATEGY_TYPE_HARD = 1;
// USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord STRATEGY_TYPE_SAVINGS = 2;
STRATEGY_TYPE_STABLECOIN_STAKERS = 2;
STRATEGY_TYPE_KAVA_FOUNDATION = 3;
} }

View File

@ -29,14 +29,15 @@ message VaultRecord {
cosmos.base.v1beta1.Coin total_supply = 2 [(gogoproto.nullable) = false]; cosmos.base.v1beta1.Coin total_supply = 2 [(gogoproto.nullable) = false];
} }
// VaultShareRecord defines the shares owned by a depositor and vault. // VaultShareRecord defines the vault shares owned by a depositor.
message VaultShareRecord { message VaultShareRecord {
// depositor represents the owner of the shares // Depositor represents the owner of the shares
bytes depositor = 1 [ bytes depositor = 1 [
(cosmos_proto.scalar) = "cosmos.AddressBytes", (cosmos_proto.scalar) = "cosmos.AddressBytes",
(gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress" (gogoproto.casttype) = "github.com/cosmos/cosmos-sdk/types.AccAddress"
]; ];
// amount_supplied represents the total amount a depositor has supplied to the // AmountSupplied represents the total amount a depositor has supplied to the
// vault. The vault is determined by the coin denom. // vault. The vault is determined by the coin denom.
cosmos.base.v1beta1.Coin amount_supplied = 2 [(gogoproto.nullable) = false]; repeated cosmos.base.v1beta1.Coin amount_supplied = 2
[(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false];
} }

View File

@ -16,7 +16,7 @@ import (
// GetTxCmd returns the transaction commands for this module // GetTxCmd returns the transaction commands for this module
func GetTxCmd() *cobra.Command { func GetTxCmd() *cobra.Command {
swapTxCmd := &cobra.Command{ earnTxCmd := &cobra.Command{
Use: types.ModuleName, Use: types.ModuleName,
Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName),
DisableFlagParsing: true, DisableFlagParsing: true,
@ -33,9 +33,9 @@ func GetTxCmd() *cobra.Command {
flags.AddTxFlagsToCmd(cmd) flags.AddTxFlagsToCmd(cmd)
} }
swapTxCmd.AddCommand(cmds...) earnTxCmd.AddCommand(cmds...)
return swapTxCmd return earnTxCmd
} }
func getCmdDeposit() *cobra.Command { func getCmdDeposit() *cobra.Command {

View File

@ -2,6 +2,7 @@ package keeper
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/kava-labs/kava/x/earn/types" "github.com/kava-labs/kava/x/earn/types"
) )
@ -25,6 +26,22 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C
vaultRecord = types.NewVaultRecord(amount.Denom) vaultRecord = types.NewVaultRecord(amount.Denom)
} }
// Get the strategy for the vault
strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
if err != nil {
return err
}
// Check if this denom is allowed for the strategy
if !strategy.IsDenomSupported(amount.Denom) {
return sdkerrors.Wrapf(
types.ErrStrategyDenomNotSupported,
"denom %s is not supported by the strategy %s",
amount.Denom,
strategy.GetStrategyType(),
)
}
// Transfer amount to module account // Transfer amount to module account
if err := k.bankKeeper.SendCoinsFromAccountToModule( if err := k.bankKeeper.SendCoinsFromAccountToModule(
ctx, ctx,
@ -36,10 +53,10 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C
} }
// Get VaultShareRecord for account, create if not exist // Get VaultShareRecord for account, create if not exist
vaultShareRecord, found := k.GetVaultShareRecord(ctx, amount.Denom, depositor) vaultShareRecord, found := k.GetVaultShareRecord(ctx, depositor)
if !found { if !found {
// Create a new empty VaultShareRecord with 0 supply // Create a new empty VaultShareRecord with 0 supply
vaultShareRecord = types.NewVaultShareRecord(depositor, amount.Denom) vaultShareRecord = types.NewVaultShareRecord(depositor)
} }
// Increment VaultRecord supply // Increment VaultRecord supply
@ -52,14 +69,8 @@ func (k *Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.C
k.SetVaultRecord(ctx, vaultRecord) k.SetVaultRecord(ctx, vaultRecord)
k.SetVaultShareRecord(ctx, vaultShareRecord) k.SetVaultShareRecord(ctx, vaultShareRecord)
// Get the strategy for the vault
strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
if err != nil {
return err
}
// Deposit to the strategy // Deposit to the strategy
if err := strategy.Deposit(amount); err != nil { if err := strategy.Deposit(ctx, amount); err != nil {
return err return err
} }

View File

@ -34,11 +34,11 @@ func TestDepositTestSuite(t *testing.T) {
} }
func (suite *depositTestSuite) TestDeposit_Balances() { func (suite *depositTestSuite) TestDeposit_Balances() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -50,18 +50,16 @@ func (suite *depositTestSuite) TestDeposit_Balances() {
sdk.NewCoins(startBalance.Sub(depositAmount)), // Account decreases by deposit sdk.NewCoins(startBalance.Sub(depositAmount)), // Account decreases by deposit
) )
// TODO: Module account balance will be zero when strategies are implemented suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount))
suite.ModuleAccountBalanceEqual( suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount))
sdk.NewCoins(depositAmount),
)
} }
func (suite *depositTestSuite) TestDeposit_Exceed() { func (suite *depositTestSuite) TestDeposit_Exceed() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 1001) depositAmount := sdk.NewInt64Coin(vaultDenom, 1001)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -82,11 +80,11 @@ func (suite *depositTestSuite) TestDeposit_Exceed() {
} }
func (suite *depositTestSuite) TestDeposit_Zero() { func (suite *depositTestSuite) TestDeposit_Zero() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 0) depositAmount := sdk.NewInt64Coin(vaultDenom, 0)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -107,7 +105,7 @@ func (suite *depositTestSuite) TestDeposit_Zero() {
} }
func (suite *depositTestSuite) TestDeposit_InvalidVault() { func (suite *depositTestSuite) TestDeposit_InvalidVault() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 1001) depositAmount := sdk.NewInt64Coin(vaultDenom, 1001)

View File

@ -33,7 +33,7 @@ func TestGrpcQueryTestSuite(t *testing.T) {
} }
func (suite *grpcQueryTestSuite) TestQueryParams() { func (suite *grpcQueryTestSuite) TestQueryParams() {
vaultDenom := "busd" vaultDenom := "usdx"
res, err := suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest()) res, err := suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
suite.Require().NoError(err) suite.Require().NoError(err)
@ -41,14 +41,14 @@ func (suite *grpcQueryTestSuite) TestQueryParams() {
suite.Require().ElementsMatch(types.DefaultParams().AllowedVaults, res.Params.AllowedVaults) suite.Require().ElementsMatch(types.DefaultParams().AllowedVaults, res.Params.AllowedVaults)
// Add vault to params // Add vault to params
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Query again for added vault // Query again for added vault
res, err = suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest()) res, err = suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Require().Equal( suite.Require().Equal(
types.AllowedVaults{ types.AllowedVaults{
types.NewAllowedVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS), types.NewAllowedVault(vaultDenom, types.STRATEGY_TYPE_HARD),
}, },
res.Params.AllowedVaults, res.Params.AllowedVaults,
) )

View File

@ -15,6 +15,10 @@ type Keeper struct {
paramSubspace paramtypes.Subspace paramSubspace paramtypes.Subspace
accountKeeper types.AccountKeeper accountKeeper types.AccountKeeper
bankKeeper types.BankKeeper bankKeeper types.BankKeeper
// Keepers used for strategies
hardKeeper types.HardKeeper
savingsKeeper types.SavingsKeeper
} }
// NewKeeper creates a new keeper // NewKeeper creates a new keeper
@ -24,6 +28,8 @@ func NewKeeper(
paramstore paramtypes.Subspace, paramstore paramtypes.Subspace,
accountKeeper types.AccountKeeper, accountKeeper types.AccountKeeper,
bankKeeper types.BankKeeper, bankKeeper types.BankKeeper,
hardKeeper types.HardKeeper,
savingsKeeper types.SavingsKeeper,
) Keeper { ) Keeper {
if !paramstore.HasKeyTable() { if !paramstore.HasKeyTable() {
paramstore = paramstore.WithKeyTable(types.ParamKeyTable()) paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
@ -35,5 +41,7 @@ func NewKeeper(
paramSubspace: paramstore, paramSubspace: paramstore,
accountKeeper: accountKeeper, accountKeeper: accountKeeper,
bankKeeper: bankKeeper, bankKeeper: bankKeeper,
hardKeeper: hardKeeper,
savingsKeeper: savingsKeeper,
} }
} }

View File

@ -34,7 +34,7 @@ func TestMsgServerTestSuite(t *testing.T) {
func (suite *msgServerTestSuite) TestDeposit() { func (suite *msgServerTestSuite) TestDeposit() {
vaultDenom := "usdx" vaultDenom := "usdx"
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
@ -85,7 +85,7 @@ func (suite *msgServerTestSuite) TestDeposit() {
func (suite *msgServerTestSuite) TestWithdraw() { func (suite *msgServerTestSuite) TestWithdraw() {
vaultDenom := "usdx" vaultDenom := "usdx"
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)

View File

@ -9,15 +9,12 @@ import (
// Strategy is the interface that must be implemented by a strategy. // Strategy is the interface that must be implemented by a strategy.
type Strategy interface { type Strategy interface {
// GetName returns the name of the strategy. // GetStrategyType returns the strategy type
GetName() string GetStrategyType() types.StrategyType
// GetDescription returns the description of the strategy. // IsDenomSupported returns true if the denom is supported for this
GetDescription() string // strategy. For example, the hard strategy supports "usdx".
IsDenomSupported(string) bool
// GetSupportedDenoms returns a slice of supported denom for this strategy.
// For example, stablecoin stakers strategy supports both "busd" and "usdc".
GetSupportedDenoms() []string
// GetEstimatedTotalAssets returns the estimated total assets denominated in // GetEstimatedTotalAssets returns the estimated total assets denominated in
// GetDenom() of this strategy. This is the value if the strategy were to // GetDenom() of this strategy. This is the value if the strategy were to
@ -25,29 +22,23 @@ type Strategy interface {
// //
// **Note:** This may not reflect the true value as it may become outdated // **Note:** This may not reflect the true value as it may become outdated
// from market changes. // from market changes.
GetEstimatedTotalAssets(denom string) (sdk.Coin, error) GetEstimatedTotalAssets(ctx sdk.Context, denom string) (sdk.Coin, error)
// Deposit the specified amount of coins into this strategy. The amount // Deposit the specified amount of coins into this strategy. The amount
// must be denominated in GetDenom(). // must be denominated in GetDenom().
Deposit(amount sdk.Coin) error Deposit(ctx sdk.Context, amount sdk.Coin) error
// Withdraw the specified amount of coins from this strategy. The amount // Withdraw the specified amount of coins from this strategy. The amount
// must be denominated in GetDenom(). // must be denominated in GetDenom().
Withdraw(amount sdk.Coin) error Withdraw(ctx sdk.Context, amount sdk.Coin) error
// LiquidateAll liquidates all of the entire strategy's positions, returning
// the amount of liquidated denominated in GetDenom(). This should be only
// called during use of emergency via governance.
LiquidateAll() (amount sdk.Coin, err error)
} }
// GetStrategy returns the strategy for the given strategy type.
func (k *Keeper) GetStrategy(strategyType types.StrategyType) (Strategy, error) { func (k *Keeper) GetStrategy(strategyType types.StrategyType) (Strategy, error) {
switch strategyType { switch strategyType {
case types.STRATEGY_TYPE_STABLECOIN_STAKERS: case types.STRATEGY_TYPE_HARD:
return (*StableCoinStrategy)(k), nil return (*HardStrategy)(k), nil
case types.STRATEGY_TYPE_KAVA_STAKERS: case types.STRATEGY_TYPE_SAVINGS:
panic("unimplemented")
case types.STRATEGY_TYPE_KAVA_FOUNDATION:
panic("unimplemented") panic("unimplemented")
default: default:
return nil, fmt.Errorf("unknown strategy type: %s", strategyType) return nil, fmt.Errorf("unknown strategy type: %s", strategyType)

View File

@ -0,0 +1,48 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/earn/types"
)
// HardStrategy defines the strategy that deposits assets to Hard
type HardStrategy Keeper
var _ Strategy = (*HardStrategy)(nil)
func (s *HardStrategy) GetStrategyType() types.StrategyType {
return types.STRATEGY_TYPE_HARD
}
func (s *HardStrategy) IsDenomSupported(denom string) bool {
return denom == "usdx"
}
func (s *HardStrategy) GetEstimatedTotalAssets(ctx sdk.Context, denom string) (sdk.Coin, error) {
macc := s.accountKeeper.GetModuleAccount(ctx, types.ModuleName)
deposit, found := s.hardKeeper.GetSyncedDeposit(ctx, macc.GetAddress())
if !found {
// Return 0 if no deposit exists for module account
return sdk.NewCoin(denom, sdk.ZeroInt()), nil
}
// Only return the deposit for the vault denom.
for _, coin := range deposit.Amount {
if coin.Denom == denom {
return coin, nil
}
}
// Return 0 if no deposit exists for the vault denom
return sdk.NewCoin(denom, sdk.ZeroInt()), nil
}
func (s *HardStrategy) Deposit(ctx sdk.Context, amount sdk.Coin) error {
macc := s.accountKeeper.GetModuleAccount(ctx, types.ModuleName)
return s.hardKeeper.Deposit(ctx, macc.GetAddress(), sdk.NewCoins(amount))
}
func (s *HardStrategy) Withdraw(ctx sdk.Context, amount sdk.Coin) error {
macc := s.accountKeeper.GetModuleAccount(ctx, types.ModuleName)
return s.hardKeeper.Withdraw(ctx, macc.GetAddress(), sdk.NewCoins(amount))
}

View File

@ -0,0 +1,300 @@
package keeper_test
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/kava-labs/kava/x/earn/testutil"
"github.com/kava-labs/kava/x/earn/types"
"github.com/stretchr/testify/suite"
)
type strategyHardTestSuite struct {
testutil.Suite
}
func (suite *strategyHardTestSuite) SetupTest() {
suite.Suite.SetupTest()
suite.Keeper.SetParams(suite.Ctx, types.DefaultParams())
}
func TestStrategyLendTestSuite(t *testing.T) {
suite.Run(t, new(strategyHardTestSuite))
}
func (suite *strategyHardTestSuite) TestGetSupportedDenoms() {
strategy, err := suite.Keeper.GetStrategy(types.STRATEGY_TYPE_HARD)
suite.Require().NoError(err)
suite.True(strategy.IsDenomSupported("usdx"))
}
func (suite *strategyHardTestSuite) TestGetStrategyType() {
strategy, err := suite.Keeper.GetStrategy(types.STRATEGY_TYPE_HARD)
suite.Require().NoError(err)
suite.Equal(types.STRATEGY_TYPE_HARD, strategy.GetStrategyType())
}
func (suite *strategyHardTestSuite) TestDeposit_InvalidDenom() {
// Not supported by hard strategy
vaultDenom := "busd"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().Error(err)
suite.Require().ErrorIs(
err,
types.ErrStrategyDenomNotSupported,
"strategy should only allow usdx deposits",
)
}
func (suite *strategyHardTestSuite) TestDeposit_SingleAcc() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
suite.HardDepositAmountEqual(sdk.NewCoins(depositAmount))
suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount))
suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount))
// Query vault total
totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(depositAmount, totalValue)
}
func (suite *strategyHardTestSuite) TestDeposit_SingleAcc_MultipleDeposits() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
// Second deposit
err = suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
expectedVaultBalance := sdk.NewCoins(depositAmount.Add(depositAmount))
suite.HardDepositAmountEqual(expectedVaultBalance)
suite.VaultTotalValuesEqual(expectedVaultBalance)
suite.VaultTotalSuppliedEqual(expectedVaultBalance)
// Query vault total
totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(depositAmount.Add(depositAmount), totalValue)
}
func (suite *strategyHardTestSuite) TestDeposit_MultipleAcc_MultipleDeposits() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
expectedTotalValue := sdk.NewCoin(vaultDenom, depositAmount.Amount.MulRaw(4))
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
// 2 deposits each account
for i := 0; i < 2; i++ {
// Deposit from acc1
err := suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), depositAmount)
suite.Require().NoError(err)
// Deposit from acc2
err = suite.Keeper.Deposit(suite.Ctx, acc2.GetAddress(), depositAmount)
suite.Require().NoError(err)
}
suite.HardDepositAmountEqual(sdk.NewCoins(expectedTotalValue))
suite.VaultTotalValuesEqual(sdk.NewCoins(expectedTotalValue))
suite.VaultTotalSuppliedEqual(sdk.NewCoins(expectedTotalValue))
// Query vault total
totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(expectedTotalValue, totalValue)
}
func (suite *strategyHardTestSuite) TestGetVaultTotalValue_Empty() {
vaultDenom := "usdx"
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Query vault total
totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(sdk.NewCoin(vaultDenom, sdk.ZeroInt()), totalValue)
}
func (suite *strategyHardTestSuite) TestGetVaultTotalValue_NoDenomDeposit() {
// 2 Vaults usdx, busd
// 1st vault has deposits
// 2nd vault has no deposits
vaultDenom := "usdx"
vaultDenomBusd := "busd"
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
suite.CreateVault(vaultDenomBusd, types.STRATEGY_TYPE_HARD)
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
// Deposit vault1
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
// Query vault total, hard deposit exists for account, but amount in busd does not
// Vault2 does not have any value, only returns amount for the correct denom
// if a hard deposit already exists
totalValueBusd, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenomBusd)
suite.Require().NoError(err)
suite.Equal(sdk.NewCoin(vaultDenomBusd, sdk.ZeroInt()), totalValueBusd)
}
// ----------------------------------------------------------------------------
// Withdraw
func (suite *strategyHardTestSuite) TestWithdraw() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
suite.HardDepositAmountEqual(sdk.NewCoins(depositAmount))
// Query vault total
totalValue, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(depositAmount, totalValue)
// Withdraw
err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err)
suite.HardDepositAmountEqual(sdk.NewCoins())
suite.VaultTotalValuesEqual(sdk.NewCoins())
suite.VaultTotalSuppliedEqual(sdk.NewCoins())
totalValue, err = suite.Keeper.GetVaultTotalValue(suite.Ctx, vaultDenom)
suite.Require().NoError(err)
suite.Equal(sdk.NewInt64Coin(vaultDenom, 0), totalValue)
// Withdraw again
err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound, "vault should be deleted when no more supply")
}
func (suite *strategyHardTestSuite) TestWithdraw_OnlyWithdrawOwnSupply() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Deposits from 2 accounts
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress()
acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1).GetAddress()
err := suite.Keeper.Deposit(suite.Ctx, acc1, depositAmount)
suite.Require().NoError(err)
err = suite.Keeper.Deposit(suite.Ctx, acc2, depositAmount)
suite.Require().NoError(err)
// Withdraw
err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount)
suite.Require().NoError(err)
// Withdraw again
err = suite.Keeper.Withdraw(suite.Ctx, acc1, depositAmount)
suite.Require().Error(err)
suite.Require().ErrorIs(
err,
types.ErrVaultShareRecordNotFound,
"should only be able to withdraw the account's own supply",
)
}
func (suite *strategyHardTestSuite) TestWithdraw_WithAccumulatedHard() {
vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Deposits from 2 accounts
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0).GetAddress()
err := suite.Keeper.Deposit(suite.Ctx, acc, depositAmount)
suite.Require().NoError(err)
// Direct hard deposit from module account to increase vault value
suite.App.FundModuleAccount(suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10)))
macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName)
suite.HardKeeper.Deposit(suite.Ctx, macc.GetAddress(), sdk.NewCoins(sdk.NewInt64Coin(vaultDenom, 10)))
// Query account value
accValue, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc)
suite.Require().NoError(err)
suite.Equal(depositAmount.AddAmount(sdk.NewInt(10)), accValue)
// Withdraw 10, 10 remaining
err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount)
suite.Require().NoError(err)
// Withdraw again -- too much
err = suite.Keeper.Withdraw(suite.Ctx, acc, depositAmount)
suite.Require().Error(err)
suite.Require().ErrorIs(
err,
types.ErrInsufficientValue,
"cannot withdraw more than account value",
)
// Half of remaining 10, 5 remaining
err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)))
suite.Require().NoError(err)
// Withdraw all
err = suite.Keeper.Withdraw(suite.Ctx, acc, sdk.NewCoin(vaultDenom, sdk.NewInt(5)))
suite.Require().NoError(err)
_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc)
suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrVaultRecordNotFound)
}

View File

@ -1,42 +0,0 @@
package keeper
import sdk "github.com/cosmos/cosmos-sdk/types"
// StableCoinStrategy defines the stablecoin strategy:
// 1. Supply USDX to Lend
type StableCoinStrategy Keeper
var _ Strategy = (*StableCoinStrategy)(nil)
func (s *StableCoinStrategy) GetName() string {
return "USDX"
}
func (s *StableCoinStrategy) GetDescription() string {
return "Supplies the USDX to Lend"
}
func (s *StableCoinStrategy) GetSupportedDenoms() []string {
return []string{"usdx"}
}
func (s *StableCoinStrategy) GetEstimatedTotalAssets(denom string) (sdk.Coin, error) {
// 1. Get amount of USDX in Lend
return sdk.Coin{}, nil
}
func (s *StableCoinStrategy) Deposit(amount sdk.Coin) error {
return nil
}
func (s *StableCoinStrategy) Withdraw(amount sdk.Coin) error {
return nil
}
// LiquidateAll liquidates all assets in the strategy, this should be called
// only in case of emergency or when all assets should be moved to a new
// strategy.
func (s *StableCoinStrategy) LiquidateAll() (amount sdk.Coin, err error) {
return sdk.Coin{}, nil
}

View File

@ -6,29 +6,6 @@ import (
"github.com/kava-labs/kava/x/earn/types" "github.com/kava-labs/kava/x/earn/types"
) )
// ViewVaultKeeper defines the read-only methods used for querying vaults.
type ViewVaultKeeper interface {
// GetVaultTotalSupplied returns the total balance supplied to a vault. This
// may not necessarily be the current value of the vault, as it is the sum
// of the supplied denom.
GetVaultTotalSupplied(ctx sdk.Context, denom string) (sdk.Coin, error)
// GetVaultTotalValue returns the total **value** of all coins in a vault,
// i.e. the realizable total value denominated by GetDenom() if the vault
// were to liquidate its entire strategies.
GetVaultTotalValue(ctx sdk.Context, denom string) (sdk.Coin, error)
// GetVaultAccountSupplied returns the supplied amount for a single address
// within the vault.
GetVaultAccountSupplied(ctx sdk.Context, denom string, acc sdk.AccAddress) (sdk.Coin, error)
// GetVaultAccountValue returns the value of a single address within a vault
// if the account were to withdraw their entire balance.
GetVaultAccountValue(ctx sdk.Context, denom string, acc sdk.AccAddress) (sdk.Coin, error)
}
var _ ViewVaultKeeper = (*Keeper)(nil)
// GetVaultTotalSupplied returns the total balance supplied to the vault. This // GetVaultTotalSupplied returns the total balance supplied to the vault. This
// may not necessarily be the current value of the vault, as it is the sum // may not necessarily be the current value of the vault, as it is the sum
// of the supplied denom and the value may be higher due to accumulated APYs. // of the supplied denom and the value may be higher due to accumulated APYs.
@ -47,6 +24,10 @@ func (k *Keeper) GetVaultTotalSupplied(
// GetTotalValue returns the total **value** of all coins in this vault, // GetTotalValue returns the total **value** of all coins in this vault,
// i.e. the realizable total value denominated by GetDenom() if the vault // i.e. the realizable total value denominated by GetDenom() if the vault
// were to liquidate its entire strategies. // were to liquidate its entire strategies.
//
// **Note:** This does not include the tokens held in bank by the module
// account. If it were to be included, also note that the module account is
// unblocked and can receive funds from bank sends.
func (k *Keeper) GetVaultTotalValue( func (k *Keeper) GetVaultTotalValue(
ctx sdk.Context, ctx sdk.Context,
denom string, denom string,
@ -61,19 +42,18 @@ func (k *Keeper) GetVaultTotalValue(
return sdk.Coin{}, types.ErrInvalidVaultStrategy return sdk.Coin{}, types.ErrInvalidVaultStrategy
} }
return strategy.GetEstimatedTotalAssets(enabledVault.Denom) return strategy.GetEstimatedTotalAssets(ctx, enabledVault.Denom)
} }
// GetVaultAccountSupplied returns the supplied amount for a single address // GetVaultAccountSupplied returns the supplied amount for a single address
// within a vault. // within a vault.
func (k *Keeper) GetVaultAccountSupplied( func (k *Keeper) GetVaultAccountSupplied(
ctx sdk.Context, ctx sdk.Context,
denom string,
acc sdk.AccAddress, acc sdk.AccAddress,
) (sdk.Coin, error) { ) (sdk.Coins, error) {
vaultShareRecord, found := k.GetVaultShareRecord(ctx, denom, acc) vaultShareRecord, found := k.GetVaultShareRecord(ctx, acc)
if !found { if !found {
return sdk.Coin{}, types.ErrVaultShareRecordNotFound return sdk.Coins{}, types.ErrVaultShareRecordNotFound
} }
return vaultShareRecord.AmountSupplied, nil return vaultShareRecord.AmountSupplied, nil
@ -91,7 +71,7 @@ func (k *Keeper) GetVaultAccountValue(
return sdk.Coin{}, err return sdk.Coin{}, err
} }
accSupplied, err := k.GetVaultAccountSupplied(ctx, denom, acc) accSupplied, err := k.GetVaultAccountSupplied(ctx, acc)
if err != nil { if err != nil {
return sdk.Coin{}, err return sdk.Coin{}, err
} }
@ -101,12 +81,12 @@ func (k *Keeper) GetVaultAccountValue(
return sdk.Coin{}, err return sdk.Coin{}, err
} }
// percent of vault account ownership = accountSupply / totalSupply // Percent of vault account ownership = accountSupply / totalSupply
// value of vault account ownership = percentOwned * totalValue // Value of vault account ownership = percentOwned * totalValue
vaultShare := accSupplied.Amount.Quo(totalSupplied.Amount) vaultShare := accSupplied.AmountOf(denom).ToDec().Quo(totalSupplied.Amount.ToDec())
shareValue := vaultTotalValue.Amount.Mul(vaultShare) shareValueDec := vaultTotalValue.Amount.ToDec().Mul(vaultShare)
return sdk.NewCoin(denom, shareValue), nil return sdk.NewCoin(denom, shareValueDec.TruncateInt()), nil
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -163,12 +143,11 @@ func (k *Keeper) SetVaultRecord(ctx sdk.Context, record types.VaultRecord) {
// account. // account.
func (k *Keeper) GetVaultShareRecord( func (k *Keeper) GetVaultShareRecord(
ctx sdk.Context, ctx sdk.Context,
vaultDenom string,
acc sdk.AccAddress, acc sdk.AccAddress,
) (types.VaultShareRecord, bool) { ) (types.VaultShareRecord, bool) {
store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix)
bz := store.Get(types.DepositorVaultSharesKey(acc, vaultDenom)) bz := store.Get(types.DepositorVaultSharesKey(acc))
if bz == nil { if bz == nil {
return types.VaultShareRecord{}, false return types.VaultShareRecord{}, false
} }
@ -187,7 +166,7 @@ func (k *Keeper) UpdateVaultShareRecord(
record types.VaultShareRecord, record types.VaultShareRecord,
) { ) {
if record.AmountSupplied.IsZero() { if record.AmountSupplied.IsZero() {
k.DeleteVaultShareRecord(ctx, record.AmountSupplied.Denom, record.Depositor) k.DeleteVaultShareRecord(ctx, record.Depositor)
} else { } else {
k.SetVaultShareRecord(ctx, record) k.SetVaultShareRecord(ctx, record)
} }
@ -197,11 +176,10 @@ func (k *Keeper) UpdateVaultShareRecord(
// account. // account.
func (k *Keeper) DeleteVaultShareRecord( func (k *Keeper) DeleteVaultShareRecord(
ctx sdk.Context, ctx sdk.Context,
vaultDenom string,
acc sdk.AccAddress, acc sdk.AccAddress,
) { ) {
store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix)
store.Delete(types.DepositorVaultSharesKey(acc, vaultDenom)) store.Delete(types.DepositorVaultSharesKey(acc))
} }
// SetVaultShareRecord sets the vault share record for a given denom and account. // SetVaultShareRecord sets the vault share record for a given denom and account.
@ -209,7 +187,7 @@ func (k *Keeper) SetVaultShareRecord(
ctx sdk.Context, ctx sdk.Context,
record types.VaultShareRecord, record types.VaultShareRecord,
) { ) {
store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix) store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix)
bz := k.cdc.MustMarshal(&record) bz := k.cdc.MustMarshal(&record)
store.Set(types.DepositorVaultSharesKey(record.Depositor, record.AmountSupplied.Denom), bz) store.Set(types.DepositorVaultSharesKey(record.Depositor), bz)
} }

View File

@ -28,7 +28,7 @@ func (suite *vaultTestSuite) TestGetVaultTotalSupplied() {
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -76,18 +76,18 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() {
deposit1Amount := sdk.NewInt64Coin(vaultDenom, 100) deposit1Amount := sdk.NewInt64Coin(vaultDenom, 100)
deposit2Amount := sdk.NewInt64Coin(vaultDenom, 100) deposit2Amount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1) acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1)
// Before deposit, account supplied is 0 // Before deposit, account supplied is 0
_, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, vaultDenom, acc1.GetAddress()) _, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc1.GetAddress())
suite.Require().Error(err) suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound)
_, err = suite.Keeper.GetVaultAccountSupplied(suite.Ctx, vaultDenom, acc2.GetAddress()) _, err = suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc2.GetAddress())
suite.Require().Error(err) suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound) suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound)
@ -101,15 +101,15 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() {
// Check balances // Check balances
vaultAcc1Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, vaultDenom, acc1.GetAddress()) vaultAcc1Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc1.GetAddress())
suite.Require().NoError(err) suite.Require().NoError(err)
vaultAcc2Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, vaultDenom, acc2.GetAddress()) vaultAcc2Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc2.GetAddress())
suite.Require().NoError(err) suite.Require().NoError(err)
// Account supply only includes the deposit from respective accounts // Account supply only includes the deposit from respective accounts
suite.Equal(deposit1Amount, vaultAcc1Supplied) suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc1Supplied)
suite.Equal(deposit1Amount, vaultAcc2Supplied) suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc2Supplied)
} }
func (suite *vaultTestSuite) TestGetVaultAccountValue() { func (suite *vaultTestSuite) TestGetVaultAccountValue() {
@ -119,16 +119,14 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue() {
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount) err := suite.Keeper.Deposit(suite.Ctx, acc.GetAddress(), depositAmount)
suite.Require().NoError(err) suite.Require().NoError(err)
suite.T().Skip("TODO: After strategy GetEstimatedTotalAssets implemented") accValue, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc.GetAddress())
suite.Require().NoError(err)
_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc.GetAddress()) suite.Equal(depositAmount, accValue, "value should be same as deposit amount")
suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound)
} }
func (suite *vaultTestSuite) TestGetVaultAccountValue_VaultNotFound() { func (suite *vaultTestSuite) TestGetVaultAccountValue_VaultNotFound() {
@ -148,7 +146,7 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue_ShareNotFound() {
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1) acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Deposit from acc1 so that vault record exists // Deposit from acc1 so that vault record exists
err := suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), depositAmount) err := suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), depositAmount)
@ -202,19 +200,19 @@ func (suite *vaultTestSuite) TestGetVaultShareRecord() {
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
record := types.NewVaultShareRecord(acc.GetAddress(), vaultDenom) record := types.NewVaultShareRecord(acc.GetAddress())
// Check share doesn't exist before deposit // Check share doesn't exist before deposit
_, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, vaultDenom, acc.GetAddress()) _, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress())
suite.Require().False(found, "vault share record should not exist before deposit") suite.Require().False(found, "vault share record should not exist before deposit")
// Update share record // Update share record
record.AmountSupplied = depositAmount record.AmountSupplied = sdk.NewCoins(depositAmount)
suite.Keeper.SetVaultShareRecord(suite.Ctx, record) suite.Keeper.SetVaultShareRecord(suite.Ctx, record)
// Check share exists and matches set value // Check share exists and matches set value
stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, vaultDenom, acc.GetAddress()) stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress())
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(record, stateRecord) suite.Require().Equal(record, stateRecord)
} }
@ -225,21 +223,19 @@ func (suite *vaultTestSuite) TestUpdateVaultShareRecord() {
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
record := types.NewVaultShareRecord(acc.GetAddress(), vaultDenom) record := types.NewVaultShareRecord(acc.GetAddress(), depositAmount)
record.AmountSupplied = depositAmount
// Update vault // Update vault
suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record)
stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, vaultDenom, acc.GetAddress()) stateRecord, found := suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress())
suite.Require().True(found, "vault share record with supply should exist") suite.Require().True(found, "vault share record with supply should exist")
suite.Require().Equal(record, stateRecord) suite.Require().Equal(record, stateRecord)
// Remove supply // Remove supply
record.AmountSupplied = sdk.NewInt64Coin("usdx", 0) record.AmountSupplied = sdk.NewCoins()
suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record) suite.Keeper.UpdateVaultShareRecord(suite.Ctx, record)
_, found = suite.Keeper.GetVaultShareRecord(suite.Ctx, vaultDenom, acc.GetAddress()) _, found = suite.Keeper.GetVaultShareRecord(suite.Ctx, acc.GetAddress())
suite.Require().False(found, "vault share record with 0 supply should be deleted") suite.Require().False(found, "vault share record with 0 supply should be deleted")
} }

View File

@ -1,6 +1,8 @@
package keeper package keeper
import ( import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -9,74 +11,106 @@ import (
// Withdraw removes the amount of supplied tokens from a vault and transfers it // Withdraw removes the amount of supplied tokens from a vault and transfers it
// back to the account. // back to the account.
func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, amount sdk.Coin) error { func (k *Keeper) Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.Coin) error {
// Get AllowedVault, if not found (not a valid vault), return error // Get AllowedVault, if not found (not a valid vault), return error
allowedVault, found := k.GetAllowedVault(ctx, amount.Denom) allowedVault, found := k.GetAllowedVault(ctx, wantAmount.Denom)
if !found { if !found {
return types.ErrInvalidVaultDenom return types.ErrInvalidVaultDenom
} }
if amount.IsZero() { if wantAmount.IsZero() {
return types.ErrInsufficientAmount return types.ErrInsufficientAmount
} }
// Check if VaultRecord exists, return error if not exist as it's empty // Check if VaultRecord exists
vaultRecord, found := k.GetVaultRecord(ctx, amount.Denom) vaultRecord, found := k.GetVaultRecord(ctx, wantAmount.Denom)
if !found { if !found {
return types.ErrVaultRecordNotFound return types.ErrVaultRecordNotFound
} }
// Get VaultShareRecord for account, create if not exist // Get account value for vault
vaultShareRecord, found := k.GetVaultShareRecord(ctx, amount.Denom, from) vaultAccValue, err := k.GetVaultAccountValue(ctx, wantAmount.Denom, from)
if err != nil {
return err
}
if vaultAccValue.IsZero() {
panic("vault account value is zero")
}
// Get account share record for the vault
vaultShareRecord, found := k.GetVaultShareRecord(ctx, from)
if !found { if !found {
return types.ErrVaultShareRecordNotFound return types.ErrVaultShareRecordNotFound
} }
// Check if VaultShareRecord has enough supplied to withdraw // Percent of vault account value the account is withdrawing
if vaultShareRecord.AmountSupplied.Amount.LT(amount.Amount) { // This is the total account value, not just the supplied amount.
withdrawAmountPercent := wantAmount.Amount.ToDec().Quo(vaultAccValue.Amount.ToDec())
// Check if account is not withdrawing more than they have
// account value < want withdraw amount
if vaultAccValue.Amount.LT(wantAmount.Amount) {
return sdkerrors.Wrapf( return sdkerrors.Wrapf(
types.ErrInvalidShares, types.ErrInsufficientValue,
"withdraw of %s shares greater than %s shares supplied", "account vault value of %s is less than %s desired withdraw amount",
amount, vaultAccValue,
vaultShareRecord.AmountSupplied, wantAmount,
) )
} }
// Send coins back to account
if err := k.bankKeeper.SendCoinsFromModuleToAccount(
ctx,
types.ModuleName,
from,
sdk.NewCoins(amount),
); err != nil {
return err
}
// Decrement VaultRecord and VaultShareRecord supplies
vaultRecord.TotalSupply = vaultRecord.TotalSupply.Sub(amount)
vaultShareRecord.AmountSupplied = vaultShareRecord.AmountSupplied.Sub(amount)
// Update VaultRecord and VaultShareRecord, deletes if zero supply
k.UpdateVaultRecord(ctx, vaultRecord)
k.UpdateVaultShareRecord(ctx, vaultShareRecord)
// Get the strategy for the vault // Get the strategy for the vault
strategy, err := k.GetStrategy(allowedVault.VaultStrategy) strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
if err != nil { if err != nil {
return err return err
} }
// Deposit to the strategy // Not necessary to check if amount denom is allowed for the strategy, as
if err := strategy.Withdraw(amount); err != nil { // there would be no vault record if it weren't allowed.
// Withdraw the wantAmount from the strategy
if err := strategy.Withdraw(ctx, wantAmount); err != nil {
return fmt.Errorf("failed to withdraw from strategy: %w", err)
}
// Send coins back to account, must withdraw from strategy first or the
// module account may not have any funds to send.
if err := k.bankKeeper.SendCoinsFromModuleToAccount(
ctx,
types.ModuleName,
from,
sdk.NewCoins(wantAmount),
); err != nil {
return err return err
} }
// Shares withdrawn from vault
// For example:
// account supplied = 10hard
// account value = 20hard
// wantAmount = 10hard
// withdrawAmountPercent = 10hard / 20hard = 0.5
// sharesWithdrawn = 0.5 * 10hard = 5hard
vaultShareAmount := vaultShareRecord.AmountSupplied.AmountOf(wantAmount.Denom)
sharesWithdrawn := sdk.NewCoin(wantAmount.Denom, vaultShareAmount.
ToDec().
Mul(withdrawAmountPercent).
TruncateInt())
// Decrement VaultRecord and VaultShareRecord supplies
vaultRecord.TotalSupply = vaultRecord.TotalSupply.Sub(sharesWithdrawn)
vaultShareRecord.AmountSupplied = vaultShareRecord.AmountSupplied.Sub(sdk.NewCoins(sharesWithdrawn))
// Update VaultRecord and VaultShareRecord, deletes if zero supply
k.UpdateVaultRecord(ctx, vaultRecord)
k.UpdateVaultShareRecord(ctx, vaultShareRecord)
ctx.EventManager().EmitEvent( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
types.EventTypeVaultWithdraw, types.EventTypeVaultWithdraw,
sdk.NewAttribute(types.AttributeKeyVaultDenom, amount.Denom), sdk.NewAttribute(types.AttributeKeyVaultDenom, wantAmount.Denom),
sdk.NewAttribute(types.AttributeKeyOwner, from.String()), sdk.NewAttribute(types.AttributeKeyOwner, from.String()),
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.Amount.String()), sdk.NewAttribute(sdk.AttributeKeyAmount, wantAmount.Amount.String()),
), ),
) )

View File

@ -24,11 +24,11 @@ func TestWithdrawTestSuite(t *testing.T) {
} }
func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() { func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 100) withdrawAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -49,13 +49,13 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() {
} }
func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() { func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
acc1DepositAmount := sdk.NewCoin(vaultDenom, sdk.NewInt(100)) acc1DepositAmount := sdk.NewCoin(vaultDenom, sdk.NewInt(100))
acc2WithdrawAmount := sdk.NewInt64Coin(vaultDenom, 100) acc2WithdrawAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
// Create deposit from acc1 so the VaultRecord exists in state // Create deposit from acc1 so the VaultRecord exists in state
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -75,18 +75,17 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() {
sdk.NewCoins(startBalance), sdk.NewCoins(startBalance),
) )
suite.ModuleAccountBalanceEqual( suite.VaultTotalValuesEqual(sdk.NewCoins(acc1DepositAmount))
sdk.NewCoins(acc1DepositAmount), suite.VaultTotalSuppliedEqual(sdk.NewCoins(acc1DepositAmount))
)
} }
func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() { func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 200) withdrawAmount := sdk.NewInt64Coin(vaultDenom, 200)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -95,7 +94,7 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount) err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount)
suite.Require().Error(err) suite.Require().Error(err)
suite.Require().ErrorIs(err, types.ErrInvalidShares) suite.Require().ErrorIs(err, types.ErrInsufficientValue)
// Balances still the same after deposit // Balances still the same after deposit
suite.AccountBalanceEqual( suite.AccountBalanceEqual(
@ -103,17 +102,16 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
sdk.NewCoins(startBalance.Sub(depositAmount)), sdk.NewCoins(startBalance.Sub(depositAmount)),
) )
suite.ModuleAccountBalanceEqual( suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount))
sdk.NewCoins(depositAmount), suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount))
)
} }
func (suite *withdrawTestSuite) TestWithdraw_Zero() { func (suite *withdrawTestSuite) TestWithdraw_Zero() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 0) withdrawAmount := sdk.NewInt64Coin(vaultDenom, 0)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -134,7 +132,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Zero() {
} }
func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() { func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 1001) withdrawAmount := sdk.NewInt64Coin(vaultDenom, 1001)
@ -159,12 +157,12 @@ func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() {
} }
func (suite *withdrawTestSuite) TestWithdraw_FullBalance() { func (suite *withdrawTestSuite) TestWithdraw_FullBalance() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 100) withdrawAmount := sdk.NewInt64Coin(vaultDenom, 100)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
@ -186,12 +184,12 @@ func (suite *withdrawTestSuite) TestWithdraw_FullBalance() {
} }
func (suite *withdrawTestSuite) TestWithdraw_Partial() { func (suite *withdrawTestSuite) TestWithdraw_Partial() {
vaultDenom := "busd" vaultDenom := "usdx"
startBalance := sdk.NewInt64Coin(vaultDenom, 1000) startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
depositAmount := sdk.NewInt64Coin(vaultDenom, 100) depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
partialWithdrawAmount := sdk.NewInt64Coin(vaultDenom, 50) partialWithdrawAmount := sdk.NewInt64Coin(vaultDenom, 50)
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS) suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0) acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)

View File

@ -3,16 +3,23 @@ package testutil
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"time"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/earn/keeper" "github.com/kava-labs/kava/x/earn/keeper"
"github.com/kava-labs/kava/x/earn/types" "github.com/kava-labs/kava/x/earn/types"
"github.com/kava-labs/kava/x/hard"
hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
hardtypes "github.com/kava-labs/kava/x/hard/types"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
savingskeeper "github.com/kava-labs/kava/x/savings/keeper"
"github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
BankKeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
@ -20,19 +27,93 @@ import (
tmtime "github.com/tendermint/tendermint/types/time" tmtime "github.com/tendermint/tendermint/types/time"
) )
// Suite implements a test suite for the swap module integration tests // Suite implements a test suite for the earn module integration tests
type Suite struct { type Suite struct {
suite.Suite suite.Suite
Keeper keeper.Keeper Keeper keeper.Keeper
App app.TestApp App app.TestApp
Ctx sdk.Context Ctx sdk.Context
BankKeeper BankKeeper.Keeper BankKeeper bankkeeper.Keeper
AccountKeeper authkeeper.AccountKeeper AccountKeeper authkeeper.AccountKeeper
// Strategy Keepers
HardKeeper hardkeeper.Keeper
SavingsKeeper savingskeeper.Keeper
} }
// SetupTest instantiates a new app, keepers, and sets suite state // SetupTest instantiates a new app, keepers, and sets suite state
func (suite *Suite) SetupTest() { func (suite *Suite) SetupTest() {
// Pricefeed required for withdrawing from hard
pricefeedGS := pricefeedtypes.GenesisState{
Params: pricefeedtypes.Params{
Markets: []pricefeedtypes.Market{
{MarketID: "usdx:usd", BaseAsset: "usdx", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
},
},
PostedPrices: []pricefeedtypes.PostedPrice{
{
MarketID: "usdx:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("1.00"),
Expiry: time.Now().Add(100 * time.Hour),
},
{
MarketID: "kava:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("2.00"),
Expiry: time.Now().Add(100 * time.Hour),
},
{
MarketID: "bnb:usd",
OracleAddress: sdk.AccAddress{},
Price: sdk.MustNewDecFromStr("10.00"),
Expiry: time.Now().Add(100 * time.Hour),
},
},
}
hardGS := hardtypes.NewGenesisState(hardtypes.NewParams(
hardtypes.MoneyMarkets{
hardtypes.NewMoneyMarket(
"usdx",
hardtypes.NewBorrowLimit(
true,
sdk.MustNewDecFromStr("20000000"),
sdk.MustNewDecFromStr("1"),
),
"usdx:usd",
sdk.NewInt(1000000),
hardtypes.NewInterestRateModel(
sdk.MustNewDecFromStr("0.05"),
sdk.MustNewDecFromStr("2"),
sdk.MustNewDecFromStr("0.8"),
sdk.MustNewDecFromStr("10"),
),
sdk.MustNewDecFromStr("0.05"),
sdk.ZeroDec(),
),
},
sdk.NewDec(10),
),
hardtypes.DefaultAccumulationTimes,
hardtypes.DefaultDeposits,
hardtypes.DefaultBorrows,
hardtypes.DefaultTotalSupplied,
hardtypes.DefaultTotalBorrowed,
hardtypes.DefaultTotalReserves,
)
tApp := app.NewTestApp() tApp := app.NewTestApp()
tApp.InitializeFromGenesisStates(
app.GenesisState{
pricefeedtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&pricefeedGS),
hardtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&hardGS),
},
)
ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()}) ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
suite.Ctx = ctx suite.Ctx = ctx
@ -40,6 +121,11 @@ func (suite *Suite) SetupTest() {
suite.Keeper = tApp.GetEarnKeeper() suite.Keeper = tApp.GetEarnKeeper()
suite.BankKeeper = tApp.GetBankKeeper() suite.BankKeeper = tApp.GetBankKeeper()
suite.AccountKeeper = tApp.GetAccountKeeper() suite.AccountKeeper = tApp.GetAccountKeeper()
suite.HardKeeper = tApp.GetHardKeeper()
suite.SavingsKeeper = tApp.GetSavingsKeeper()
hard.BeginBlocker(suite.Ctx, suite.HardKeeper)
} }
// GetEvents returns emitted events on the sdk context // GetEvents returns emitted events on the sdk context
@ -47,16 +133,16 @@ func (suite *Suite) GetEvents() sdk.Events {
return suite.Ctx.EventManager().Events() return suite.Ctx.EventManager().Events()
} }
// AddCoinsToModule adds coins to the swap module account // AddCoinsToModule adds coins to the earn module account
func (suite *Suite) AddCoinsToModule(amount sdk.Coins) { func (suite *Suite) AddCoinsToModule(amount sdk.Coins) {
// Does not use suite.BankKeeper.MintCoins as module account would not have permission to mint // Does not use suite.BankKeeper.MintCoins as module account would not have permission to mint
err := simapp.FundModuleAccount(suite.BankKeeper, suite.Ctx, types.ModuleName, amount) err := simapp.FundModuleAccount(suite.BankKeeper, suite.Ctx, types.ModuleName, amount)
suite.Require().NoError(err) suite.Require().NoError(err)
} }
// RemoveCoinsFromModule removes coins to the swap module account // RemoveCoinsFromModule removes coins to the earn module account
func (suite *Suite) RemoveCoinsFromModule(amount sdk.Coins) { func (suite *Suite) RemoveCoinsFromModule(amount sdk.Coins) {
// Swap module does not have BurnCoins permission so we need to transfer to gov first to burn // Earn module does not have BurnCoins permission so we need to transfer to gov first to burn
err := suite.BankKeeper.SendCoinsFromModuleToModule(suite.Ctx, types.ModuleAccountName, govtypes.ModuleName, amount) err := suite.BankKeeper.SendCoinsFromModuleToModule(suite.Ctx, types.ModuleAccountName, govtypes.ModuleName, amount)
suite.Require().NoError(err) suite.Require().NoError(err)
err = suite.BankKeeper.BurnCoins(suite.Ctx, govtypes.ModuleName, amount) err = suite.BankKeeper.BurnCoins(suite.Ctx, govtypes.ModuleName, amount)
@ -96,12 +182,12 @@ func (suite *Suite) CreateVault(vaultDenom string, vaultStrategy types.StrategyT
vault := types.NewAllowedVault(vaultDenom, vaultStrategy) vault := types.NewAllowedVault(vaultDenom, vaultStrategy)
suite.Require().NoError(vault.Validate()) suite.Require().NoError(vault.Validate())
// allowedVaults := suite.Keeper.GetAllowedVaults(suite.Ctx) allowedVaults := suite.Keeper.GetAllowedVaults(suite.Ctx)
// allowedVaults = append(allowedVaults, vault) allowedVaults = append(allowedVaults, vault)
suite.Keeper.SetParams( suite.Keeper.SetParams(
suite.Ctx, suite.Ctx,
types.NewParams(types.AllowedVaults{vault}), types.NewParams(allowedVaults),
) )
} }
@ -111,7 +197,7 @@ func (suite *Suite) AccountBalanceEqual(addr sdk.AccAddress, coins sdk.Coins) {
suite.Equal(coins, balance, fmt.Sprintf("expected account balance to equal coins %s, but got %s", coins, balance)) suite.Equal(coins, balance, fmt.Sprintf("expected account balance to equal coins %s, but got %s", coins, balance))
} }
// ModuleAccountBalanceEqual asserts that the swap module account balance matches the provided coins // ModuleAccountBalanceEqual asserts that the earn module account balance matches the provided coins
func (suite *Suite) ModuleAccountBalanceEqual(coins sdk.Coins) { func (suite *Suite) ModuleAccountBalanceEqual(coins sdk.Coins) {
balance := suite.BankKeeper.GetAllBalances( balance := suite.BankKeeper.GetAllBalances(
suite.Ctx, suite.Ctx,
@ -120,6 +206,58 @@ func (suite *Suite) ModuleAccountBalanceEqual(coins sdk.Coins) {
suite.Equal(coins, balance, fmt.Sprintf("expected module account balance to equal coins %s, but got %s", coins, balance)) suite.Equal(coins, balance, fmt.Sprintf("expected module account balance to equal coins %s, but got %s", coins, balance))
} }
// ----------------------------------------------------------------------------
// Earn
func (suite *Suite) VaultTotalValuesEqual(expected sdk.Coins) {
for _, coin := range expected {
vaultBal, err := suite.Keeper.GetVaultTotalValue(suite.Ctx, coin.Denom)
suite.Require().NoError(err, "failed to get vault balance")
suite.Require().Equal(coin, vaultBal)
}
}
func (suite *Suite) VaultTotalSuppliedEqual(expected sdk.Coins) {
for _, coin := range expected {
vaultBal, err := suite.Keeper.GetVaultTotalSupplied(suite.Ctx, coin.Denom)
suite.Require().NoError(err, "failed to get vault balance")
suite.Require().Equal(coin, vaultBal)
}
}
func (suite *Suite) AccountTotalSuppliedEqual(accs []sdk.AccAddress, supplies []sdk.Coins) {
for i, acc := range accs {
coins := supplies[i]
accVaultBal, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc)
suite.Require().NoError(err)
suite.Require().True(coins.IsEqual(accVaultBal), "expected account vault balance to equal coins %s, but got %s", coins, accVaultBal)
}
}
// ----------------------------------------------------------------------------
// Hard
func (suite *Suite) HardDepositAmountEqual(expected sdk.Coins) {
macc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName)
hardDeposit, found := suite.HardKeeper.GetSyncedDeposit(suite.Ctx, macc.GetAddress())
if expected.IsZero() {
suite.Require().False(found)
return
}
suite.Require().True(found, "hard should have a deposit")
suite.Require().Equalf(
expected,
hardDeposit.Amount,
"hard should have a deposit with the amount %v",
expected,
)
}
// ----------------------------------------------------------------------------
// EventsContains asserts that the expected event is in the provided events // EventsContains asserts that the expected event is in the provided events
func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) { func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) {
foundMatch := false foundMatch := false

View File

@ -9,7 +9,8 @@ var (
ErrInvalidVaultDenom = sdkerrors.Register(ModuleName, 2, "invalid vault denom") ErrInvalidVaultDenom = sdkerrors.Register(ModuleName, 2, "invalid vault denom")
ErrInvalidVaultStrategy = sdkerrors.Register(ModuleName, 3, "invalid vault strategy") ErrInvalidVaultStrategy = sdkerrors.Register(ModuleName, 3, "invalid vault strategy")
ErrInsufficientAmount = sdkerrors.Register(ModuleName, 4, "insufficient amount") ErrInsufficientAmount = sdkerrors.Register(ModuleName, 4, "insufficient amount")
ErrInvalidShares = sdkerrors.Register(ModuleName, 5, "invalid shares") ErrInsufficientValue = sdkerrors.Register(ModuleName, 5, "insufficient vault account value")
ErrVaultRecordNotFound = sdkerrors.Register(ModuleName, 6, "vault record not found") ErrVaultRecordNotFound = sdkerrors.Register(ModuleName, 6, "vault record not found")
ErrVaultShareRecordNotFound = sdkerrors.Register(ModuleName, 7, "vault share record not found") ErrVaultShareRecordNotFound = sdkerrors.Register(ModuleName, 7, "vault share record not found")
ErrStrategyDenomNotSupported = sdkerrors.Register(ModuleName, 8, "denom not supported for strategy")
) )

View File

@ -1,6 +1,6 @@
package types package types
// Event types for swap module // Event types for earn module
const ( const (
AttributeValueCategory = ModuleName AttributeValueCategory = ModuleName
EventTypeVaultDeposit = "vault_deposit" EventTypeVaultDeposit = "vault_deposit"

View File

@ -3,6 +3,9 @@ package types
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/types"
hardtypes "github.com/kava-labs/kava/x/hard/types"
savingstypes "github.com/kava-labs/kava/x/savings/types"
) )
// AccountKeeper defines the expected account keeper // AccountKeeper defines the expected account keeper
@ -21,3 +24,19 @@ type BankKeeper interface {
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
} }
// HardKeeper defines the expected interface needed for the hard strategy.
type HardKeeper interface {
Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
GetSyncedDeposit(ctx sdk.Context, depositor sdk.AccAddress) (hardtypes.Deposit, bool)
}
// SavingsKeeper defines the expected interface needed for the savings strategy.
type SavingsKeeper interface {
Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error
GetDeposit(ctx sdk.Context, depositor sdk.AccAddress) (savingstypes.Deposit, bool)
}

View File

@ -23,7 +23,7 @@ var _ = math.Inf
// proto package needs to be updated. // proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// GenesisState defines the swap module's genesis state. // GenesisState defines the earn module's genesis state.
type GenesisState struct { type GenesisState struct {
// params defines all the paramaters related to earn // params defines all the paramaters related to earn
Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`

View File

@ -25,9 +25,7 @@ const (
// key prefixes for store // key prefixes for store
var ( var (
VaultRecordKeyPrefix = []byte{0x01} // denom -> vault VaultRecordKeyPrefix = []byte{0x01} // denom -> vault
VaultSharePrefix = []byte{0x02} VaultShareRecordKeyPrefix = []byte{0x02} // depositor address -> vault shares
sep = []byte("|")
) )
// Vault returns a key generated from a vault denom // Vault returns a key generated from a vault denom
@ -35,14 +33,7 @@ func VaultKey(denom string) []byte {
return []byte(denom) return []byte(denom)
} }
// DepositorVaultSharesKey returns a key from a depositor and vault denom // DepositorVaultSharesKey returns a key from a depositor address
func DepositorVaultSharesKey(depositor sdk.AccAddress, vaultDenom string) []byte { func DepositorVaultSharesKey(depositor sdk.AccAddress) []byte {
return createKey(depositor, sep, []byte(vaultDenom)) return depositor.Bytes()
}
func createKey(bytes ...[]byte) (r []byte) {
for _, b := range bytes {
r = append(r, b...)
}
return
} }

View File

@ -27,24 +27,20 @@ type StrategyType int32
const ( const (
STRATEGY_TYPE_UNKNOWN StrategyType = 0 STRATEGY_TYPE_UNKNOWN StrategyType = 0
STRATEGY_TYPE_KAVA_STAKERS StrategyType = 1 STRATEGY_TYPE_HARD StrategyType = 1
// USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord STRATEGY_TYPE_SAVINGS StrategyType = 2
STRATEGY_TYPE_STABLECOIN_STAKERS StrategyType = 2
STRATEGY_TYPE_KAVA_FOUNDATION StrategyType = 3
) )
var StrategyType_name = map[int32]string{ var StrategyType_name = map[int32]string{
0: "STRATEGY_TYPE_UNKNOWN", 0: "STRATEGY_TYPE_UNKNOWN",
1: "STRATEGY_TYPE_KAVA_STAKERS", 1: "STRATEGY_TYPE_HARD",
2: "STRATEGY_TYPE_STABLECOIN_STAKERS", 2: "STRATEGY_TYPE_SAVINGS",
3: "STRATEGY_TYPE_KAVA_FOUNDATION",
} }
var StrategyType_value = map[string]int32{ var StrategyType_value = map[string]int32{
"STRATEGY_TYPE_UNKNOWN": 0, "STRATEGY_TYPE_UNKNOWN": 0,
"STRATEGY_TYPE_KAVA_STAKERS": 1, "STRATEGY_TYPE_HARD": 1,
"STRATEGY_TYPE_STABLECOIN_STAKERS": 2, "STRATEGY_TYPE_SAVINGS": 2,
"STRATEGY_TYPE_KAVA_FOUNDATION": 3,
} }
func (x StrategyType) String() string { func (x StrategyType) String() string {
@ -62,23 +58,21 @@ func init() {
func init() { proto.RegisterFile("kava/earn/v1beta1/strategy.proto", fileDescriptor_257c4968dd48fa09) } func init() { proto.RegisterFile("kava/earn/v1beta1/strategy.proto", fileDescriptor_257c4968dd48fa09) }
var fileDescriptor_257c4968dd48fa09 = []byte{ var fileDescriptor_257c4968dd48fa09 = []byte{
// 278 bytes of a gzipped FileDescriptorProto // 243 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc8, 0x4e, 0x2c, 0x4b, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xc8, 0x4e, 0x2c, 0x4b,
0xd4, 0x4f, 0x4d, 0x2c, 0xca, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x2e, 0xd4, 0x4f, 0x4d, 0x2c, 0xca, 0xd3, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x2f, 0x2e,
0x29, 0x4a, 0x2c, 0x49, 0x4d, 0xaf, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x04, 0xa9, 0x29, 0x4a, 0x2c, 0x49, 0x4d, 0xaf, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x04, 0xa9,
0xd0, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83, 0xd0, 0x03, 0xa9, 0xd0, 0x83, 0xaa, 0x90, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0xcb, 0xea, 0x83,
0x58, 0x10, 0x85, 0x52, 0x72, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x49, 0x89, 0xc5, 0xa9, 0x58, 0x10, 0x85, 0x52, 0x72, 0xc9, 0xf9, 0xc5, 0xb9, 0xf9, 0xc5, 0xfa, 0x49, 0x89, 0xc5, 0xa9,
0x70, 0xc3, 0x92, 0xf3, 0x33, 0xf3, 0xa0, 0xf2, 0x92, 0x10, 0xf9, 0x78, 0x88, 0x46, 0x08, 0x07, 0x70, 0xc3, 0x92, 0xf3, 0x33, 0xf3, 0xa0, 0xf2, 0x92, 0x10, 0xf9, 0x78, 0x88, 0x46, 0x08, 0x07,
0x22, 0xa5, 0x35, 0x83, 0x91, 0x8b, 0x27, 0x18, 0x6a, 0x6d, 0x48, 0x65, 0x41, 0xaa, 0x90, 0x24, 0x22, 0xa5, 0x95, 0xc4, 0xc5, 0x13, 0x0c, 0xb5, 0x35, 0xa4, 0xb2, 0x20, 0x55, 0x48, 0x92, 0x4b,
0x97, 0x68, 0x70, 0x48, 0x90, 0x63, 0x88, 0xab, 0x7b, 0x64, 0x7c, 0x48, 0x64, 0x80, 0x6b, 0x7c, 0x34, 0x38, 0x24, 0xc8, 0x31, 0xc4, 0xd5, 0x3d, 0x32, 0x3e, 0x24, 0x32, 0xc0, 0x35, 0x3e, 0xd4,
0xa8, 0x9f, 0xb7, 0x9f, 0x7f, 0xb8, 0x9f, 0x00, 0x83, 0x90, 0x1c, 0x97, 0x14, 0xaa, 0x94, 0xb7, 0xcf, 0xdb, 0xcf, 0x3f, 0xdc, 0x4f, 0x80, 0x41, 0x48, 0x8c, 0x4b, 0x08, 0x55, 0xca, 0xc3, 0x31,
0x63, 0x98, 0x63, 0x7c, 0x70, 0x88, 0xa3, 0xb7, 0x6b, 0x50, 0xb0, 0x00, 0xa3, 0x90, 0x0a, 0x97, 0xc8, 0x45, 0x80, 0x11, 0x53, 0x4b, 0xb0, 0x63, 0x98, 0xa7, 0x9f, 0x7b, 0xb0, 0x00, 0x93, 0x14,
0x02, 0xaa, 0x7c, 0x70, 0x88, 0xa3, 0x93, 0x8f, 0xab, 0xb3, 0xbf, 0xa7, 0x1f, 0x5c, 0x15, 0x93, 0x4b, 0xc7, 0x62, 0x39, 0x06, 0x27, 0x87, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c,
0x90, 0x22, 0x97, 0x2c, 0x16, 0x53, 0xdc, 0xfc, 0x43, 0xfd, 0x5c, 0x1c, 0x43, 0x3c, 0xfd, 0xfd, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63,
0x04, 0x98, 0xa5, 0x58, 0x3a, 0x16, 0xcb, 0x31, 0x38, 0x39, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x88, 0x52, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x07, 0x79, 0x56,
0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0x37, 0x27, 0x31, 0xa9, 0x18, 0xcc, 0xd2, 0xaf, 0x80, 0x04, 0x4d, 0x49, 0x65, 0x41, 0x6a, 0x71,
0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x5a, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x12, 0x1b, 0xd8, 0xb1, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x62, 0x68, 0x2c, 0xbc, 0x34,
0x3e, 0x28, 0x8c, 0x74, 0x73, 0x12, 0x93, 0x8a, 0xc1, 0x2c, 0xfd, 0x0a, 0x48, 0x88, 0x96, 0x54, 0x01, 0x00, 0x00,
0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0xfd, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x85, 0x84,
0x93, 0x9f, 0x6b, 0x01, 0x00, 0x00,
} }

View File

@ -18,11 +18,12 @@ type VaultRecords []VaultRecord
type VaultShareRecords []VaultShareRecord type VaultShareRecords []VaultShareRecord
// NewVaultShareRecord returns a new VaultShareRecord with 0 supply. // NewVaultShareRecord returns a new VaultShareRecord with the provided supplied
func NewVaultShareRecord(depositor sdk.AccAddress, vaultDenom string) VaultShareRecord { // coins.
func NewVaultShareRecord(depositor sdk.AccAddress, supplied ...sdk.Coin) VaultShareRecord {
return VaultShareRecord{ return VaultShareRecord{
Depositor: depositor, Depositor: depositor,
AmountSupplied: sdk.NewCoin(vaultDenom, sdk.ZeroInt()), AmountSupplied: sdk.NewCoins(supplied...),
} }
} }

View File

@ -140,13 +140,13 @@ func (m *VaultRecord) GetTotalSupply() types.Coin {
return types.Coin{} return types.Coin{}
} }
// VaultShareRecord defines the shares owned by a depositor and vault. // VaultShareRecord defines the vault shares owned by a depositor.
type VaultShareRecord struct { type VaultShareRecord struct {
// depositor represents the owner of the shares // Depositor represents the owner of the shares
Depositor github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=depositor,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"depositor,omitempty"` Depositor github_com_cosmos_cosmos_sdk_types.AccAddress `protobuf:"bytes,1,opt,name=depositor,proto3,casttype=github.com/cosmos/cosmos-sdk/types.AccAddress" json:"depositor,omitempty"`
// amount_supplied represents the total amount a depositor has supplied to the // AmountSupplied represents the total amount a depositor has supplied to the
// vault. The vault is determined by the coin denom. // vault. The vault is determined by the coin denom.
AmountSupplied types.Coin `protobuf:"bytes,2,opt,name=amount_supplied,json=amountSupplied,proto3" json:"amount_supplied"` AmountSupplied github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount_supplied,json=amountSupplied,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount_supplied"`
} }
func (m *VaultShareRecord) Reset() { *m = VaultShareRecord{} } func (m *VaultShareRecord) Reset() { *m = VaultShareRecord{} }
@ -189,11 +189,11 @@ func (m *VaultShareRecord) GetDepositor() github_com_cosmos_cosmos_sdk_types.Acc
return nil return nil
} }
func (m *VaultShareRecord) GetAmountSupplied() types.Coin { func (m *VaultShareRecord) GetAmountSupplied() github_com_cosmos_cosmos_sdk_types.Coins {
if m != nil { if m != nil {
return m.AmountSupplied return m.AmountSupplied
} }
return types.Coin{} return nil
} }
func init() { func init() {
@ -205,32 +205,34 @@ func init() {
func init() { proto.RegisterFile("kava/earn/v1beta1/vault.proto", fileDescriptor_884eb89509fbdc04) } func init() { proto.RegisterFile("kava/earn/v1beta1/vault.proto", fileDescriptor_884eb89509fbdc04) }
var fileDescriptor_884eb89509fbdc04 = []byte{ var fileDescriptor_884eb89509fbdc04 = []byte{
// 398 bytes of a gzipped FileDescriptorProto // 417 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x51, 0xcd, 0xae, 0xd2, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xbf, 0xae, 0xd3, 0x30,
0x14, 0x6e, 0x8d, 0x9a, 0x30, 0x20, 0x6a, 0x65, 0x01, 0x24, 0x16, 0xc2, 0xc2, 0xb0, 0xe9, 0x34, 0x14, 0xc6, 0x93, 0xcb, 0x1f, 0xe9, 0xba, 0xa5, 0x40, 0xb8, 0x43, 0xef, 0x95, 0x48, 0xa2, 0x0e,
0xe0, 0x0b, 0x48, 0x4d, 0x0c, 0xeb, 0xd6, 0xb8, 0x70, 0x43, 0xa6, 0x9d, 0xb1, 0x34, 0xb4, 0x3d, 0xa8, 0x4b, 0x1c, 0xee, 0xe5, 0x05, 0x68, 0x90, 0x10, 0x73, 0x82, 0x18, 0x58, 0x2a, 0x27, 0x36,
0x4d, 0x67, 0x8a, 0xf6, 0x2d, 0x7c, 0x18, 0x1f, 0xc1, 0x05, 0x4b, 0xe2, 0xca, 0x15, 0xb9, 0x81, 0x69, 0xd4, 0x24, 0x27, 0x8a, 0x9d, 0x42, 0xde, 0x82, 0xe7, 0x60, 0xe6, 0x21, 0x3a, 0x56, 0x4c,
0xb7, 0xb8, 0xab, 0x9b, 0xce, 0x0c, 0xdc, 0x9b, 0x90, 0x9b, 0xdc, 0xd5, 0xfc, 0x7c, 0xe7, 0x3b, 0x4c, 0x05, 0xb5, 0x2f, 0xc0, 0xcc, 0x84, 0xfc, 0xa7, 0x05, 0xa9, 0x02, 0x31, 0xc5, 0xf6, 0x77,
0xdf, 0xf7, 0x9d, 0x83, 0xde, 0x6f, 0xc8, 0x96, 0xb8, 0x8c, 0x94, 0xb9, 0xbb, 0x9d, 0x85, 0x4c, 0xbe, 0xf3, 0x3b, 0x9f, 0x63, 0xf4, 0x78, 0x49, 0x56, 0x24, 0x64, 0xa4, 0xad, 0xc3, 0xd5, 0x75,
0x90, 0x99, 0xbb, 0x25, 0x55, 0x2a, 0x70, 0x51, 0x82, 0x00, 0xeb, 0x6d, 0x03, 0xe3, 0x06, 0xc6, 0xca, 0x04, 0xb9, 0x0e, 0x57, 0xa4, 0x2b, 0x05, 0x6e, 0x5a, 0x10, 0xe0, 0x3c, 0x94, 0x32, 0x96,
0x1a, 0x1e, 0xf6, 0x62, 0x88, 0x41, 0xa2, 0x6e, 0x73, 0x53, 0x85, 0x43, 0x3b, 0x02, 0x9e, 0x01, 0x32, 0x36, 0xf2, 0xd5, 0x45, 0x0e, 0x39, 0x28, 0x35, 0x94, 0x2b, 0x5d, 0x78, 0xe5, 0x66, 0xc0,
0x77, 0x43, 0xc2, 0xd9, 0xa5, 0x53, 0x04, 0x49, 0xae, 0xf1, 0x81, 0xc2, 0x57, 0x8a, 0xa8, 0x1e, 0x2b, 0xe0, 0x61, 0x4a, 0x38, 0x3b, 0x76, 0xca, 0xa0, 0xa8, 0x8d, 0x7e, 0xa9, 0xf5, 0xb9, 0x36,
0x1a, 0x1a, 0x5f, 0x5b, 0xe0, 0xa2, 0x24, 0x82, 0xc5, 0xb5, 0xaa, 0x98, 0xa4, 0xa8, 0xb3, 0x48, 0xea, 0x8d, 0x91, 0xfc, 0xd3, 0x11, 0xb8, 0x68, 0x89, 0x60, 0x79, 0xaf, 0x2b, 0x26, 0x25, 0x1a,
0x53, 0xf8, 0xc9, 0xe8, 0xb7, 0xc6, 0x9b, 0xd5, 0x43, 0x2f, 0x28, 0xcb, 0x21, 0xeb, 0x9b, 0x63, 0xce, 0xca, 0x12, 0xde, 0x33, 0xfa, 0x46, 0xce, 0xe6, 0x5c, 0xa0, 0x3b, 0x94, 0xd5, 0x50, 0x8d,
0x73, 0xda, 0xf2, 0xd5, 0xc3, 0xfa, 0x82, 0xba, 0xd2, 0xfa, 0xea, 0xcc, 0xee, 0x3f, 0x1b, 0x9b, 0x6d, 0xdf, 0x9e, 0x9e, 0xc7, 0x7a, 0xe3, 0xbc, 0x44, 0x23, 0x35, 0xfa, 0xfc, 0xe0, 0x1e, 0x9f,
0xd3, 0xee, 0x7c, 0x84, 0xaf, 0x42, 0xe0, 0x40, 0x97, 0x7c, 0xad, 0x0b, 0xe6, 0xbf, 0x92, 0xb4, 0xf9, 0xf6, 0x74, 0x74, 0xe3, 0xe1, 0x93, 0x10, 0x38, 0x31, 0x25, 0xaf, 0xfb, 0x86, 0xc5, 0xf7,
0xf3, 0xd7, 0x24, 0x46, 0x6d, 0x29, 0xe3, 0xb3, 0x08, 0x4a, 0xfa, 0x88, 0x98, 0x87, 0x3a, 0x02, 0x94, 0xed, 0x70, 0x34, 0xc9, 0xd1, 0x40, 0x61, 0x62, 0x96, 0x41, 0x4b, 0xff, 0x02, 0x8b, 0xd0,
0x04, 0x49, 0x57, 0xbc, 0x2a, 0x8a, 0x54, 0x49, 0xb5, 0xe7, 0x03, 0xac, 0x93, 0x35, 0x63, 0xb8, 0x50, 0x80, 0x20, 0xe5, 0x9c, 0x77, 0x4d, 0x53, 0x6a, 0xd4, 0xe0, 0xe6, 0x12, 0x9b, 0x64, 0xf2,
0x88, 0x7d, 0x86, 0x24, 0xf7, 0x9e, 0xef, 0x0e, 0x23, 0xc3, 0x6f, 0x4b, 0x52, 0x20, 0x39, 0x93, 0x1a, 0x8e, 0xb0, 0x17, 0x50, 0xd4, 0xd1, 0xed, 0xf5, 0xd6, 0xb3, 0xe2, 0x81, 0x32, 0x25, 0xca,
0xbf, 0x26, 0x7a, 0x23, 0x95, 0x82, 0x35, 0x29, 0x99, 0x96, 0xfb, 0x81, 0x5a, 0x94, 0x15, 0xc0, 0x33, 0xf9, 0x61, 0xa3, 0x07, 0x8a, 0x94, 0x2c, 0x48, 0xcb, 0x0c, 0xee, 0x1d, 0x3a, 0xa7, 0xac,
0x13, 0x01, 0xa5, 0x94, 0xec, 0x78, 0xcb, 0xdb, 0xc3, 0xc8, 0x89, 0x13, 0xb1, 0xae, 0x42, 0x1c, 0x01, 0x5e, 0x08, 0x68, 0x15, 0x72, 0x18, 0xbd, 0xfa, 0xb9, 0xf5, 0x82, 0xbc, 0x10, 0x8b, 0x2e,
0x41, 0xa6, 0xa7, 0xa7, 0x0f, 0x87, 0xd3, 0x8d, 0x2b, 0xea, 0x82, 0x71, 0xbc, 0x88, 0xa2, 0x05, 0xc5, 0x19, 0x54, 0xe6, 0xf6, 0xcc, 0x27, 0xe0, 0x74, 0x19, 0x8a, 0xbe, 0x61, 0x1c, 0xcf, 0xb2,
0xa5, 0x25, 0xe3, 0xfc, 0xdf, 0x1f, 0xe7, 0x9d, 0x76, 0xa2, 0x7f, 0xbc, 0x5a, 0x30, 0xee, 0xdf, 0x6c, 0x46, 0x69, 0xcb, 0x38, 0xff, 0xf2, 0x39, 0x78, 0x64, 0x26, 0x31, 0x27, 0x51, 0x2f, 0x18,
0xb7, 0xb6, 0x96, 0xe8, 0x35, 0xc9, 0xa0, 0xca, 0x85, 0x4a, 0x90, 0x30, 0xfa, 0xd4, 0x0c, 0x5d, 0x8f, 0x7f, 0xb7, 0x76, 0x04, 0xba, 0x4f, 0x2a, 0xe8, 0x6a, 0xa1, 0x13, 0x14, 0x8c, 0x8e, 0xcf,
0xc5, 0x0b, 0x34, 0xcd, 0xfb, 0xb4, 0x3b, 0xda, 0xe6, 0xfe, 0x68, 0x9b, 0x37, 0x47, 0xdb, 0xfc, 0xfc, 0x5b, 0xff, 0xce, 0xf0, 0x54, 0x66, 0xf8, 0xf4, 0xcd, 0x9b, 0xfe, 0xc7, 0x30, 0xd2, 0xc0,
0x7d, 0xb2, 0x8d, 0xfd, 0xc9, 0x36, 0xfe, 0x9f, 0x6c, 0xe3, 0xfb, 0x87, 0x07, 0xa6, 0x9b, 0x1d, 0xe3, 0x91, 0x66, 0x24, 0x06, 0x11, 0x3d, 0x5f, 0xef, 0x5c, 0x7b, 0xb3, 0x73, 0xed, 0xef, 0x3b,
0x38, 0x29, 0x09, 0xb9, 0xbc, 0xb9, 0xbf, 0xd4, 0xc2, 0xa5, 0xf1, 0xf0, 0xa5, 0x5c, 0xf3, 0xc7, 0xd7, 0xfe, 0xb8, 0x77, 0xad, 0xcd, 0xde, 0xb5, 0xbe, 0xee, 0x5d, 0xeb, 0xed, 0x93, 0x3f, 0x7a,
0xbb, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc6, 0x8b, 0xa9, 0xd7, 0x8d, 0x02, 0x00, 0x00, 0xca, 0xff, 0x15, 0x94, 0x24, 0xe5, 0x6a, 0x15, 0x7e, 0xd0, 0x8f, 0x43, 0xf5, 0x4d, 0xef, 0xaa,
0x27, 0xf1, 0xec, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x82, 0x2c, 0xf8, 0xd8, 0xb9, 0x02, 0x00,
0x00,
} }
func (m *AllowedVault) Marshal() (dAtA []byte, err error) { func (m *AllowedVault) Marshal() (dAtA []byte, err error) {
@ -328,8 +330,10 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if len(m.AmountSupplied) > 0 {
for iNdEx := len(m.AmountSupplied) - 1; iNdEx >= 0; iNdEx-- {
{ {
size, err := m.AmountSupplied.MarshalToSizedBuffer(dAtA[:i]) size, err := m.AmountSupplied[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -338,6 +342,8 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
} }
i-- i--
dAtA[i] = 0x12 dAtA[i] = 0x12
}
}
if len(m.Depositor) > 0 { if len(m.Depositor) > 0 {
i -= len(m.Depositor) i -= len(m.Depositor)
copy(dAtA[i:], m.Depositor) copy(dAtA[i:], m.Depositor)
@ -400,8 +406,12 @@ func (m *VaultShareRecord) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovVault(uint64(l)) n += 1 + l + sovVault(uint64(l))
} }
l = m.AmountSupplied.Size() if len(m.AmountSupplied) > 0 {
for _, e := range m.AmountSupplied {
l = e.Size()
n += 1 + l + sovVault(uint64(l)) n += 1 + l + sovVault(uint64(l))
}
}
return n return n
} }
@ -719,7 +729,8 @@ func (m *VaultShareRecord) Unmarshal(dAtA []byte) error {
if postIndex > l { if postIndex > l {
return io.ErrUnexpectedEOF return io.ErrUnexpectedEOF
} }
if err := m.AmountSupplied.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { m.AmountSupplied = append(m.AmountSupplied, types.Coin{})
if err := m.AmountSupplied[len(m.AmountSupplied)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex