Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add MsgTerminatePrivatePlan #154

Merged
merged 4 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/docs/statik/statik.go

Large diffs are not rendered by default.

47,994 changes: 5,495 additions & 42,499 deletions client/docs/swagger-ui/swagger.yaml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions proto/crescent/lpfarm/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ option (gogoproto.goproto_getters_all) = false;

service Msg {
rpc CreatePrivatePlan(MsgCreatePrivatePlan) returns (MsgCreatePrivatePlanResponse);
rpc TerminatePrivatePlan(MsgTerminatePrivatePlan) returns (MsgTerminatePrivatePlanResponse);
rpc Farm(MsgFarm) returns (MsgFarmResponse);
rpc Unfarm(MsgUnfarm) returns (MsgUnfarmResponse);
rpc Harvest(MsgHarvest) returns (MsgHarvestResponse);
Expand All @@ -30,6 +31,13 @@ message MsgCreatePrivatePlanResponse {
string farming_pool_address = 2;
}

message MsgTerminatePrivatePlan {
string creator = 1;
uint64 plan_id = 2;
}

message MsgTerminatePrivatePlanResponse {}

message MsgFarm {
string farmer = 1;
cosmos.base.v1beta1.Coin coin = 2 [(gogoproto.nullable) = false];
Expand Down
37 changes: 37 additions & 0 deletions x/lpfarm/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func GetTxCmd() *cobra.Command {

cmd.AddCommand(
NewCreatePrivatePlanCmd(),
NewTerminatePrivatePlanCmd(),
NewFarmCmd(),
NewUnfarmCmd(),
NewHarvestCmd(),
Expand Down Expand Up @@ -117,6 +118,42 @@ $ %s tx %s create-private-plan "New Farming Plan" 2022-01-01T00:00:00Z 2023-01-0
return cmd
}

func NewTerminatePrivatePlanCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "terminate-private-plan [plan-id]",
Args: cobra.ExactArgs(1),
Short: "Terminate a private farming plan",
Long: strings.TrimSpace(
fmt.Sprintf(`Terminate a private farming plan.
The plan's termination address must be same with the message sender(original plan creator).

Example:
$ %s tx %s terminate-private-plan 1 --from mykey
`,
version.AppName, types.ModuleName,
),
),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

planId, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("invalid plan id: %w", err)
}

msg := types.NewMsgTerminatePrivatePlan(clientCtx.GetFromAddress(), planId)
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}

func NewFarmCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "farm [coin]",
Expand Down
3 changes: 3 additions & 0 deletions x/lpfarm/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgCreatePrivatePlan:
res, err := msgServer.CreatePrivatePlan(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgTerminatePrivatePlan:
res, err := msgServer.TerminatePrivatePlan(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgFarm:
res, err := msgServer.Farm(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
Expand Down
25 changes: 25 additions & 0 deletions x/lpfarm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

"github.com/crescent-network/crescent/v4/x/lpfarm/types"
)
Expand Down Expand Up @@ -41,6 +42,30 @@ func (k msgServer) CreatePrivatePlan(goCtx context.Context, msg *types.MsgCreate
}, nil
}

// TerminatePrivatePlan defines a method to terminate a private plan.
func (k msgServer) TerminatePrivatePlan(goCtx context.Context, msg *types.MsgTerminatePrivatePlan) (*types.MsgTerminatePrivatePlanResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

plan, found := k.GetPlan(ctx, msg.PlanId)
if !found {
return nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "plan not found: %d", msg.PlanId)
}
if !plan.IsPrivate {
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot terminate public plan")
}
if plan.TerminationAddress != msg.Creator {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrUnauthorized,
"plan's termination address must be same with the sender's address")
}

if err := k.Keeper.TerminatePlan(ctx, plan); err != nil {
return nil, err
}

return &types.MsgTerminatePrivatePlanResponse{}, nil
}

// Farm defines a method for farming coins.
func (k msgServer) Farm(goCtx context.Context, msg *types.MsgFarm) (*types.MsgFarmResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
Expand Down
51 changes: 51 additions & 0 deletions x/lpfarm/keeper/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package keeper_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

utils "github.com/crescent-network/crescent/v4/types"
"github.com/crescent-network/crescent/v4/x/lpfarm/keeper"
"github.com/crescent-network/crescent/v4/x/lpfarm/types"
)

Expand Down Expand Up @@ -61,6 +63,55 @@ func (s *KeeperTestSuite) TestCreatePrivatePlan_PairNotFound() {
err, "pair 2 not found: not found")
}

func (s *KeeperTestSuite) TestTerminatePrivatePlan() {
s.createPairWithLastPrice("denom1", "denom2", sdk.NewDec(1))
s.createPool(1, utils.ParseCoins("100_000000denom1,100_000000denom2"))

creatorAddr := utils.TestAddress(1)
s.fundAddr(creatorAddr, s.keeper.GetPrivatePlanCreationFee(s.ctx))
plan, err := s.keeper.CreatePrivatePlan(
s.ctx, creatorAddr, "", []types.RewardAllocation{
types.NewPairRewardAllocation(1, utils.ParseCoins("100_000000stake")),
}, sampleStartTime, sampleEndTime)
s.Require().NoError(err)
s.fundAddr(plan.GetFarmingPoolAddress(), utils.ParseCoins("10000_000000stake"))

s.farm(utils.TestAddress(2), utils.ParseCoin("1000000pool1"))

s.nextBlock()
s.nextBlock()
s.nextBlock()

balancesBefore := s.getBalances(creatorAddr)
remainingFarmingRewards := s.getBalances(plan.GetFarmingPoolAddress())

msgServer := keeper.NewMsgServerImpl(s.keeper)
msg := types.NewMsgTerminatePrivatePlan(creatorAddr, 1)
_, err = msgServer.TerminatePrivatePlan(sdk.WrapSDKContext(s.ctx), msg)
s.Require().NoError(err)

s.assertEq(sdk.Coins{}, s.getBalances(plan.GetFarmingPoolAddress()))
s.assertEq(balancesBefore.Add(remainingFarmingRewards...), s.getBalances(creatorAddr))
}

func (s *KeeperTestSuite) TestTerminatePrivatePlan_Unauthorized() {
s.createPairWithLastPrice("denom1", "denom2", sdk.NewDec(1))

creatorAddr := utils.TestAddress(1)
s.fundAddr(creatorAddr, s.keeper.GetPrivatePlanCreationFee(s.ctx))
plan, err := s.keeper.CreatePrivatePlan(
s.ctx, creatorAddr, "", []types.RewardAllocation{
types.NewPairRewardAllocation(1, utils.ParseCoins("100_000000stake")),
}, sampleStartTime, sampleEndTime)
s.Require().NoError(err)
s.fundAddr(plan.GetFarmingPoolAddress(), utils.ParseCoins("10000_000000stake"))

msgServer := keeper.NewMsgServerImpl(s.keeper)
msg := types.NewMsgTerminatePrivatePlan(utils.TestAddress(2), 1)
_, err = msgServer.TerminatePrivatePlan(sdk.WrapSDKContext(s.ctx), msg)
s.Require().ErrorIs(err, sdkerrors.ErrUnauthorized)
}

func (s *KeeperTestSuite) TestAllocateRewards_NoFarmer() {
s.createPairWithLastPrice("denom1", "denom2", sdk.NewDec(1))
s.createPool(1, utils.ParseCoins("100_000000denom1,100_000000denom2"))
Expand Down
83 changes: 71 additions & 12 deletions x/lpfarm/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,17 @@ import (

// Simulation operation weights constants.
const (
OpWeightMsgCreatePrivatePlan = "op_weight_msg_create_private_plan"
OpWeightMsgFarm = "op_weight_msg_farm"
OpWeightMsgUnfarm = "op_weight_msg_unfarm"
OpWeightMsgHarvest = "op_weight_msg_harvest"
OpWeightMsgCreatePrivatePlan = "op_weight_msg_create_private_plan"
OpWeightMsgTerminatePrivatePlan = "op_weight_msg_terminate_private_plan"
OpWeightMsgFarm = "op_weight_msg_farm"
OpWeightMsgUnfarm = "op_weight_msg_unfarm"
OpWeightMsgHarvest = "op_weight_msg_harvest"

DefaultWeightCreatePrivatePlan = 10
DefaultWeightFarm = 40
DefaultWeightUnfarm = 50
DefaultWeightHarvest = 20
DefaultWeightCreatePrivatePlan = 10
DefaultWeightTerminatePrivatePlan = 5
DefaultWeightFarm = 40
DefaultWeightUnfarm = 50
DefaultWeightHarvest = 20
)

var (
Expand All @@ -43,14 +45,18 @@ func WeightedOperations(
ak types.AccountKeeper, bk types.BankKeeper, lk types.LiquidityKeeper, k keeper.Keeper,
) simulation.WeightedOperations {
var (
weightMsgCreatePrivatePlan int
weightMsgFarm int
weightMsgUnfarm int
weightMsgHarvest int
weightMsgCreatePrivatePlan int
weightMsgTerminatePrivatePlan int
weightMsgFarm int
weightMsgUnfarm int
weightMsgHarvest int
)
appParams.GetOrGenerate(cdc, OpWeightMsgCreatePrivatePlan, &weightMsgCreatePrivatePlan, nil, func(_ *rand.Rand) {
weightMsgCreatePrivatePlan = DefaultWeightCreatePrivatePlan
})
appParams.GetOrGenerate(cdc, OpWeightMsgTerminatePrivatePlan, &weightMsgTerminatePrivatePlan, nil, func(_ *rand.Rand) {
weightMsgTerminatePrivatePlan = DefaultWeightTerminatePrivatePlan
})
appParams.GetOrGenerate(cdc, OpWeightMsgFarm, &weightMsgFarm, nil, func(_ *rand.Rand) {
weightMsgFarm = DefaultWeightFarm
})
Expand All @@ -66,6 +72,10 @@ func WeightedOperations(
weightMsgCreatePrivatePlan,
SimulateMsgCreatePrivatePlan(ak, bk, lk, k),
),
simulation.NewWeightedOperation(
weightMsgTerminatePrivatePlan,
SimulateMsgTerminatePrivatePlan(ak, bk, k),
),
simulation.NewWeightedOperation(
weightMsgFarm,
SimulateMsgFarm(ak, bk),
Expand Down Expand Up @@ -137,6 +147,55 @@ func SimulateMsgCreatePrivatePlan(
}
}

func SimulateMsgTerminatePrivatePlan(
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
accs = utils.ShuffleSimAccounts(r, accs)
var simAccount simtypes.Account
var planId uint64
skip := true
for _, simAccount = range accs {
k.IterateAllPlans(ctx, func(plan types.Plan) (stop bool) {
if !plan.IsTerminated && plan.GetTerminationAddress().Equals(simAccount.Address) {
planId = plan.Id
return true
}
return false
})
if planId > 0 {
skip = false
break
}
}
if skip {
return simtypes.NoOpMsg(
types.ModuleName, types.TypeMsgFarm, "no account to farm"), nil, nil
}

msg := types.NewMsgTerminatePrivatePlan(simAccount.Address, planId)

txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: appparams.MakeTestEncodingConfig().TxConfig,
Msg: msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: bk.SpendableCoins(ctx, simAccount.Address),
}

return utils.GenAndDeliverTxWithFees(txCtx, gas, fees)
}
}

func SimulateMsgFarm(ak types.AccountKeeper, bk types.BankKeeper) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
Expand Down
24 changes: 24 additions & 0 deletions x/lpfarm/simulation/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ func (s *SimTestSuite) TestSimulateMsgCreatePrivatePlan() {
s.Require().Equal(utils.ParseTime("2022-01-06T00:00:00Z"), msg.EndTime)
}

func (s *SimTestSuite) TestSimulateMsgTerminatePrivatePlan() {
r := rand.New(rand.NewSource(0))
accs := s.getTestingAccounts(r, 1)

_, err := s.app.LPFarmKeeper.CreatePrivatePlan(s.ctx, accs[0].Address, "", []types.RewardAllocation{
types.NewDenomRewardAllocation("stake", utils.ParseCoins("100_000000stake")),
}, utils.ParseTime("0001-01-01T00:00:00Z"), utils.ParseTime("9999-12-31T00:00:00Z"))
s.Require().NoError(err)

op := simulation.SimulateMsgTerminatePrivatePlan(s.app.AccountKeeper, s.app.BankKeeper, s.keeper)
opMsg, futureOps, err := op(r, s.app.BaseApp, s.ctx, accs, "")
s.Require().NoError(err)
s.Require().True(opMsg.OK)
s.Require().Len(futureOps, 0)

var msg types.MsgTerminatePrivatePlan
types.ModuleCdc.MustUnmarshalJSON(opMsg.Msg, &msg)

s.Require().Equal(types.TypeMsgTerminatePrivatePlan, msg.Type())
s.Require().Equal(types.ModuleName, msg.Route())
s.Require().Equal("cosmos1tp4es44j4vv8m59za3z0tm64dkmlnm8wg2frhc", msg.Creator)
s.Require().Equal(uint64(1), msg.PlanId)
}

func (s *SimTestSuite) TestSimulateMsgFarm() {
r := rand.New(rand.NewSource(0))
accs := s.getTestingAccounts(r, 1)
Expand Down
2 changes: 2 additions & 0 deletions x/lpfarm/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// on the provided LegacyAmino codec. These types are used for Amino JSON serialization.
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&MsgCreatePrivatePlan{}, "lpfarm/MsgCreatePrivatePlan", nil)
cdc.RegisterConcrete(&MsgTerminatePrivatePlan{}, "lpfarm/MsgTerminatePrivatePlan", nil)
cdc.RegisterConcrete(&MsgFarm{}, "lpfarm/MsgFarm", nil)
cdc.RegisterConcrete(&MsgUnfarm{}, "lpfarm/MsgUnfarm", nil)
cdc.RegisterConcrete(&MsgHarvest{}, "lpfarm/MsgHarvest", nil)
Expand All @@ -25,6 +26,7 @@ func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
registry.RegisterImplementations(
(*sdk.Msg)(nil),
&MsgCreatePrivatePlan{},
&MsgTerminatePrivatePlan{},
&MsgFarm{},
&MsgUnfarm{},
&MsgHarvest{},
Expand Down
Loading