github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/vesting/client/cli/tx.go (about) 1 package cli 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "strconv" 9 10 "github.com/spf13/cobra" 11 12 "cosmossdk.io/core/address" 13 14 "github.com/cosmos/cosmos-sdk/client" 15 "github.com/cosmos/cosmos-sdk/client/flags" 16 "github.com/cosmos/cosmos-sdk/client/tx" 17 sdk "github.com/cosmos/cosmos-sdk/types" 18 "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" 19 ) 20 21 // Transaction command flags 22 const ( 23 FlagDelayed = "delayed" 24 ) 25 26 // GetTxCmd returns vesting module's transaction commands. 27 func GetTxCmd(ac address.Codec) *cobra.Command { 28 txCmd := &cobra.Command{ 29 Use: types.ModuleName, 30 Short: "Vesting transaction subcommands", 31 DisableFlagParsing: true, 32 SuggestionsMinimumDistance: 2, 33 RunE: client.ValidateCmd, 34 } 35 36 txCmd.AddCommand( 37 NewMsgCreateVestingAccountCmd(ac), 38 NewMsgCreatePermanentLockedAccountCmd(ac), 39 NewMsgCreatePeriodicVestingAccountCmd(ac), 40 ) 41 42 return txCmd 43 } 44 45 // NewMsgCreateVestingAccountCmd returns a CLI command handler for creating a 46 // MsgCreateVestingAccount transaction. 47 func NewMsgCreateVestingAccountCmd(ac address.Codec) *cobra.Command { 48 cmd := &cobra.Command{ 49 Use: "create-vesting-account [to_address] [amount] [end_time]", 50 Short: "Create a new vesting account funded with an allocation of tokens.", 51 Long: `Create a new vesting account funded with an allocation of tokens. The 52 account can either be a delayed or continuous vesting account, which is determined 53 by the '--delayed' flag. All vesting accounts created will have their start time 54 set by the committed block's time. The end_time must be provided as a UNIX epoch 55 timestamp.`, 56 Args: cobra.ExactArgs(3), 57 RunE: func(cmd *cobra.Command, args []string) error { 58 clientCtx, err := client.GetClientTxContext(cmd) 59 if err != nil { 60 return err 61 } 62 toAddr, err := ac.StringToBytes(args[0]) 63 if err != nil { 64 return err 65 } 66 67 if args[1] == "" { 68 return errors.New("amount is empty") 69 } 70 71 amount, err := sdk.ParseCoinsNormalized(args[1]) 72 if err != nil { 73 return err 74 } 75 76 endTime, err := strconv.ParseInt(args[2], 10, 64) 77 if err != nil { 78 return err 79 } 80 81 delayed, _ := cmd.Flags().GetBool(FlagDelayed) 82 83 msg := types.NewMsgCreateVestingAccount(clientCtx.GetFromAddress(), toAddr, amount, endTime, delayed) 84 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 85 }, 86 } 87 88 cmd.Flags().Bool(FlagDelayed, false, "Create a delayed vesting account if true") 89 flags.AddTxFlagsToCmd(cmd) 90 91 return cmd 92 } 93 94 // NewMsgCreatePermanentLockedAccountCmd returns a CLI command handler for creating a 95 // MsgCreatePermanentLockedAccount transaction. 96 func NewMsgCreatePermanentLockedAccountCmd(ac address.Codec) *cobra.Command { 97 cmd := &cobra.Command{ 98 Use: "create-permanent-locked-account [to_address] [amount]", 99 Short: "Create a new permanently locked account funded with an allocation of tokens.", 100 Long: `Create a new account funded with an allocation of permanently locked tokens. These 101 tokens may be used for staking but are non-transferable. Staking rewards will acrue as liquid and transferable 102 tokens.`, 103 Args: cobra.ExactArgs(2), 104 RunE: func(cmd *cobra.Command, args []string) error { 105 clientCtx, err := client.GetClientTxContext(cmd) 106 if err != nil { 107 return err 108 } 109 toAddr, err := ac.StringToBytes(args[0]) 110 if err != nil { 111 return err 112 } 113 114 if args[1] == "" { 115 return errors.New("amount is empty") 116 } 117 118 amount, err := sdk.ParseCoinsNormalized(args[1]) 119 if err != nil { 120 return err 121 } 122 123 msg := types.NewMsgCreatePermanentLockedAccount(clientCtx.GetFromAddress(), toAddr, amount) 124 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 125 }, 126 } 127 128 flags.AddTxFlagsToCmd(cmd) 129 130 return cmd 131 } 132 133 type VestingData struct { 134 StartTime int64 `json:"start_time"` 135 Periods []InputPeriod `json:"periods"` 136 } 137 138 type InputPeriod struct { 139 Coins string `json:"coins"` 140 Length int64 `json:"length_seconds"` 141 } 142 143 // NewMsgCreatePeriodicVestingAccountCmd returns a CLI command handler for creating a 144 // MsgCreatePeriodicVestingAccountCmd transaction. 145 func NewMsgCreatePeriodicVestingAccountCmd(ac address.Codec) *cobra.Command { 146 cmd := &cobra.Command{ 147 Use: "create-periodic-vesting-account [to_address] [periods_json_file]", 148 Short: "Create a new vesting account funded with an allocation of tokens.", 149 Long: `A sequence of coins and period length in seconds. Periods are sequential, in that the duration of of a period only starts at the end of the previous period. The duration of the first period starts upon account creation. For instance, the following periods.json file shows 20 "test" coins vesting 30 days apart from each other. 150 Where periods.json contains: 151 152 An array of coin strings and unix epoch times for coins to vest 153 { "start_time": 1625204910, 154 "periods":[ 155 { 156 "coins": "10test", 157 "length_seconds":2592000 //30 days 158 }, 159 { 160 "coins": "10test", 161 "length_seconds":2592000 //30 days 162 }, 163 ] 164 } 165 `, 166 Args: cobra.ExactArgs(2), 167 RunE: func(cmd *cobra.Command, args []string) error { 168 clientCtx, err := client.GetClientTxContext(cmd) 169 if err != nil { 170 return err 171 } 172 173 toAddr, err := ac.StringToBytes(args[0]) 174 if err != nil { 175 return err 176 } 177 178 contents, err := os.ReadFile(args[1]) 179 if err != nil { 180 return err 181 } 182 183 var vestingData VestingData 184 185 err = json.Unmarshal(contents, &vestingData) 186 if err != nil { 187 return err 188 } 189 190 var periods []types.Period 191 192 for i, p := range vestingData.Periods { 193 194 amount, err := sdk.ParseCoinsNormalized(p.Coins) 195 if err != nil { 196 return err 197 } 198 199 if p.Length < 0 { 200 return fmt.Errorf("invalid period length of %d in period %d, length must be greater than 0", p.Length, i) 201 } 202 203 period := types.Period{Length: p.Length, Amount: amount} 204 periods = append(periods, period) 205 } 206 207 msg := types.NewMsgCreatePeriodicVestingAccount(clientCtx.GetFromAddress(), toAddr, vestingData.StartTime, periods) 208 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 209 }, 210 } 211 212 flags.AddTxFlagsToCmd(cmd) 213 214 return cmd 215 }