# Modules
Review the following sections to better understand modules in the Cosmos SDK:
Modules are functional components that address application-level concerns such as token management or governance. The Cosmos SDK includes several ready-made modules so that application developers can focus on the truly unique aspects of their application.
A code example that illustrates module creation and an introduction to your checkers blockchain can be found at the end of this section.
Each Cosmos chain is a purpose-built blockchain. Cosmos SDK modules define the unique properties of each chain. Modules can be considered state machines within the larger state machine. They contain the storage layout, or state, and the state transition functions, which are the message methods.
Modules define most of the logic of Cosmos SDK applications.
When a transaction is relayed from the underlying CometBFT consensus engine, BaseApp
decomposes the Messages
contained within the transaction and routes messages to the appropriate module for processing. Interpretation and execution occur when the appropriate module message handler receives the message.
Developers compose together modules using the Cosmos SDK to build custom application-specific blockchains.
# Module scope
Modules include core functionality that every blockchain node needs:
- A boilerplate implementation of the Application Blockchain Interface (ABCI) that communicates with CometBFT.
- A general-purpose data store that persists the module state called
multistore
. - A server and interfaces to facilitate interactions with the node.
Modules implement the majority of the application logic while the core attends to wiring and infrastructure concerns, and enables modules to be composed into higher-order modules.
A module defines a subset of the overall state, using:
- One or more keys or value stores, known as
KVStore
. - A subset of message types that are needed by the application and do not exist yet.
Modules also define interactions with other modules that already exist.
Most of the work for developers involved in building a Cosmos SDK application consists of building custom modules required by their application that do not exist yet, and integrating them with modules that already exist into one coherent application. Existing modules can come either from the Cosmos SDK itself or from third-party developers. You can download these from an online module repository.
# Module components
It is best practice to define a module in the x/moduleName
folder. For example, the module called Checkers
would go in x/checkers
. If you look at the Cosmos SDK's base code, it also defines its modules (opens new window) in an x/
folder.
Modules implement several elements:
Msg
service to handle messages and one gRPC Query
service to handle queries.# Interfaces
A module must implement three application module interfaces to be integrated with the rest of the application:
AppModuleBasic
: implements non-dependent elements of the module.AppModule
: interdependent, specialized elements of the module that are unique to the application.AppModuleGenesis
: interdependent, genesis/initialization elements of the module that establish the initial state of the blockchain at inception.
You define AppModule
and AppModuleBasic
, and their functions, in your module's x/moduleName/module.go
file.
# Protobuf services
Each module defines two Protobuf services:
Msg
: a set of RPC methods related one-to-one to Protobuf request types, to handle messages.Query
: gRPC query service, to handle queries.
If this topic is new to you, read an introduction to Protocol Buffers (opens new window).
# Msg
service
Regarding the Msg
service, keep in mind:
- Best practice is to define the
Msg
Protobuf service in thetx.proto
file. - Each module should implement the
RegisterServices
method as part of theAppModule
interface. This lets the application know which messages and queries the module can handle. - Service methods should use a keeper, which encapsulates knowledge about the storage layout and presents methods for updating the state.
# gRPC Query
service
For the gRPC Query
service, keep in mind:
- Best practice is to define the
Query
Protobuf service in thequery.proto
file. - It allows users to query the state using gRPC.
- Each gRPC endpoint corresponds to a service method, named with the
rpc
prefix inside the gRPCQuery
service. - It can be configured under the
grpc.enable
andgrpc.address
fields inapp.toml
.
Protobuf generates a QueryServer
interface containing all the service methods for each module. Modules implement this QueryServer
interface by providing the concrete implementation of each service method in separate files. These implementation methods are the handlers of the corresponding gRPC query endpoints. This division of concerns across different files makes the setup safe from a re-generation of files by Protobuf.
gRPC (opens new window) is a modern, open-source, high-performance framework that supports multiple languages. It is the recommended standard for external clients such as wallets, browsers, and backend services to interact with a node.
gRPC-Gateway REST endpoints support external clients that may not wish to use gRPC. The Cosmos SDK provides a gRPC-gateway REST endpoint for each gRPC service.
See the gRPC-Gateway documentation (opens new window) for more on the gRPC-Gateway plugin.
# Command-line commands
Each module defines commands for a command-line interface (CLI). Commands related to a module are defined in a folder called client/cli
. The CLI divides commands into two categories: transactions and queries. These are the same as those which you defined in tx.go
and query.go
respectively.
# Keeper
Keepers are the gatekeepers to any stores in the module. It is mandatory to go through a module’s keeper to access a store. A keeper encapsulates the knowledge about the layout of the storage within the store and contains methods to update and inspect it. If you come from a model-view-controller (MVC) world, then it helps to think of the keeper as the controller.
Other modules may need access to a store, but other modules are also potentially malicious or poorly written. For this reason, developers need to consider who and what should have access to their module stores. To prevent a module from randomly accessing another module at runtime, a module needs to declare its intent to use another module at construction. At this point, such a module is granted a runtime key that lets it access the other module. Only modules that hold this key to a store can access the store. This is part of what is called an object-capability model.
Keepers are defined in keeper.go
. A keeper's type definition generally consists of keys to the module's own store in the multistore
, references to the keepers of other modules, and a reference to the application's codec.
# Core modules
The Cosmos SDK includes a set of core modules that address common concerns with well-solved, standardized implementations. Core modules address application needs such as tokens, staking, and governance.
Core modules offer several advantages over ad-hoc solutions:
- Standardization is established early, which helps ensure good interoperability with wallets, analytics, other modules, and other Cosmos SDK applications.
- Duplication of effort is significantly reduced because application developers focus on what is unique about their application.
- Core modules are working examples of Cosmos SDK modules that provide strong hints about suggested structure, style, and best practices.
Developers create coherent applications by selecting and composing core modules first and then implementing the custom logic.
Why not explore the list of core modules and the application concerns they address (opens new window)?
# Recommended folder structure
The following ideas are meant to be applied as suggestions. Application developers are encouraged to improve and contribute to the module structure and development design.
# Structure
A typical Cosmos SDK module can be structured as follows:
- The serializable data types and Protobuf interfaces:
{module_name}.proto
: the module's common message type definitions.event.proto
: the module's message type definitions related to events.genesis.proto
: the module's message type definitions related to the genesis state.query.proto
: the module'sQuery
service and related message type definitions.tx.proto
: the module'sMsg
service and related message type definitions.
- Then the rest of the code elements:
client/
: the module's CLI client functionality implementation and the module's integration testing suite.exported/
: the module's exported types - typically interface types (see also the following note).keeper/
: the module'sKeeper
andMsgServer
implementations.module/
: the module'sAppModule
andAppModuleBasic
implementations.simulation/
: the module's simulation package defines functions used by the blockchain simulator application (simapp
).spec/
: the module's specification documents outlining important concepts, state storage structure, and message and event type definitions.- The root directory includes type definitions for messages, events, and genesis state, including the type definitions generated by Protocol Buffers:
abci.go
: the module'sBeginBlocker
andEndBlocker
implementations. This file is only required ifBeginBlocker
and/orEndBlocker
need to be defined.codec.go
: the module's registry methods for interface types.errors.go
: the module's sentinel errors.events.go
: the module's event types and constructors.expected_keepers.go
: the module's expected other keeper interfaces.genesis.go
: the module's genesis state methods and helper functions.keys.go
: the module's store keys and associated helper functions.msgs.go
: the module's message type definitions and associated methods.params.go
: the module's parameter type definitions and associated methods.*.pb.go
: the module's type definitions generated by Protocol Buffers as defined in the respective*.proto
files.
If a module relies on keepers from another module, the exported/
code element expects to receive the keepers as interface contracts to avoid a direct dependency on the module implementing the keepers. However, these interface contracts can define methods that operate on (or return types that are specific to) the module that is implementing the keepers.
The interface types defined in exported/
use canonical types that allow for the module to receive the interface contracts through the expected_keepers.go
file. This pattern allows for code to remain DRY (opens new window) and also alleviates import cycle chaos.
# Errors
Modules are encouraged to define and register their own errors to provide better context for failed messages or handler executions. Errors should be common or general errors, which can be further wrapped to provide additional specific execution context.
For more details see the Cosmos SDK documentation on errors when building modules (opens new window).
# Registration
Modules should define and register their custom errors in x/{module}/errors.go
. Registration of errors is handled via the types/errors
package.
Each custom module error must provide the codespace, which is typically the module name (for example, "distribution") and is unique per module, and a uint32
code. The codespace and code together provide a globally unique Cosmos SDK error.
The only restrictions on error codes are the following:
- They must be greater than one, as a code value of one is reserved for internal errors.
- They must be unique within the module.
The Cosmos SDK provides a core set of common errors. These errors are defined in types/errors/errors.go
(opens new window).
# Wrapping
The custom module errors can be returned as their concrete type, as they already fulfill the error interface. Module errors can be wrapped to provide further context and meaning to failed executions.
Regardless of whether an error is wrapped or not, the Cosmos SDK's errors package provides an API to determine if an error is of a particular kind via Is
.
# ABCI
If a module error is registered, the Cosmos SDK errors package allows ABCI information to be extracted through the ABCIInfo
API. The package also provides ResponseCheckTx
and ResponseDeliverTx
as auxiliary APIs to automatically get CheckTx
and DeliverTx
responses from an error.
# Code example
Now your application is starting to take shape.
The checkers
module
When you create your checkers blockchain application, you ought to include a majority of the standard modules like auth
, bank
, and so on. With the Cosmos SDK boilerplate in place, the checkers part of your checkers application will most likely reside in a single checkers
module. This is the module that you author.
Game wager
Earlier the goal was to let players play with money. With the introduction of modules like bank
you can start handling that.
The initial ideas are:
- The wager amount is declared when creating a game.
- Each player is billed the amount when making their first move, which is interpreted as "challenge accepted". The amount should not be deducted on the game creation. If the game times out, the first player gets refunded.
- Subsequent moves by a player do not cost anything.
- If a game ends in a win or times out on a forfeit, the winning player gets the total wager amount.
- If a game ends in a draw, then both players get back their amount.
How would this look in terms of code? You need to add the wager to:
The game:
The message to create a game:
Wager payment
Now you must decide how the tokens are moved. When a player accepts a challenge, the amount is deducted from that player's balance. But where does it go? You could burn the tokens and re-mint them at a later date, but this would make the total supply fluctuate wildly for no apparent benefit.
It is possible to transfer from a player to a module. The module acts as the escrow account for all games. So when playing for the first time, a player would:
"stake"
identifies the likely name of the base token of your application, the token that is used with the consensus. Of course, you can choose to have the wager paid in any denomination, even one decided by players when creating the game. Conversely, when paying a winner you would have:
Note that:
- It is a standard error when the player cannot pay, which is easily fixed by the player.
- It is a panic (an internal error) when the escrow account cannot pay, because if the escrow cannot pay it means there is a logic problem somewhere.
If you want to go beyond the code samples in the expandable above and instead see in more detail how to define all this, go to Run Your Own Cosmos Chain.
More specifically, you can jump to:
- Ignite CLI to create a new blockchain with your checkers module.
- The advanced Handle Wager Payments to have your checkers module access and use the bank module.
- Add a leaderboard module and loosely couple it with another module with the use of the hooks pattern. It also leverages the module's
Params
construct. - The IBC-advanced Extend the Checkers Game With a Leaderboard to add a second custom IBC module to your checkers blockchain.
- The IBC-advanced Create a Leaderboard Chain to create a new blockchain with a module, once again.
To summarize, this section has explored:
- How Cosmos SDK modules can be viewed as purpose-specific state machines that define the unique properties of the larger state machine that is each blockchain.
- How messages are decomposed from the incoming transaction containing them and routed to the appropriate module for processing.
- How all modules comprise three core functionalities: an implementation of ABCI to communicate with CometBFT; a general-purpose data store which persists the module state; and the server and interfaces which facilitate interactions with the node.
- How the majority of work for developers is in building custom modules that satisfy their unique needs, which are then integrated into a coherent application alongside existing modules from the Cosmos SDK of third-party developers.
- How the Cosmos SDK's set of core modules address common applications needs (such as tokens, staking, and governance) while providing useful benefits like standardization across the Ecosystem, less duplication of effort, and practical examples of effective structure, style, and best practices.
- How modules should ideally define and register their own set of errors (in addition to the Cosmos SDK's set of common errors), allowing developers to add context and meaning to failed executions.