Synopsis

Transactions are objects created by end-users to trigger state changes in the application.
Pre-requisite Readings

Transactions

Transactions are comprised of metadata held in contexts and sdk.Msgs that trigger state changes within a module through the module’s Protobuf Msg service. When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction’s sdk.Msg must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click here.

Type Definition

Transaction objects are Cosmos SDK types that implement the Tx interface
package types

import (

	"encoding/json"
	fmt "fmt"
	strings "strings"
    "github.com/cosmos/gogoproto/proto"
	protov2 "google.golang.org/protobuf/proto"
    "github.com/cosmos/cosmos-sdk/codec"
	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)

type (
	/ Msg defines the interface a transaction message needed to fulfill.
	Msg = proto.Message

	/ LegacyMsg defines the interface a transaction message needed to fulfill up through
	/ v0.47.
	LegacyMsg interface {
    Msg

		/ GetSigners returns the addrs of signers that must sign.
		/ CONTRACT: All signatures must be present to be valid.
		/ CONTRACT: Returns addrs in some deterministic order.
		GetSigners() []AccAddress
}

	/ Fee defines an interface for an application application-defined concrete
	/ transaction type to be able to set and return the transaction fee.
	Fee interface {
    GetGas()

uint64
		GetAmount()

Coins
}

	/ Signature defines an interface for an application application-defined
	/ concrete transaction type to be able to set and return transaction signatures.
	Signature interface {
    GetPubKey()

cryptotypes.PubKey
		GetSignature() []byte
}

	/ HasMsgs defines an interface a transaction must fulfill.
	HasMsgs interface {
		/ GetMsgs gets the all the transaction's messages.
		GetMsgs() []Msg
}

	/ Tx defines an interface a transaction must fulfill.
	Tx interface {
    HasMsgs

		/ GetMsgsV2 gets the transaction's messages as google.golang.org/protobuf/proto.Message's.
		GetMsgsV2() ([]protov2.Message, error)
}

	/ FeeTx defines the interface to be implemented by Tx to use the FeeDecorators
	FeeTx interface {
    Tx
		GetGas()

uint64
		GetFee()

Coins
		FeePayer() []byte
		FeeGranter()

string
}

	/ TxWithMemo must have GetMemo()

method to use ValidateMemoDecorator
	TxWithMemo interface {
    Tx
		GetMemo()

string
}

	/ TxWithTimeoutHeight extends the Tx interface by allowing a transaction to
	/ set a height timeout.
	TxWithTimeoutHeight interface {
    Tx

		GetTimeoutHeight()

uint64
}

	/ HasValidateBasic defines a type that has a ValidateBasic method.
	/ ValidateBasic is deprecated and now facultative.
	/ Prefer validating messages directly in the msg server.
	HasValidateBasic interface {
		/ ValidateBasic does a simple validation check that
		/ doesn't require access to any other information.
		ValidateBasic()

error
}
)

/ TxDecoder unmarshals transaction bytes
type TxDecoder func(txBytes []byte) (Tx, error)

/ TxEncoder marshals transaction to bytes
type TxEncoder func(tx Tx) ([]byte, error)

/ MsgTypeURL returns the TypeURL of a `sdk.Msg`.
func MsgTypeURL(msg proto.Message)

string {
    if m, ok := msg.(protov2.Message); ok {
    return "/" + string(m.ProtoReflect().Descriptor().FullName())
}

return "/" + proto.MessageName(msg)
}

/ GetMsgFromTypeURL returns a `sdk.Msg` message type from a type URL
func GetMsgFromTypeURL(cdc codec.Codec, input string) (Msg, error) {
    var msg 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 determine sdk.Msg for %s URL : %w", input, err)
}

return msg, nil
}

/ GetModuleNameFromTypeURL assumes that module name is the second element of the msg type URL
/ e.g. "cosmos.bank.v1beta1.MsgSend" => "bank"
/ It returns an empty string if the input is not a valid type URL
func GetModuleNameFromTypeURL(input string)

string {
    moduleName := strings.Split(input, ".")
    if len(moduleName) > 1 {
    return moduleName[1]
}

return ""
}
It contains the following methods:
  • GetMsgs: unwraps the transaction and returns a list of contained sdk.Msgs - one transaction may have one or multiple messages, which are defined by module developers.
  • ValidateBasic: lightweight, stateless checks used by ABCI messages CheckTx and DeliverTx to make sure transactions are not invalid. For example, the auth module’s ValidateBasic function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user’s maximum. When runTx is checking a transaction created from the auth module, it first runs ValidateBasic on each message, then runs the auth module AnteHandler which calls ValidateBasic for the transaction itself.
This function is different from the deprecated sdk.Msg ValidateBasic methods, which was performing basic validity checks on messages only.
As a developer, you should rarely manipulate Tx directly, as Tx is really an intermediate type used for transaction generation. Instead, developers should prefer the TxBuilder interface, which you can learn more about below.

Signing Transactions

Every message in a transaction must be signed by the addresses specified by its GetSigners. The Cosmos SDK currently allows signing transactions in two different ways.

SIGN_MODE_DIRECT (preferred)

The most used implementation of the Tx interface is the Protobuf Tx message, which is used in SIGN_MODE_DIRECT:

// Tx is the standard type used for broadcasting transactions.
message Tx {
  // body is the processable content of the transaction
  TxBody body = 1;

  // auth_info is the authorization related content of the transaction,
  // specifically signers, signer modes and fee
  AuthInfo auth_info = 2;

  // signatures is a list of signatures that matches the length and order of
  // AuthInfo's signer_infos to allow connecting signature meta information like
  // public key and signing mode by position.
  repeated bytes signatures = 3;
Because Protobuf serialization is not deterministic, the Cosmos SDK uses an additional TxRaw type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid body and auth_info for a transaction, and serialize these two messages using Protobuf. TxRaw then pins the user’s exact binary representation of body and auth_info, called respectively body_bytes and auth_info_bytes. The document that is signed by all signers of the transaction is SignDoc (deterministically serialized using ADR-027):

// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT.
message SignDoc {
  // body_bytes is protobuf serialization of a TxBody that matches the
  // representation in TxRaw.
  bytes body_bytes = 1;

  // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the
  // representation in TxRaw.
  bytes auth_info_bytes = 2;

  // chain_id is the unique identifier of the chain this transaction targets.
  // It prevents signed transactions from being used on another chain by an
  // attacker
  string chain_id = 3;

  // account_number is the account number of the account in state
  uint64 account_number = 4;
Once signed by all signers, the body_bytes, auth_info_bytes and signatures are gathered into TxRaw, whose serialized bytes are broadcasted over the network.

SIGN_MODE_LEGACY_AMINO_JSON

The legacy implementation of the Tx interface is the StdTx struct from x/auth:
package legacytx

import (

	errorsmod "cosmossdk.io/errors"
    "cosmossdk.io/math"
    "github.com/cosmos/cosmos-sdk/codec/legacy"
	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
	sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/tx"
    "github.com/cosmos/cosmos-sdk/types/tx/signing"
)

/ Interface implementation checks
var (
	_ codectypes.UnpackInterfacesMessage = (*StdTx)(nil)

	_ codectypes.UnpackInterfacesMessage = (*StdSignature)(nil)
)

/ StdFee includes the amount of coins paid in fees and the maximum
/ gas to be used by the transaction. The ratio yields an effective "gasprice",
/ which must be above some miminum to be accepted into the mempool.
/ [Deprecated]
type StdFee struct {
    Amount  sdk.Coins `json:"amount" yaml:"amount"`
	Gas     uint64    `json:"gas" yaml:"gas"`
	Payer   string    `json:"payer,omitempty" yaml:"payer"`
	Granter string    `json:"granter,omitempty" yaml:"granter"`
}

/ Deprecated: NewStdFee returns a new instance of StdFee
func NewStdFee(gas uint64, amount sdk.Coins)

StdFee {
    return StdFee{
    Amount: amount,
    Gas:    gas,
}
}

/ GetGas returns the fee's (wanted)

gas.
func (fee StdFee)

GetGas()

uint64 {
    return fee.Gas
}

/ GetAmount returns the fee's amount.
func (fee StdFee)

GetAmount()

sdk.Coins {
    return fee.Amount
}

/ Bytes returns the encoded bytes of a StdFee.
func (fee StdFee)

Bytes() []byte {
    if len(fee.Amount) == 0 {
    fee.Amount = sdk.NewCoins()
}

bz, err := legacy.Cdc.MarshalJSON(fee)
    if err != nil {
    panic(err)
}

return bz
}

/ GasPrices returns the gas prices for a StdFee.
/
/ NOTE: The gas prices returned are not the true gas prices that were
/ originally part of the submitted transaction because the fee is computed
/ as fee = ceil(gasWanted * gasPrices).
func (fee StdFee)

GasPrices()

sdk.DecCoins {
    return sdk.NewDecCoinsFromCoins(fee.Amount...).QuoDec(math.LegacyNewDec(int64(fee.Gas)))
}

/ StdTip is the tips used in a tipped transaction.
type StdTip struct {
    Amount sdk.Coins `json:"amount" yaml:"amount"`
	Tipper string    `json:"tipper" yaml:"tipper"`
}

/ StdTx is the legacy transaction format for wrapping a Msg with Fee and Signatures.
/ It only works with Amino, please prefer the new protobuf Tx in types/tx.
/ NOTE: the first signature is the fee payer (Signatures must not be nil).
/ Deprecated
type StdTx struct {
    Msgs          []sdk.Msg      `json:"msg" yaml:"msg"`
	Fee           StdFee         `json:"fee" yaml:"fee"`
	Signatures    []StdSignature `json:"signatures" yaml:"signatures"`
	Memo          string         `json:"memo" yaml:"memo"`
	TimeoutHeight uint64         `json:"timeout_height" yaml:"timeout_height"`
}

/ Deprecated
func NewStdTx(msgs []sdk.Msg, fee StdFee, sigs []StdSignature, memo string)

StdTx {
    return StdTx{
    Msgs:       msgs,
    Fee:        fee,
    Signatures: sigs,
    Memo:       memo,
}
}

/ GetMsgs returns the all the transaction's messages.
func (tx StdTx)

GetMsgs() []sdk.Msg {
    return tx.Msgs
}

/ Deprecated: AsAny implements intoAny. It doesn't work for protobuf serialization,
/ so it can't be saved into protobuf configured storage. We are using it only for API
/ compatibility.
func (tx *StdTx)

AsAny() *codectypes.Any {
    return codectypes.UnsafePackAny(tx)
}

/ GetMemo returns the memo
func (tx StdTx)

GetMemo()

string {
    return tx.Memo
}

/ GetTimeoutHeight returns the transaction's timeout height (if set).
func (tx StdTx)

GetTimeoutHeight()

uint64 {
    return tx.TimeoutHeight
}

/ GetSignatures returns the signature of signers who signed the Msg.
/ CONTRACT: Length returned is same as length of
/ pubkeys returned from MsgKeySigners, and the order
/ matches.
/ CONTRACT: If the signature is missing (ie the Msg is
/ invalid), then the corresponding signature is
/ .Empty().
func (tx StdTx)

GetSignatures() [][]byte {
    sigs := make([][]byte, len(tx.Signatures))
    for i, stdSig := range tx.Signatures {
    sigs[i] = stdSig.Signature
}

return sigs
}

/ GetSignaturesV2 implements SigVerifiableTx.GetSignaturesV2
func (tx StdTx)

GetSignaturesV2() ([]signing.SignatureV2, error) {
    res := make([]signing.SignatureV2, len(tx.Signatures))
    for i, sig := range tx.Signatures {
    var err error
		res[i], err = StdSignatureToSignatureV2(legacy.Cdc, sig)
    if err != nil {
    return nil, errorsmod.Wrapf(err, "Unable to convert signature %v to V2", sig)
}

}

return res, nil
}

/ GetPubkeys returns the pubkeys of signers if the pubkey is included in the signature
/ If pubkey is not included in the signature, then nil is in the slice instead
func (tx StdTx)

GetPubKeys() ([]cryptotypes.PubKey, error) {
    pks := make([]cryptotypes.PubKey, len(tx.Signatures))
    for i, stdSig := range tx.Signatures {
    pks[i] = stdSig.GetPubKey()
}

return pks, nil
}

/ GetGas returns the Gas in StdFee
func (tx StdTx)

GetGas()

uint64 {
    return tx.Fee.Gas
}

/ GetFee returns the FeeAmount in StdFee
func (tx StdTx)

GetFee()

sdk.Coins {
    return tx.Fee.Amount
}

/ FeeGranter always returns nil for StdTx
func (tx StdTx)

FeeGranter()

sdk.AccAddress {
    return nil
}

/ GetTip always returns nil for StdTx
func (tx StdTx)

GetTip() *tx.Tip {
    return nil
}

func (tx StdTx)

UnpackInterfaces(unpacker codectypes.AnyUnpacker)

error {
    for _, m := range tx.Msgs {
    err := codectypes.UnpackInterfaces(m, unpacker)
    if err != nil {
    return err
}

}

	/ Signatures contain PubKeys, which need to be unpacked.
    for _, s := range tx.Signatures {
    err := s.UnpackInterfaces(unpacker)
    if err != nil {
    return err
}

}

return nil
}
The document signed by all signers is StdSignDoc:
package legacytx

import (

	"encoding/json"
    "fmt"
    "sigs.k8s.io/yaml"
    "cosmossdk.io/errors"
    "github.com/cosmos/cosmos-sdk/codec"
    "github.com/cosmos/cosmos-sdk/codec/legacy"
	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    "github.com/cosmos/cosmos-sdk/crypto/types/multisig"
	sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/tx"
    "github.com/cosmos/cosmos-sdk/types/tx/signing"
)

/ LegacyMsg defines the old interface a message must fulfill,
/ containing Amino signing method.
/ Deprecated: Please use `Msg` instead.
type LegacyMsg interface {
    sdk.Msg

	/ Get the canonical byte representation of the Msg.
	GetSignBytes() []byte
}

/ StdSignDoc is replay-prevention structure.
/ It includes the result of msg.GetSignBytes(),
/ as well as the ChainID (prevent cross chain replay)
/ and the Sequence numbers for each signature (prevent
/ inchain replay and enforce tx ordering per account).
type StdSignDoc struct {
    AccountNumber uint64            `json:"account_number" yaml:"account_number"`
	Sequence      uint64            `json:"sequence" yaml:"sequence"`
	TimeoutHeight uint64            `json:"timeout_height,omitempty" yaml:"timeout_height"`
	ChainID       string            `json:"chain_id" yaml:"chain_id"`
	Memo          string            `json:"memo" yaml:"memo"`
	Fee           json.RawMessage   `json:"fee" yaml:"fee"`
	Msgs          []json.RawMessage `json:"msgs" yaml:"msgs"`
	Tip           *StdTip           `json:"tip,omitempty" yaml:"tip"`
}

var RegressionTestingAminoCodec *codec.LegacyAmino

/ StdSignBytes returns the bytes to sign for a transaction.
/ Deprecated: Please use x/tx/signing/aminojson instead.
func StdSignBytes(chainID string, accnum, sequence, timeout uint64, fee StdFee, msgs []sdk.Msg, memo string, tip *tx.Tip) []byte {
    if RegressionTestingAminoCodec == nil {
    panic(fmt.Errorf("must set RegressionTestingAminoCodec before calling StdSignBytes"))
}
    msgsBytes := make([]json.RawMessage, 0, len(msgs))
    for _, msg := range msgs {
    bz := RegressionTestingAminoCodec.MustMarshalJSON(msg)

msgsBytes = append(msgsBytes, sdk.MustSortJSON(bz))
}

var stdTip *StdTip
    if tip != nil {
    if tip.Tipper == "" {
    panic(fmt.Errorf("tipper cannot be empty"))
}

stdTip = &StdTip{
    Amount: tip.Amount,
    Tipper: tip.Tipper
}

}

bz, err := legacy.Cdc.MarshalJSON(StdSignDoc{
    AccountNumber: accnum,
    ChainID:       chainID,
    Fee:           json.RawMessage(fee.Bytes()),
    Memo:          memo,
    Msgs:          msgsBytes,
    Sequence:      sequence,
    TimeoutHeight: timeout,
    Tip:           stdTip,
})
    if err != nil {
    panic(err)
}

return sdk.MustSortJSON(bz)
}

/ Deprecated: StdSignature represents a sig
type StdSignature struct {
    cryptotypes.PubKey `json:"pub_key" yaml:"pub_key"` / optional
	Signature          []byte                          `json:"signature" yaml:"signature"`
}

/ Deprecated
func NewStdSignature(pk cryptotypes.PubKey, sig []byte)

StdSignature {
    return StdSignature{
    PubKey: pk,
    Signature: sig
}
}

/ GetSignature returns the raw signature bytes.
func (ss StdSignature)

GetSignature() []byte {
    return ss.Signature
}

/ GetPubKey returns the public key of a signature as a cryptotypes.PubKey using the
/ Amino codec.
func (ss StdSignature)

GetPubKey()

cryptotypes.PubKey {
    return ss.PubKey
}

/ MarshalYAML returns the YAML representation of the signature.
func (ss StdSignature)

MarshalYAML() (interface{
}, error) {
    pk := ""
    if ss.PubKey != nil {
    pk = ss.PubKey.String()
}

bz, err := yaml.Marshal(struct {
    PubKey    string `json:"pub_key"`
		Signature string `json:"signature"`
}{
    pk,
		fmt.Sprintf("%X", ss.Signature),
})
    if err != nil {
    return nil, err
}

return string(bz), err
}

func (ss StdSignature)

UnpackInterfaces(unpacker codectypes.AnyUnpacker)

error {
    return codectypes.UnpackInterfaces(ss.PubKey, unpacker)
}

/ StdSignatureToSignatureV2 converts a StdSignature to a SignatureV2
func StdSignatureToSignatureV2(cdc *codec.LegacyAmino, sig StdSignature) (signing.SignatureV2, error) {
    pk := sig.GetPubKey()

data, err := pubKeySigToSigData(cdc, pk, sig.Signature)
    if err != nil {
    return signing.SignatureV2{
}, err
}

return signing.SignatureV2{
    PubKey: pk,
    Data:   data,
}, nil
}

func pubKeySigToSigData(cdc *codec.LegacyAmino, key cryptotypes.PubKey, sig []byte) (signing.SignatureData, error) {
    multiPK, ok := key.(multisig.PubKey)
    if !ok {
    return &signing.SingleSignatureData{
    SignMode:  signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
    Signature: sig,
}, nil
}

var multiSig multisig.AminoMultisignature
    err := cdc.Unmarshal(sig, &multiSig)
    if err != nil {
    return nil, err
}
    sigs := multiSig.Sigs
    sigDatas := make([]signing.SignatureData, len(sigs))
    pubKeys := multiPK.GetPubKeys()
    bitArray := multiSig.BitArray
    n := multiSig.BitArray.Count()
    signatures := multisig.NewMultisig(n)
    sigIdx := 0
    for i := 0; i < n; i++ {
    if bitArray.GetIndex(i) {
    data, err := pubKeySigToSigData(cdc, pubKeys[i], multiSig.Sigs[sigIdx])
    if err != nil {
    return nil, errors.Wrapf(err, "Unable to convert Signature to SigData %d", sigIdx)
}

sigDatas[sigIdx] = data
			multisig.AddSignature(signatures, data, sigIdx)

sigIdx++
}

}

return signatures, nil
}
which is encoded into bytes using Amino JSON. Once all signatures are gathered into StdTx, StdTx is serialized using Amino JSON, and these bytes are broadcasted over the network.

Other Sign Modes

The Cosmos SDK also provides a couple of other sign modes for particular use cases.

SIGN_MODE_DIRECT_AUX

SIGN_MODE_DIRECT_AUX is a sign mode released in the Cosmos SDK v0.46 which targets transactions with multiple signers. Whereas SIGN_MODE_DIRECT expects each signer to sign over both TxBody and AuthInfo (which includes all other signers’ signer infos, i.e. their account sequence, public key and mode info), SIGN_MODE_DIRECT_AUX allows N-1 signers to only sign over TxBody and their own signer info. Morever, each auxiliary signer (i.e. a signer using SIGN_MODE_DIRECT_AUX) doesn’t need to sign over the fees:

// SignDocDirectAux is the type used for generating sign bytes for
// SIGN_MODE_DIRECT_AUX.
//
// Since: cosmos-sdk 0.46
message SignDocDirectAux {
  // body_bytes is protobuf serialization of a TxBody that matches the
  // representation in TxRaw.
  bytes body_bytes = 1;

  // public_key is the public key of the signing account.
  google.protobuf.Any public_key = 2;

  // chain_id is the identifier of the chain this transaction targets.
  // It prevents signed transactions from being used on another chain by an
  // attacker.
  string chain_id = 3;

  // account_number is the account number of the account in state.
  uint64 account_number = 4;

  // sequence is the sequence number of the signing account.
  uint64 sequence = 5;

  // Tip is the optional tip used for transactions fees paid in another denom.
  // It should be left empty if the signer is not the tipper for this
  // transaction.
  //
  // This field is ignored if the chain didn't enable tips, i.e. didn't add the
  // `TipDecorator` in its posthandler.
  Tip tip = 6;
}
The use case is a multi-signer transaction, where one of the signers is appointed to gather all signatures, broadcast the signature and pay for fees, and the others only care about the transaction body. This generally allows for a better multi-signing UX. If Alice, Bob and Charlie are part of a 3-signer transaction, then Alice and Bob can both use SIGN_MODE_DIRECT_AUX to sign over the TxBody and their own signer info (no need an additional step to gather other signers’ ones, like in SIGN_MODE_DIRECT), without specifying a fee in their SignDoc. Charlie can then gather both signatures from Alice and Bob, and create the final transaction by appending a fee. Note that the fee payer of the transaction (in our case Charlie) must sign over the fees, so must use SIGN_MODE_DIRECT or SIGN_MODE_LEGACY_AMINO_JSON.

SIGN_MODE_TEXTUAL

SIGN_MODE_TEXTUAL is a new sign mode for delivering a better signing experience on hardware wallets and it is included in the v0.50 release. In this mode, the signer signs over the human-readable string representation of the transaction (CBOR) and makes all data being displayed easier to read. The data is formatted as screens, and each screen is meant to be displayed in its entirety even on small devices like the Ledger Nano. There are also expert screens, which will only be displayed if the user has chosen that option in its hardware device. These screens contain things like account number, account sequence and the sign data hash. Data is formatted using a set of ValueRenderer which the SDK provides defaults for all the known messages and value types. Chain developers can also opt to implement their own ValueRenderer for a type/message if they’d like to display information differently. If you wish to learn more, please refer to ADR-050.

Custom Sign modes

There is the opportunity to add your own custom sign mode to the Cosmos-SDK. While we can not accept the implementation of the sign mode to the repository, we can accept a pull request to add the custom signmode to the SignMode enum located here

Transaction Process

The process of an end-user sending a transaction is:
  • decide on the messages to put into the transaction,
  • generate the transaction using the Cosmos SDK’s TxBuilder,
  • broadcast the transaction using one of the available interfaces.
The next paragraphs will describe each of these components, in this order.

Messages

Module sdk.Msgs are not to be confused with ABCI Messages which define interactions between the CometBFT and application layers.
Messages (or sdk.Msgs) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf Msg service, and also implement the corresponding MsgServer. Each sdk.Msgs is related to exactly one Protobuf Msg service RPC, defined inside each module’s tx.proto file. A SDK app router automatically maps every sdk.Msg to a corresponding RPC. Protobuf generates a MsgServer interface for each module Msg service, and the module developer needs to implement this interface. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively. To learn more about Protobuf Msg services and how to implement MsgServer, click here. While messages contain the information for state transition logic, a transaction’s other metadata and relevant information are stored in the TxBuilder and Context.

Transaction Generation

The TxBuilder interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction:
package client

import (

	txsigning "cosmossdk.io/x/tx/signing"
	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
	sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/tx"
	signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
    "github.com/cosmos/cosmos-sdk/x/auth/signing"
)

type (
	/ TxEncodingConfig defines an interface that contains transaction
	/ encoders and decoders
	TxEncodingConfig interface {
    TxEncoder()

sdk.TxEncoder
		TxDecoder()

sdk.TxDecoder
		TxJSONEncoder()

sdk.TxEncoder
		TxJSONDecoder()

sdk.TxDecoder
		MarshalSignatureJSON([]signingtypes.SignatureV2) ([]byte, error)

UnmarshalSignatureJSON([]byte) ([]signingtypes.SignatureV2, error)
}

	/ TxConfig defines an interface a client can utilize to generate an
	/ application-defined concrete transaction type. The type returned must
	/ implement TxBuilder.
	TxConfig interface {
    TxEncodingConfig

		NewTxBuilder()

TxBuilder
		WrapTxBuilder(sdk.Tx) (TxBuilder, error)

SignModeHandler() *txsigning.HandlerMap
		SigningContext() *txsigning.Context
}

	/ TxBuilder defines an interface which an application-defined concrete transaction
	/ type must implement. Namely, it must be able to set messages, generate
	/ signatures, and provide canonical bytes to sign over. The transaction must
	/ also know how to encode itself.
	TxBuilder interface {
    GetTx()

signing.Tx

		SetMsgs(msgs ...sdk.Msg)

error
		SetSignatures(signatures ...signingtypes.SignatureV2)

error
		SetMemo(memo string)

SetFeeAmount(amount sdk.Coins)

SetFeePayer(feePayer sdk.AccAddress)

SetGasLimit(limit uint64)

SetTip(tip *tx.Tip)

SetTimeoutHeight(height uint64)

SetFeeGranter(feeGranter sdk.AccAddress)

AddAuxSignerData(tx.AuxSignerData)

error
}

	/ ExtendedTxBuilder extends the TxBuilder interface,
	/ which is used to set extension options to be included in a transaction.
	ExtendedTxBuilder interface {
    SetExtensionOptions(extOpts ...*codectypes.Any)
}
)
  • Msgs, the array of messages included in the transaction.
  • GasLimit, option chosen by the users for how to calculate how much gas they will need to pay.
  • Memo, a note or comment to send with the transaction.
  • FeeAmount, the maximum amount the user is willing to pay in fees.
  • TimeoutHeight, block height until which the transaction is valid.
  • Signatures, the array of signatures from all signers of the transaction.
As there are currently two sign modes for signing transactions, there are also two implementations of TxBuilder:
  • wrapper for creating transactions for SIGN_MODE_DIRECT,
  • StdTxBuilder for SIGN_MODE_LEGACY_AMINO_JSON.
However, the two implementations of TxBuilder should be hidden away from end-users, as they should prefer using the overarching TxConfig interface:
package client

import (

	txsigning "cosmossdk.io/x/tx/signing"
	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
	sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/types/tx"
	signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
    "github.com/cosmos/cosmos-sdk/x/auth/signing"
)

type (
	/ TxEncodingConfig defines an interface that contains transaction
	/ encoders and decoders
	TxEncodingConfig interface {
    TxEncoder()

sdk.TxEncoder
		TxDecoder()

sdk.TxDecoder
		TxJSONEncoder()

sdk.TxEncoder
		TxJSONDecoder()

sdk.TxDecoder
		MarshalSignatureJSON([]signingtypes.SignatureV2) ([]byte, error)

UnmarshalSignatureJSON([]byte) ([]signingtypes.SignatureV2, error)
}

	/ TxConfig defines an interface a client can utilize to generate an
	/ application-defined concrete transaction type. The type returned must
	/ implement TxBuilder.
	TxConfig interface {
    TxEncodingConfig

		NewTxBuilder()

TxBuilder
		WrapTxBuilder(sdk.Tx) (TxBuilder, error)

SignModeHandler() *txsigning.HandlerMap
		SigningContext() *txsigning.Context
}

	/ TxBuilder defines an interface which an application-defined concrete transaction
	/ type must implement. Namely, it must be able to set messages, generate
	/ signatures, and provide canonical bytes to sign over. The transaction must
	/ also know how to encode itself.
	TxBuilder interface {
    GetTx()

signing.Tx

		SetMsgs(msgs ...sdk.Msg)

error
		SetSignatures(signatures ...signingtypes.SignatureV2)

error
		SetMemo(memo string)

SetFeeAmount(amount sdk.Coins)

SetFeePayer(feePayer sdk.AccAddress)

SetGasLimit(limit uint64)

SetTip(tip *tx.Tip)

SetTimeoutHeight(height uint64)

SetFeeGranter(feeGranter sdk.AccAddress)

AddAuxSignerData(tx.AuxSignerData)

error
}

	/ ExtendedTxBuilder extends the TxBuilder interface,
	/ which is used to set extension options to be included in a transaction.
	ExtendedTxBuilder interface {
    SetExtensionOptions(extOpts ...*codectypes.Any)
}
)
TxConfig is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with SIGN_MODE_DIRECT or SIGN_MODE_LEGACY_AMINO_JSON. By calling txBuilder := txConfig.NewTxBuilder(), a new TxBuilder will be created with the appropriate sign mode. Once TxBuilder is correctly populated with the setters exposed above, TxConfig will also take care of correctly encoding the bytes (again, either using SIGN_MODE_DIRECT or SIGN_MODE_LEGACY_AMINO_JSON). Here’s a pseudo-code snippet of how to generate and encode a transaction, using the TxEncoder() method:
txBuilder := txConfig.NewTxBuilder()

txBuilder.SetMsgs(...) / and other setters on txBuilder

bz, err := txConfig.TxEncoder()(txBuilder.GetTx())
/ bz are bytes to be broadcasted over the network

Broadcasting the Transaction

Once the transaction bytes are generated, there are currently three ways of broadcasting it.

CLI

Application developers create entry points to the application by creating a command-line interface, gRPC and/or REST interface, typically found in the application’s ./cmd folder. These interfaces allow users to interact with the application through command-line. For the command-line interface, module developers create subcommands to add as children to the application top-level transaction command TxCmd. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the Interacting with a Node section. An example transaction made using CLI looks like:
simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake

gRPC

gRPC is the main component for the Cosmos SDK’s RPC layer. Its principal usage is in the context of modules’ Query services. However, the Cosmos SDK also exposes a few other module-agnostic gRPC services, one of them being the Tx service:
syntax = "proto3";
package cosmos.tx.v1beta1;

import "google/api/annotations.proto";
import "cosmos/base/abci/v1beta1/abci.proto";
import "cosmos/tx/v1beta1/tx.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "tendermint/types/block.proto";
import "tendermint/types/types.proto";

option go_package = "github.com/cosmos/cosmos-sdk/types/tx";

/ Service defines a gRPC service for interacting with transactions.
service Service {
  / Simulate simulates executing a transaction for estimating gas usage.
  rpc Simulate(SimulateRequest)

returns (SimulateResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/simulate"
      body: "*"
};
}
  / GetTx fetches a tx by hash.
  rpc GetTx(GetTxRequest)

returns (GetTxResponse) {
    option (google.api.http).get = "/cosmos/tx/v1beta1/txs/{
    hash
}";
}
  / BroadcastTx broadcast transaction.
  rpc BroadcastTx(BroadcastTxRequest)

returns (BroadcastTxResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/txs"
      body: "*"
};
}
  / GetTxsEvent fetches txs by event.
  rpc GetTxsEvent(GetTxsEventRequest)

returns (GetTxsEventResponse) {
    option (google.api.http).get = "/cosmos/tx/v1beta1/txs";
}
  / GetBlockWithTxs fetches a block with decoded txs.
  /
  / Since: cosmos-sdk 0.45.2
  rpc GetBlockWithTxs(GetBlockWithTxsRequest)

returns (GetBlockWithTxsResponse) {
    option (google.api.http).get = "/cosmos/tx/v1beta1/txs/block/{
    height
}";
}
  / TxDecode decodes the transaction.
  /
  / Since: cosmos-sdk 0.47
  rpc TxDecode(TxDecodeRequest)

returns (TxDecodeResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/decode"
      body: "*"
};
}
  / TxEncode encodes the transaction.
  /
  / Since: cosmos-sdk 0.47
  rpc TxEncode(TxEncodeRequest)

returns (TxEncodeResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/encode"
      body: "*"
};
}
  / TxEncodeAmino encodes an Amino transaction from JSON to encoded bytes.
  /
  / Since: cosmos-sdk 0.47
  rpc TxEncodeAmino(TxEncodeAminoRequest)

returns (TxEncodeAminoResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/encode/amino"
      body: "*"
};
}
  / TxDecodeAmino decodes an Amino transaction from encoded bytes to JSON.
  /
  / Since: cosmos-sdk 0.47
  rpc TxDecodeAmino(TxDecodeAminoRequest)

returns (TxDecodeAminoResponse) {
    option (google.api.http) = {
    post: "/cosmos/tx/v1beta1/decode/amino"
      body: "*"
};
}
}

/ GetTxsEventRequest is the request type for the Service.TxsByEvents
/ RPC method.
message GetTxsEventRequest {
  / events is the list of transaction event type.
  / Deprecated post v0.47.x: use query instead, which should contain a valid
  / events query.
  repeated string events = 1 [deprecated = true];

  / pagination defines a pagination for the request.
  / Deprecated post v0.46.x: use page and limit instead.
  cosmos.base.query.v1beta1.PageRequest pagination = 2 [deprecated = true];

  OrderBy order_by = 3;

  / page is the page number to query, starts at 1. If not provided, will
  / default to first page.
  uint64 page = 4;

  / limit is the total number of results to be returned in the result page.
  / If left empty it will default to a value to be set by each app.
  uint64 limit = 5;

  / query defines the transaction event query that is proxied to Tendermint's
  / TxSearch RPC method. The query must be valid.
  /
  / Since cosmos-sdk 0.50
  string query = 6;
}

/ OrderBy defines the sorting order
enum OrderBy {
  / ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults
  / to ASC in this case.
  ORDER_BY_UNSPECIFIED = 0;
  / ORDER_BY_ASC defines ascending order
  ORDER_BY_ASC = 1;
  / ORDER_BY_DESC defines descending order
  ORDER_BY_DESC = 2;
}

/ GetTxsEventResponse is the response type for the Service.TxsByEvents
/ RPC method.
message GetTxsEventResponse {
  / txs is the list of queried transactions.
  repeated cosmos.tx.v1beta1.Tx txs = 1;
  / tx_responses is the list of queried TxResponses.
  repeated cosmos.base.abci.v1beta1.TxResponse tx_responses = 2;
  / pagination defines a pagination for the response.
  / Deprecated post v0.46.x: use total instead.
  cosmos.base.query.v1beta1.PageResponse pagination = 3 [deprecated = true];
  / total is total number of results available
  uint64 total = 4;
}

/ BroadcastTxRequest is the request type for the Service.BroadcastTxRequest
/ RPC method.
message BroadcastTxRequest {
  / tx_bytes is the raw transaction.
  bytes         tx_bytes = 1;
  BroadcastMode mode     = 2;
}

/ BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC
/ method.
enum BroadcastMode {
  / zero-value for mode ordering
  BROADCAST_MODE_UNSPECIFIED = 0;
  / DEPRECATED: use BROADCAST_MODE_SYNC instead,
  / BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x onwards.
  BROADCAST_MODE_BLOCK = 1 [deprecated = true];
  / BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits
  / for a CheckTx execution response only.
  BROADCAST_MODE_SYNC = 2;
  / BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client
  / returns immediately.
  BROADCAST_MODE_ASYNC = 3;
}

/ BroadcastTxResponse is the response type for the
/ Service.BroadcastTx method.
message BroadcastTxResponse {
  / tx_response is the queried TxResponses.
  cosmos.base.abci.v1beta1.TxResponse tx_response = 1;
}

/ SimulateRequest is the request type for the Service.Simulate
/ RPC method.
message SimulateRequest {
  / tx is the transaction to simulate.
  / Deprecated. Send raw tx bytes instead.
  cosmos.tx.v1beta1.Tx tx = 1 [deprecated = true];
  / tx_bytes is the raw transaction.
  /
  / Since: cosmos-sdk 0.43
  bytes tx_bytes = 2;
}

/ SimulateResponse is the response type for the
/ Service.SimulateRPC method.
message SimulateResponse {
  / gas_info is the information about gas used in the simulation.
  cosmos.base.abci.v1beta1.GasInfo gas_info = 1;
  / result is the result of the simulation.
  cosmos.base.abci.v1beta1.Result result = 2;
}

/ GetTxRequest is the request type for the Service.GetTx
/ RPC method.
message GetTxRequest {
  / hash is the tx hash to query, encoded as a hex string.
  string hash = 1;
}

/ GetTxResponse is the response type for the Service.GetTx method.
message GetTxResponse {
  / tx is the queried transaction.
  cosmos.tx.v1beta1.Tx tx = 1;
  / tx_response is the queried TxResponses.
  cosmos.base.abci.v1beta1.TxResponse tx_response = 2;
}

/ GetBlockWithTxsRequest is the request type for the Service.GetBlockWithTxs
/ RPC method.
/
/ Since: cosmos-sdk 0.45.2
message GetBlockWithTxsRequest {
  / height is the height of the block to query.
  int64 height = 1;
  / pagination defines a pagination for the request.
  cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

/ GetBlockWithTxsResponse is the response type for the Service.GetBlockWithTxs
/ method.
/
/ Since: cosmos-sdk 0.45.2
message GetBlockWithTxsResponse {
  / txs are the transactions in the block.
  repeated cosmos.tx.v1beta1.Tx txs      = 1;
  .tendermint.types.BlockID     block_id = 2;
  .tendermint.types.Block       block    = 3;
  / pagination defines a pagination for the response.
  cosmos.base.query.v1beta1.PageResponse pagination = 4;
}

/ TxDecodeRequest is the request type for the Service.TxDecode
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxDecodeRequest {
  / tx_bytes is the raw transaction.
  bytes tx_bytes = 1;
}

/ TxDecodeResponse is the response type for the
/ Service.TxDecode method.
/
/ Since: cosmos-sdk 0.47
message TxDecodeResponse {
  / tx is the decoded transaction.
  cosmos.tx.v1beta1.Tx tx = 1;
}

/ TxEncodeRequest is the request type for the Service.TxEncode
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxEncodeRequest {
  / tx is the transaction to encode.
  cosmos.tx.v1beta1.Tx tx = 1;
}

/ TxEncodeResponse is the response type for the
/ Service.TxEncode method.
/
/ Since: cosmos-sdk 0.47
message TxEncodeResponse {
  / tx_bytes is the encoded transaction bytes.
  bytes tx_bytes = 1;
}

/ TxEncodeAminoRequest is the request type for the Service.TxEncodeAmino
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxEncodeAminoRequest {
    string amino_json = 1;
}

/ TxEncodeAminoResponse is the response type for the Service.TxEncodeAmino
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxEncodeAminoResponse {
    bytes amino_binary = 1;
}

/ TxDecodeAminoRequest is the request type for the Service.TxDecodeAmino
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxDecodeAminoRequest {
    bytes amino_binary = 1;
}

/ TxDecodeAminoResponse is the response type for the Service.TxDecodeAmino
/ RPC method.
/
/ Since: cosmos-sdk 0.47
message TxDecodeAminoResponse {
    string amino_json = 1;
}
The Tx service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions. Examples of broadcasting and simulating a transaction are shown here.

REST

Each gRPC method has its corresponding REST endpoint, generated using gRPC-gateway. Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the POST /cosmos/tx/v1beta1/txs endpoint. An example can be seen here

CometBFT RPC

The three methods presented above are actually higher abstractions over the CometBFT RPC /broadcast_tx_{async,sync,commit} endpoints, documented here. This means that you can use the CometBFT RPC endpoints directly to broadcast the transaction, if you wish so.