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  }