package cli import ( "errors" "fmt" "github.com/spf13/cobra" sdkmath "cosmossdk.io/math" "cosmossdk.io/x/bank/types" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" ) var FlagSplit = "split" // NewTxCmd returns a root CLI command handler for all x/bank transaction commands. func NewTxCmd() *cobra.Command { txCmd := &cobra.Command{ Use: types.ModuleName, Short: "Bank transaction subcommands", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } txCmd.AddCommand( NewMultiSendTxCmd(), ) return txCmd } // NewMultiSendTxCmd returns a CLI command handler for creating a MsgMultiSend transaction. // For a better UX this command is limited to send funds from one account to two or more accounts. func NewMultiSendTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: "multi-send ... ", Short: "Send funds from one account to two or more accounts.", Long: `Send funds from one account to two or more accounts. By default, sends the [amount] to each address of the list. Using the '--split' flag, the [amount] is split equally between the addresses. Note, the '--from' flag is ignored as it is implied from [from_key_or_address] and separate addresses with space. When using '--dry-run' a key name cannot be used, only a bech32 address.`, Example: fmt.Sprintf("%s tx bank multi-send cosmos1... cosmos1... cosmos1... cosmos1... 10stake", version.AppName), Args: cobra.MinimumNArgs(4), RunE: func(cmd *cobra.Command, args []string) error { err := cmd.Flags().Set(flags.FlagFrom, args[0]) if err != nil { return err } clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err } coins, err := sdk.ParseCoinsNormalized(args[len(args)-1]) if err != nil { return err } if coins.IsZero() { return errors.New("must send positive amount") } split, err := cmd.Flags().GetBool(FlagSplit) if err != nil { return err } totalAddrs := sdkmath.NewInt(int64(len(args) - 2)) // coins to be received by the addresses sendCoins := coins if split { sendCoins = coins.QuoInt(totalAddrs) } var output []types.Output for _, arg := range args[1 : len(args)-1] { _, err = clientCtx.AddressCodec.StringToBytes(arg) if err != nil { return err } output = append(output, types.NewOutput(arg, sendCoins)) } // amount to be send from the from address var amount sdk.Coins if split { // user input: 1000stake to send to 3 addresses // actual: 333stake to each address (=> 999stake actually sent) amount = sendCoins.MulInt(totalAddrs) } else { amount = coins.MulInt(totalAddrs) } fromAddr, err := clientCtx.AddressCodec.BytesToString(clientCtx.FromAddress) if err != nil { return err } msg := types.NewMsgMultiSend(types.NewInput(fromAddr, amount), output) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } cmd.Flags().Bool(FlagSplit, false, "Send the equally split token amount to each address") flags.AddTxFlagsToCmd(cmd) return cmd }