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 }