-
Notifications
You must be signed in to change notification settings - Fork 40
/
multi_send.go
138 lines (114 loc) · 3.58 KB
/
multi_send.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package commands
import (
"encoding/json"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/bnb-chain/node/wire"
)
const (
flagTransfers = "transfers"
flagTransfersFile = "transfers-file"
)
type Transfer struct {
To string `json:"to"`
Amount string `json:"amount"`
}
type Transfers []Transfer
// MultiSendCmd will create a send tx and sign it with the given key
func MultiSendCmd(cdc *wire.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "multi-send",
Short: "Create and sign a multi send tx",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := txbuilder.NewTxBuilderFromCLI().WithCodec(cdc)
ctx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
if err := ctx.EnsureAccountExists(); err != nil {
return err
}
// get the from/to address
from, err := ctx.GetFromAddress()
if err != nil {
return err
}
txPath := viper.GetString(flagTransfersFile)
var txBytes []byte
if txPath != "" {
txBytes, err = os.ReadFile(txPath)
if err != nil {
return err
}
} else {
txStr := viper.GetString(flagTransfers)
txBytes = []byte(txStr)
}
txs := Transfers{}
err = json.Unmarshal(txBytes, &txs)
if err != nil {
return err
}
if len(txs) == 0 {
return errors.New("tx is empty")
}
toAddrs := make([]sdk.AccAddress, 0, len(txs))
toCoins := make([]sdk.Coins, 0, len(txs))
for _, tx := range txs {
to, err := sdk.AccAddressFromBech32(tx.To)
if err != nil {
return err
}
toAddrs = append(toAddrs, to)
toCoin, err := sdk.ParseCoins(tx.Amount)
if err != nil {
return err
}
toCoins = append(toCoins, toCoin)
}
fromCoins := sdk.Coins{}
for _, toCoin := range toCoins {
fromCoins = fromCoins.Plus(toCoin)
}
if !fromCoins.IsPositive() {
return errors.Errorf("The number of coins you want to send(%s) should be positive!", fromCoins.String())
}
if !viper.GetBool(client.FlagOffline) {
// ensure account has enough toCoins
account, err := ctx.GetAccount(from)
if err != nil {
return err
}
if !account.GetCoins().IsGTE(fromCoins) {
return errors.Errorf("Address %s doesn't have enough toCoins to pay for this transaction.", from)
}
}
// build and sign the transaction, then broadcast to Tendermint
msg := BuildMultiSendMsg(from, fromCoins, toAddrs, toCoins)
if ctx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, ctx, []sdk.Msg{msg})
}
return utils.CompleteAndBroadcastTxCli(txBldr, ctx, []sdk.Msg{msg})
},
}
cmd.Flags().String(flagTransfers, "", "Transfers details, format: [{\"to\": \"addr\", \"amount\": \"1:BNB,2:BTC\"}, ...]")
cmd.Flags().String(flagTransfersFile, "", "File of transfers details, if transfers-file is not empty, --transfers will be ignored")
return cmd
}
func BuildMultiSendMsg(from sdk.AccAddress, fromCoins sdk.Coins, toAddrs []sdk.AccAddress, toCoins []sdk.Coins) sdk.Msg {
input := bank.NewInput(from, fromCoins)
output := make([]bank.Output, 0, len(toAddrs))
for idx, toAddr := range toAddrs {
output = append(output, bank.NewOutput(toAddr, toCoins[idx]))
}
msg := bank.NewMsgSend([]bank.Input{input}, output)
return msg
}