mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-24 15:25:18 +00:00
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:
parent
ae181604ff
commit
88d4868316
@ -624,6 +624,8 @@ func NewApp(
|
||||
earnSubspace,
|
||||
app.accountKeeper,
|
||||
app.bankKeeper,
|
||||
hardKeeper,
|
||||
savingsKeeper,
|
||||
)
|
||||
|
||||
// create committee keeper with router
|
||||
@ -996,9 +998,11 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
|
||||
func (app *App) loadBlockedMaccAddrs() map[string]bool {
|
||||
modAccAddrs := app.ModuleAccountAddrs()
|
||||
kavadistMaccAddr := app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName)
|
||||
earnMaccAddr := app.accountKeeper.GetModuleAddress(earntypes.ModuleName)
|
||||
|
||||
for addr := range modAccAddrs {
|
||||
// Set the kavadist module account address as unblocked
|
||||
if addr == kavadistMaccAddr.String() {
|
||||
// Set the kavadist and earn module account address as unblocked
|
||||
if addr == kavadistMaccAddr.String() || addr == earnMaccAddr.String() {
|
||||
modAccAddrs[addr] = false
|
||||
}
|
||||
}
|
||||
|
@ -2752,9 +2752,8 @@ Msg defines the committee Msg service
|
||||
| Name | Number | Description |
|
||||
| ---- | ------ | ----------- |
|
||||
| STRATEGY_TYPE_UNKNOWN | 0 | |
|
||||
| STRATEGY_TYPE_KAVA_STAKERS | 1 | |
|
||||
| STRATEGY_TYPE_STABLECOIN_STAKERS | 2 | USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord |
|
||||
| STRATEGY_TYPE_KAVA_FOUNDATION | 3 | |
|
||||
| STRATEGY_TYPE_HARD | 1 | |
|
||||
| STRATEGY_TYPE_SAVINGS | 2 | |
|
||||
|
||||
|
||||
<!-- end enums -->
|
||||
@ -2809,13 +2808,13 @@ vault.
|
||||
<a name="kava.earn.v1beta1.VaultShareRecord"></a>
|
||||
|
||||
### VaultShareRecord
|
||||
VaultShareRecord defines the shares owned by a depositor and vault.
|
||||
VaultShareRecord defines the vault shares owned by a depositor.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `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. |
|
||||
| `depositor` | [bytes](#bytes) | | Depositor represents the owner of the shares |
|
||||
| `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>
|
||||
|
||||
### GenesisState
|
||||
GenesisState defines the swap module's genesis state.
|
||||
GenesisState defines the earn module's genesis state.
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
|
@ -7,7 +7,7 @@ import "kava/earn/v1beta1/vault.proto";
|
||||
import "kava/earn/v1beta1/params.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
// GenesisState defines the swap module's genesis state.
|
||||
// GenesisState defines the earn module's genesis state.
|
||||
message GenesisState {
|
||||
// params defines all the paramaters related to earn
|
||||
Params params = 1 [(gogoproto.nullable) = false];
|
||||
|
@ -11,8 +11,6 @@ enum StrategyType {
|
||||
option (gogoproto.goproto_enum_prefix) = false;
|
||||
|
||||
STRATEGY_TYPE_UNKNOWN = 0;
|
||||
STRATEGY_TYPE_KAVA_STAKERS = 1;
|
||||
// USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord
|
||||
STRATEGY_TYPE_STABLECOIN_STAKERS = 2;
|
||||
STRATEGY_TYPE_KAVA_FOUNDATION = 3;
|
||||
STRATEGY_TYPE_HARD = 1;
|
||||
STRATEGY_TYPE_SAVINGS = 2;
|
||||
}
|
||||
|
@ -29,14 +29,15 @@ message VaultRecord {
|
||||
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 {
|
||||
// depositor represents the owner of the shares
|
||||
// Depositor represents the owner of the shares
|
||||
bytes depositor = 1 [
|
||||
(cosmos_proto.scalar) = "cosmos.AddressBytes",
|
||||
(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.
|
||||
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];
|
||||
}
|
@ -16,7 +16,7 @@ import (
|
||||
|
||||
// GetTxCmd returns the transaction commands for this module
|
||||
func GetTxCmd() *cobra.Command {
|
||||
swapTxCmd := &cobra.Command{
|
||||
earnTxCmd := &cobra.Command{
|
||||
Use: types.ModuleName,
|
||||
Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName),
|
||||
DisableFlagParsing: true,
|
||||
@ -33,9 +33,9 @@ func GetTxCmd() *cobra.Command {
|
||||
flags.AddTxFlagsToCmd(cmd)
|
||||
}
|
||||
|
||||
swapTxCmd.AddCommand(cmds...)
|
||||
earnTxCmd.AddCommand(cmds...)
|
||||
|
||||
return swapTxCmd
|
||||
return earnTxCmd
|
||||
}
|
||||
|
||||
func getCmdDeposit() *cobra.Command {
|
||||
|
@ -2,6 +2,7 @@ package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"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)
|
||||
}
|
||||
|
||||
// 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
|
||||
if err := k.bankKeeper.SendCoinsFromAccountToModule(
|
||||
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
|
||||
vaultShareRecord, found := k.GetVaultShareRecord(ctx, amount.Denom, depositor)
|
||||
vaultShareRecord, found := k.GetVaultShareRecord(ctx, depositor)
|
||||
if !found {
|
||||
// Create a new empty VaultShareRecord with 0 supply
|
||||
vaultShareRecord = types.NewVaultShareRecord(depositor, amount.Denom)
|
||||
vaultShareRecord = types.NewVaultShareRecord(depositor)
|
||||
}
|
||||
|
||||
// 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.SetVaultShareRecord(ctx, vaultShareRecord)
|
||||
|
||||
// Get the strategy for the vault
|
||||
strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deposit to the strategy
|
||||
if err := strategy.Deposit(amount); err != nil {
|
||||
if err := strategy.Deposit(ctx, amount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -34,11 +34,11 @@ func TestDepositTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *depositTestSuite) TestDeposit_Balances() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -50,18 +50,16 @@ func (suite *depositTestSuite) TestDeposit_Balances() {
|
||||
sdk.NewCoins(startBalance.Sub(depositAmount)), // Account decreases by deposit
|
||||
)
|
||||
|
||||
// TODO: Module account balance will be zero when strategies are implemented
|
||||
suite.ModuleAccountBalanceEqual(
|
||||
sdk.NewCoins(depositAmount),
|
||||
)
|
||||
suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount))
|
||||
suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount))
|
||||
}
|
||||
|
||||
func (suite *depositTestSuite) TestDeposit_Exceed() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -82,11 +80,11 @@ func (suite *depositTestSuite) TestDeposit_Exceed() {
|
||||
}
|
||||
|
||||
func (suite *depositTestSuite) TestDeposit_Zero() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -107,7 +105,7 @@ func (suite *depositTestSuite) TestDeposit_Zero() {
|
||||
}
|
||||
|
||||
func (suite *depositTestSuite) TestDeposit_InvalidVault() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 1001)
|
||||
|
||||
|
@ -33,7 +33,7 @@ func TestGrpcQueryTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *grpcQueryTestSuite) TestQueryParams() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
|
||||
res, err := suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
|
||||
suite.Require().NoError(err)
|
||||
@ -41,14 +41,14 @@ func (suite *grpcQueryTestSuite) TestQueryParams() {
|
||||
suite.Require().ElementsMatch(types.DefaultParams().AllowedVaults, res.Params.AllowedVaults)
|
||||
|
||||
// Add vault to params
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS)
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
|
||||
|
||||
// Query again for added vault
|
||||
res, err = suite.queryClient.Params(context.Background(), types.NewQueryParamsRequest())
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(
|
||||
types.AllowedVaults{
|
||||
types.NewAllowedVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS),
|
||||
types.NewAllowedVault(vaultDenom, types.STRATEGY_TYPE_HARD),
|
||||
},
|
||||
res.Params.AllowedVaults,
|
||||
)
|
||||
|
@ -15,6 +15,10 @@ type Keeper struct {
|
||||
paramSubspace paramtypes.Subspace
|
||||
accountKeeper types.AccountKeeper
|
||||
bankKeeper types.BankKeeper
|
||||
|
||||
// Keepers used for strategies
|
||||
hardKeeper types.HardKeeper
|
||||
savingsKeeper types.SavingsKeeper
|
||||
}
|
||||
|
||||
// NewKeeper creates a new keeper
|
||||
@ -24,6 +28,8 @@ func NewKeeper(
|
||||
paramstore paramtypes.Subspace,
|
||||
accountKeeper types.AccountKeeper,
|
||||
bankKeeper types.BankKeeper,
|
||||
hardKeeper types.HardKeeper,
|
||||
savingsKeeper types.SavingsKeeper,
|
||||
) Keeper {
|
||||
if !paramstore.HasKeyTable() {
|
||||
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
||||
@ -35,5 +41,7 @@ func NewKeeper(
|
||||
paramSubspace: paramstore,
|
||||
accountKeeper: accountKeeper,
|
||||
bankKeeper: bankKeeper,
|
||||
hardKeeper: hardKeeper,
|
||||
savingsKeeper: savingsKeeper,
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ func TestMsgServerTestSuite(t *testing.T) {
|
||||
|
||||
func (suite *msgServerTestSuite) TestDeposit() {
|
||||
vaultDenom := "usdx"
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS)
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
|
||||
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
@ -85,7 +85,7 @@ func (suite *msgServerTestSuite) TestDeposit() {
|
||||
|
||||
func (suite *msgServerTestSuite) TestWithdraw() {
|
||||
vaultDenom := "usdx"
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_STABLECOIN_STAKERS)
|
||||
suite.CreateVault(vaultDenom, types.STRATEGY_TYPE_HARD)
|
||||
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
|
@ -9,15 +9,12 @@ import (
|
||||
|
||||
// Strategy is the interface that must be implemented by a strategy.
|
||||
type Strategy interface {
|
||||
// GetName returns the name of the strategy.
|
||||
GetName() string
|
||||
// GetStrategyType returns the strategy type
|
||||
GetStrategyType() types.StrategyType
|
||||
|
||||
// GetDescription returns the description of the strategy.
|
||||
GetDescription() string
|
||||
|
||||
// GetSupportedDenoms returns a slice of supported denom for this strategy.
|
||||
// For example, stablecoin stakers strategy supports both "busd" and "usdc".
|
||||
GetSupportedDenoms() []string
|
||||
// IsDenomSupported returns true if the denom is supported for this
|
||||
// strategy. For example, the hard strategy supports "usdx".
|
||||
IsDenomSupported(string) bool
|
||||
|
||||
// GetEstimatedTotalAssets returns the estimated total assets denominated in
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// must be denominated in GetDenom().
|
||||
Withdraw(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)
|
||||
Withdraw(ctx sdk.Context, amount sdk.Coin) error
|
||||
}
|
||||
|
||||
// GetStrategy returns the strategy for the given strategy type.
|
||||
func (k *Keeper) GetStrategy(strategyType types.StrategyType) (Strategy, error) {
|
||||
switch strategyType {
|
||||
case types.STRATEGY_TYPE_STABLECOIN_STAKERS:
|
||||
return (*StableCoinStrategy)(k), nil
|
||||
case types.STRATEGY_TYPE_KAVA_STAKERS:
|
||||
panic("unimplemented")
|
||||
case types.STRATEGY_TYPE_KAVA_FOUNDATION:
|
||||
case types.STRATEGY_TYPE_HARD:
|
||||
return (*HardStrategy)(k), nil
|
||||
case types.STRATEGY_TYPE_SAVINGS:
|
||||
panic("unimplemented")
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown strategy type: %s", strategyType)
|
||||
|
48
x/earn/keeper/strategy_hard.go
Normal file
48
x/earn/keeper/strategy_hard.go
Normal 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))
|
||||
}
|
300
x/earn/keeper/strategy_hard_test.go
Normal file
300
x/earn/keeper/strategy_hard_test.go
Normal 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)
|
||||
}
|
@ -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
|
||||
}
|
@ -6,29 +6,6 @@ import (
|
||||
"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
|
||||
// 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.
|
||||
@ -47,6 +24,10 @@ func (k *Keeper) GetVaultTotalSupplied(
|
||||
// GetTotalValue returns the total **value** of all coins in this vault,
|
||||
// i.e. the realizable total value denominated by GetDenom() if the vault
|
||||
// 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(
|
||||
ctx sdk.Context,
|
||||
denom string,
|
||||
@ -61,19 +42,18 @@ func (k *Keeper) GetVaultTotalValue(
|
||||
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
|
||||
// within a vault.
|
||||
func (k *Keeper) GetVaultAccountSupplied(
|
||||
ctx sdk.Context,
|
||||
denom string,
|
||||
acc sdk.AccAddress,
|
||||
) (sdk.Coin, error) {
|
||||
vaultShareRecord, found := k.GetVaultShareRecord(ctx, denom, acc)
|
||||
) (sdk.Coins, error) {
|
||||
vaultShareRecord, found := k.GetVaultShareRecord(ctx, acc)
|
||||
if !found {
|
||||
return sdk.Coin{}, types.ErrVaultShareRecordNotFound
|
||||
return sdk.Coins{}, types.ErrVaultShareRecordNotFound
|
||||
}
|
||||
|
||||
return vaultShareRecord.AmountSupplied, nil
|
||||
@ -91,7 +71,7 @@ func (k *Keeper) GetVaultAccountValue(
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
|
||||
accSupplied, err := k.GetVaultAccountSupplied(ctx, denom, acc)
|
||||
accSupplied, err := k.GetVaultAccountSupplied(ctx, acc)
|
||||
if err != nil {
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
@ -101,12 +81,12 @@ func (k *Keeper) GetVaultAccountValue(
|
||||
return sdk.Coin{}, err
|
||||
}
|
||||
|
||||
// percent of vault account ownership = accountSupply / totalSupply
|
||||
// value of vault account ownership = percentOwned * totalValue
|
||||
vaultShare := accSupplied.Amount.Quo(totalSupplied.Amount)
|
||||
shareValue := vaultTotalValue.Amount.Mul(vaultShare)
|
||||
// Percent of vault account ownership = accountSupply / totalSupply
|
||||
// Value of vault account ownership = percentOwned * totalValue
|
||||
vaultShare := accSupplied.AmountOf(denom).ToDec().Quo(totalSupplied.Amount.ToDec())
|
||||
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.
|
||||
func (k *Keeper) GetVaultShareRecord(
|
||||
ctx sdk.Context,
|
||||
vaultDenom string,
|
||||
acc sdk.AccAddress,
|
||||
) (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 {
|
||||
return types.VaultShareRecord{}, false
|
||||
}
|
||||
@ -187,7 +166,7 @@ func (k *Keeper) UpdateVaultShareRecord(
|
||||
record types.VaultShareRecord,
|
||||
) {
|
||||
if record.AmountSupplied.IsZero() {
|
||||
k.DeleteVaultShareRecord(ctx, record.AmountSupplied.Denom, record.Depositor)
|
||||
k.DeleteVaultShareRecord(ctx, record.Depositor)
|
||||
} else {
|
||||
k.SetVaultShareRecord(ctx, record)
|
||||
}
|
||||
@ -197,11 +176,10 @@ func (k *Keeper) UpdateVaultShareRecord(
|
||||
// account.
|
||||
func (k *Keeper) DeleteVaultShareRecord(
|
||||
ctx sdk.Context,
|
||||
vaultDenom string,
|
||||
acc sdk.AccAddress,
|
||||
) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.VaultRecordKeyPrefix)
|
||||
store.Delete(types.DepositorVaultSharesKey(acc, vaultDenom))
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix)
|
||||
store.Delete(types.DepositorVaultSharesKey(acc))
|
||||
}
|
||||
|
||||
// SetVaultShareRecord sets the vault share record for a given denom and account.
|
||||
@ -209,7 +187,7 @@ func (k *Keeper) SetVaultShareRecord(
|
||||
ctx sdk.Context,
|
||||
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)
|
||||
store.Set(types.DepositorVaultSharesKey(record.Depositor, record.AmountSupplied.Denom), bz)
|
||||
store.Set(types.DepositorVaultSharesKey(record.Depositor), bz)
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func (suite *vaultTestSuite) TestGetVaultTotalSupplied() {
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -76,18 +76,18 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() {
|
||||
deposit1Amount := 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)
|
||||
acc2 := suite.CreateAccount(sdk.NewCoins(startBalance), 1)
|
||||
|
||||
// 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().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().ErrorIs(err, types.ErrVaultShareRecordNotFound)
|
||||
|
||||
@ -101,15 +101,15 @@ func (suite *vaultTestSuite) TestGetVaultAccountSupplied() {
|
||||
|
||||
// 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)
|
||||
|
||||
vaultAcc2Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, vaultDenom, acc2.GetAddress())
|
||||
vaultAcc2Supplied, err := suite.Keeper.GetVaultAccountSupplied(suite.Ctx, acc2.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Account supply only includes the deposit from respective accounts
|
||||
suite.Equal(deposit1Amount, vaultAcc1Supplied)
|
||||
suite.Equal(deposit1Amount, vaultAcc2Supplied)
|
||||
suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc1Supplied)
|
||||
suite.Equal(sdk.NewCoins(deposit1Amount), vaultAcc2Supplied)
|
||||
}
|
||||
|
||||
func (suite *vaultTestSuite) TestGetVaultAccountValue() {
|
||||
@ -119,16 +119,14 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue() {
|
||||
|
||||
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)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.T().Skip("TODO: After strategy GetEstimatedTotalAssets implemented")
|
||||
|
||||
_, err = suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc.GetAddress())
|
||||
suite.Require().Error(err)
|
||||
suite.Require().ErrorIs(err, types.ErrVaultShareRecordNotFound)
|
||||
accValue, err := suite.Keeper.GetVaultAccountValue(suite.Ctx, vaultDenom, acc.GetAddress())
|
||||
suite.Require().NoError(err)
|
||||
suite.Equal(depositAmount, accValue, "value should be same as deposit amount")
|
||||
}
|
||||
|
||||
func (suite *vaultTestSuite) TestGetVaultAccountValue_VaultNotFound() {
|
||||
@ -148,7 +146,7 @@ func (suite *vaultTestSuite) TestGetVaultAccountValue_ShareNotFound() {
|
||||
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
|
||||
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
|
||||
err := suite.Keeper.Deposit(suite.Ctx, acc1.GetAddress(), depositAmount)
|
||||
@ -202,19 +200,19 @@ func (suite *vaultTestSuite) TestGetVaultShareRecord() {
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
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
|
||||
|
||||
_, 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")
|
||||
|
||||
// Update share record
|
||||
record.AmountSupplied = depositAmount
|
||||
record.AmountSupplied = sdk.NewCoins(depositAmount)
|
||||
suite.Keeper.SetVaultShareRecord(suite.Ctx, record)
|
||||
|
||||
// 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().Equal(record, stateRecord)
|
||||
}
|
||||
@ -225,21 +223,19 @@ func (suite *vaultTestSuite) TestUpdateVaultShareRecord() {
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
acc := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
|
||||
|
||||
record := types.NewVaultShareRecord(acc.GetAddress(), vaultDenom)
|
||||
|
||||
record.AmountSupplied = depositAmount
|
||||
record := types.NewVaultShareRecord(acc.GetAddress(), depositAmount)
|
||||
|
||||
// Update vault
|
||||
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().Equal(record, stateRecord)
|
||||
|
||||
// Remove supply
|
||||
record.AmountSupplied = sdk.NewInt64Coin("usdx", 0)
|
||||
record.AmountSupplied = sdk.NewCoins()
|
||||
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")
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
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
|
||||
// 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
|
||||
allowedVault, found := k.GetAllowedVault(ctx, amount.Denom)
|
||||
allowedVault, found := k.GetAllowedVault(ctx, wantAmount.Denom)
|
||||
if !found {
|
||||
return types.ErrInvalidVaultDenom
|
||||
}
|
||||
|
||||
if amount.IsZero() {
|
||||
if wantAmount.IsZero() {
|
||||
return types.ErrInsufficientAmount
|
||||
}
|
||||
|
||||
// Check if VaultRecord exists, return error if not exist as it's empty
|
||||
vaultRecord, found := k.GetVaultRecord(ctx, amount.Denom)
|
||||
// Check if VaultRecord exists
|
||||
vaultRecord, found := k.GetVaultRecord(ctx, wantAmount.Denom)
|
||||
if !found {
|
||||
return types.ErrVaultRecordNotFound
|
||||
}
|
||||
|
||||
// Get VaultShareRecord for account, create if not exist
|
||||
vaultShareRecord, found := k.GetVaultShareRecord(ctx, amount.Denom, from)
|
||||
// Get account value for vault
|
||||
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 {
|
||||
return types.ErrVaultShareRecordNotFound
|
||||
}
|
||||
|
||||
// Check if VaultShareRecord has enough supplied to withdraw
|
||||
if vaultShareRecord.AmountSupplied.Amount.LT(amount.Amount) {
|
||||
// Percent of vault account value the account is withdrawing
|
||||
// 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(
|
||||
types.ErrInvalidShares,
|
||||
"withdraw of %s shares greater than %s shares supplied",
|
||||
amount,
|
||||
vaultShareRecord.AmountSupplied,
|
||||
types.ErrInsufficientValue,
|
||||
"account vault value of %s is less than %s desired withdraw amount",
|
||||
vaultAccValue,
|
||||
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
|
||||
strategy, err := k.GetStrategy(allowedVault.VaultStrategy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deposit to the strategy
|
||||
if err := strategy.Withdraw(amount); err != nil {
|
||||
// Not necessary to check if amount denom is allowed for the strategy, as
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeVaultWithdraw,
|
||||
sdk.NewAttribute(types.AttributeKeyVaultDenom, amount.Denom),
|
||||
sdk.NewAttribute(types.AttributeKeyVaultDenom, wantAmount.Denom),
|
||||
sdk.NewAttribute(types.AttributeKeyOwner, from.String()),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, amount.Amount.String()),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, wantAmount.Amount.String()),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -24,11 +24,11 @@ func TestWithdrawTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -49,13 +49,13 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultRecord() {
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
|
||||
acc1DepositAmount := sdk.NewCoin(vaultDenom, sdk.NewInt(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
|
||||
acc1 := suite.CreateAccount(sdk.NewCoins(startBalance), 0)
|
||||
@ -75,18 +75,17 @@ func (suite *withdrawTestSuite) TestWithdraw_NoVaultShareRecord() {
|
||||
sdk.NewCoins(startBalance),
|
||||
)
|
||||
|
||||
suite.ModuleAccountBalanceEqual(
|
||||
sdk.NewCoins(acc1DepositAmount),
|
||||
)
|
||||
suite.VaultTotalValuesEqual(sdk.NewCoins(acc1DepositAmount))
|
||||
suite.VaultTotalSuppliedEqual(sdk.NewCoins(acc1DepositAmount))
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
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)
|
||||
|
||||
@ -95,7 +94,7 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
|
||||
|
||||
err = suite.Keeper.Withdraw(suite.Ctx, acc.GetAddress(), withdrawAmount)
|
||||
suite.Require().Error(err)
|
||||
suite.Require().ErrorIs(err, types.ErrInvalidShares)
|
||||
suite.Require().ErrorIs(err, types.ErrInsufficientValue)
|
||||
|
||||
// Balances still the same after deposit
|
||||
suite.AccountBalanceEqual(
|
||||
@ -103,17 +102,16 @@ func (suite *withdrawTestSuite) TestWithdraw_ExceedBalance() {
|
||||
sdk.NewCoins(startBalance.Sub(depositAmount)),
|
||||
)
|
||||
|
||||
suite.ModuleAccountBalanceEqual(
|
||||
sdk.NewCoins(depositAmount),
|
||||
)
|
||||
suite.VaultTotalValuesEqual(sdk.NewCoins(depositAmount))
|
||||
suite.VaultTotalSuppliedEqual(sdk.NewCoins(depositAmount))
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_Zero() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
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)
|
||||
|
||||
@ -134,7 +132,7 @@ func (suite *withdrawTestSuite) TestWithdraw_Zero() {
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
withdrawAmount := sdk.NewInt64Coin(vaultDenom, 1001)
|
||||
|
||||
@ -159,12 +157,12 @@ func (suite *withdrawTestSuite) TestWithdraw_InvalidVault() {
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_FullBalance() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := 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)
|
||||
|
||||
@ -186,12 +184,12 @@ func (suite *withdrawTestSuite) TestWithdraw_FullBalance() {
|
||||
}
|
||||
|
||||
func (suite *withdrawTestSuite) TestWithdraw_Partial() {
|
||||
vaultDenom := "busd"
|
||||
vaultDenom := "usdx"
|
||||
startBalance := sdk.NewInt64Coin(vaultDenom, 1000)
|
||||
depositAmount := sdk.NewInt64Coin(vaultDenom, 100)
|
||||
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)
|
||||
|
||||
|
@ -3,16 +3,23 @@ package testutil
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/earn/keeper"
|
||||
"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"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
||||
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"
|
||||
"github.com/stretchr/testify/suite"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -20,19 +27,93 @@ import (
|
||||
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 {
|
||||
suite.Suite
|
||||
Keeper keeper.Keeper
|
||||
App app.TestApp
|
||||
Ctx sdk.Context
|
||||
BankKeeper BankKeeper.Keeper
|
||||
BankKeeper bankkeeper.Keeper
|
||||
AccountKeeper authkeeper.AccountKeeper
|
||||
|
||||
// Strategy Keepers
|
||||
HardKeeper hardkeeper.Keeper
|
||||
SavingsKeeper savingskeeper.Keeper
|
||||
}
|
||||
|
||||
// SetupTest instantiates a new app, keepers, and sets suite state
|
||||
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.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()})
|
||||
|
||||
suite.Ctx = ctx
|
||||
@ -40,6 +121,11 @@ func (suite *Suite) SetupTest() {
|
||||
suite.Keeper = tApp.GetEarnKeeper()
|
||||
suite.BankKeeper = tApp.GetBankKeeper()
|
||||
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
|
||||
@ -47,16 +133,16 @@ func (suite *Suite) GetEvents() sdk.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) {
|
||||
// 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)
|
||||
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) {
|
||||
// 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)
|
||||
suite.Require().NoError(err)
|
||||
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)
|
||||
suite.Require().NoError(vault.Validate())
|
||||
|
||||
// allowedVaults := suite.Keeper.GetAllowedVaults(suite.Ctx)
|
||||
// allowedVaults = append(allowedVaults, vault)
|
||||
allowedVaults := suite.Keeper.GetAllowedVaults(suite.Ctx)
|
||||
allowedVaults = append(allowedVaults, vault)
|
||||
|
||||
suite.Keeper.SetParams(
|
||||
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))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
balance := suite.BankKeeper.GetAllBalances(
|
||||
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))
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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
|
||||
func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) {
|
||||
foundMatch := false
|
||||
|
@ -9,7 +9,8 @@ var (
|
||||
ErrInvalidVaultDenom = sdkerrors.Register(ModuleName, 2, "invalid vault denom")
|
||||
ErrInvalidVaultStrategy = sdkerrors.Register(ModuleName, 3, "invalid vault strategy")
|
||||
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")
|
||||
ErrVaultShareRecordNotFound = sdkerrors.Register(ModuleName, 7, "vault share record not found")
|
||||
ErrStrategyDenomNotSupported = sdkerrors.Register(ModuleName, 8, "denom not supported for strategy")
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package types
|
||||
|
||||
// Event types for swap module
|
||||
// Event types for earn module
|
||||
const (
|
||||
AttributeValueCategory = ModuleName
|
||||
EventTypeVaultDeposit = "vault_deposit"
|
||||
|
@ -3,6 +3,9 @@ package types
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/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
|
||||
@ -21,3 +24,19 @@ type BankKeeper interface {
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
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 {
|
||||
// params defines all the paramaters related to earn
|
||||
Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
|
||||
|
@ -25,9 +25,7 @@ const (
|
||||
// key prefixes for store
|
||||
var (
|
||||
VaultRecordKeyPrefix = []byte{0x01} // denom -> vault
|
||||
VaultSharePrefix = []byte{0x02}
|
||||
|
||||
sep = []byte("|")
|
||||
VaultShareRecordKeyPrefix = []byte{0x02} // depositor address -> vault shares
|
||||
)
|
||||
|
||||
// Vault returns a key generated from a vault denom
|
||||
@ -35,14 +33,7 @@ func VaultKey(denom string) []byte {
|
||||
return []byte(denom)
|
||||
}
|
||||
|
||||
// DepositorVaultSharesKey returns a key from a depositor and vault denom
|
||||
func DepositorVaultSharesKey(depositor sdk.AccAddress, vaultDenom string) []byte {
|
||||
return createKey(depositor, sep, []byte(vaultDenom))
|
||||
}
|
||||
|
||||
func createKey(bytes ...[]byte) (r []byte) {
|
||||
for _, b := range bytes {
|
||||
r = append(r, b...)
|
||||
}
|
||||
return
|
||||
// DepositorVaultSharesKey returns a key from a depositor address
|
||||
func DepositorVaultSharesKey(depositor sdk.AccAddress) []byte {
|
||||
return depositor.Bytes()
|
||||
}
|
||||
|
@ -27,24 +27,20 @@ type StrategyType int32
|
||||
|
||||
const (
|
||||
STRATEGY_TYPE_UNKNOWN StrategyType = 0
|
||||
STRATEGY_TYPE_KAVA_STAKERS StrategyType = 1
|
||||
// USDC / BUSD vaults use the same strategy but with the denom set in VaultRecord
|
||||
STRATEGY_TYPE_STABLECOIN_STAKERS StrategyType = 2
|
||||
STRATEGY_TYPE_KAVA_FOUNDATION StrategyType = 3
|
||||
STRATEGY_TYPE_HARD StrategyType = 1
|
||||
STRATEGY_TYPE_SAVINGS StrategyType = 2
|
||||
)
|
||||
|
||||
var StrategyType_name = map[int32]string{
|
||||
0: "STRATEGY_TYPE_UNKNOWN",
|
||||
1: "STRATEGY_TYPE_KAVA_STAKERS",
|
||||
2: "STRATEGY_TYPE_STABLECOIN_STAKERS",
|
||||
3: "STRATEGY_TYPE_KAVA_FOUNDATION",
|
||||
1: "STRATEGY_TYPE_HARD",
|
||||
2: "STRATEGY_TYPE_SAVINGS",
|
||||
}
|
||||
|
||||
var StrategyType_value = map[string]int32{
|
||||
"STRATEGY_TYPE_UNKNOWN": 0,
|
||||
"STRATEGY_TYPE_KAVA_STAKERS": 1,
|
||||
"STRATEGY_TYPE_STABLECOIN_STAKERS": 2,
|
||||
"STRATEGY_TYPE_KAVA_FOUNDATION": 3,
|
||||
"STRATEGY_TYPE_HARD": 1,
|
||||
"STRATEGY_TYPE_SAVINGS": 2,
|
||||
}
|
||||
|
||||
func (x StrategyType) String() string {
|
||||
@ -62,23 +58,21 @@ func init() {
|
||||
func init() { proto.RegisterFile("kava/earn/v1beta1/strategy.proto", fileDescriptor_257c4968dd48fa09) }
|
||||
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
0x97, 0x68, 0x70, 0x48, 0x90, 0x63, 0x88, 0xab, 0x7b, 0x64, 0x7c, 0x48, 0x64, 0x80, 0x6b, 0x7c,
|
||||
0xa8, 0x9f, 0xb7, 0x9f, 0x7f, 0xb8, 0x9f, 0x00, 0x83, 0x90, 0x1c, 0x97, 0x14, 0xaa, 0x94, 0xb7,
|
||||
0x63, 0x98, 0x63, 0x7c, 0x70, 0x88, 0xa3, 0xb7, 0x6b, 0x50, 0xb0, 0x00, 0xa3, 0x90, 0x0a, 0x97,
|
||||
0x02, 0xaa, 0x7c, 0x70, 0x88, 0xa3, 0x93, 0x8f, 0xab, 0xb3, 0xbf, 0xa7, 0x1f, 0x5c, 0x15, 0x93,
|
||||
0x90, 0x22, 0x97, 0x2c, 0x16, 0x53, 0xdc, 0xfc, 0x43, 0xfd, 0x5c, 0x1c, 0x43, 0x3c, 0xfd, 0xfd,
|
||||
0x04, 0x98, 0xa5, 0x58, 0x3a, 0x16, 0xcb, 0x31, 0x38, 0x39, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1,
|
||||
0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70,
|
||||
0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x5a, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae,
|
||||
0x3e, 0x28, 0x8c, 0x74, 0x73, 0x12, 0x93, 0x8a, 0xc1, 0x2c, 0xfd, 0x0a, 0x48, 0x88, 0x96, 0x54,
|
||||
0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, 0xfd, 0x68, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x85, 0x84,
|
||||
0x93, 0x9f, 0x6b, 0x01, 0x00, 0x00,
|
||||
0x22, 0xa5, 0x95, 0xc4, 0xc5, 0x13, 0x0c, 0xb5, 0x35, 0xa4, 0xb2, 0x20, 0x55, 0x48, 0x92, 0x4b,
|
||||
0x34, 0x38, 0x24, 0xc8, 0x31, 0xc4, 0xd5, 0x3d, 0x32, 0x3e, 0x24, 0x32, 0xc0, 0x35, 0x3e, 0xd4,
|
||||
0xcf, 0xdb, 0xcf, 0x3f, 0xdc, 0x4f, 0x80, 0x41, 0x48, 0x8c, 0x4b, 0x08, 0x55, 0xca, 0xc3, 0x31,
|
||||
0xc8, 0x45, 0x80, 0x11, 0x53, 0x4b, 0xb0, 0x63, 0x98, 0xa7, 0x9f, 0x7b, 0xb0, 0x00, 0x93, 0x14,
|
||||
0x4b, 0xc7, 0x62, 0x39, 0x06, 0x27, 0x87, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c,
|
||||
0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63,
|
||||
0x88, 0x52, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x07, 0x79, 0x56,
|
||||
0x37, 0x27, 0x31, 0xa9, 0x18, 0xcc, 0xd2, 0xaf, 0x80, 0x04, 0x4d, 0x49, 0x65, 0x41, 0x6a, 0x71,
|
||||
0x12, 0x1b, 0xd8, 0xb1, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x62, 0x68, 0x2c, 0xbc, 0x34,
|
||||
0x01, 0x00, 0x00,
|
||||
}
|
||||
|
@ -18,11 +18,12 @@ type VaultRecords []VaultRecord
|
||||
|
||||
type VaultShareRecords []VaultShareRecord
|
||||
|
||||
// NewVaultShareRecord returns a new VaultShareRecord with 0 supply.
|
||||
func NewVaultShareRecord(depositor sdk.AccAddress, vaultDenom string) VaultShareRecord {
|
||||
// NewVaultShareRecord returns a new VaultShareRecord with the provided supplied
|
||||
// coins.
|
||||
func NewVaultShareRecord(depositor sdk.AccAddress, supplied ...sdk.Coin) VaultShareRecord {
|
||||
return VaultShareRecord{
|
||||
Depositor: depositor,
|
||||
AmountSupplied: sdk.NewCoin(vaultDenom, sdk.ZeroInt()),
|
||||
AmountSupplied: sdk.NewCoins(supplied...),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,13 +140,13 @@ func (m *VaultRecord) GetTotalSupply() 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 {
|
||||
// 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"`
|
||||
// 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.
|
||||
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{} }
|
||||
@ -189,11 +189,11 @@ func (m *VaultShareRecord) GetDepositor() github_com_cosmos_cosmos_sdk_types.Acc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *VaultShareRecord) GetAmountSupplied() types.Coin {
|
||||
func (m *VaultShareRecord) GetAmountSupplied() github_com_cosmos_cosmos_sdk_types.Coins {
|
||||
if m != nil {
|
||||
return m.AmountSupplied
|
||||
}
|
||||
return types.Coin{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -205,32 +205,34 @@ func init() {
|
||||
func init() { proto.RegisterFile("kava/earn/v1beta1/vault.proto", fileDescriptor_884eb89509fbdc04) }
|
||||
|
||||
var fileDescriptor_884eb89509fbdc04 = []byte{
|
||||
// 398 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x51, 0xcd, 0xae, 0xd2, 0x40,
|
||||
0x14, 0x6e, 0x8d, 0x9a, 0x30, 0x20, 0x6a, 0x65, 0x01, 0x24, 0x16, 0xc2, 0xc2, 0xb0, 0xe9, 0x34,
|
||||
0xe0, 0x0b, 0x48, 0x4d, 0x0c, 0xeb, 0xd6, 0xb8, 0x70, 0x43, 0xa6, 0x9d, 0xb1, 0x34, 0xb4, 0x3d,
|
||||
0x4d, 0x67, 0x8a, 0xf6, 0x2d, 0x7c, 0x18, 0x1f, 0xc1, 0x05, 0x4b, 0xe2, 0xca, 0x15, 0xb9, 0x81,
|
||||
0xb7, 0xb8, 0xab, 0x9b, 0xce, 0x0c, 0xdc, 0x9b, 0x90, 0x9b, 0xdc, 0xd5, 0xfc, 0x7c, 0xe7, 0x3b,
|
||||
0xdf, 0xf7, 0x9d, 0x83, 0xde, 0x6f, 0xc8, 0x96, 0xb8, 0x8c, 0x94, 0xb9, 0xbb, 0x9d, 0x85, 0x4c,
|
||||
0x90, 0x99, 0xbb, 0x25, 0x55, 0x2a, 0x70, 0x51, 0x82, 0x00, 0xeb, 0x6d, 0x03, 0xe3, 0x06, 0xc6,
|
||||
0x1a, 0x1e, 0xf6, 0x62, 0x88, 0x41, 0xa2, 0x6e, 0x73, 0x53, 0x85, 0x43, 0x3b, 0x02, 0x9e, 0x01,
|
||||
0x77, 0x43, 0xc2, 0xd9, 0xa5, 0x53, 0x04, 0x49, 0xae, 0xf1, 0x81, 0xc2, 0x57, 0x8a, 0xa8, 0x1e,
|
||||
0x1a, 0x1a, 0x5f, 0x5b, 0xe0, 0xa2, 0x24, 0x82, 0xc5, 0xb5, 0xaa, 0x98, 0xa4, 0xa8, 0xb3, 0x48,
|
||||
0x53, 0xf8, 0xc9, 0xe8, 0xb7, 0xc6, 0x9b, 0xd5, 0x43, 0x2f, 0x28, 0xcb, 0x21, 0xeb, 0x9b, 0x63,
|
||||
0x73, 0xda, 0xf2, 0xd5, 0xc3, 0xfa, 0x82, 0xba, 0xd2, 0xfa, 0xea, 0xcc, 0xee, 0x3f, 0x1b, 0x9b,
|
||||
0xd3, 0xee, 0x7c, 0x84, 0xaf, 0x42, 0xe0, 0x40, 0x97, 0x7c, 0xad, 0x0b, 0xe6, 0xbf, 0x92, 0xb4,
|
||||
0xf3, 0xd7, 0x24, 0x46, 0x6d, 0x29, 0xe3, 0xb3, 0x08, 0x4a, 0xfa, 0x88, 0x98, 0x87, 0x3a, 0x02,
|
||||
0x04, 0x49, 0x57, 0xbc, 0x2a, 0x8a, 0x54, 0x49, 0xb5, 0xe7, 0x03, 0xac, 0x93, 0x35, 0x63, 0xb8,
|
||||
0x88, 0x7d, 0x86, 0x24, 0xf7, 0x9e, 0xef, 0x0e, 0x23, 0xc3, 0x6f, 0x4b, 0x52, 0x20, 0x39, 0x93,
|
||||
0xbf, 0x26, 0x7a, 0x23, 0x95, 0x82, 0x35, 0x29, 0x99, 0x96, 0xfb, 0x81, 0x5a, 0x94, 0x15, 0xc0,
|
||||
0x13, 0x01, 0xa5, 0x94, 0xec, 0x78, 0xcb, 0xdb, 0xc3, 0xc8, 0x89, 0x13, 0xb1, 0xae, 0x42, 0x1c,
|
||||
0x41, 0xa6, 0xa7, 0xa7, 0x0f, 0x87, 0xd3, 0x8d, 0x2b, 0xea, 0x82, 0x71, 0xbc, 0x88, 0xa2, 0x05,
|
||||
0xa5, 0x25, 0xe3, 0xfc, 0xdf, 0x1f, 0xe7, 0x9d, 0x76, 0xa2, 0x7f, 0xbc, 0x5a, 0x30, 0xee, 0xdf,
|
||||
0xb7, 0xb6, 0x96, 0xe8, 0x35, 0xc9, 0xa0, 0xca, 0x85, 0x4a, 0x90, 0x30, 0xfa, 0xd4, 0x0c, 0x5d,
|
||||
0xc5, 0x0b, 0x34, 0xcd, 0xfb, 0xb4, 0x3b, 0xda, 0xe6, 0xfe, 0x68, 0x9b, 0x37, 0x47, 0xdb, 0xfc,
|
||||
0x7d, 0xb2, 0x8d, 0xfd, 0xc9, 0x36, 0xfe, 0x9f, 0x6c, 0xe3, 0xfb, 0x87, 0x07, 0xa6, 0x9b, 0x1d,
|
||||
0x38, 0x29, 0x09, 0xb9, 0xbc, 0xb9, 0xbf, 0xd4, 0xc2, 0xa5, 0xf1, 0xf0, 0xa5, 0x5c, 0xf3, 0xc7,
|
||||
0xbb, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc6, 0x8b, 0xa9, 0xd7, 0x8d, 0x02, 0x00, 0x00,
|
||||
// 417 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xbf, 0xae, 0xd3, 0x30,
|
||||
0x14, 0xc6, 0x93, 0xcb, 0x1f, 0xe9, 0xba, 0xa5, 0x40, 0xb8, 0x43, 0xef, 0x95, 0x48, 0xa2, 0x0e,
|
||||
0xa8, 0x4b, 0x1c, 0xee, 0xe5, 0x05, 0x68, 0x90, 0x10, 0x73, 0x82, 0x18, 0x58, 0x2a, 0x27, 0x36,
|
||||
0x69, 0xd4, 0x24, 0x27, 0x8a, 0x9d, 0x42, 0xde, 0x82, 0xe7, 0x60, 0xe6, 0x21, 0x3a, 0x56, 0x4c,
|
||||
0x4c, 0x05, 0xb5, 0x2f, 0xc0, 0xcc, 0x84, 0xfc, 0xa7, 0x05, 0xa9, 0x02, 0x31, 0xc5, 0xf6, 0x77,
|
||||
0xbe, 0xf3, 0x3b, 0x9f, 0x63, 0xf4, 0x78, 0x49, 0x56, 0x24, 0x64, 0xa4, 0xad, 0xc3, 0xd5, 0x75,
|
||||
0xca, 0x04, 0xb9, 0x0e, 0x57, 0xa4, 0x2b, 0x05, 0x6e, 0x5a, 0x10, 0xe0, 0x3c, 0x94, 0x32, 0x96,
|
||||
0x32, 0x36, 0xf2, 0xd5, 0x45, 0x0e, 0x39, 0x28, 0x35, 0x94, 0x2b, 0x5d, 0x78, 0xe5, 0x66, 0xc0,
|
||||
0x2b, 0xe0, 0x61, 0x4a, 0x38, 0x3b, 0x76, 0xca, 0xa0, 0xa8, 0x8d, 0x7e, 0xa9, 0xf5, 0xb9, 0x36,
|
||||
0xea, 0x8d, 0x91, 0xfc, 0xd3, 0x11, 0xb8, 0x68, 0x89, 0x60, 0x79, 0xaf, 0x2b, 0x26, 0x25, 0x1a,
|
||||
0xce, 0xca, 0x12, 0xde, 0x33, 0xfa, 0x46, 0xce, 0xe6, 0x5c, 0xa0, 0x3b, 0x94, 0xd5, 0x50, 0x8d,
|
||||
0x6d, 0xdf, 0x9e, 0x9e, 0xc7, 0x7a, 0xe3, 0xbc, 0x44, 0x23, 0x35, 0xfa, 0xfc, 0xe0, 0x1e, 0x9f,
|
||||
0xf9, 0xf6, 0x74, 0x74, 0xe3, 0xe1, 0x93, 0x10, 0x38, 0x31, 0x25, 0xaf, 0xfb, 0x86, 0xc5, 0xf7,
|
||||
0x94, 0xed, 0x70, 0x34, 0xc9, 0xd1, 0x40, 0x61, 0x62, 0x96, 0x41, 0x4b, 0xff, 0x02, 0x8b, 0xd0,
|
||||
0x50, 0x80, 0x20, 0xe5, 0x9c, 0x77, 0x4d, 0x53, 0x6a, 0xd4, 0xe0, 0xe6, 0x12, 0x9b, 0x64, 0xf2,
|
||||
0x1a, 0x8e, 0xb0, 0x17, 0x50, 0xd4, 0xd1, 0xed, 0xf5, 0xd6, 0xb3, 0xe2, 0x81, 0x32, 0x25, 0xca,
|
||||
0x33, 0xf9, 0x61, 0xa3, 0x07, 0x8a, 0x94, 0x2c, 0x48, 0xcb, 0x0c, 0xee, 0x1d, 0x3a, 0xa7, 0xac,
|
||||
0x01, 0x5e, 0x08, 0x68, 0x15, 0x72, 0x18, 0xbd, 0xfa, 0xb9, 0xf5, 0x82, 0xbc, 0x10, 0x8b, 0x2e,
|
||||
0xc5, 0x19, 0x54, 0xe6, 0xf6, 0xcc, 0x27, 0xe0, 0x74, 0x19, 0x8a, 0xbe, 0x61, 0x1c, 0xcf, 0xb2,
|
||||
0x6c, 0x46, 0x69, 0xcb, 0x38, 0xff, 0xf2, 0x39, 0x78, 0x64, 0x26, 0x31, 0x27, 0x51, 0x2f, 0x18,
|
||||
0x8f, 0x7f, 0xb7, 0x76, 0x04, 0xba, 0x4f, 0x2a, 0xe8, 0x6a, 0xa1, 0x13, 0x14, 0x8c, 0x8e, 0xcf,
|
||||
0xfc, 0x5b, 0xff, 0xce, 0xf0, 0x54, 0x66, 0xf8, 0xf4, 0xcd, 0x9b, 0xfe, 0xc7, 0x30, 0xd2, 0xc0,
|
||||
0xe3, 0x91, 0x66, 0x24, 0x06, 0x11, 0x3d, 0x5f, 0xef, 0x5c, 0x7b, 0xb3, 0x73, 0xed, 0xef, 0x3b,
|
||||
0xd7, 0xfe, 0xb8, 0x77, 0xad, 0xcd, 0xde, 0xb5, 0xbe, 0xee, 0x5d, 0xeb, 0xed, 0x93, 0x3f, 0x7a,
|
||||
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) {
|
||||
@ -328,8 +330,10 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = 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 {
|
||||
return 0, err
|
||||
}
|
||||
@ -338,6 +342,8 @@ func (m *VaultShareRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
}
|
||||
if len(m.Depositor) > 0 {
|
||||
i -= len(m.Depositor)
|
||||
copy(dAtA[i:], m.Depositor)
|
||||
@ -400,8 +406,12 @@ func (m *VaultShareRecord) Size() (n int) {
|
||||
if l > 0 {
|
||||
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))
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -719,7 +729,8 @@ func (m *VaultShareRecord) Unmarshal(dAtA []byte) error {
|
||||
if postIndex > l {
|
||||
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
|
||||
}
|
||||
iNdEx = postIndex
|
||||
|
Loading…
Reference in New Issue
Block a user