feat: CLI tooling to generate proposal JSONs (#13304)

This commit is contained in:
Julien Robert 2022-09-19 14:22:04 +02:00 committed by GitHub
parent 73debf46d9
commit 7252f4a758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 457 additions and 19 deletions

View File

@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
* (cli) [#13304](https://github.com/cosmos/cosmos-sdk/pull/13304) Add `tx gov draft-proposal` command for generating proposal JSONs.
* (cli) [#13207](https://github.com/cosmos/cosmos-sdk/pull/13207) Reduce user's password prompts when calling keyring `List()` function
* (x/authz) [#12648](https://github.com/cosmos/cosmos-sdk/pull/12648) Add an allow list, an optional list of addresses allowed to receive bank assets via authz MsgSend grant.
* (sdk.Coins) [#12627](https://github.com/cosmos/cosmos-sdk/pull/12627) Make a Denoms method on sdk.Coins.

View File

@ -4,6 +4,7 @@ package nftv1beta1
import (
_ "cosmossdk.io/api/cosmos/msg/v1"
fmt "fmt"
_ "github.com/cosmos/cosmos-proto"
runtime "github.com/cosmos/cosmos-proto/runtime"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoiface "google.golang.org/protobuf/runtime/protoiface"
@ -1091,14 +1092,19 @@ var file_cosmos_nft_v1beta1_tx_proto_rawDesc = []byte{
0x0a, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6e, 0x66, 0x74, 0x2f, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61,
0x31, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31,
0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x75, 0x0a, 0x07, 0x4d, 0x73,
0x67, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x64,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65,
0x69, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65,
0x31, 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x63, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73, 0x67, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x01, 0x0a, 0x07, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e,
0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x06,
0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4,
0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x34,
0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65,
0x69, 0x76, 0x65, 0x72, 0x3a, 0x0b, 0x82, 0xe7, 0xb0, 0x2a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65,
0x72, 0x22, 0x11, 0x0a, 0x0f, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x32, 0x56, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x48, 0x0a, 0x04, 0x53,

57
client/prompts.go Normal file
View File

@ -0,0 +1,57 @@
package client
import (
"fmt"
"net/url"
"unicode"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// ValidatePromptNotEmpty validates that the input is not empty.
func ValidatePromptNotEmpty(input string) error {
if input == "" {
return fmt.Errorf("input cannot be empty")
}
return nil
}
// ValidatePromptURL validates that the input is a valid URL.
func ValidatePromptURL(input string) error {
_, err := url.ParseRequestURI(input)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}
return nil
}
// ValidatePromptAddress validates that the input is a valid Bech32 address.
func ValidatePromptAddress(input string) error {
if _, err := sdk.AccAddressFromBech32(input); err != nil {
return fmt.Errorf("invalid address: %w", err)
}
return nil
}
// ValidatePromptYesNo validates that the input is valid sdk.COins
func ValidatePromptCoins(input string) error {
if _, err := sdk.ParseCoinsNormalized(input); err != nil {
return fmt.Errorf("invalid coins: %w", err)
}
return nil
}
// CamelCaseToString converts a camel case string to a string with spaces.
func CamelCaseToString(str string) string {
w := []rune(str)
for i := len(w) - 1; i > 1; i-- {
if unicode.IsUpper(w[i]) {
w = append(w[:i], append([]rune{' '}, w[i:]...)...)
}
}
return string(w)
}

2
go.mod
View File

@ -37,6 +37,7 @@ require (
github.com/jhump/protoreflect v1.12.1-0.20220721211354-060cc04fc18b
github.com/lazyledger/smt v0.2.1-0.20210709230900-03ea40719554
github.com/magiconair/properties v1.8.6
github.com/manifoldco/promptui v0.9.0
github.com/mattn/go-isatty v0.0.16
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.13.0
@ -75,6 +76,7 @@ require (
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/ledger-go v0.9.2 // indirect
github.com/creachadair/taskgroup v0.3.2 // indirect

5
go.sum
View File

@ -147,8 +147,11 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
@ -560,6 +563,8 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=

View File

@ -3,6 +3,7 @@ package cosmos.nft.v1beta1;
option go_package = "github.com/cosmos/cosmos-sdk/x/nft";
import "cosmos_proto/cosmos.proto";
import "cosmos/msg/v1/msg.proto";
// Msg defines the nft Msg service.
@ -24,10 +25,10 @@ message MsgSend {
string id = 2;
// sender is the address of the owner of nft
string sender = 3;
string sender = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// receiver is the receiver address of nft
string receiver = 4;
string receiver = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}
// MsgSendResponse defines the Msg/Send response type.
message MsgSendResponse {}

View File

@ -37,6 +37,7 @@ require (
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/coinbase/rosetta-sdk-go v0.8.0 // indirect
github.com/confio/ics23/go v0.7.0 // indirect
@ -97,6 +98,7 @@ require (
github.com/lib/pq v1.10.6 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect

View File

@ -147,8 +147,11 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
@ -548,6 +551,8 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=

View File

@ -36,6 +36,7 @@ require (
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
github.com/coinbase/rosetta-sdk-go v0.8.0 // indirect
github.com/confio/ics23/go v0.7.0 // indirect
@ -96,6 +97,7 @@ require (
github.com/lib/pq v1.10.6 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect

View File

@ -147,8 +147,11 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
@ -550,6 +553,8 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=

View File

@ -35,11 +35,15 @@ The following specification uses *ATOM* as the native staking token. The module
can be adapted to any Proof-Of-Stake blockchain by replacing *ATOM* with the native
staking token of the chain.
* [`x/gov`](#xgov)
* [Abstract](#abstract)
* [Contents](#contents)
* [Concepts](#concepts)
* [Proposal submission](#proposal-submission)
* [Right to submit a proposal](#right-to-submit-a-proposal)
* [Proposal Messages](#proposal-messages)
* [Deposit](#deposit)
* [Deposit refund and burn](#deposit-refund-and-burn)
* [Vote](#vote)
* [Participants](#participants)
* [Voting period](#voting-period)
@ -51,8 +55,11 @@ staking token of the chain.
* [Validators punishment for non-voting](#validators-punishment-for-non-voting)
* [Governance address](#governance-address)
* [Software Upgrade](#software-upgrade)
* [Signal](#signal)
* [Switch](#switch)
* [State](#state)
* [Proposals](#proposals)
* [Writing a module that uses governance](#writing-a-module-that-uses-governance)
* [Parameters and base types](#parameters-and-base-types)
* [DepositParams](#depositparams)
* [VotingParams](#votingparams)
@ -75,11 +82,48 @@ staking token of the chain.
* [MsgDeposit](#msgdeposit)
* [Future Improvements](#future-improvements)
* [Parameters](#parameters)
* [SubKeys](#subkeys)
* [Client](#client)
* [CLI](#cli)
* [Query](#query)
* [deposit](#deposit-3)
* [deposits](#deposits)
* [param](#param)
* [params](#params)
* [proposal](#proposal)
* [proposals](#proposals-1)
* [proposer](#proposer)
* [tally](#tally)
* [vote](#vote-2)
* [votes](#votes)
* [Transactions](#transactions)
* [deposit](#deposit-4)
* [draft-proposal](#draft-proposal)
* [submit-proposal](#submit-proposal)
* [submit-legacy-proposal](#submit-legacy-proposal)
* [vote](#vote-3)
* [weighted-vote](#weighted-vote)
* [gRPC](#grpc)
* [Proposal](#proposal-1)
* [Proposals](#proposals-2)
* [Vote](#vote-4)
* [Votes](#votes-1)
* [Params](#params-1)
* [Deposit](#deposit-5)
* [deposits](#deposits-1)
* [TallyResult](#tallyresult)
* [REST](#rest)
* [proposal](#proposal-2)
* [proposals](#proposals-3)
* [voter vote](#voter-vote)
* [votes](#votes-2)
* [params](#params-2)
* [deposits](#deposits-2)
* [proposal deposits](#proposal-deposits)
* [tally](#tally-1)
* [Metadata](#metadata)
* [Proposal](#proposal-3)
* [Vote](#vote-5)
<!-- order: 1 -->
@ -1149,6 +1193,16 @@ Example:
simd tx gov deposit 1 10000000stake --from cosmos1..
```
#### draft-proposal
The `draft-proposal` command allows users to draft any type of proposal.
The command returns a `draft_proposal.json`, to be used by `submit-proposal` after being completed.
The `draft_metadata.json` is meant to be uploaded to [IPFS](#metadata).
```bash
simd tx gov draft-proposal
```
#### submit-proposal
The `submit-proposal` command allows users to submit a governance proposal along with some messages and metadata.
@ -2612,8 +2666,8 @@ Location: off-chain as json object stored on IPFS (mirrors [group proposal](../.
"authors": "",
"summary": "",
"details": "",
"proposalForumURL": "",
"voteOptionContext": "",
"proposal_forum_url": "",
"vote_option_context": "",
}
```

297
x/gov/client/cli/prompt.go Normal file
View File

@ -0,0 +1,297 @@
package cli
import (
"encoding/json"
"fmt"
"os"
"reflect"
"sort"
"strconv"
"strings"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
const (
proposalText = "text"
proposalOther = "other"
draftProposalFileName = "draft_proposal.json"
draftMetadataFileName = "draft_metadata.json"
)
// ProposalMetadata is the metadata of a proposal
// This metadata is supposed to live off-chain when submitted in a proposal
type ProposalMetadata struct {
Title string `json:"title"`
Authors string `json:"authors"`
Summary string `json:"summary"`
Details string `json:"details"`
ProposalForumUrl string `json:"proposal_forum_url"` // named 'Url' instead of 'URL' for avoiding the camel case split
VoteOptionContext string `json:"vote_option_context"`
}
// Prompt prompts the user for all values of the given type.
// data is the struct to be filled
// namePrefix is the name to be display as "Enter <namePrefix> <field>"
func Prompt[T any](data T, namePrefix string) (T, error) {
v := reflect.ValueOf(&data).Elem()
if v.Kind() == reflect.Interface {
v = reflect.ValueOf(data)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
}
for i := 0; i < v.NumField(); i++ {
if v.Field(i).Kind() == reflect.Struct || v.Field(i).Kind() == reflect.Slice {
// if the field is a struct skip
// in a future we can add a recursive call to Prompt
continue
}
// create prompts
prompt := promptui.Prompt{
Label: fmt.Sprintf("Enter %s %s", namePrefix, strings.ToLower(client.CamelCaseToString(v.Type().Field(i).Name))),
Validate: client.ValidatePromptNotEmpty,
}
fieldName := strings.ToLower(v.Type().Field(i).Name)
// validation per field name
if strings.Contains(fieldName, "url") {
prompt.Validate = client.ValidatePromptURL
}
if strings.EqualFold(fieldName, "authority") {
// pre-fill with gov address
prompt.Default = authtypes.NewModuleAddress(types.ModuleName).String()
prompt.Validate = client.ValidatePromptAddress
}
if strings.Contains(fieldName, "addr") ||
strings.Contains(fieldName, "sender") ||
strings.Contains(fieldName, "voter") ||
strings.Contains(fieldName, "depositor") ||
strings.Contains(fieldName, "granter") ||
strings.Contains(fieldName, "grantee") ||
strings.Contains(fieldName, "recipient") {
prompt.Validate = client.ValidatePromptAddress
}
result, err := prompt.Run()
if err != nil {
return data, fmt.Errorf("failed to prompt for %s: %w", fieldName, err)
}
switch v.Field(i).Kind() {
case reflect.String:
v.Field(i).SetString(result)
case reflect.Int:
resultInt, _ := strconv.Atoi(result)
v.Field(i).SetInt(int64(resultInt))
default:
// skip other types
// possibly in the future we can add more types (like slices)
continue
}
}
return data, nil
}
type proposalTypes struct {
Type string
MsgType string
Msg sdk.Msg
}
// Prompt the proposal type values and return the proposal and its metadata
func (p *proposalTypes) Prompt(cdc codec.Codec) (*proposal, ProposalMetadata, error) {
proposal := &proposal{}
// set metadata
metadata, err := Prompt(ProposalMetadata{}, "proposal")
if err != nil {
return nil, metadata, fmt.Errorf("failed to set proposal metadata: %w", err)
}
proposal.Metadata = "ipfs://CID"
// set deposit
depositPrompt := promptui.Prompt{
Label: "Enter proposal deposit",
Validate: client.ValidatePromptCoins,
}
proposal.Deposit, err = depositPrompt.Run()
if err != nil {
return nil, metadata, fmt.Errorf("failed to set proposal deposit: %w", err)
}
if p.Msg == nil {
return proposal, metadata, nil
}
// set messages field
result, err := Prompt(p.Msg, "msg")
if err != nil {
return nil, metadata, fmt.Errorf("failed to set proposal message: %w", err)
}
message, err := cdc.MarshalInterfaceJSON(result)
if err != nil {
return nil, metadata, fmt.Errorf("failed to marshal proposal message: %w", err)
}
proposal.Messages = append(proposal.Messages, message)
return proposal, metadata, nil
}
var supportedProposalTypes = []proposalTypes{
{
Type: proposalText,
MsgType: "", // no message for text proposal
},
{
Type: "community-pool-spend",
MsgType: "/cosmos.distribution.v1beta1.MsgCommunityPoolSpend",
},
{
Type: "software-upgrade",
MsgType: "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade",
},
{
Type: "cancel-software-upgrade",
MsgType: "/cosmos.upgrade.v1beta1.MsgCancelUpgrade",
},
{
Type: proposalOther,
MsgType: "", // user will input the message type
},
}
func getProposalTypes() []string {
types := make([]string, len(supportedProposalTypes))
for i, p := range supportedProposalTypes {
types[i] = p.Type
}
return types
}
func getProposalMsg(cdc codec.Codec, input string) (sdk.Msg, error) {
var msg sdk.Msg
bz, err := json.Marshal(struct {
Type string `json:"@type"`
}{
Type: input,
})
if err != nil {
return nil, err
}
if err := cdc.UnmarshalInterfaceJSON(bz, &msg); err != nil {
return nil, fmt.Errorf("failed to determined sdk.Msg from %s proposal type : %w", input, err)
}
return msg, nil
}
// NewCmdDraftProposal let a user generate a draft proposal.
func NewCmdDraftProposal() *cobra.Command {
cmd := &cobra.Command{
Use: "draft-proposal",
Short: "Generate a draft proposal json file. The generated proposal json contains only one message.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
// prompt proposal type
proposalTypesPrompt := promptui.Select{
Label: "Select proposal type",
Items: getProposalTypes(),
}
_, proposalType, err := proposalTypesPrompt.Run()
if err != nil {
return fmt.Errorf("failed to prompt proposal types: %w", err)
}
var proposal proposalTypes
for _, p := range supportedProposalTypes {
if strings.EqualFold(p.Type, proposalType) {
proposal = p
break
}
}
// create any proposal type
if proposal.Type == proposalOther {
// prompt proposal type
msgPrompt := promptui.Select{
Label: "Select proposal message type:",
Items: func() []string {
msgs := clientCtx.InterfaceRegistry.ListImplementations(sdk.MsgInterfaceProtoName)
sort.Strings(msgs)
return msgs
}(),
}
_, result, err := msgPrompt.Run()
if err != nil {
return fmt.Errorf("failed to prompt proposal types: %w", err)
}
proposal.MsgType = result
}
if proposal.MsgType != "" {
proposal.Msg, err = getProposalMsg(clientCtx.Codec, proposal.MsgType)
if err != nil {
// should never happen
panic(err)
}
}
prop, metadata, err := proposal.Prompt(clientCtx.Codec)
if err != nil {
return err
}
if err := writeFile(draftMetadataFileName, metadata); err != nil {
return err
}
if err := writeFile(draftProposalFileName, prop); err != nil {
return err
}
fmt.Printf("Your draft proposal has successfully been generated.\nProposals should contain off-chain metadata, please upload the metadata JSON to IPFS.\nThen, replace the generated metadata field with the IPFS CID.\n")
return nil
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
func writeFile(fileName string, input any) error {
raw, err := json.MarshalIndent(input, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal proposal: %w", err)
}
if err := os.WriteFile(fileName, raw, 0o600); err != nil {
return err
}
return nil
}

View File

@ -70,6 +70,7 @@ func NewTxCmd(legacyPropCmds []*cobra.Command) *cobra.Command {
NewCmdVote(),
NewCmdWeightedVote(),
NewCmdSubmitProposal(),
NewCmdDraftProposal(),
// Deprecated
cmdSubmitLegacyProp,

View File

@ -77,9 +77,9 @@ func parseSubmitLegacyProposalFlags(fs *pflag.FlagSet) (*legacyProposal, error)
// proposal defines the new Msg-based proposal.
type proposal struct {
// Msgs defines an array of sdk.Msgs proto-JSON-encoded as Anys.
Messages []json.RawMessage
Metadata string
Deposit string
Messages []json.RawMessage `json:"messages,omitempty"`
Metadata string `json:"metadata"`
Deposit string `json:"deposit"`
}
func parseSubmitProposal(cdc codec.Codec, path string) ([]sdk.Msg, string, sdk.Coins, error) {

View File

@ -2073,8 +2073,8 @@ Location: off-chain as json object stored on IPFS (mirrors [gov proposal](../../
"authors": "",
"summary": "",
"details": "",
"proposalForumURL": "",
"voteOptionContext": "",
"proposal_forum_url": "",
"vote_option_context": "",
}
```
@ -2096,8 +2096,8 @@ Location: off-chain as json object stored on IPFS
{
"name": "",
"description": "",
"groupWebsiteURL": "",
"groupForumURL": "",
"group_website_url": "",
"group_forum_url": "",
}
```