diff --git a/.github/.codecov.yml b/.github/.codecov.yml new file mode 100644 index 00000000..6f590683 --- /dev/null +++ b/.github/.codecov.yml @@ -0,0 +1,23 @@ +# To validate: +# cat codecov.yml | curl --data-binary @- https://codecov.io/validate + +codecov: + notify: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "50...80" + + status: + project: + default: + target: auto + threshold: 1% + patch: + default: + enabled: no # disable patch since it is noisy and not correct + if_not_found: success + +comment: false \ No newline at end of file diff --git a/x/farming/keeper/proposal_handler.go b/x/farming/keeper/proposal_handler.go index 6b1234a4..49ab8366 100644 --- a/x/farming/keeper/proposal_handler.go +++ b/x/farming/keeper/proposal_handler.go @@ -31,6 +31,7 @@ func HandlePublicPlanProposal(ctx sdk.Context, k Keeper, proposal *types.PublicP if err := types.ValidateName(plans); err != nil { return err } + if err := types.ValidateTotalEpochRatio(plans); err != nil { return err } @@ -45,12 +46,13 @@ func (k Keeper) AddPublicPlanProposal(ctx sdk.Context, proposals []*types.AddReq if err != nil { return err } + terminationAcc, err := sdk.AccAddressFromBech32(p.GetTerminationAddress()) if err != nil { return err } - if !p.EpochAmount.IsZero() && !p.EpochAmount.IsAnyNegative() { + if p.EpochAmount.IsAllPositive() { msg := types.NewMsgCreateFixedAmountPlan( p.GetName(), farmingPoolAddrAcc, @@ -68,7 +70,7 @@ func (k Keeper) AddPublicPlanProposal(ctx sdk.Context, proposals []*types.AddReq logger := k.Logger(ctx) logger.Info("created public fixed amount plan", "fixed_amount_plan", plan) - } else if !p.EpochRatio.IsZero() && !p.EpochRatio.IsNegative() && !p.EpochRatio.IsNil() { + } else if p.EpochRatio.IsPositive() { msg := types.NewMsgCreateRatioPlan( p.GetName(), farmingPoolAddrAcc, @@ -103,8 +105,13 @@ func (k Keeper) UpdatePublicPlanProposal(ctx sdk.Context, proposals []*types.Upd return sdkerrors.Wrapf(sdkerrors.ErrNotFound, "plan %d is not found", p.GetPlanId()) } - switch plan := plan.(type) { - case *types.FixedAmountPlan: + if p.EpochAmount.IsAllPositive() { + if p.GetName() != "" { + if err := plan.SetName(p.GetName()); err != nil { + return err + } + } + if p.GetFarmingPoolAddress() != "" { farmingPoolAddrAcc, err := sdk.AccAddressFromBech32(p.GetFarmingPoolAddress()) if err != nil { @@ -143,12 +150,9 @@ func (k Keeper) UpdatePublicPlanProposal(ctx sdk.Context, proposals []*types.Upd } } - if p.GetName() != "" { - plan.Name = p.GetName() - } - - if p.GetEpochAmount() != nil { - plan.EpochAmount = p.GetEpochAmount() + // change the plan to fixed amount plan if an epoch amount exists + if p.GetEpochAmount().IsAllPositive() { + plan = types.NewFixedAmountPlan(plan.GetBasePlan(), p.GetEpochAmount()) } k.SetPlan(ctx, plan) @@ -156,9 +160,11 @@ func (k Keeper) UpdatePublicPlanProposal(ctx sdk.Context, proposals []*types.Upd logger := k.Logger(ctx) logger.Info("updated public fixed amount plan", "fixed_amount_plan", plan) - case *types.RatioPlan: - if err := plan.Validate(); err != nil { - return err + } else if p.EpochRatio.IsPositive() { + if p.GetName() != "" { + if err := plan.SetName(p.GetName()); err != nil { + return err + } } if p.GetFarmingPoolAddress() != "" { @@ -199,12 +205,9 @@ func (k Keeper) UpdatePublicPlanProposal(ctx sdk.Context, proposals []*types.Upd } } - if p.GetName() != "" { - plan.Name = p.GetName() - } - - if !p.EpochRatio.IsZero() { - plan.EpochRatio = p.EpochRatio + // change the plan to ratio plan if an epoch ratio exists + if p.EpochRatio.IsPositive() { + plan = types.NewRatioPlan(plan.GetBasePlan(), p.EpochRatio) } k.SetPlan(ctx, plan) @@ -212,8 +215,6 @@ func (k Keeper) UpdatePublicPlanProposal(ctx sdk.Context, proposals []*types.Upd logger := k.Logger(ctx) logger.Info("updated public ratio plan", "ratio_plan", plan) - default: - return sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized plan type: %T", p) } } diff --git a/x/farming/keeper/proposal_handler_test.go b/x/farming/keeper/proposal_handler_test.go index ed17111b..79fe959c 100644 --- a/x/farming/keeper/proposal_handler_test.go +++ b/x/farming/keeper/proposal_handler_test.go @@ -4,124 +4,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/tendermint/farming/app" "github.com/tendermint/farming/x/farming/keeper" "github.com/tendermint/farming/x/farming/types" _ "github.com/stretchr/testify/suite" ) -func (suite *KeeperTestSuite) TestAddPublicPlanProposal() { - addrs := app.AddTestAddrs(suite.app, suite.ctx, 2, sdk.NewInt(100_000_000)) - farmerAddr := addrs[0] - name := "test" - terminationAddr := sdk.AccAddress("terminationAddr") - coinWeights := sdk.NewDecCoins( - sdk.DecCoin{ - Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", - Amount: sdk.MustNewDecFromStr("1.0"), - }, - ) - - // case1 - req := &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - case1 := []*types.AddRequestProposal{req} - - // case2 - req = &types.AddRequestProposal{ - Name: `OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM`, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - case2 := []*types.AddRequestProposal{req} - - // case3 - req = &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: sdk.NewDecCoins(), - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case3 := []*types.AddRequestProposal{req} - - // case4 - req = &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: sdk.NewDecCoins( - sdk.DecCoin{ - Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", - Amount: sdk.MustNewDecFromStr("0.1"), - }, - ), - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case4 := []*types.AddRequestProposal{req} - - // case5 - req = &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-13T00:00:00Z"), - EndTime: types.ParseTime("2021-08-06T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case5 := []*types.AddRequestProposal{req} - - // case6 - req = &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 1)), - EpochRatio: sdk.NewDec(1), - } - case6 := []*types.AddRequestProposal{req} - - // case7 - req = &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(), - EpochRatio: sdk.ZeroDec(), - } - case7 := []*types.AddRequestProposal{req} - +func (suite *KeeperTestSuite) TestValidateAddPublicPlanProposal() { for _, tc := range []struct { name string addRequest []*types.AddRequestProposal @@ -129,7 +18,19 @@ func (suite *KeeperTestSuite) TestAddPublicPlanProposal() { }{ { "happy case", - case1, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 100_000_000)), + sdk.ZeroDec(), + )}, nil, }, { @@ -137,41 +38,110 @@ func (suite *KeeperTestSuite) TestAddPublicPlanProposal() { nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proposal request must not be empty"), }, - { - "request case #2", - []*types.AddRequestProposal{}, - sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proposal request must not be empty"), - }, { "name case #1", - case2, + []*types.AddRequestProposal{types.NewAddRequestProposal( + `OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM`, + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 100_000_000)), + sdk.ZeroDec(), + )}, sdkerrors.Wrapf(types.ErrInvalidPlanNameLength, "plan name cannot be longer than max length of %d", types.MaxNameLength), }, { "staking coin weights case #1", - case3, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins(), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 100_000_000)), + sdk.ZeroDec(), + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "staking coin weights must not be empty"), }, { "staking coin weights case #2", - case4, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.DecCoin{ + Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", + Amount: sdk.MustNewDecFromStr("0.1"), + }, + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 100_000_000)), + sdk.ZeroDec(), + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "total weight must be 1"), }, { "start time & end time case #1", - case5, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), + ), + types.ParseTime("2021-08-13T00:00:00Z"), + types.ParseTime("2021-08-06T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 100_000_000)), + sdk.ZeroDec(), + )}, sdkerrors.Wrapf(types.ErrInvalidPlanEndTime, "end time %s must be greater than start time %s", types.ParseTime("2021-08-06T00:00:00Z"), types.ParseTime("2021-08-13T00:00:00Z")), }, { "epoch amount & epoch ratio case #1", - case6, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(sdk.NewInt64Coin(denom3, 1)), + sdk.NewDec(1), + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "either epoch amount or epoch ratio should be provided"), }, { "epoch amount & epoch ratio case #2", - case7, + []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + sdk.NewCoins(), + sdk.ZeroDec(), + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "either epoch amount or epoch ratio must not be zero"), }, } { @@ -190,7 +160,6 @@ func (suite *KeeperTestSuite) TestAddPublicPlanProposal() { suite.Require().NoError(err) _, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) - // TODO: need to check each field same as expected suite.Require().Equal(true, found) } else { suite.EqualError(err, tc.expectedErr.Error()) @@ -199,179 +168,52 @@ func (suite *KeeperTestSuite) TestAddPublicPlanProposal() { } } -func (suite *KeeperTestSuite) TestUpdatePublicPlanProposal() { - addrs := app.AddTestAddrs(suite.app, suite.ctx, 2, sdk.NewInt(100_000_000)) - farmerAddr := addrs[0] - name := "test" - terminationAddr := sdk.AccAddress("terminationAddr") - coinWeights := sdk.NewDecCoins( - sdk.DecCoin{ - Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", - Amount: sdk.MustNewDecFromStr("1.0"), - }, - ) - - // add request proposal - addReq := &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - addRequests := []*types.AddRequestProposal{addReq} - - proposal := &types.PublicPlanProposal{ - Title: "testTitle", - Description: "testDescription", - AddRequestProposals: addRequests, - UpdateRequestProposals: nil, - DeleteRequestProposals: nil, +func (suite *KeeperTestSuite) TestValidateUpdatePublicPlanProposal() { + // create a ratio public plan + addRequests := []*types.AddRequestProposal{ + types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), // 30% + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), // 70% + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + nil, + sdk.NewDecWithPrec(10, 2), // 10% + ), } - err := proposal.ValidateBasic() - suite.Require().NoError(err) - - err = keeper.HandlePublicPlanProposal(suite.ctx, suite.keeper, proposal) + err := keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", addRequests, nil, nil), + ) suite.Require().NoError(err) - _, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) + plan, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) suite.Require().Equal(true, found) - // case1 - startTime := types.ParseTime("2021-08-06T00:00:00Z") - endTime := types.ParseTime("2021-08-13T00:00:00Z") - - req := &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - case1 := []*types.UpdateRequestProposal{req} - - // case2 - req = &types.UpdateRequestProposal{ - PlanId: uint64(0), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - case2 := []*types.UpdateRequestProposal{req} - - // case3 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: `OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM - OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM`, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - case3 := []*types.UpdateRequestProposal{req} - - // case4 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: sdk.NewDecCoins(), - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case4 := []*types.UpdateRequestProposal{req} - - // case5 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: sdk.NewDecCoins( - sdk.DecCoin{ - Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", - Amount: sdk.MustNewDecFromStr("0.1"), - }, - ), - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case5 := []*types.UpdateRequestProposal{req} - - // case6 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &endTime, - EndTime: &startTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 0)), - EpochRatio: sdk.ZeroDec(), - } - case6 := []*types.UpdateRequestProposal{req} - - // case7 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 1)), - EpochRatio: sdk.NewDec(1), - } - case7 := []*types.UpdateRequestProposal{req} - - // case8 - req = &types.UpdateRequestProposal{ - PlanId: uint64(1), - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: &startTime, - EndTime: &endTime, - EpochAmount: sdk.NewCoins(), - EpochRatio: sdk.ZeroDec(), - } - case8 := []*types.UpdateRequestProposal{req} - for _, tc := range []struct { name string updateRequest []*types.UpdateRequestProposal expectedErr error }{ { - "happy case", - case1, + "happy case #1 - decrease epoch ratio to 5%", + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + sdk.NewDecWithPrec(5, 2), + )}, nil, }, { @@ -379,46 +221,121 @@ func (suite *KeeperTestSuite) TestUpdatePublicPlanProposal() { nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proposal request must not be empty"), }, - { - "request case #2", - []*types.UpdateRequestProposal{}, - sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "proposal request must not be empty"), - }, { "plan id case #1", - case2, + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + uint64(0), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid plan id: %d", uint64(0)), }, { "name case #1", - case3, + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + plan.GetId(), + `OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM + OVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERMOVERMAXLENGTHOVERMAXLENGTHOVERMAXLENGTHOVERM`, // max length of name + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrapf(types.ErrInvalidPlanNameLength, "plan name cannot be longer than max length of %d", types.MaxNameLength), }, { "staking coin weights case #1", - case4, + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + sdk.NewDecCoins(), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "staking coin weights must not be empty"), }, { "staking coin weights case #2", - case5, + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + sdk.NewDecCoins( + sdk.DecCoin{ + Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", + Amount: sdk.MustNewDecFromStr("0.1"), + }, + ), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "total weight must be 1"), }, { "start time & end time case #1", - case6, + []*types.UpdateRequestProposal{types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + types.ParseTime("2021-08-13T00:00:00Z"), + types.ParseTime("2021-08-06T00:00:00Z"), + nil, + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrapf(types.ErrInvalidPlanEndTime, "end time %s must be greater than start time %s", types.ParseTime("2021-08-06T00:00:00Z"), types.ParseTime("2021-08-13T00:00:00Z")), }, { "epoch amount & epoch ratio case #1", - case7, + []*types.UpdateRequestProposal{ + types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + sdk.NewCoins(sdk.NewInt64Coin("stake", 100_000)), + plan.(*types.RatioPlan).EpochRatio, + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "either epoch amount or epoch ratio should be provided"), }, { "epoch amount & epoch ratio case #2", - case8, + []*types.UpdateRequestProposal{ + types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + sdk.NewCoins(sdk.NewInt64Coin("stake", 0)), + sdk.ZeroDec(), + )}, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "either epoch amount or epoch ratio must not be zero"), }, } { @@ -436,7 +353,7 @@ func (suite *KeeperTestSuite) TestUpdatePublicPlanProposal() { err := keeper.HandlePublicPlanProposal(suite.ctx, suite.keeper, proposal) suite.Require().NoError(err) - _, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) + _, found := suite.keeper.GetPlan(suite.ctx, tc.updateRequest[0].GetPlanId()) suite.Require().Equal(true, found) } else { suite.EqualError(err, tc.expectedErr.Error()) @@ -445,54 +362,124 @@ func (suite *KeeperTestSuite) TestUpdatePublicPlanProposal() { } } -func (suite *KeeperTestSuite) TestDeletePublicPlanProposal() { - addrs := app.AddTestAddrs(suite.app, suite.ctx, 2, sdk.NewInt(100_000_000)) - farmerAddr := addrs[0] - name := "test" - terminationAddr := sdk.AccAddress("terminationAddr") - coinWeights := sdk.NewDecCoins( - sdk.DecCoin{ - Denom: "poolD35A0CC16EE598F90B044CE296A405BA9C381E38837599D96F2F70C2F02A23A4", - Amount: sdk.MustNewDecFromStr("1.0"), - }, +func (suite *KeeperTestSuite) TestValidateDeletePublicPlanProposal() { + // create a ratio public plan + addRequests := []*types.AddRequestProposal{types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), // 30% + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), // 70% + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + nil, + sdk.NewDecWithPrec(10, 2), // 10% + )} + + err := keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", addRequests, nil, nil), + ) + suite.Require().NoError(err) + + // should exist + _, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) + suite.Require().Equal(true, found) + + // delete the proposal + deleteRequests := []*types.DeleteRequestProposal{types.NewDeleteRequestProposal(uint64(1))} + + err = keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", nil, nil, deleteRequests), ) + suite.Require().NoError(err) - // add request proposal - addReq := &types.AddRequestProposal{ - Name: name, - FarmingPoolAddress: farmerAddr.String(), - TerminationAddress: terminationAddr.String(), - StakingCoinWeights: coinWeights, - StartTime: types.ParseTime("2021-08-06T00:00:00Z"), - EndTime: types.ParseTime("2021-08-13T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin("uatom", 100_000_000)), - EpochRatio: sdk.ZeroDec(), - } - addRequests := []*types.AddRequestProposal{addReq} + // shouldn't exist + _, found = suite.keeper.GetPlan(suite.ctx, uint64(1)) + suite.Require().Equal(false, found) +} - proposal := &types.PublicPlanProposal{ - Title: "testTitle", - Description: "testDescription", - AddRequestProposals: addRequests, - UpdateRequestProposals: nil, - DeleteRequestProposals: nil, +func (suite *KeeperTestSuite) TestUpdatePlanType() { + // create a ratio public plan + addRequests := []*types.AddRequestProposal{ + types.NewAddRequestProposal( + "testPlan", + suite.addrs[0].String(), + suite.addrs[0].String(), + sdk.NewDecCoins( + sdk.NewDecCoinFromDec(denom1, sdk.NewDecWithPrec(3, 1)), // 30% + sdk.NewDecCoinFromDec(denom2, sdk.NewDecWithPrec(7, 1)), // 70% + ), + types.ParseTime("2021-08-01T00:00:00Z"), + types.ParseTime("2021-08-30T00:00:00Z"), + nil, + sdk.NewDecWithPrec(10, 2), // 10% + ), } - err := proposal.ValidateBasic() + err := keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", addRequests, nil, nil), + ) suite.Require().NoError(err) - err = keeper.HandlePublicPlanProposal(suite.ctx, suite.keeper, proposal) + plan, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) + suite.Require().Equal(true, found) + suite.Require().Equal(plan.(*types.RatioPlan).EpochRatio, sdk.NewDecWithPrec(10, 2)) + + // update the ratio plan to fixed amount plan type + updateRequests := []*types.UpdateRequestProposal{ + types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + sdk.NewCoins(sdk.NewInt64Coin("stake", 100_000)), + sdk.ZeroDec(), + )} + + err = keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", nil, updateRequests, nil), + ) suite.Require().NoError(err) - _, found := suite.keeper.GetPlan(suite.ctx, uint64(1)) + plan, found = suite.keeper.GetPlan(suite.ctx, uint64(1)) suite.Require().Equal(true, found) + suite.Require().Equal(plan.(*types.FixedAmountPlan).EpochAmount, sdk.NewCoins(sdk.NewInt64Coin("stake", 100_000))) + + // update the fixed amount plan back to ratio plan + updateRequests = []*types.UpdateRequestProposal{ + types.NewUpdateRequestProposal( + plan.GetId(), + plan.GetName(), + plan.GetFarmingPoolAddress().String(), + plan.GetTerminationAddress().String(), + plan.GetStakingCoinWeights(), + plan.GetStartTime(), + plan.GetEndTime(), + nil, + sdk.NewDecWithPrec(7, 2), // 7% + )} - // delete the proposal - req := &types.DeleteRequestProposal{ - PlanId: uint64(1), - } - proposals := []*types.DeleteRequestProposal{req} - - err = suite.keeper.DeletePublicPlanProposal(suite.ctx, proposals) + err = keeper.HandlePublicPlanProposal( + suite.ctx, + suite.keeper, + types.NewPublicPlanProposal("testTitle", "testDescription", nil, updateRequests, nil), + ) suite.Require().NoError(err) + + plan, found = suite.keeper.GetPlan(suite.ctx, uint64(1)) + suite.Require().Equal(true, found) + suite.Require().Equal(plan.(*types.RatioPlan).EpochRatio, sdk.NewDecWithPrec(7, 2)) } diff --git a/x/farming/types/plan.go b/x/farming/types/plan.go index 50647b50..b9aa2916 100644 --- a/x/farming/types/plan.go +++ b/x/farming/types/plan.go @@ -144,6 +144,22 @@ func (plan *BasePlan) SetDistributedCoins(distributedCoins sdk.Coins) error { return nil } +func (plan BasePlan) GetBasePlan() *BasePlan { + return &BasePlan{ + Id: plan.GetId(), + Name: plan.GetName(), + Type: plan.GetType(), + FarmingPoolAddress: plan.GetFarmingPoolAddress().String(), + TerminationAddress: plan.GetTerminationAddress().String(), + StakingCoinWeights: plan.GetStakingCoinWeights(), + StartTime: plan.GetStartTime(), + EndTime: plan.GetEndTime(), + Terminated: plan.GetTerminated(), + LastDistributionTime: plan.GetLastDistributionTime(), + DistributedCoins: plan.GetDistributedCoins(), + } +} + // Validate checks for errors on the Plan fields func (plan BasePlan) Validate() error { if plan.Type != PlanTypePrivate && plan.Type != PlanTypePublic { @@ -244,6 +260,8 @@ type PlanI interface { GetDistributedCoins() sdk.Coins SetDistributedCoins(sdk.Coins) error + GetBasePlan() *BasePlan + String() string Validate() error diff --git a/x/farming/types/proposal.go b/x/farming/types/proposal.go index 63f4fa36..d88637fc 100644 --- a/x/farming/types/proposal.go +++ b/x/farming/types/proposal.go @@ -2,6 +2,7 @@ package types import ( "fmt" + time "time" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -20,6 +21,7 @@ func init() { gov.RegisterProposalTypeCodec(&PublicPlanProposal{}, "cosmos-sdk/PublicPlanProposal") } +// NewPublicPlanProposal creates a new PublicPlanProposal object. func NewPublicPlanProposal( title string, description string, @@ -83,6 +85,29 @@ func (p PublicPlanProposal) String() string { `, p.Title, p.Description, p.AddRequestProposals, p.UpdateRequestProposals, p.DeleteRequestProposals) } +// NewAddRequestProposal creates a new AddRequestProposal object +func NewAddRequestProposal( + name string, + farmingPoolAddr string, + terminationAddr string, + stakingCoinWeights sdk.DecCoins, + startTime time.Time, + endTime time.Time, + epochAmount sdk.Coins, + epochRatio sdk.Dec, +) *AddRequestProposal { + return &AddRequestProposal{ + Name: name, + FarmingPoolAddress: farmingPoolAddr, + TerminationAddress: terminationAddr, + StakingCoinWeights: stakingCoinWeights, + StartTime: startTime, + EndTime: endTime, + EpochAmount: epochAmount, + EpochRatio: epochRatio, + } +} + func (p *AddRequestProposal) Validate() error { if len(p.Name) > MaxNameLength { return sdkerrors.Wrapf(ErrInvalidPlanNameLength, "plan name cannot be longer than max length of %d", MaxNameLength) @@ -114,6 +139,31 @@ func (p *AddRequestProposal) Validate() error { return nil } +// NewUpdateRequestProposal creates a new UpdateRequestProposal object. +func NewUpdateRequestProposal( + id uint64, + name string, + farmingPoolAddr string, + terminationAddr string, + stakingCoinWeights sdk.DecCoins, + startTime time.Time, + endTime time.Time, + epochAmount sdk.Coins, + epochRatio sdk.Dec, +) *UpdateRequestProposal { + return &UpdateRequestProposal{ + PlanId: id, + Name: name, + FarmingPoolAddress: farmingPoolAddr, + TerminationAddress: terminationAddr, + StakingCoinWeights: stakingCoinWeights, + StartTime: &startTime, + EndTime: &endTime, + EpochAmount: epochAmount, + EpochRatio: epochRatio, + } +} + func (p *UpdateRequestProposal) Validate() error { if p.PlanId == 0 { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid plan id: %d", p.PlanId) @@ -148,6 +198,13 @@ func (p *UpdateRequestProposal) Validate() error { return nil } +// NewDeleteRequestProposal creates a new DeleteRequestProposal object. +func NewDeleteRequestProposal(id uint64) *DeleteRequestProposal { + return &DeleteRequestProposal{ + PlanId: id, + } +} + func (p *DeleteRequestProposal) Validate() error { if p.PlanId == 0 { return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid plan id: %d", p.PlanId)