github.com/Finschia/finschia-sdk@v0.48.1/x/distribution/client/cli/tx.go (about)

     1  package cli
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/spf13/cobra"
     8  	"github.com/spf13/pflag"
     9  
    10  	"github.com/Finschia/finschia-sdk/client"
    11  	"github.com/Finschia/finschia-sdk/client/flags"
    12  	"github.com/Finschia/finschia-sdk/client/tx"
    13  	sdk "github.com/Finschia/finschia-sdk/types"
    14  	"github.com/Finschia/finschia-sdk/version"
    15  	"github.com/Finschia/finschia-sdk/x/distribution/types"
    16  	govtypes "github.com/Finschia/finschia-sdk/x/gov/types"
    17  )
    18  
    19  // Transaction flags for the x/distribution module
    20  var (
    21  	FlagCommission       = "commission"
    22  	FlagMaxMessagesPerTx = "max-msgs"
    23  )
    24  
    25  const (
    26  	MaxMessagesPerTxDefault = 0
    27  )
    28  
    29  // NewTxCmd returns a root CLI command handler for all x/distribution transaction commands.
    30  func NewTxCmd() *cobra.Command {
    31  	distTxCmd := &cobra.Command{
    32  		Use:                        types.ModuleName,
    33  		Short:                      "Distribution transactions subcommands",
    34  		DisableFlagParsing:         true,
    35  		SuggestionsMinimumDistance: 2,
    36  		RunE:                       client.ValidateCmd,
    37  	}
    38  
    39  	distTxCmd.AddCommand(
    40  		NewWithdrawRewardsCmd(),
    41  		NewWithdrawAllRewardsCmd(),
    42  		NewSetWithdrawAddrCmd(),
    43  		NewFundCommunityPoolCmd(),
    44  	)
    45  
    46  	return distTxCmd
    47  }
    48  
    49  type newGenerateOrBroadcastFunc func(client.Context, *pflag.FlagSet, ...sdk.Msg) error
    50  
    51  func newSplitAndApply(
    52  	genOrBroadcastFn newGenerateOrBroadcastFunc, clientCtx client.Context,
    53  	fs *pflag.FlagSet, msgs []sdk.Msg, chunkSize int,
    54  ) error {
    55  	totalMessages := len(msgs)
    56  	if chunkSize == 0 || totalMessages == 0 {
    57  		return genOrBroadcastFn(clientCtx, fs, msgs...)
    58  	}
    59  
    60  	// split messages into slices of length chunkSize
    61  	for i := 0; i < len(msgs); i += chunkSize {
    62  
    63  		sliceEnd := i + chunkSize
    64  		if sliceEnd > totalMessages {
    65  			sliceEnd = totalMessages
    66  		}
    67  
    68  		msgChunk := msgs[i:sliceEnd]
    69  		if err := genOrBroadcastFn(clientCtx, fs, msgChunk...); err != nil {
    70  			return err
    71  		}
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  func NewWithdrawRewardsCmd() *cobra.Command {
    78  	bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix()
    79  
    80  	cmd := &cobra.Command{
    81  		Use:   "withdraw-rewards [validator-addr]",
    82  		Short: "Withdraw rewards from a given delegation address, and optionally withdraw validator commission if the delegation address given is a validator operator",
    83  		Long: strings.TrimSpace(
    84  			fmt.Sprintf(`Withdraw rewards from a given delegation address,
    85  and optionally withdraw validator commission if the delegation address given is a validator operator.
    86  
    87  Example:
    88  $ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey
    89  $ %s tx distribution withdraw-rewards %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj --from mykey --commission
    90  `,
    91  				version.AppName, bech32PrefixValAddr, version.AppName, bech32PrefixValAddr,
    92  			),
    93  		),
    94  		Args: cobra.ExactArgs(1),
    95  		RunE: func(cmd *cobra.Command, args []string) error {
    96  			clientCtx, err := client.GetClientTxContext(cmd)
    97  			if err != nil {
    98  				return err
    99  			}
   100  			delAddr := clientCtx.GetFromAddress()
   101  			valAddr, err := sdk.ValAddressFromBech32(args[0])
   102  			if err != nil {
   103  				return err
   104  			}
   105  
   106  			msgs := []sdk.Msg{types.NewMsgWithdrawDelegatorReward(delAddr, valAddr)}
   107  
   108  			if commission, _ := cmd.Flags().GetBool(FlagCommission); commission {
   109  				msgs = append(msgs, types.NewMsgWithdrawValidatorCommission(valAddr))
   110  			}
   111  
   112  			for _, msg := range msgs {
   113  				if err := msg.ValidateBasic(); err != nil {
   114  					return err
   115  				}
   116  			}
   117  
   118  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msgs...)
   119  		},
   120  	}
   121  
   122  	cmd.Flags().Bool(FlagCommission, false, "Withdraw the validator's commission in addition to the rewards")
   123  	flags.AddTxFlagsToCmd(cmd)
   124  
   125  	return cmd
   126  }
   127  
   128  func NewWithdrawAllRewardsCmd() *cobra.Command {
   129  	cmd := &cobra.Command{
   130  		Use:   "withdraw-all-rewards",
   131  		Short: "withdraw all delegations rewards for a delegator",
   132  		Long: strings.TrimSpace(
   133  			fmt.Sprintf(`Withdraw all rewards for a single delegator.
   134  Note that if you use this command with --%[2]s=%[3]s or --%[2]s=%[4]s, the %[5]s flag will automatically be set to 0.
   135  
   136  Example:
   137  $ %[1]s tx distribution withdraw-all-rewards --from mykey
   138  `,
   139  				version.AppName, flags.FlagBroadcastMode, flags.BroadcastSync, flags.BroadcastAsync, FlagMaxMessagesPerTx,
   140  			),
   141  		),
   142  		Args: cobra.NoArgs,
   143  		RunE: func(cmd *cobra.Command, _ []string) error {
   144  			clientCtx, err := client.GetClientTxContext(cmd)
   145  			if err != nil {
   146  				return err
   147  			}
   148  			delAddr := clientCtx.GetFromAddress()
   149  
   150  			// The transaction cannot be generated offline since it requires a query
   151  			// to get all the validators.
   152  			if clientCtx.Offline {
   153  				return fmt.Errorf("cannot generate tx in offline mode")
   154  			}
   155  
   156  			queryClient := types.NewQueryClient(clientCtx)
   157  			delValsRes, err := queryClient.DelegatorValidators(cmd.Context(), &types.QueryDelegatorValidatorsRequest{DelegatorAddress: delAddr.String()})
   158  			if err != nil {
   159  				return err
   160  			}
   161  
   162  			validators := delValsRes.Validators
   163  			// build multi-message transaction
   164  			msgs := make([]sdk.Msg, 0, len(validators))
   165  			for _, valAddr := range validators {
   166  				val, err := sdk.ValAddressFromBech32(valAddr)
   167  				if err != nil {
   168  					return err
   169  				}
   170  
   171  				msg := types.NewMsgWithdrawDelegatorReward(delAddr, val)
   172  				if err := msg.ValidateBasic(); err != nil {
   173  					return err
   174  				}
   175  				msgs = append(msgs, msg)
   176  			}
   177  
   178  			chunkSize, _ := cmd.Flags().GetInt(FlagMaxMessagesPerTx)
   179  			if clientCtx.BroadcastMode != flags.BroadcastBlock && chunkSize > 0 {
   180  				return fmt.Errorf("cannot use broadcast mode %[1]s with %[2]s != 0",
   181  					clientCtx.BroadcastMode, FlagMaxMessagesPerTx)
   182  			}
   183  
   184  			return newSplitAndApply(tx.GenerateOrBroadcastTxCLI, clientCtx, cmd.Flags(), msgs, chunkSize)
   185  		},
   186  	}
   187  
   188  	cmd.Flags().Int(FlagMaxMessagesPerTx, MaxMessagesPerTxDefault, "Limit the number of messages per tx (0 for unlimited)")
   189  	flags.AddTxFlagsToCmd(cmd)
   190  
   191  	return cmd
   192  }
   193  
   194  func NewSetWithdrawAddrCmd() *cobra.Command {
   195  	bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
   196  
   197  	cmd := &cobra.Command{
   198  		Use:   "set-withdraw-addr [withdraw-addr]",
   199  		Short: "change the default withdraw address for rewards associated with an address",
   200  		Long: strings.TrimSpace(
   201  			fmt.Sprintf(`Set the withdraw address for rewards associated with a delegator address.
   202  
   203  Example:
   204  $ %s tx distribution set-withdraw-addr %s1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p --from mykey
   205  `,
   206  				version.AppName, bech32PrefixAccAddr,
   207  			),
   208  		),
   209  		Args: cobra.ExactArgs(1),
   210  		RunE: func(cmd *cobra.Command, args []string) error {
   211  			clientCtx, err := client.GetClientTxContext(cmd)
   212  			if err != nil {
   213  				return err
   214  			}
   215  			delAddr := clientCtx.GetFromAddress()
   216  			withdrawAddr, err := sdk.AccAddressFromBech32(args[0])
   217  			if err != nil {
   218  				return err
   219  			}
   220  
   221  			msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
   222  
   223  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   224  		},
   225  	}
   226  
   227  	flags.AddTxFlagsToCmd(cmd)
   228  
   229  	return cmd
   230  }
   231  
   232  func NewFundCommunityPoolCmd() *cobra.Command {
   233  	cmd := &cobra.Command{
   234  		Use:   "fund-community-pool [amount]",
   235  		Args:  cobra.ExactArgs(1),
   236  		Short: "Funds the community pool with the specified amount",
   237  		Long: strings.TrimSpace(
   238  			fmt.Sprintf(`Funds the community pool with the specified amount
   239  
   240  Example:
   241  $ %s tx distribution fund-community-pool 100uatom --from mykey
   242  `,
   243  				version.AppName,
   244  			),
   245  		),
   246  		RunE: func(cmd *cobra.Command, args []string) error {
   247  			clientCtx, err := client.GetClientTxContext(cmd)
   248  			if err != nil {
   249  				return err
   250  			}
   251  			depositorAddr := clientCtx.GetFromAddress()
   252  			amount, err := sdk.ParseCoinsNormalized(args[0])
   253  			if err != nil {
   254  				return err
   255  			}
   256  
   257  			msg := types.NewMsgFundCommunityPool(amount, depositorAddr)
   258  
   259  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   260  		},
   261  	}
   262  
   263  	flags.AddTxFlagsToCmd(cmd)
   264  
   265  	return cmd
   266  }
   267  
   268  // GetCmdSubmitProposal implements the command to submit a community-pool-spend proposal
   269  func GetCmdSubmitProposal() *cobra.Command {
   270  	bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
   271  
   272  	cmd := &cobra.Command{
   273  		Use:   "community-pool-spend [proposal-file]",
   274  		Args:  cobra.ExactArgs(1),
   275  		Short: "Submit a community pool spend proposal",
   276  		Long: strings.TrimSpace(
   277  			fmt.Sprintf(`Submit a community pool spend proposal along with an initial deposit.
   278  The proposal details must be supplied via a JSON file.
   279  
   280  Example:
   281  $ %s tx gov submit-proposal community-pool-spend <path/to/proposal.json> --from=<key_or_address>
   282  
   283  Where proposal.json contains:
   284  
   285  {
   286    "title": "Community Pool Spend",
   287    "description": "Pay me some Atoms!",
   288    "recipient": "%s1s5afhd6gxevu37mkqcvvsj8qeylhn0rz46zdlq",
   289    "amount": "1000stake",
   290    "deposit": "1000stake"
   291  }
   292  `,
   293  				version.AppName, bech32PrefixAccAddr,
   294  			),
   295  		),
   296  		RunE: func(cmd *cobra.Command, args []string) error {
   297  			clientCtx, err := client.GetClientTxContext(cmd)
   298  			if err != nil {
   299  				return err
   300  			}
   301  			proposal, err := ParseCommunityPoolSpendProposalWithDeposit(clientCtx.Codec, args[0])
   302  			if err != nil {
   303  				return err
   304  			}
   305  
   306  			amount, err := sdk.ParseCoinsNormalized(proposal.Amount)
   307  			if err != nil {
   308  				return err
   309  			}
   310  
   311  			deposit, err := sdk.ParseCoinsNormalized(proposal.Deposit)
   312  			if err != nil {
   313  				return err
   314  			}
   315  
   316  			from := clientCtx.GetFromAddress()
   317  			recpAddr, err := sdk.AccAddressFromBech32(proposal.Recipient)
   318  			if err != nil {
   319  				return err
   320  			}
   321  			content := types.NewCommunityPoolSpendProposal(proposal.Title, proposal.Description, recpAddr, amount)
   322  
   323  			msg, err := govtypes.NewMsgSubmitProposal(content, deposit, from)
   324  			if err != nil {
   325  				return err
   326  			}
   327  
   328  			return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
   329  		},
   330  	}
   331  
   332  	return cmd
   333  }