mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 03:07:28 +00:00 
			
		
		
		
	Add Init/ExportGenesis for Earn module (#1280)
* Add vault and vault shares to genesis * Fix error and denoms * Update init and export genesis with correct data * Fix total supply genesis check, remove legacy test for enum errors * Add vault slice validate tests * Update denom validation, add additional test
This commit is contained in:
		
							parent
							
								
									82e2f26e14
								
							
						
					
					
						commit
						ed9991d44a
					
				@ -10,16 +10,54 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InitGenesis initializes genesis state
 | 
					// InitGenesis initializes genesis state
 | 
				
			||||||
func InitGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper, gs types.GenesisState) {
 | 
					func InitGenesis(
 | 
				
			||||||
 | 
						ctx sdk.Context,
 | 
				
			||||||
 | 
						k keeper.Keeper,
 | 
				
			||||||
 | 
						ak types.AccountKeeper,
 | 
				
			||||||
 | 
						gs types.GenesisState,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
	if err := gs.Validate(); err != nil {
 | 
						if err := gs.Validate(); err != nil {
 | 
				
			||||||
		panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err))
 | 
							panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Total of all vault share records, vault record total supply should equal this
 | 
				
			||||||
 | 
						vaultTotalSupplies := sdk.NewCoins()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vaultShareRecord := range gs.VaultShareRecords {
 | 
				
			||||||
 | 
							if err := vaultShareRecord.Validate(); err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("invalid vault share: %s", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vaultTotalSupplies = vaultTotalSupplies.Add(vaultShareRecord.AmountSupplied...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							k.SetVaultShareRecord(ctx, vaultShareRecord)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vaultRecord := range gs.VaultRecords {
 | 
				
			||||||
 | 
							if err := vaultRecord.Validate(); err != nil {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("invalid vault record: %s", err))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !vaultRecord.TotalSupply.Amount.Equal(vaultTotalSupplies.AmountOf(vaultRecord.Denom)) {
 | 
				
			||||||
 | 
								panic(fmt.Sprintf(
 | 
				
			||||||
 | 
									"invalid vault record total supply for %s, got %s but sum of vault shares is %s",
 | 
				
			||||||
 | 
									vaultRecord.Denom,
 | 
				
			||||||
 | 
									vaultRecord.TotalSupply.Amount,
 | 
				
			||||||
 | 
									vaultTotalSupplies.AmountOf(vaultRecord.Denom),
 | 
				
			||||||
 | 
								))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							k.SetVaultRecord(ctx, vaultRecord)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	k.SetParams(ctx, gs.Params)
 | 
						k.SetParams(ctx, gs.Params)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ExportGenesis returns a GenesisState for a given context and keeper
 | 
					// ExportGenesis returns a GenesisState for a given context and keeper
 | 
				
			||||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
 | 
					func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
 | 
				
			||||||
	params := k.GetParams(ctx)
 | 
						params := k.GetParams(ctx)
 | 
				
			||||||
	return types.NewGenesisState(params)
 | 
						vaultRecords := k.GetAllVaultRecords(ctx)
 | 
				
			||||||
 | 
						vaultShareRecords := k.GetAllVaultShareRecords(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return types.NewGenesisState(params, vaultRecords, vaultShareRecords)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										145
									
								
								x/earn/genesis_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								x/earn/genesis_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					package earn_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/app"
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/earn"
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/earn/testutil"
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/earn/types"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/suite"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type genesisTestSuite struct {
 | 
				
			||||||
 | 
						testutil.Suite
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *genesisTestSuite) Test_InitGenesis_ValidationPanic() {
 | 
				
			||||||
 | 
						invalidState := types.NewGenesisState(
 | 
				
			||||||
 | 
							types.Params{
 | 
				
			||||||
 | 
								AllowedVaults: types.AllowedVaults{
 | 
				
			||||||
 | 
									types.NewAllowedVault("usdx", types.STRATEGY_TYPE_HARD),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultRecords{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Denom:       "",
 | 
				
			||||||
 | 
									TotalSupply: sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultShareRecords{},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.Panics(func() {
 | 
				
			||||||
 | 
							earn.InitGenesis(suite.Ctx, suite.Keeper, suite.AccountKeeper, invalidState)
 | 
				
			||||||
 | 
						}, "expected init genesis to panic with invalid state")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *genesisTestSuite) Test_InitAndExportGenesis() {
 | 
				
			||||||
 | 
						depositor_1, err := sdk.AccAddressFromBech32("kava1esagqd83rhqdtpy5sxhklaxgn58k2m3s3mnpea")
 | 
				
			||||||
 | 
						suite.Require().NoError(err)
 | 
				
			||||||
 | 
						depositor_2, err := sdk.AccAddressFromBech32("kava1mq9qxlhze029lm0frzw2xr6hem8c3k9ts54w0w")
 | 
				
			||||||
 | 
						suite.Require().NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// slices are sorted by key as stored in the data store, so init and export can be compared with equal
 | 
				
			||||||
 | 
						state := types.NewGenesisState(
 | 
				
			||||||
 | 
							types.Params{
 | 
				
			||||||
 | 
								AllowedVaults: types.AllowedVaults{
 | 
				
			||||||
 | 
									types.NewAllowedVault("usdx", types.STRATEGY_TYPE_HARD),
 | 
				
			||||||
 | 
									types.NewAllowedVault("ukava", types.STRATEGY_TYPE_SAVINGS),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultRecords{
 | 
				
			||||||
 | 
								types.VaultRecord{
 | 
				
			||||||
 | 
									Denom:       "ukava",
 | 
				
			||||||
 | 
									TotalSupply: sdk.NewInt64Coin("ukava", 2000000),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								types.VaultRecord{
 | 
				
			||||||
 | 
									Denom:       "usdx",
 | 
				
			||||||
 | 
									TotalSupply: sdk.NewInt64Coin("usdx", 1000000),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultShareRecords{
 | 
				
			||||||
 | 
								types.VaultShareRecord{
 | 
				
			||||||
 | 
									Depositor:      depositor_1,
 | 
				
			||||||
 | 
									AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 1900000)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								types.VaultShareRecord{
 | 
				
			||||||
 | 
									Depositor:      depositor_2,
 | 
				
			||||||
 | 
									AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 100000)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						earn.InitGenesis(suite.Ctx, suite.Keeper, suite.AccountKeeper, state)
 | 
				
			||||||
 | 
						suite.Equal(state.Params, suite.Keeper.GetParams(suite.Ctx))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vaultRecord1, _ := suite.Keeper.GetVaultRecord(suite.Ctx, "ukava")
 | 
				
			||||||
 | 
						vaultRecord2, _ := suite.Keeper.GetVaultRecord(suite.Ctx, "usdx")
 | 
				
			||||||
 | 
						suite.Equal(state.VaultRecords[0], vaultRecord1)
 | 
				
			||||||
 | 
						suite.Equal(state.VaultRecords[1], vaultRecord2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shareRecord1, _ := suite.Keeper.GetVaultShareRecord(suite.Ctx, depositor_1)
 | 
				
			||||||
 | 
						shareRecord2, _ := suite.Keeper.GetVaultShareRecord(suite.Ctx, depositor_2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.Equal(state.VaultShareRecords[0], shareRecord1)
 | 
				
			||||||
 | 
						suite.Equal(state.VaultShareRecords[1], shareRecord2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exportedState := earn.ExportGenesis(suite.Ctx, suite.Keeper)
 | 
				
			||||||
 | 
						suite.Equal(state, exportedState)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *genesisTestSuite) Test_Marshall() {
 | 
				
			||||||
 | 
						depositor_1, err := sdk.AccAddressFromBech32("kava1esagqd83rhqdtpy5sxhklaxgn58k2m3s3mnpea")
 | 
				
			||||||
 | 
						suite.Require().NoError(err)
 | 
				
			||||||
 | 
						depositor_2, err := sdk.AccAddressFromBech32("kava1mq9qxlhze029lm0frzw2xr6hem8c3k9ts54w0w")
 | 
				
			||||||
 | 
						suite.Require().NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// slices are sorted by key as stored in the data store, so init and export can be compared with equal
 | 
				
			||||||
 | 
						state := types.NewGenesisState(
 | 
				
			||||||
 | 
							types.Params{
 | 
				
			||||||
 | 
								AllowedVaults: types.AllowedVaults{
 | 
				
			||||||
 | 
									types.NewAllowedVault("usdx", types.STRATEGY_TYPE_HARD),
 | 
				
			||||||
 | 
									types.NewAllowedVault("ukava", types.STRATEGY_TYPE_SAVINGS),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultRecords{
 | 
				
			||||||
 | 
								types.VaultRecord{
 | 
				
			||||||
 | 
									Denom:       "ukava",
 | 
				
			||||||
 | 
									TotalSupply: sdk.NewInt64Coin("ukava", 2000000),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								types.VaultRecord{
 | 
				
			||||||
 | 
									Denom:       "usdx",
 | 
				
			||||||
 | 
									TotalSupply: sdk.NewInt64Coin("usdx", 1000000),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							types.VaultShareRecords{
 | 
				
			||||||
 | 
								types.VaultShareRecord{
 | 
				
			||||||
 | 
									Depositor:      depositor_1,
 | 
				
			||||||
 | 
									AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 1900000)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								types.VaultShareRecord{
 | 
				
			||||||
 | 
									Depositor:      depositor_2,
 | 
				
			||||||
 | 
									AmountSupplied: sdk.NewCoins(sdk.NewInt64Coin("usdx", 500000), sdk.NewInt64Coin("ukava", 100000)),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						encodingCfg := app.MakeEncodingConfig()
 | 
				
			||||||
 | 
						cdc := encodingCfg.Marshaler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bz, err := cdc.Marshal(&state)
 | 
				
			||||||
 | 
						suite.Require().NoError(err, "expected genesis state to marshal without error")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var decodedState types.GenesisState
 | 
				
			||||||
 | 
						err = cdc.Unmarshal(bz, &decodedState)
 | 
				
			||||||
 | 
						suite.Require().NoError(err, "expected genesis state to unmarshal without error")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						suite.Equal(state, decodedState)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGenesisTestSuite(t *testing.T) {
 | 
				
			||||||
 | 
						suite.Run(t, new(genesisTestSuite))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -222,3 +222,34 @@ func (k *Keeper) SetVaultShareRecord(
 | 
				
			|||||||
	bz := k.cdc.MustMarshal(&record)
 | 
						bz := k.cdc.MustMarshal(&record)
 | 
				
			||||||
	store.Set(types.DepositorVaultSharesKey(record.Depositor), bz)
 | 
						store.Set(types.DepositorVaultSharesKey(record.Depositor), bz)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IterateVaultShareRecords iterates over all vault share objects in the store
 | 
				
			||||||
 | 
					// and performs a callback function.
 | 
				
			||||||
 | 
					func (k Keeper) IterateVaultShareRecords(
 | 
				
			||||||
 | 
						ctx sdk.Context,
 | 
				
			||||||
 | 
						cb func(record types.VaultShareRecord) (stop bool),
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
						store := prefix.NewStore(ctx.KVStore(k.key), types.VaultShareRecordKeyPrefix)
 | 
				
			||||||
 | 
						iterator := sdk.KVStorePrefixIterator(store, []byte{})
 | 
				
			||||||
 | 
						defer iterator.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for ; iterator.Valid(); iterator.Next() {
 | 
				
			||||||
 | 
							var record types.VaultShareRecord
 | 
				
			||||||
 | 
							k.cdc.MustUnmarshal(iterator.Value(), &record)
 | 
				
			||||||
 | 
							if cb(record) {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetAllVaultShareRecords returns all vault share records from the store.
 | 
				
			||||||
 | 
					func (k Keeper) GetAllVaultShareRecords(ctx sdk.Context) types.VaultShareRecords {
 | 
				
			||||||
 | 
						var records types.VaultShareRecords
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						k.IterateVaultShareRecords(ctx, func(record types.VaultShareRecord) bool {
 | 
				
			||||||
 | 
							records = append(records, record)
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return records
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,15 @@
 | 
				
			|||||||
package types
 | 
					package types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewGenesisState creates a new genesis state.
 | 
					// NewGenesisState creates a new genesis state.
 | 
				
			||||||
func NewGenesisState(params Params) GenesisState {
 | 
					func NewGenesisState(
 | 
				
			||||||
 | 
						params Params,
 | 
				
			||||||
 | 
						vaultRecords VaultRecords,
 | 
				
			||||||
 | 
						vaultShareRecords VaultShareRecords,
 | 
				
			||||||
 | 
					) GenesisState {
 | 
				
			||||||
	return GenesisState{
 | 
						return GenesisState{
 | 
				
			||||||
		Params: params,
 | 
							Params:            params,
 | 
				
			||||||
 | 
							VaultRecords:      vaultRecords,
 | 
				
			||||||
 | 
							VaultShareRecords: vaultShareRecords,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,6 +19,14 @@ func (gs GenesisState) Validate() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := gs.VaultRecords.Validate(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := gs.VaultShareRecords.Validate(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,5 +34,7 @@ func (gs GenesisState) Validate() error {
 | 
				
			|||||||
func DefaultGenesisState() GenesisState {
 | 
					func DefaultGenesisState() GenesisState {
 | 
				
			||||||
	return NewGenesisState(
 | 
						return NewGenesisState(
 | 
				
			||||||
		DefaultParams(),
 | 
							DefaultParams(),
 | 
				
			||||||
 | 
							VaultRecords{},
 | 
				
			||||||
 | 
							VaultShareRecords{},
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
 | 
						sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewVaultRecord returns a new VaultRecord with 0 supply.
 | 
					// NewVaultRecord returns a new VaultRecord with 0 supply.
 | 
				
			||||||
@ -14,9 +15,48 @@ func NewVaultRecord(vaultDenom string) VaultRecord {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate returns an error if a VaultRecord is invalid.
 | 
				
			||||||
 | 
					func (vr *VaultRecord) Validate() error {
 | 
				
			||||||
 | 
						if err := sdk.ValidateDenom(vr.Denom); err != nil {
 | 
				
			||||||
 | 
							return sdkerrors.Wrap(ErrInvalidVaultDenom, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if vr.TotalSupply.Denom != vr.Denom {
 | 
				
			||||||
 | 
							return fmt.Errorf(
 | 
				
			||||||
 | 
								"total supply denom %v does not match vault record denom %v",
 | 
				
			||||||
 | 
								vr.TotalSupply.Denom,
 | 
				
			||||||
 | 
								vr.Denom,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := vr.TotalSupply.Validate(); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("vault total supply is invalid: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VaultRecords is a slice of VaultRecord.
 | 
				
			||||||
type VaultRecords []VaultRecord
 | 
					type VaultRecords []VaultRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type VaultShareRecords []VaultShareRecord
 | 
					// Validate returns an error if a slice of VaultRecords is invalid.
 | 
				
			||||||
 | 
					func (vrs VaultRecords) Validate() error {
 | 
				
			||||||
 | 
						denoms := make(map[string]bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vr := range vrs {
 | 
				
			||||||
 | 
							if err := vr.Validate(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if denoms[vr.Denom] {
 | 
				
			||||||
 | 
								return fmt.Errorf("duplicate vault denom %s", vr.Denom)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							denoms[vr.Denom] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewVaultShareRecord returns a new VaultShareRecord with the provided supplied
 | 
					// NewVaultShareRecord returns a new VaultShareRecord with the provided supplied
 | 
				
			||||||
// coins.
 | 
					// coins.
 | 
				
			||||||
@ -27,6 +67,41 @@ func NewVaultShareRecord(depositor sdk.AccAddress, supplied ...sdk.Coin) VaultSh
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate returns an error if an VaultShareRecord is invalid.
 | 
				
			||||||
 | 
					func (vsr VaultShareRecord) Validate() error {
 | 
				
			||||||
 | 
						if vsr.Depositor.Empty() {
 | 
				
			||||||
 | 
							return fmt.Errorf("depositor is empty")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := vsr.AmountSupplied.Validate(); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("invalid vault share record amount supplied: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VaultShareRecords is a slice of VaultShareRecord.
 | 
				
			||||||
 | 
					type VaultShareRecords []VaultShareRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate returns an error if a slice of VaultRecords is invalid.
 | 
				
			||||||
 | 
					func (vsrs VaultShareRecords) Validate() error {
 | 
				
			||||||
 | 
						addrs := make(map[string]bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, vr := range vsrs {
 | 
				
			||||||
 | 
							if err := vr.Validate(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if _, found := addrs[vr.Depositor.String()]; found {
 | 
				
			||||||
 | 
								return fmt.Errorf("duplicate address %s", vr.Depositor.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							addrs[vr.Depositor.String()] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewAllowedVaults returns a new AllowedVaults with the given denom and strategy type.
 | 
					// NewAllowedVaults returns a new AllowedVaults with the given denom and strategy type.
 | 
				
			||||||
func NewAllowedVault(denom string, strategyType StrategyType) AllowedVault {
 | 
					func NewAllowedVault(denom string, strategyType StrategyType) AllowedVault {
 | 
				
			||||||
	return AllowedVault{
 | 
						return AllowedVault{
 | 
				
			||||||
@ -55,8 +130,8 @@ func (a AllowedVaults) Validate() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *AllowedVault) Validate() error {
 | 
					func (a *AllowedVault) Validate() error {
 | 
				
			||||||
	if a.Denom == "" {
 | 
						if err := sdk.ValidateDenom(a.Denom); err != nil {
 | 
				
			||||||
		return ErrInvalidVaultDenom
 | 
							return sdkerrors.Wrap(ErrInvalidVaultDenom, err.Error())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if a.VaultStrategy == STRATEGY_TYPE_UNSPECIFIED {
 | 
						if a.VaultStrategy == STRATEGY_TYPE_UNSPECIFIED {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										310
									
								
								x/earn/types/vault_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								x/earn/types/vault_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,310 @@
 | 
				
			|||||||
 | 
					package types_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdk "github.com/cosmos/cosmos-sdk/types"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/app"
 | 
				
			||||||
 | 
						"github.com/kava-labs/kava/x/earn/types"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestVaultRecordValidate(t *testing.T) {
 | 
				
			||||||
 | 
						type errArgs struct {
 | 
				
			||||||
 | 
							expectPass bool
 | 
				
			||||||
 | 
							contains   string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							vaultRecords types.VaultRecords
 | 
				
			||||||
 | 
							errArgs      errArgs
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "valid vault records",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "usdx",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "ukava",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("ukava", 5),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: true,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - duplicate denom",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "usdx",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "usdx",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("usdx", 5),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "duplicate vault denom usdx",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - invalid denom",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("ukava", 0),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "invalid denom",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - mismatch denom",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "usdx",
 | 
				
			||||||
 | 
										TotalSupply: sdk.NewInt64Coin("ukava", 0),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "total supply denom ukava does not match vault record denom usdx",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - negative",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:       "usdx",
 | 
				
			||||||
 | 
										TotalSupply: sdk.Coin{Denom: "usdx", Amount: sdk.NewInt(-1)},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "negative coin amount",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := test.vaultRecords.Validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if test.errArgs.expectPass {
 | 
				
			||||||
 | 
									require.NoError(t, err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									require.Error(t, err)
 | 
				
			||||||
 | 
									require.Contains(t, err.Error(), test.errArgs.contains)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestVaultShareRecordsValidate(t *testing.T) {
 | 
				
			||||||
 | 
						_, addrs := app.GeneratePrivKeyAddressPairs(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type errArgs struct {
 | 
				
			||||||
 | 
							expectPass bool
 | 
				
			||||||
 | 
							contains   string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							vaultRecords types.VaultShareRecords
 | 
				
			||||||
 | 
							errArgs      errArgs
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "valid vault share records",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultShareRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: addrs[0],
 | 
				
			||||||
 | 
										AmountSupplied: sdk.NewCoins(
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: addrs[1],
 | 
				
			||||||
 | 
										AmountSupplied: sdk.NewCoins(
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("ukava", 5),
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: true,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - duplicate address",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultShareRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: addrs[0],
 | 
				
			||||||
 | 
										AmountSupplied: sdk.NewCoins(
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: addrs[0],
 | 
				
			||||||
 | 
										AmountSupplied: sdk.NewCoins(
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("ukava", 5),
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "duplicate address",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - invalid address",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultShareRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: sdk.AccAddress{},
 | 
				
			||||||
 | 
										AmountSupplied: sdk.NewCoins(
 | 
				
			||||||
 | 
											sdk.NewInt64Coin("usdx", 0),
 | 
				
			||||||
 | 
										),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "depositor is empty",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - negative",
 | 
				
			||||||
 | 
								vaultRecords: types.VaultShareRecords{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Depositor: addrs[0],
 | 
				
			||||||
 | 
										AmountSupplied: sdk.Coins{
 | 
				
			||||||
 | 
											sdk.Coin{Denom: "ukava", Amount: sdk.NewInt(-1)},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "amount is not positive",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := test.vaultRecords.Validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if test.errArgs.expectPass {
 | 
				
			||||||
 | 
									require.NoError(t, err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									require.Error(t, err)
 | 
				
			||||||
 | 
									require.Contains(t, err.Error(), test.errArgs.contains)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestAllowedVaultsValidate(t *testing.T) {
 | 
				
			||||||
 | 
						type errArgs struct {
 | 
				
			||||||
 | 
							expectPass bool
 | 
				
			||||||
 | 
							contains   string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							vaultRecords types.AllowedVaults
 | 
				
			||||||
 | 
							errArgs      errArgs
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "valid vault share records",
 | 
				
			||||||
 | 
								vaultRecords: types.AllowedVaults{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "usdx",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_HARD,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "busd",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_HARD,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: true,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - duplicate denom",
 | 
				
			||||||
 | 
								vaultRecords: types.AllowedVaults{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "usdx",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_HARD,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "usdx",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_HARD,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "duplicate vault denom usdx",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - invalid denom",
 | 
				
			||||||
 | 
								vaultRecords: types.AllowedVaults{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_HARD,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "invalid denom",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid - invalid strategy",
 | 
				
			||||||
 | 
								vaultRecords: types.AllowedVaults{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Denom:         "usdx",
 | 
				
			||||||
 | 
										VaultStrategy: types.STRATEGY_TYPE_UNSPECIFIED,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errArgs: errArgs{
 | 
				
			||||||
 | 
									expectPass: false,
 | 
				
			||||||
 | 
									contains:   "invalid vault strategy",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, test := range tests {
 | 
				
			||||||
 | 
							t.Run(test.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := test.vaultRecords.Validate()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if test.errArgs.expectPass {
 | 
				
			||||||
 | 
									require.NoError(t, err)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									require.Error(t, err)
 | 
				
			||||||
 | 
									require.Contains(t, err.Error(), test.errArgs.contains)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNewVaultShareRecord(t *testing.T) {
 | 
				
			||||||
 | 
						_, addrs := app.GeneratePrivKeyAddressPairs(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						coins := sdk.NewCoins(
 | 
				
			||||||
 | 
							sdk.NewInt64Coin("usdx", 10),
 | 
				
			||||||
 | 
							sdk.NewInt64Coin("ukava", 5),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						shareRecord := types.NewVaultShareRecord(addrs[0], coins...)
 | 
				
			||||||
 | 
						require.Equal(t, coins, shareRecord.AmountSupplied)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user