mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05: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,
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 |
|
||||||
|
@ -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];
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
}
|
}
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
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"
|
"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)
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
)
|
)
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
@ -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"`
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
|
@ -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...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user