Closes: #9595 Enables creating a periodic vesting account on a running chain. I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
173 lines
4.6 KiB
Go
173 lines
4.6 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strconv"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"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/x/auth/vesting/types"
|
|
)
|
|
|
|
// Transaction command flags
|
|
const (
|
|
FlagDelayed = "delayed"
|
|
)
|
|
|
|
// GetTxCmd returns vesting module's transaction commands.
|
|
func GetTxCmd() *cobra.Command {
|
|
txCmd := &cobra.Command{
|
|
Use: types.ModuleName,
|
|
Short: "Vesting transaction subcommands",
|
|
DisableFlagParsing: true,
|
|
SuggestionsMinimumDistance: 2,
|
|
RunE: client.ValidateCmd,
|
|
}
|
|
|
|
txCmd.AddCommand(
|
|
NewMsgCreateVestingAccountCmd(),
|
|
NewMsgCreatePeriodicVestingAccountCmd(),
|
|
)
|
|
|
|
return txCmd
|
|
}
|
|
|
|
// NewMsgCreateVestingAccountCmd returns a CLI command handler for creating a
|
|
// MsgCreateVestingAccount transaction.
|
|
func NewMsgCreateVestingAccountCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "create-vesting-account [to_address] [amount] [end_time]",
|
|
Short: "Create a new vesting account funded with an allocation of tokens.",
|
|
Long: `Create a new vesting account funded with an allocation of tokens. The
|
|
account can either be a delayed or continuous vesting account, which is determined
|
|
by the '--delayed' flag. All vesting accouts created will have their start time
|
|
set by the committed block's time. The end_time must be provided as a UNIX epoch
|
|
timestamp.`,
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
toAddr, err := sdk.AccAddressFromBech32(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
amount, err := sdk.ParseCoinsNormalized(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
endTime, err := strconv.ParseInt(args[2], 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
delayed, _ := cmd.Flags().GetBool(FlagDelayed)
|
|
|
|
msg := types.NewMsgCreateVestingAccount(clientCtx.GetFromAddress(), toAddr, amount, endTime, delayed)
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
cmd.Flags().Bool(FlagDelayed, false, "Create a delayed vesting account if true")
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
type VestingData struct {
|
|
StartTime int64 `json:"start_time"`
|
|
Periods []InputPeriod `json:"periods"`
|
|
}
|
|
|
|
type InputPeriod struct {
|
|
Coins string `json:"coins"`
|
|
Length int64 `json:"length_seconds"`
|
|
}
|
|
|
|
// NewMsgCreatePeriodicVestingAccountCmd returns a CLI command handler for creating a
|
|
// MsgCreatePeriodicVestingAccountCmd transaction.
|
|
func NewMsgCreatePeriodicVestingAccountCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "create-periodic-vesting-account [to_address] [periods_json_file]",
|
|
Short: "Create a new vesting account funded with an allocation of tokens.",
|
|
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.
|
|
Where periods.json contains:
|
|
|
|
An array of coin strings and unix epoch times for coins to vest
|
|
{ "start_time": 1625204910,
|
|
"period":[
|
|
{
|
|
"coins": "10test",
|
|
"length_seconds":2592000 //30 days
|
|
},
|
|
{
|
|
"coins": "10test",
|
|
"length_seconds":2592000 //30 days
|
|
},
|
|
]
|
|
}
|
|
`,
|
|
Args: cobra.ExactArgs(2),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
toAddr, err := sdk.AccAddressFromBech32(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
contents, err := ioutil.ReadFile(args[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var vestingData VestingData
|
|
|
|
err = json.Unmarshal(contents, &vestingData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var periods []types.Period
|
|
|
|
for i, p := range vestingData.Periods {
|
|
|
|
amount, err := sdk.ParseCoinsNormalized(p.Coins)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.Length < 0 {
|
|
return fmt.Errorf("invalid period length of %d in period %d, length must be greater than 0", p.Length, i)
|
|
}
|
|
period := types.Period{Length: p.Length, Amount: amount}
|
|
periods = append(periods, period)
|
|
}
|
|
|
|
msg := types.NewMsgCreatePeriodicVestingAccount(clientCtx.GetFromAddress(), toAddr, vestingData.StartTime, periods)
|
|
if err := msg.ValidateBasic(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|