From f0731ceb0eafbf78fea2f35af73712e61575b3f1 Mon Sep 17 00:00:00 2001 From: Robert Pirtle Date: Mon, 12 Dec 2022 17:10:36 -0800 Subject: [PATCH] Setup CLI for x/community lend proposals (#1427) * add tx for CommunityPoolLendDepositProposal * add cli cmd for CommunityPoolWithdrawProposal --- app/app.go | 3 + x/community/client/cli/tx.go | 144 +++++++++++++++++++++++++ x/community/client/proposal_handler.go | 35 ++++++ x/community/client/utils/utils.go | 39 +++++++ x/community/client/utils/utils_test.go | 63 +++++++++++ 5 files changed, 284 insertions(+) create mode 100644 x/community/client/proposal_handler.go create mode 100644 x/community/client/utils/utils.go create mode 100644 x/community/client/utils/utils_test.go diff --git a/app/app.go b/app/app.go index aaed2a77..15829a17 100644 --- a/app/app.go +++ b/app/app.go @@ -104,6 +104,7 @@ import ( committeekeeper "github.com/kava-labs/kava/x/committee/keeper" committeetypes "github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/community" + communityclient "github.com/kava-labs/kava/x/community/client" communitykeeper "github.com/kava-labs/kava/x/community/keeper" communitytypes "github.com/kava-labs/kava/x/community/types" earn "github.com/kava-labs/kava/x/earn" @@ -176,6 +177,8 @@ var ( committeeclient.ProposalHandler, earnclient.DepositProposalHandler, earnclient.WithdrawProposalHandler, + communityclient.LendDepositProposalHandler, + communityclient.LendWithdrawProposalHandler, ), params.AppModuleBasic{}, crisis.AppModuleBasic{}, diff --git a/x/community/client/cli/tx.go b/x/community/client/cli/tx.go index bcf103bb..26bba71b 100644 --- a/x/community/client/cli/tx.go +++ b/x/community/client/cli/tx.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strings" "github.com/spf13/cobra" @@ -10,10 +11,16 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/kava-labs/kava/x/community/client/utils" "github.com/kava-labs/kava/x/community/types" ) +const ( + flagDeposit = "deposit" +) + // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { communityTxCmd := &cobra.Command{ @@ -64,3 +71,140 @@ func getCmdFundCommunityPool() *cobra.Command { }, } } + +// NewCmdSubmitCommunityPoolLendDepositProposal implements the command to submit a community-pool lend deposit proposal +func NewCmdSubmitCommunityPoolLendDepositProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool-lend-deposit [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a community pool lend deposit proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a community pool lend deposit proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. +Note that --deposit below is the initial proposal deposit submitted along with the proposal. +Example: +$ %s tx gov submit-proposal community-pool-lend-deposit --deposit 1000000000ukava --from= +Where proposal.json contains: +{ + "title": "Community Pool Deposit", + "description": "Deposit some KAVA from community pool!", + "amount": [ + { + "denom": "ukava", + "amount": "100000000000" + } + ] +} +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + // parse proposal + proposal, err := utils.ParseCommunityPoolLendDepositProposal(clientCtx.Codec, args[0]) + if err != nil { + return err + } + + deposit, err := parseInitialDeposit(cmd) + if err != nil { + return err + } + + from := clientCtx.GetFromAddress() + msg, err := govtypes.NewMsgSubmitProposal(&proposal, deposit, from) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(flagDeposit, "", "Initial deposit for the proposal") + + return cmd +} + +// NewCmdSubmitCommunityPoolLendWithdrawProposal implements the command to submit a community-pool lend withdraw proposal +func NewCmdSubmitCommunityPoolLendWithdrawProposal() *cobra.Command { + cmd := &cobra.Command{ + Use: "community-pool-lend-withdraw [proposal-file]", + Args: cobra.ExactArgs(1), + Short: "Submit a community pool lend withdraw proposal", + Long: strings.TrimSpace( + fmt.Sprintf(`Submit a community pool lend withdraw proposal along with an initial deposit. +The proposal details must be supplied via a JSON file. +Note that --deposit below is the initial proposal deposit submitted along with the proposal. +Example: +$ %s tx gov submit-proposal community-pool-lend-withdraw --deposit 1000000000ukava --from= +Where proposal.json contains: +{ + "title": "Community Pool Withdrawal", + "description": "Withdraw some KAVA from community pool!", + "amount": [ + { + "denom": "ukava", + "amount": "100000000000" + } + ] +} +`, + version.AppName, + ), + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + // parse proposal + proposal, err := utils.ParseCommunityPoolLendWithdrawProposal(clientCtx.Codec, args[0]) + if err != nil { + return err + } + + deposit, err := parseInitialDeposit(cmd) + if err != nil { + return err + } + from := clientCtx.GetFromAddress() + msg, err := govtypes.NewMsgSubmitProposal(&proposal, deposit, from) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + cmd.Flags().String(flagDeposit, "", "Initial deposit for the proposal") + + return cmd +} + +func parseInitialDeposit(cmd *cobra.Command) (sdk.Coins, error) { + // parse initial deposit + depositStr, err := cmd.Flags().GetString(flagDeposit) + if err != nil { + return nil, fmt.Errorf("no initial deposit found. did you set --deposit? %s", err) + } + deposit, err := sdk.ParseCoinsNormalized(depositStr) + if err != nil { + return nil, fmt.Errorf("unable to parse deposit: %s", err) + } + if !deposit.IsValid() || deposit.IsZero() { + return nil, fmt.Errorf("no initial deposit set, use --deposit flag") + } + return deposit, nil +} diff --git a/x/community/client/proposal_handler.go b/x/community/client/proposal_handler.go new file mode 100644 index 00000000..2ba9f328 --- /dev/null +++ b/x/community/client/proposal_handler.go @@ -0,0 +1,35 @@ +package client + +import ( + "net/http" + + "github.com/cosmos/cosmos-sdk/client" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + + "github.com/kava-labs/kava/x/community/client/cli" + "github.com/kava-labs/kava/x/community/types" +) + +// community-pool deposit/withdraw lend proposal handlers +var ( + LendDepositProposalHandler = govclient.NewProposalHandler( + cli.NewCmdSubmitCommunityPoolLendDepositProposal, + notImplementedRestHandler(types.ProposalTypeCommunityPoolLendDeposit), + ) + LendWithdrawProposalHandler = govclient.NewProposalHandler( + cli.NewCmdSubmitCommunityPoolLendWithdrawProposal, + notImplementedRestHandler(types.ProposalTypeCommunityPoolLendDeposit), + ) +) + +func notImplementedRestHandler(subRoute string) govclient.RESTHandlerFn { + return func(ctx client.Context) govrest.ProposalRESTHandler { + return govrest.ProposalRESTHandler{ + SubRoute: subRoute, + Handler: func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "Unimplemented", http.StatusNotImplemented) + }, + } + } +} diff --git a/x/community/client/utils/utils.go b/x/community/client/utils/utils.go new file mode 100644 index 00000000..5b409a88 --- /dev/null +++ b/x/community/client/utils/utils.go @@ -0,0 +1,39 @@ +package utils + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/kava-labs/kava/x/community/types" +) + +// ParseCommunityPoolLendDepositProposal reads a JSON file and parses it to a CommunityPoolLendDepositProposal +func ParseCommunityPoolLendDepositProposal( + cdc codec.JSONCodec, + proposalFile string, +) (types.CommunityPoolLendDepositProposal, error) { + proposal := types.CommunityPoolLendDepositProposal{} + contents, err := os.ReadFile(proposalFile) + if err != nil { + return proposal, err + } + + err = cdc.UnmarshalJSON(contents, &proposal) + return proposal, err +} + +// ParseCommunityPoolLendWithdrawProposal reads a JSON file and parses it to a CommunityPoolLendWithdrawProposal +func ParseCommunityPoolLendWithdrawProposal( + cdc codec.JSONCodec, + proposalFile string, +) (types.CommunityPoolLendWithdrawProposal, error) { + proposal := types.CommunityPoolLendWithdrawProposal{} + contents, err := os.ReadFile(proposalFile) + if err != nil { + return proposal, err + } + + err = cdc.UnmarshalJSON(contents, &proposal) + return proposal, err +} diff --git a/x/community/client/utils/utils_test.go b/x/community/client/utils/utils_test.go new file mode 100644 index 00000000..9b582d71 --- /dev/null +++ b/x/community/client/utils/utils_test.go @@ -0,0 +1,63 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/kava-labs/kava/x/community/client/utils" +) + +func TestParseDepositProposal(t *testing.T) { + cdc := codec.NewAminoCodec(codec.NewLegacyAmino()) + okJSON := testutil.WriteToNewTempFile(t, ` +{ + "title": "Community Pool Lend Deposit", + "description": "Deposit some KAVA from community pool to Lend!", + "amount": [ + { + "denom": "ukava", + "amount": "100000000000" + } + ] +} +`) + proposal, err := utils.ParseCommunityPoolLendDepositProposal(cdc, okJSON.Name()) + require.NoError(t, err) + + expectedAmount, err := sdk.ParseCoinsNormalized("100000000000ukava") + require.NoError(t, err) + + require.Equal(t, "Community Pool Lend Deposit", proposal.Title) + require.Equal(t, "Deposit some KAVA from community pool to Lend!", proposal.Description) + require.Equal(t, expectedAmount, proposal.Amount) +} + +func TestParseWithdrawProposal(t *testing.T) { + cdc := codec.NewAminoCodec(codec.NewLegacyAmino()) + okJSON := testutil.WriteToNewTempFile(t, ` +{ + "title": "Community Pool Lend Withdraw", + "description": "Withdraw some KAVA from community pool to Lend!", + "amount": [ + { + "denom": "ukava", + "amount": "100000000000" + } + ] +} +`) + proposal, err := utils.ParseCommunityPoolLendWithdrawProposal(cdc, okJSON.Name()) + require.NoError(t, err) + + expectedAmount, err := sdk.ParseCoinsNormalized("100000000000ukava") + require.NoError(t, err) + + require.Equal(t, "Community Pool Lend Withdraw", proposal.Title) + require.Equal(t, "Withdraw some KAVA from community pool to Lend!", proposal.Description) + require.Equal(t, expectedAmount, proposal.Amount) +}