github.com/Finschia/finschia-sdk@v0.48.1/x/staking/client/cli/tx.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/spf13/cobra" 9 flag "github.com/spf13/pflag" 10 11 "github.com/Finschia/finschia-sdk/client" 12 "github.com/Finschia/finschia-sdk/client/flags" 13 "github.com/Finschia/finschia-sdk/client/tx" 14 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 15 sdk "github.com/Finschia/finschia-sdk/types" 16 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 17 "github.com/Finschia/finschia-sdk/version" 18 "github.com/Finschia/finschia-sdk/x/staking/types" 19 ) 20 21 // default values 22 var ( 23 DefaultTokens = sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) 24 defaultAmount = DefaultTokens.String() + sdk.DefaultBondDenom 25 defaultCommissionRate = "0.1" 26 defaultCommissionMaxRate = "0.2" 27 defaultCommissionMaxChangeRate = "0.01" 28 defaultMinSelfDelegation = "1" 29 ) 30 31 // NewTxCmd returns a root CLI command handler for all x/staking transaction commands. 32 func NewTxCmd() *cobra.Command { 33 stakingTxCmd := &cobra.Command{ 34 Use: types.ModuleName, 35 Short: "Staking transaction subcommands", 36 DisableFlagParsing: true, 37 SuggestionsMinimumDistance: 2, 38 RunE: client.ValidateCmd, 39 } 40 41 stakingTxCmd.AddCommand( 42 NewCreateValidatorCmd(), 43 NewEditValidatorCmd(), 44 NewDelegateCmd(), 45 NewRedelegateCmd(), 46 NewUnbondCmd(), 47 ) 48 49 return stakingTxCmd 50 } 51 52 func NewCreateValidatorCmd() *cobra.Command { 53 cmd := &cobra.Command{ 54 Use: "create-validator", 55 Short: "create new validator initialized with a self-delegation to it", 56 RunE: func(cmd *cobra.Command, args []string) error { 57 clientCtx, err := client.GetClientTxContext(cmd) 58 if err != nil { 59 return err 60 } 61 62 txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()). 63 WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) 64 txf, msg, err := newBuildCreateValidatorMsg(clientCtx, txf, cmd.Flags()) 65 if err != nil { 66 return err 67 } 68 69 return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg) 70 }, 71 } 72 73 cmd.Flags().AddFlagSet(FlagSetPublicKey()) 74 cmd.Flags().AddFlagSet(FlagSetAmount()) 75 cmd.Flags().AddFlagSet(flagSetDescriptionCreate()) 76 cmd.Flags().AddFlagSet(FlagSetCommissionCreate()) 77 cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation()) 78 79 cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", flags.FlagGenerateOnly)) 80 cmd.Flags().String(FlagNodeID, "", "The node's ID") 81 flags.AddTxFlagsToCmd(cmd) 82 83 _ = cmd.MarkFlagRequired(flags.FlagFrom) 84 _ = cmd.MarkFlagRequired(FlagAmount) 85 _ = cmd.MarkFlagRequired(FlagPubKey) 86 _ = cmd.MarkFlagRequired(FlagMoniker) 87 88 return cmd 89 } 90 91 func NewEditValidatorCmd() *cobra.Command { 92 cmd := &cobra.Command{ 93 Use: "edit-validator", 94 Short: "edit an existing validator account", 95 RunE: func(cmd *cobra.Command, args []string) error { 96 clientCtx, err := client.GetClientTxContext(cmd) 97 if err != nil { 98 return err 99 } 100 valAddr := clientCtx.GetFromAddress() 101 moniker, _ := cmd.Flags().GetString(FlagEditMoniker) 102 identity, _ := cmd.Flags().GetString(FlagIdentity) 103 website, _ := cmd.Flags().GetString(FlagWebsite) 104 security, _ := cmd.Flags().GetString(FlagSecurityContact) 105 details, _ := cmd.Flags().GetString(FlagDetails) 106 description := types.NewDescription(moniker, identity, website, security, details) 107 108 var newRate *sdk.Dec 109 110 commissionRate, _ := cmd.Flags().GetString(FlagCommissionRate) 111 if commissionRate != "" { 112 rate, err := sdk.NewDecFromStr(commissionRate) 113 if err != nil { 114 return fmt.Errorf("invalid new commission rate: %v", err) 115 } 116 117 newRate = &rate 118 } 119 120 var newMinSelfDelegation *sdk.Int 121 122 minSelfDelegationString, _ := cmd.Flags().GetString(FlagMinSelfDelegation) 123 if minSelfDelegationString != "" { 124 msb, ok := sdk.NewIntFromString(minSelfDelegationString) 125 if !ok { 126 return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer") 127 } 128 129 newMinSelfDelegation = &msb 130 } 131 132 msg := types.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation) 133 134 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 135 }, 136 } 137 138 cmd.Flags().AddFlagSet(flagSetDescriptionEdit()) 139 cmd.Flags().AddFlagSet(flagSetCommissionUpdate()) 140 cmd.Flags().AddFlagSet(FlagSetMinSelfDelegation()) 141 flags.AddTxFlagsToCmd(cmd) 142 143 return cmd 144 } 145 146 func NewDelegateCmd() *cobra.Command { 147 bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() 148 149 cmd := &cobra.Command{ 150 Use: "delegate [validator-addr] [amount]", 151 Args: cobra.ExactArgs(2), 152 Short: "Delegate liquid tokens to a validator", 153 Long: strings.TrimSpace( 154 fmt.Sprintf(`Delegate an amount of liquid coins to a validator from your wallet. 155 156 Example: 157 $ %s tx staking delegate %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 1000stake --from mykey 158 `, 159 version.AppName, bech32PrefixValAddr, 160 ), 161 ), 162 RunE: func(cmd *cobra.Command, args []string) error { 163 clientCtx, err := client.GetClientTxContext(cmd) 164 if err != nil { 165 return err 166 } 167 amount, err := sdk.ParseCoinNormalized(args[1]) 168 if err != nil { 169 return err 170 } 171 172 delAddr := clientCtx.GetFromAddress() 173 valAddr, err := sdk.ValAddressFromBech32(args[0]) 174 if err != nil { 175 return err 176 } 177 178 msg := types.NewMsgDelegate(delAddr, valAddr, amount) 179 180 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 181 }, 182 } 183 184 flags.AddTxFlagsToCmd(cmd) 185 186 return cmd 187 } 188 189 func NewRedelegateCmd() *cobra.Command { 190 bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() 191 192 cmd := &cobra.Command{ 193 Use: "redelegate [src-validator-addr] [dst-validator-addr] [amount]", 194 Short: "Redelegate illiquid tokens from one validator to another", 195 Args: cobra.ExactArgs(3), 196 Long: strings.TrimSpace( 197 fmt.Sprintf(`Redelegate an amount of illiquid staking tokens from one validator to another. 198 199 Example: 200 $ %s tx staking redelegate %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj %s1l2rsakp388kuv9k8qzq6lrm9taddae7fpx59wm 100stake --from mykey 201 `, 202 version.AppName, bech32PrefixValAddr, bech32PrefixValAddr, 203 ), 204 ), 205 RunE: func(cmd *cobra.Command, args []string) error { 206 clientCtx, err := client.GetClientTxContext(cmd) 207 if err != nil { 208 return err 209 } 210 delAddr := clientCtx.GetFromAddress() 211 valSrcAddr, err := sdk.ValAddressFromBech32(args[0]) 212 if err != nil { 213 return err 214 } 215 216 valDstAddr, err := sdk.ValAddressFromBech32(args[1]) 217 if err != nil { 218 return err 219 } 220 221 amount, err := sdk.ParseCoinNormalized(args[2]) 222 if err != nil { 223 return err 224 } 225 226 msg := types.NewMsgBeginRedelegate(delAddr, valSrcAddr, valDstAddr, amount) 227 228 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 229 }, 230 } 231 232 flags.AddTxFlagsToCmd(cmd) 233 234 return cmd 235 } 236 237 func NewUnbondCmd() *cobra.Command { 238 bech32PrefixValAddr := sdk.GetConfig().GetBech32ValidatorAddrPrefix() 239 240 cmd := &cobra.Command{ 241 Use: "unbond [validator-addr] [amount]", 242 Short: "Unbond shares from a validator", 243 Args: cobra.ExactArgs(2), 244 Long: strings.TrimSpace( 245 fmt.Sprintf(`Unbond an amount of bonded shares from a validator. 246 247 Example: 248 $ %s tx staking unbond %s1gghjut3ccd8ay0zduzj64hwre2fxs9ldmqhffj 100stake --from mykey 249 `, 250 version.AppName, bech32PrefixValAddr, 251 ), 252 ), 253 RunE: func(cmd *cobra.Command, args []string) error { 254 clientCtx, err := client.GetClientTxContext(cmd) 255 if err != nil { 256 return err 257 } 258 delAddr := clientCtx.GetFromAddress() 259 valAddr, err := sdk.ValAddressFromBech32(args[0]) 260 if err != nil { 261 return err 262 } 263 264 amount, err := sdk.ParseCoinNormalized(args[1]) 265 if err != nil { 266 return err 267 } 268 269 msg := types.NewMsgUndelegate(delAddr, valAddr, amount) 270 271 return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 272 }, 273 } 274 275 flags.AddTxFlagsToCmd(cmd) 276 277 return cmd 278 } 279 280 func newBuildCreateValidatorMsg(clientCtx client.Context, txf tx.Factory, fs *flag.FlagSet) (tx.Factory, *types.MsgCreateValidator, error) { 281 fAmount, _ := fs.GetString(FlagAmount) 282 amount, err := sdk.ParseCoinNormalized(fAmount) 283 if err != nil { 284 return txf, nil, err 285 } 286 287 valAddr := clientCtx.GetFromAddress() 288 pkStr, err := fs.GetString(FlagPubKey) 289 if err != nil { 290 return txf, nil, err 291 } 292 293 var pk cryptotypes.PubKey 294 if err := clientCtx.Codec.UnmarshalInterfaceJSON([]byte(pkStr), &pk); err != nil { 295 return txf, nil, err 296 } 297 298 moniker, _ := fs.GetString(FlagMoniker) 299 identity, _ := fs.GetString(FlagIdentity) 300 website, _ := fs.GetString(FlagWebsite) 301 security, _ := fs.GetString(FlagSecurityContact) 302 details, _ := fs.GetString(FlagDetails) 303 description := types.NewDescription( 304 moniker, 305 identity, 306 website, 307 security, 308 details, 309 ) 310 311 // get the initial validator commission parameters 312 rateStr, _ := fs.GetString(FlagCommissionRate) 313 maxRateStr, _ := fs.GetString(FlagCommissionMaxRate) 314 maxChangeRateStr, _ := fs.GetString(FlagCommissionMaxChangeRate) 315 316 commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) 317 if err != nil { 318 return txf, nil, err 319 } 320 321 // get the initial validator min self delegation 322 msbStr, _ := fs.GetString(FlagMinSelfDelegation) 323 324 minSelfDelegation, ok := sdk.NewIntFromString(msbStr) 325 if !ok { 326 return txf, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer") 327 } 328 329 msg, err := types.NewMsgCreateValidator( 330 sdk.ValAddress(valAddr), pk, amount, description, commissionRates, minSelfDelegation, 331 ) 332 if err != nil { 333 return txf, nil, err 334 } 335 if err := msg.ValidateBasic(); err != nil { 336 return txf, nil, err 337 } 338 339 genOnly, _ := fs.GetBool(flags.FlagGenerateOnly) 340 if genOnly { 341 ip, _ := fs.GetString(FlagIP) 342 nodeID, _ := fs.GetString(FlagNodeID) 343 344 if nodeID != "" && ip != "" { 345 txf = txf.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) 346 } 347 } 348 349 return txf, msg, nil 350 } 351 352 // Return the flagset, particular flags, and a description of defaults 353 // this is anticipated to be used with the gen-tx 354 func CreateValidatorMsgFlagSet(ipDefault string) (fs *flag.FlagSet, defaultsDesc string) { 355 fsCreateValidator := flag.NewFlagSet("", flag.ContinueOnError) 356 fsCreateValidator.String(FlagIP, ipDefault, "The node's public IP") 357 fsCreateValidator.String(FlagNodeID, "", "The node's NodeID") 358 fsCreateValidator.String(FlagMoniker, "", "The validator's (optional) moniker") 359 fsCreateValidator.String(FlagWebsite, "", "The validator's (optional) website") 360 fsCreateValidator.String(FlagSecurityContact, "", "The validator's (optional) security contact email") 361 fsCreateValidator.String(FlagDetails, "", "The validator's (optional) details") 362 fsCreateValidator.String(FlagIdentity, "", "The (optional) identity signature (ex. UPort or Keybase)") 363 fsCreateValidator.AddFlagSet(FlagSetCommissionCreate()) 364 fsCreateValidator.AddFlagSet(FlagSetMinSelfDelegation()) 365 fsCreateValidator.AddFlagSet(FlagSetAmount()) 366 fsCreateValidator.AddFlagSet(FlagSetPublicKey()) 367 368 defaultsDesc = fmt.Sprintf(` 369 delegation amount: %s 370 commission rate: %s 371 commission max rate: %s 372 commission max change rate: %s 373 minimum self delegation: %s 374 `, defaultAmount, defaultCommissionRate, 375 defaultCommissionMaxRate, defaultCommissionMaxChangeRate, 376 defaultMinSelfDelegation) 377 378 return fsCreateValidator, defaultsDesc 379 } 380 381 type TxCreateValidatorConfig struct { 382 ChainID string 383 NodeID string 384 Moniker string 385 386 Amount string 387 388 CommissionRate string 389 CommissionMaxRate string 390 CommissionMaxChangeRate string 391 MinSelfDelegation string 392 393 PubKey cryptotypes.PubKey 394 395 IP string 396 Website string 397 SecurityContact string 398 Details string 399 Identity string 400 } 401 402 func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, chainID string, valPubKey cryptotypes.PubKey) (TxCreateValidatorConfig, error) { 403 c := TxCreateValidatorConfig{} 404 405 ip, err := flagSet.GetString(FlagIP) 406 if err != nil { 407 return c, err 408 } 409 if ip == "" { 410 _, _ = fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ 411 "the tx's memo field will be unset") 412 } 413 c.IP = ip 414 415 website, err := flagSet.GetString(FlagWebsite) 416 if err != nil { 417 return c, err 418 } 419 c.Website = website 420 421 securityContact, err := flagSet.GetString(FlagSecurityContact) 422 if err != nil { 423 return c, err 424 } 425 c.SecurityContact = securityContact 426 427 details, err := flagSet.GetString(FlagDetails) 428 if err != nil { 429 return c, err 430 } 431 c.SecurityContact = details 432 433 identity, err := flagSet.GetString(FlagIdentity) 434 if err != nil { 435 return c, err 436 } 437 c.Identity = identity 438 439 c.Amount, err = flagSet.GetString(FlagAmount) 440 if err != nil { 441 return c, err 442 } 443 444 c.CommissionRate, err = flagSet.GetString(FlagCommissionRate) 445 if err != nil { 446 return c, err 447 } 448 449 c.CommissionMaxRate, err = flagSet.GetString(FlagCommissionMaxRate) 450 if err != nil { 451 return c, err 452 } 453 454 c.CommissionMaxChangeRate, err = flagSet.GetString(FlagCommissionMaxChangeRate) 455 if err != nil { 456 return c, err 457 } 458 459 c.MinSelfDelegation, err = flagSet.GetString(FlagMinSelfDelegation) 460 if err != nil { 461 return c, err 462 } 463 464 c.NodeID = nodeID 465 c.PubKey = valPubKey 466 c.Website = website 467 c.SecurityContact = securityContact 468 c.Details = details 469 c.Identity = identity 470 c.ChainID = chainID 471 c.Moniker = moniker 472 473 if c.Amount == "" { 474 c.Amount = defaultAmount 475 } 476 477 if c.CommissionRate == "" { 478 c.CommissionRate = defaultCommissionRate 479 } 480 481 if c.CommissionMaxRate == "" { 482 c.CommissionMaxRate = defaultCommissionMaxRate 483 } 484 485 if c.CommissionMaxChangeRate == "" { 486 c.CommissionMaxChangeRate = defaultCommissionMaxChangeRate 487 } 488 489 if c.MinSelfDelegation == "" { 490 c.MinSelfDelegation = defaultMinSelfDelegation 491 } 492 493 return c, nil 494 } 495 496 // BuildCreateValidatorMsg makes a new MsgCreateValidator. 497 func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr tx.Factory, generateOnly bool) (tx.Factory, sdk.Msg, error) { 498 amounstStr := config.Amount 499 amount, err := sdk.ParseCoinNormalized(amounstStr) 500 if err != nil { 501 return txBldr, nil, err 502 } 503 504 valAddr := clientCtx.GetFromAddress() 505 description := types.NewDescription( 506 config.Moniker, 507 config.Identity, 508 config.Website, 509 config.SecurityContact, 510 config.Details, 511 ) 512 513 // get the initial validator commission parameters 514 rateStr := config.CommissionRate 515 maxRateStr := config.CommissionMaxRate 516 maxChangeRateStr := config.CommissionMaxChangeRate 517 commissionRates, err := buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr) 518 if err != nil { 519 return txBldr, nil, err 520 } 521 522 // get the initial validator min self delegation 523 msbStr := config.MinSelfDelegation 524 minSelfDelegation, ok := sdk.NewIntFromString(msbStr) 525 526 if !ok { 527 return txBldr, nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "minimum self delegation must be a positive integer") 528 } 529 530 msg, err := types.NewMsgCreateValidator( 531 sdk.ValAddress(valAddr), config.PubKey, amount, description, commissionRates, minSelfDelegation, 532 ) 533 if err != nil { 534 return txBldr, msg, err 535 } 536 if generateOnly { 537 ip := config.IP 538 nodeID := config.NodeID 539 540 if nodeID != "" && ip != "" { 541 txBldr = txBldr.WithMemo(fmt.Sprintf("%s@%s:26656", nodeID, ip)) 542 } 543 } 544 545 return txBldr, msg, nil 546 }