diff --git a/x/farming/keeper/genesis.go b/x/farming/keeper/genesis.go index 23a5fe8d..eb300f67 100644 --- a/x/farming/keeper/genesis.go +++ b/x/farming/keeper/genesis.go @@ -18,7 +18,6 @@ func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { ctx, writeCache := ctx.CacheContext() k.SetParams(ctx, genState.Params) - // TODO: what if CurrentEpochDays field was empty? k.SetCurrentEpochDays(ctx, genState.CurrentEpochDays) moduleAcc := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName) k.accountKeeper.SetModuleAccount(ctx, moduleAcc) diff --git a/x/farming/keeper/genesis_test.go b/x/farming/keeper/genesis_test.go index 11c0e70c..d5e78fc3 100644 --- a/x/farming/keeper/genesis_test.go +++ b/x/farming/keeper/genesis_test.go @@ -154,6 +154,13 @@ func (suite *KeeperTestSuite) TestInitGenesisPanics() { }, true, }, + { + "invalid current epoch days", + func(genState *types.GenesisState) { + genState.CurrentEpochDays = 0 + }, + true, + }, } { suite.Run(tc.name, func() { genState := suite.keeper.ExportGenesis(cacheCtx) diff --git a/x/farming/keeper/reward.go b/x/farming/keeper/reward.go index b2cf0576..07e97e0d 100644 --- a/x/farming/keeper/reward.go +++ b/x/farming/keeper/reward.go @@ -473,7 +473,7 @@ func (k Keeper) ValidateOutstandingRewardsAmount(ctx sdk.Context) error { rewardsReservePoolBalances := sdk.NewDecCoinsFromCoins(k.bankKeeper.GetAllBalances(ctx, k.GetRewardsReservePoolAcc(ctx))...) _, hasNeg := rewardsReservePoolBalances.SafeSub(totalOutstandingRewards) if hasNeg { - return types.ErrInvalidRemainingRewardsAmount // TODO: use different error type + return types.ErrInvalidOutstandingRewardsAmount } return nil diff --git a/x/farming/keeper/reward_test.go b/x/farming/keeper/reward_test.go index b51310d5..4e7017df 100644 --- a/x/farming/keeper/reward_test.go +++ b/x/farming/keeper/reward_test.go @@ -311,7 +311,23 @@ func (suite *KeeperTestSuite) TestHarvest() { } func (suite *KeeperTestSuite) TestMultipleHarvest() { - // TODO: implement + suite.SetFixedAmountPlan(1, suite.addrs[4], map[string]string{denom1: "1"}, map[string]int64{denom3: 1000000}) + + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + + suite.AdvanceEpoch() + suite.AdvanceEpoch() + + balancesBefore := suite.app.BankKeeper.GetAllBalances(suite.ctx, suite.addrs[0]) + suite.Harvest(suite.addrs[0], []string{denom1}) + balancesAfter := suite.app.BankKeeper.GetAllBalances(suite.ctx, suite.addrs[0]) + delta := balancesAfter.Sub(balancesBefore) + suite.Require().True(coinsEq(sdk.NewCoins(sdk.NewInt64Coin(denom3, 1000000)), delta)) + + balancesBefore = balancesAfter + suite.Harvest(suite.addrs[0], []string{denom1}) + balancesAfter = suite.app.BankKeeper.GetAllBalances(suite.ctx, suite.addrs[0]) + suite.Require().True(coinsEq(balancesBefore, balancesAfter)) } func (suite *KeeperTestSuite) TestHistoricalRewards() { diff --git a/x/farming/keeper/staking.go b/x/farming/keeper/staking.go index 99467306..77170c7d 100644 --- a/x/farming/keeper/staking.go +++ b/x/farming/keeper/staking.go @@ -314,8 +314,6 @@ func (k Keeper) Stake(ctx sdk.Context, farmerAcc sdk.AccAddress, amount sdk.Coin // Unstake unstakes an amount of staking coins from the staking reserve account. // It causes accumulated rewards to be withdrawn to the farmer. func (k Keeper) Unstake(ctx sdk.Context, farmerAcc sdk.AccAddress, amount sdk.Coins) error { - // TODO: send coins at once, not in every WithdrawRewards - for _, coin := range amount { staking, found := k.GetStaking(ctx, coin.Denom, farmerAcc) if !found { diff --git a/x/farming/keeper/staking_test.go b/x/farming/keeper/staking_test.go index ee18bbc3..e3acd161 100644 --- a/x/farming/keeper/staking_test.go +++ b/x/farming/keeper/staking_test.go @@ -45,7 +45,28 @@ func (suite *KeeperTestSuite) TestStake() { } func (suite *KeeperTestSuite) TestMultipleStake() { - // TODO: implement + suite.SetFixedAmountPlan(1, suite.addrs[4], map[string]string{denom1: "1"}, map[string]int64{denom3: 1000000}) + + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + + suite.Require().True(coinsEq( + sdk.NewCoins(sdk.NewInt64Coin(denom1, 2000000)), + suite.keeper.GetAllQueuedCoinsByFarmer(suite.ctx, suite.addrs[0]))) + suite.Require().True(coinsEq(sdk.Coins{}, suite.keeper.GetAllStakedCoinsByFarmer(suite.ctx, suite.addrs[0]))) + + suite.AdvanceEpoch() + + suite.Require().True(coinsEq( + sdk.NewCoins(sdk.NewInt64Coin(denom1, 2000000)), + suite.keeper.GetAllStakedCoinsByFarmer(suite.ctx, suite.addrs[0]))) + suite.Require().True(coinsEq(sdk.Coins{}, suite.keeper.GetAllQueuedCoinsByFarmer(suite.ctx, suite.addrs[0]))) + + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + balanceBefore := suite.app.BankKeeper.GetBalance(suite.ctx, suite.addrs[0], denom3) + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + balanceAfter := suite.app.BankKeeper.GetBalance(suite.ctx, suite.addrs[0], denom3) + suite.Require().True(intEq(balanceBefore.Amount, balanceAfter.Amount)) } func (suite *KeeperTestSuite) TestStakeInAdvance() { @@ -203,7 +224,18 @@ func (suite *KeeperTestSuite) TestUnstakeNotAlwaysWithdraw() { } func (suite *KeeperTestSuite) TestMultipleUnstake() { - // TODO: implement + suite.SetFixedAmountPlan(1, suite.addrs[4], map[string]string{denom1: "1"}, map[string]int64{denom3: 1000000}) + + suite.Stake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 1000000))) + + suite.AdvanceEpoch() + suite.AdvanceEpoch() + + suite.Unstake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 250000))) + balanceBefore := suite.app.BankKeeper.GetBalance(suite.ctx, suite.addrs[0], denom3) + suite.Unstake(suite.addrs[0], sdk.NewCoins(sdk.NewInt64Coin(denom1, 250000))) + balanceAfter := suite.app.BankKeeper.GetBalance(suite.ctx, suite.addrs[0], denom3) + suite.Require().True(intEq(balanceBefore.Amount, balanceAfter.Amount)) } func (suite *KeeperTestSuite) TestTotalStakings() { diff --git a/x/farming/types/errors.go b/x/farming/types/errors.go index 6a919329..3de9bb70 100644 --- a/x/farming/types/errors.go +++ b/x/farming/types/errors.go @@ -6,13 +6,14 @@ import ( // farming module sentinel errors var ( - ErrInvalidPlanType = sdkerrors.Register(ModuleName, 2, "invalid plan type") - ErrInvalidPlanName = sdkerrors.Register(ModuleName, 3, "invalid plan name") - ErrInvalidPlanEndTime = sdkerrors.Register(ModuleName, 4, "invalid plan end time") - ErrInvalidStakingCoinWeights = sdkerrors.Register(ModuleName, 5, "invalid staking coin weights") - ErrInvalidTotalEpochRatio = sdkerrors.Register(ModuleName, 6, "invalid total epoch ratio") - ErrStakingNotExists = sdkerrors.Register(ModuleName, 7, "staking not exists") - ErrConflictPrivatePlanFarmingPool = sdkerrors.Register(ModuleName, 8, "the address is already in use, please use a different plan name") - ErrInvalidStakingReservedAmount = sdkerrors.Register(ModuleName, 9, "staking reserved amount invariant broken") - ErrInvalidRemainingRewardsAmount = sdkerrors.Register(ModuleName, 10, "remaining rewards amount invariant broken") + ErrInvalidPlanType = sdkerrors.Register(ModuleName, 2, "invalid plan type") + ErrInvalidPlanName = sdkerrors.Register(ModuleName, 3, "invalid plan name") + ErrInvalidPlanEndTime = sdkerrors.Register(ModuleName, 4, "invalid plan end time") + ErrInvalidStakingCoinWeights = sdkerrors.Register(ModuleName, 5, "invalid staking coin weights") + ErrInvalidTotalEpochRatio = sdkerrors.Register(ModuleName, 6, "invalid total epoch ratio") + ErrStakingNotExists = sdkerrors.Register(ModuleName, 7, "staking not exists") + ErrConflictPrivatePlanFarmingPool = sdkerrors.Register(ModuleName, 8, "the address is already in use, please use a different plan name") + ErrInvalidStakingReservedAmount = sdkerrors.Register(ModuleName, 9, "staking reserved amount invariant broken") + ErrInvalidRemainingRewardsAmount = sdkerrors.Register(ModuleName, 10, "remaining rewards amount invariant broken") + ErrInvalidOutstandingRewardsAmount = sdkerrors.Register(ModuleName, 11, "outstanding rewards amount invariant broken") ) diff --git a/x/farming/types/genesis_test.go b/x/farming/types/genesis_test.go index 5af33dbd..51d1c21a 100644 --- a/x/farming/types/genesis_test.go +++ b/x/farming/types/genesis_test.go @@ -377,13 +377,13 @@ func TestValidateGenesis(t *testing.T) { }, "coin 0denom1 amount is not positive", }, - //{ - // "invalid current epoch days", - // func(genState *types.GenesisState) { - // genState.CurrentEpochDays = 0 - // }, - // "not implemented", - //}, + { + "invalid current epoch days", + func(genState *types.GenesisState) { + genState.CurrentEpochDays = 0 + }, + "current epoch days must be positive", + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/x/farming/types/plan_test.go b/x/farming/types/plan_test.go index cad2d66a..51f080d5 100644 --- a/x/farming/types/plan_test.go +++ b/x/farming/types/plan_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" @@ -493,8 +495,6 @@ func TestPrivatePlanFarmingPoolAddress(t *testing.T) { require.Equal(t, "cosmos172yhzhxwgwul3s8m6qpgw2ww3auedq4k3dt224543d0sd44fgx4spcjthr", testAcc2.String()) } -// TODO: needs to cover more cases -// https://github.com/tendermint/farming/issues/90 func TestUnpackPlan(t *testing.T) { plan := []types.PlanI{ types.NewRatioPlan( @@ -518,19 +518,53 @@ func TestUnpackPlan(t *testing.T) { marshaled, err := any.Marshal() require.NoError(t, err) - any.Value = []byte{} - err = any.Unmarshal(marshaled) + var any2 codectypes.Any + err = any2.Unmarshal(marshaled) require.NoError(t, err) - reMarshal, err := any.Marshal() + reMarshal, err := any2.Marshal() require.NoError(t, err) require.Equal(t, marshaled, reMarshal) planRecord := types.PlanRecord{ - Plan: *any, + Plan: any2, FarmingPoolCoins: sdk.NewCoins(), } _, err = types.UnpackPlan(&planRecord.Plan) require.NoError(t, err) } + +func TestUnpackPlanJSON(t *testing.T) { + plan := types.NewRatioPlan( + types.NewBasePlan( + 1, + "testPlan1", + types.PlanTypePrivate, + types.PrivatePlanFarmingPoolAddress("farmingPoolAddr1", 1).String(), + sdk.AccAddress("terminationAddr1").String(), + sdk.NewDecCoins(sdk.DecCoin{Denom: "testFarmStakingCoinDenom", Amount: sdk.MustNewDecFromStr("1.0")}), + types.ParseTime("2021-08-03T00:00:00Z"), + types.ParseTime("2021-08-07T00:00:00Z"), + ), + sdk.NewDec(1), + ) + + any, err := types.PackPlan(plan) + require.NoError(t, err) + + registry := codectypes.NewInterfaceRegistry() + types.RegisterInterfaces(registry) + cdc := codec.NewProtoCodec(registry) + + bz := cdc.MustMarshalJSON(any) + + var any2 codectypes.Any + err = cdc.UnmarshalJSON(bz, &any2) + require.NoError(t, err) + + plan2, err := types.UnpackPlan(&any2) + require.NoError(t, err) + + require.Equal(t, uint64(1), plan2.GetId()) +}