如需了解 CLI、gRPC 和 REST 在 Cosmos SDK 应用中如何协同工作的概念性概览,请参阅 CLI、gRPC 与 REST。

概览

autocli 会为你的 gRPC 服务中定义的每个方法生成 CLI 命令和标志。默认情况下,它会为每个 gRPC 服务方法生成一个命令。这些命令的名称基于服务方法名。 例如,给定如下服务的 protobuf 定义:
service MyService {
  rpc MyMethod(MyRequest) returns (MyResponse) {}
}
autocli 包会为 MyMethod 方法生成一个名为 my-method 的命令。该命令会为 MyRequest 消息中的每个字段生成对应的标志。 你也可以通过为每个服务定义选项,自定义交易和查询命令的生成。

应用接线

以下是使用 AutoCLI 的步骤:
  1. 确保你应用中的模块实现了 appmodule.AppModule 接口。
  2. (可选)通过在模块上实现 func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions 方法,配置 autocli 在命令生成期间的行为。
  3. 调用 app.AutoCliOpts() 获取一个由模块管理器填充的 autocli.AppOptions,然后为其设置 ClientCtx 以接入 keyring。
  4. 调用 EnhanceRootCommand(),将生成的 CLI 命令添加到根命令中。
AutoCLI 只做增量增强,这意味着对根命令执行增强时,只会添加尚未注册的子命令。这意味着你可以在应用中将 AutoCLI 与其他自定义命令一起使用。
实际效果如下所示(摘自 example chain):
autoCliOpts := app.AutoCliOpts()
autoCliOpts.ClientCtx = initClientCtx  // wires keyring + node connection

if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
    panic(err)
}

Keyring

AutoCLI 使用 client.Context 中的 keyring 来解析密钥名并对交易进行签名。在运行时,它会从命令的活动上下文中读取 keyring(由 PersistentPreRunE 中的 SetCmdClientContextHandler 设置,参见根命令设置),并在内部通过 keyring.NewAutoCLIKeyring 将其适配为 cosmossdk.io/client/v2/autocli/keyring 接口。 如果未提供 keyring,AutoCLI 生成的命令仍然可以查询链,但无法对交易签名。
由于 AutoCLI 会从 keyring 中解析密钥名,因此你可以直接使用账户名,而不是地址:
<appd> q bank balances alice
<appd> tx bank send alice bob 1000denom

签名

autocli 支持使用 keyring 对交易进行签名。 cosmos.msg.v1.signer protobuf 注解定义了消息的签名者字段。 使用 --from 标志或将签名者定义为位置参数时,该字段会被自动填充。
AutoCLI 当前每笔交易只支持一个签名者。

模块接线与自定义

模块上的 AutoCLIOptions() 方法允许你在 RpcCommandOptions 结构体中像配置 cobra.Command 实例一样,为每个服务指定自定义命令、子命令或标志。定义这些选项后,可以自定义 autocli 的命令生成行为;默认情况下,它会为你的 gRPC 服务中的每个方法生成一个命令。
autocliv1.RpcCommandOptions{
    RpcMethod: "Params", // The name of the gRPC service
  Use:       "params", // Command usage that is displayed in the help
  Short:     "Query the parameters of the governance process", // Short description of the command
  Long:      "Query the parameters of the governance process. Specify specific param types (voting|tallying|deposit)

to filter results.", // Long description of the command
  PositionalArgs: []*autocliv1.PositionalArgDescriptor{
    {
    ProtoField: "params_type",
    Optional: true
}, // Transform a flag into a positional argument
},
}
AutoCLI 只需在 autocliv1.RpcCommandOptions 结构体中将 GovProposal 字段设置为 true,就可以为任意 tx 创建 gov proposal。 不过,用户也可以使用 --no-proposal 标志禁用 proposal 创建(当某条链上的 authority 不是 gov 模块时,这会很有用)。

指定子命令

默认情况下,autocli 会为你的 gRPC 服务中的每个方法生成一个命令。不过,你可以通过指定子命令将相关命令组织在一起。要指定子命令,请使用 autocliv1.ServiceCommandDescriptor 结构体。 如需查看真实示例,请参阅 Cosmos SDK 中 gov 模块的 autocli.go。该文件在同一处演示了 ServiceCommandDescriptor、RpcCommandOptions、PositionalArgs、SubCommands、EnhanceCustomCommand 和 GovProposal 的用法。

位置参数

默认情况下,autocli 会为 protobuf 消息中的每个字段生成一个标志。不过,你也可以选择对某些字段使用位置参数,而不是标志。 要为命令添加位置参数,请使用 autocliv1.PositionalArgDescriptor 结构体,如下面的示例所示。指定 ProtoField 参数,它表示应作为位置参数使用的 protobuf 字段名称。此外,如果该参数是可变长度参数,你可以将 Varargs 参数指定为 true。这只能应用于最后一个位置参数,并且 ProtoField 必须是一个重复字段。 如需查看真实示例,请参阅 Cosmos SDK 中 auth 模块的 autocli.go。它展示了如何为每个查询方法接入位置参数,并将 Account 方法中的 address 设为位置参数。 接入位置参数后,可以像下面这样使用该命令,而不必指定 --address 标志:
<appd> query auth account cosmos1abcd...xyz

位置参数中的扁平化字段

AutoCLI 还支持将嵌套消息字段扁平化为位置参数。这意味着你可以在 ProtoField 参数中使用点表示法访问嵌套字段。当你希望直接将嵌套消息字段设置为位置参数时,这项功能尤其有用。 例如,如果你有如下嵌套消息结构:
message Permissions {
    string level = 1;
    repeated string limit_type_urls = 2;
}

message MsgAuthorizeCircuitBreaker {
    string grantee = 1;
    Permissions permissions = 2;
}
你可以在 AutoCLI 配置中将这些字段扁平化:
{
    RpcMethod: "AuthorizeCircuitBreaker",
    Use:       "authorize [grantee] [level] [msg_type_urls]",
    PositionalArgs: []*autocliv1.PositionalArgDescriptor{
        {ProtoField: "grantee"},
        {ProtoField: "permissions.level"},
        {ProtoField: "permissions.limit_type_urls", Varargs: true},
    },
}
这样一来,用户就可以直接通过位置参数为嵌套字段提供值:
<appd> tx circuit authorize cosmos1... super-admin "/cosmos.bank.v1beta1.MsgSend" "/cosmos.bank.v1beta1.MsgMultiSend"
相比为嵌套字段提供复杂的 JSON 结构,扁平化可以让 CLI 直接访问嵌套字段,从而更加易用。

自定义标志名称

默认情况下,autocli 会根据 protobuf 消息中字段的名称生成标志名。不过,你可以通过提供 FlagOptions 来自定义标志名。该参数允许你根据消息字段名为标志指定自定义名称。 例如,如果你有一个包含 test 和 test1 字段的消息,可以使用以下命名选项来自定义标志:
autocliv1.RpcCommandOptions{
    FlagOptions: map[string]*autocliv1.FlagOptions{ 
        "test": {
    Name: "custom_name",
}, 
        "test1": {
    Name: "other_name",
},
},
}

在模块内将 AutoCLI 与其他命令结合使用

AutoCLI 可以与模块内的其他命令一起使用。例如,gov 模块将 AutoCLI 用于其查询命令,同时仍保留了为 submit-proposal、weighted-vote 等命令手写的 tx 命令。 在每个你希望 AutoCLI 与现有命令并存添加生成命令的 ServiceCommandDescriptor 上设置 EnhanceCustomCommand: true:
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
    return &autocliv1.ModuleOptions{
        Query: &autocliv1.ServiceCommandDescriptor{
            Service:              govv1.Query_ServiceDesc.ServiceName,
            EnhanceCustomCommand: true, // keep hand-written gov query commands
            RpcCommandOptions:    []*autocliv1.RpcCommandOptions{ /* ... */ },
        },
        Tx: &autocliv1.ServiceCommandDescriptor{
            Service:              govv1.Msg_ServiceDesc.ServiceName,
            EnhanceCustomCommand: true, // keep hand-written gov tx commands
        },
    }
}
如果 EnhanceCustomCommand 未设置为 true,对于任何已经通过 GetTxCmd() 或 GetQueryCmd() 注册了命令的服务,AutoCLI 都会跳过命令生成。

跳过某个命令

AutoCLI 会检查 cosmos_proto.method_added_in protobuf 注解,并跳过那些引入版本比当前运行的 SDK 版本更新的命令。 此外,也可以通过 autocliv1.RpcCommandOptions 手动跳过某个命令:
autocliv1.RpcCommandOptions{
    RpcMethod: "Params", // The name of the gRPC method
    Skip: true,
}

将 AutoCLI 用于非模块命令

也可以将 AutoCLI 用于非模块命令。常见模式是在调用 AutoCliOpts() 后,直接将选项添加到 autoCliOpts.ModuleOptions:
nodeCmds := nodeservice.NewNodeCommands()
autoCliOpts.ModuleOptions[nodeCmds.Name()] = nodeCmds.AutoCLIOptions()
AutoCliOpts() 只会提取已注册到模块管理器中的模块;非模块命令始终需要手动添加到 ModuleOptions 中,就像示例链中对 nodeservice.NewNodeCommands() 的处理一样。 如需查看这一模式的更完整示例,请参阅 Cosmos SDK 中的 client/grpc/cmtservice/autocli.go 和 client/grpc/node/autocli.go。

根命令设置

为了让 AutoCLI 生成的命令(以及手写命令)能够正常工作,包括签名交易、查询链和读取配置,根命令必须在 PersistentPreRunE 函数中设置 client.Context 和 server.Context。该函数会在每个子命令执行前运行,并使这两个上下文对所有子命令可用。完整示例请参阅 simapp/simd/cmd/root.go。 PersistentPreRun 中有两个关键调用:
  • SetCmdClientContextHandler 会通过 ReadPersistentCommandFlags 读取持久标志,创建一个 client.Context,并将其设置到命令上下文中。这正是 AutoCLI 和手写命令用来签名交易并连接节点的机制。
  • InterceptConfigsPreRunHandler 会创建 server.Context,从节点 home 目录加载 app.toml 和 config.toml,并将它们绑定到 server context 的 viper 实例上。这使得应用配置在启动时可用。

自定义 logger

默认情况下,InterceptConfigsPreRunHandler 会设置默认的 SDK logger。要使用自定义 logger,请改用 InterceptConfigsAndCreateContext,然后手动设置 logger:
-return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customCMTConfig)

+serverCtx, err := server.InterceptConfigsAndCreateContext(cmd, customAppTemplate, customAppConfig, customCMTConfig)
+if err != nil {
+	return err
+}
+
+// overwrite default server logger
+logger, err := server.CreateSDKLogger(serverCtx, cmd.OutOrStdout())
+if err != nil {
+	return err
+}
+serverCtx.Logger = logger.With(log.ModuleKey, "server")
+
+// set server context
+return server.SetCmdServerContext(cmd, serverCtx)

环境变量

每个 CLI flag 都会自动绑定到一个环境变量。该变量名由应用的 basename 转为大写后,加上 flag 名称组成,并将 - 替换为 _。例如,对于 basename 为 GAIA 的应用,--node 会绑定到 GAIA_NODE。 这样你就可以预先配置常用 flag,而不用在每次执行命令时都传入:

# set once in .env or shell profile
GAIA_HOME=<path to home>
GAIA_NODE=<node address>
GAIA_CHAIN_ID="cosmoshub-4"
GAIA_KEYRING_BACKEND="test"


# then just run
gaiad tx bank send alice bob 1000uatom --fees 500uatom

手写命令

AutoCLI 覆盖的是标准场景:一个 protobuf RPC 方法映射为一个 CLI 命令。对于不符合这一模型的命令,你可以手动编写 Cobra 命令,并通过 EnhanceCustomCommand: true 将其与 AutoCLI 结合使用。 需要手动编写命令的常见原因包括:
  • 复杂参数解析 — 在构建消息之前,需要对多个位置参数执行自定义校验或 coin 解析
  • 跨多个 RPC 调用的命令 — 例如,根据需要先执行查询得到的输入来构建交易
  • 非标准 UX — 交互式提示、离线签名流程,或生成输出而不是广播的命令

模式

一个手动交易命令会使用 client.GetClientTxContext 获取签名上下文,构造消息,然后将其传给 tx.GenerateOrBroadcastTxCLI:
func NewSendTxCmd(ac address.Codec) *cobra.Command {
    cmd := &cobra.Command{
        Use:   "send [from_key_or_address] [to_address] [amount]",
        Short: "Send tokens from one account to another",
        Args:  cobra.ExactArgs(3),
        RunE: func(cmd *cobra.Command, args []string) error {
            // set --from from the first positional arg
            if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil {
                return err
            }
            clientCtx, err := client.GetClientTxContext(cmd)
            if err != nil {
                return err
            }

            toAddr, err := ac.StringToBytes(args[1])
            if err != nil {
                return err
            }

            coins, err := sdk.ParseCoinsNormalized(args[2])
            if err != nil {
                return err
            }

            msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins)
            return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
        },
    }

    flags.AddTxFlagsToCmd(cmd)
    return cmd
}
关键要素:
  • client.GetClientTxContext(cmd) 获取客户端上下文(签名者、节点连接、codec)
  • flags.AddTxFlagsToCmd(cmd) 添加标准交易 flag(--from、--fees、--gas 等)
  • tx.GenerateOrBroadcastTxCLI 同时处理 --generate-only(离线)和实时广播模式

For a conceptual overview of how CLI, gRPC, and REST fit together in a Cosmos SDK app, see CLI, gRPC & REST.

Overview

autocli generates CLI commands and flags for each method defined in your gRPC service. By default, it generates a command for each gRPC service method. The commands are named based on the name of the service method. For example, given the following protobuf definition for a service:
service MyService {
  rpc MyMethod(MyRequest) returns (MyResponse) {}
}
The autocli package will generate a command named my-method for the MyMethod method. The command will have flags for each field in the MyRequest message. It is possible to customize the generation of transactions and queries by defining options for each service.

Application Wiring

Here are the steps to use AutoCLI:
  1. Ensure your app’s modules implement the appmodule.AppModule interface.
  2. (optional) Configure how autocli behaves during command generation, by implementing the func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions method on the module.
  3. Call app.AutoCliOpts() to get an autocli.AppOptions populated from the module manager, then set ClientCtx on it to wire in the keyring.
  4. Call EnhanceRootCommand() to add the generated CLI commands to your root command.
AutoCLI is additive only, meaning enhancing the root command will only add subcommands that are not already registered. This means that you can use AutoCLI alongside other custom commands within your app.
In practice this looks like (from the example chain):
autoCliOpts := app.AutoCliOpts()
autoCliOpts.ClientCtx = initClientCtx  // wires keyring + node connection

if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
    panic(err)
}

Keyring

AutoCLI resolves key names and signs transactions using the keyring from client.Context. At runtime, it reads the keyring from the command’s live context (set by SetCmdClientContextHandler in PersistentPreRunE — see Root Command Setup) and adapts it to the cosmossdk.io/client/v2/autocli/keyring interface via keyring.NewAutoCLIKeyring internally. If no keyring is provided, AutoCLI-generated commands can still query the chain but cannot sign transactions.
Because AutoCLI resolves key names from the keyring, you can use account names directly instead of addresses:
<appd> q bank balances alice
<appd> tx bank send alice bob 1000denom

Signing

autocli supports signing transactions with the keyring. The cosmos.msg.v1.signer protobuf annotation defines the signer field of the message. This field is automatically filled when using the --from flag or defining the signer as a positional argument.
AutoCLI currently supports only one signer per transaction.

Module wiring & Customization

The AutoCLIOptions() method on your module allows to specify custom commands, sub-commands or flags for each service, as it was a cobra.Command instance, within the RpcCommandOptions struct. Defining such options will customize the behavior of the autocli command generation, which by default generates a command for each method in your gRPC service.
autocliv1.RpcCommandOptions{
    RpcMethod: "Params", // The name of the gRPC service
  Use:       "params", // Command usage that is displayed in the help
  Short:     "Query the parameters of the governance process", // Short description of the command
  Long:      "Query the parameters of the governance process. Specify specific param types (voting|tallying|deposit)

to filter results.", // Long description of the command
  PositionalArgs: []*autocliv1.PositionalArgDescriptor{
    {
    ProtoField: "params_type",
    Optional: true
}, // Transform a flag into a positional argument
},
}
AutoCLI can create a gov proposal of any tx by simply setting the GovProposal field to true in the autocliv1.RpcCommandOptions struct. Users can however use the --no-proposal flag to disable the proposal creation (which is useful if the authority isn’t the gov module on a chain).

Specifying Subcommands

By default, autocli generates a command for each method in your gRPC service. However, you can specify subcommands to group related commands together. To specify subcommands, use the autocliv1.ServiceCommandDescriptor struct. For a real-world example, see the gov module’s autocli.go in the Cosmos SDK. It demonstrates ServiceCommandDescriptor with RpcCommandOptions, PositionalArgs, SubCommands, EnhanceCustomCommand, and GovProposal all in one file.

Positional Arguments

By default autocli generates a flag for each field in your protobuf message. However, you can choose to use positional arguments instead of flags for certain fields. To add positional arguments to a command, use the autocliv1.PositionalArgDescriptor struct, as seen in the example below. Specify the ProtoField parameter, which is the name of the protobuf field that should be used as the positional argument. In addition, if the parameter is a variable-length argument, you can specify the Varargs parameter as true. This can only be applied to the last positional parameter, and the ProtoField must be a repeated field. For a real-world example, see the auth module’s autocli.go in the Cosmos SDK. It shows positional args wired for every query method, with address as a positional argument on the Account method. After wiring positional args, the command can be used as follows, instead of having to specify the --address flag:
<appd> query auth account cosmos1abcd...xyz

Flattened Fields in Positional Arguments

AutoCLI also supports flattening nested message fields as positional arguments. This means you can access nested fields using dot notation in the ProtoField parameter. This is particularly useful when you want to directly set nested message fields as positional arguments. For example, if you have a nested message structure like this:
message Permissions {
    string level = 1;
    repeated string limit_type_urls = 2;
}

message MsgAuthorizeCircuitBreaker {
    string grantee = 1;
    Permissions permissions = 2;
}
You can flatten the fields in your AutoCLI configuration:
{
    RpcMethod: "AuthorizeCircuitBreaker",
    Use:       "authorize [grantee] [level] [msg_type_urls]",
    PositionalArgs: []*autocliv1.PositionalArgDescriptor{
        {ProtoField: "grantee"},
        {ProtoField: "permissions.level"},
        {ProtoField: "permissions.limit_type_urls", Varargs: true},
    },
}
This allows users to provide values for nested fields directly as positional arguments:
<appd> tx circuit authorize cosmos1... super-admin "/cosmos.bank.v1beta1.MsgSend" "/cosmos.bank.v1beta1.MsgMultiSend"
Instead of having to provide a complex JSON structure for nested fields, flattening makes the CLI more user-friendly by allowing direct access to nested fields.

Customizing Flag Names

By default, autocli generates flag names based on the names of the fields in your protobuf message. However, you can customize the flag names by providing a FlagOptions. This parameter allows you to specify custom names for flags based on the names of the message fields. For example, if you have a message with the fields test and test1, you can use the following naming options to customize the flags:
autocliv1.RpcCommandOptions{
    FlagOptions: map[string]*autocliv1.FlagOptions{ 
        "test": {
    Name: "custom_name",
}, 
        "test1": {
    Name: "other_name",
},
},
}

Combining AutoCLI with Other Commands Within A Module

AutoCLI can be used alongside other commands within a module. For example, the gov module uses AutoCLI for its query commands while also keeping hand-written tx commands for submit-proposal, weighted-vote, and similar. Set EnhanceCustomCommand: true on each ServiceCommandDescriptor where you want AutoCLI to add generated commands alongside existing ones:
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
    return &autocliv1.ModuleOptions{
        Query: &autocliv1.ServiceCommandDescriptor{
            Service:              govv1.Query_ServiceDesc.ServiceName,
            EnhanceCustomCommand: true, // keep hand-written gov query commands
            RpcCommandOptions:    []*autocliv1.RpcCommandOptions{ /* ... */ },
        },
        Tx: &autocliv1.ServiceCommandDescriptor{
            Service:              govv1.Msg_ServiceDesc.ServiceName,
            EnhanceCustomCommand: true, // keep hand-written gov tx commands
        },
    }
}
If EnhanceCustomCommand is not set to true, AutoCLI skips command generation for any service that already has commands registered via GetTxCmd() or GetQueryCmd().

Skip a command

AutoCLI checks the cosmos_proto.method_added_in protobuf annotation and skips commands that were introduced in a newer SDK version than the one currently running. Additionally, a command can be manually skipped using the autocliv1.RpcCommandOptions:
autocliv1.RpcCommandOptions{
    RpcMethod: "Params", // The name of the gRPC method
    Skip: true,
}

Use AutoCLI for non module commands

It is possible to use AutoCLI for non-module commands. The pattern is to add the options directly to autoCliOpts.ModuleOptions after calling AutoCliOpts():
nodeCmds := nodeservice.NewNodeCommands()
autoCliOpts.ModuleOptions[nodeCmds.Name()] = nodeCmds.AutoCLIOptions()
AutoCliOpts() only picks up modules registered with the module manager — non-module commands always need to be added to ModuleOptions manually, as the example chain does with nodeservice.NewNodeCommands(). For a more complete example of this pattern, see client/grpc/cmtservice/autocli.go and client/grpc/node/autocli.go in the Cosmos SDK.

Root Command Setup

For AutoCLI-generated commands (and hand-written commands) to work correctly — signing transactions, querying the chain, reading configuration — the root command must set up the client.Context and server.Context in a PersistentPreRunE function. This runs before every subcommand and makes both contexts available to all child commands. See simapp/simd/cmd/root.go for a complete example. The two key calls inside PersistentPreRun are:
  • SetCmdClientContextHandler reads persistent flags via ReadPersistentCommandFlags, creates a client.Context, and sets it on the command context. This is what AutoCLI and hand-written commands use to sign transactions and connect to a node.
  • InterceptConfigsPreRunHandler creates the server.Context, loads app.toml and config.toml from the node home directory, and binds them to the server context’s viper instance. This is what makes application configuration available at startup.

Custom logger

By default, InterceptConfigsPreRunHandler sets the default SDK logger. To use a custom logger, use InterceptConfigsAndCreateContext instead and set the logger manually:
-return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customCMTConfig)

+serverCtx, err := server.InterceptConfigsAndCreateContext(cmd, customAppTemplate, customAppConfig, customCMTConfig)
+if err != nil {
+	return err
+}

+// overwrite default server logger
+logger, err := server.CreateSDKLogger(serverCtx, cmd.OutOrStdout())
+if err != nil {
+	return err
+}
+serverCtx.Logger = logger.With(log.ModuleKey, "server")

+// set server context
+return server.SetCmdServerContext(cmd, serverCtx)

Environment Variables

Every CLI flag is automatically bound to an environment variable. The variable name is the app’s basename in uppercase followed by the flag name, with - replaced by _. For example, --node for an app with basename GAIA binds to GAIA_NODE. This lets you pre-configure common flags instead of passing them on every command:
# set once in .env or shell profile
GAIA_HOME=<path to home>
GAIA_NODE=<node address>
GAIA_CHAIN_ID="cosmoshub-4"
GAIA_KEYRING_BACKEND="test"

# then just run
gaiad tx bank send alice bob 1000uatom --fees 500uatom

Hand-Written Commands

AutoCLI covers the standard case: one protobuf RPC method maps to one CLI command. For commands that don’t fit that model, you can write Cobra commands manually and combine them with AutoCLI using EnhanceCustomCommand: true. Common reasons to write a command manually:
  • Complex argument parsing — multiple positional args that require custom validation or coin parsing before the message is built
  • Commands that span multiple RPC calls — e.g., building a transaction from inputs that require a preceding query
  • Non-standard UX — interactive prompts, offline signing flows, or commands that generate output rather than broadcast

Pattern

A manual transaction command uses client.GetClientTxContext to retrieve the signing context, constructs a message, and passes it to tx.GenerateOrBroadcastTxCLI:
func NewSendTxCmd(ac address.Codec) *cobra.Command {
    cmd := &cobra.Command{
        Use:   "send [from_key_or_address] [to_address] [amount]",
        Short: "Send tokens from one account to another",
        Args:  cobra.ExactArgs(3),
        RunE: func(cmd *cobra.Command, args []string) error {
            // set --from from the first positional arg
            if err := cmd.Flags().Set(flags.FlagFrom, args[0]); err != nil {
                return err
            }
            clientCtx, err := client.GetClientTxContext(cmd)
            if err != nil {
                return err
            }

            toAddr, err := ac.StringToBytes(args[1])
            if err != nil {
                return err
            }

            coins, err := sdk.ParseCoinsNormalized(args[2])
            if err != nil {
                return err
            }

            msg := types.NewMsgSend(clientCtx.GetFromAddress(), toAddr, coins)
            return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
        },
    }

    flags.AddTxFlagsToCmd(cmd)
    return cmd
}
Key elements:
  • client.GetClientTxContext(cmd) retrieves the client context (signer, node connection, codec)
  • flags.AddTxFlagsToCmd(cmd) adds standard transaction flags (--from, --fees, --gas, etc.)
  • tx.GenerateOrBroadcastTxCLI handles both --generate-only (offline) and live broadcast modes