# Module Manager

Cosmos SDK modules need to implement the AppModule interfaces, in order to be managed by the application's module manager. The module manager plays an important role in message and query routing, and allows application developers to set the order of execution of a variety of functions like BeginBlocker and EndBlocker.

# Pre-requisite Readings

# Application Module Interfaces

Application module interfaces exist to facilitate the composition of modules together to form a functional Cosmos SDK application. There are 3 main application module interfaces:

  • AppModuleBasic for independent module functionalities.
  • AppModule for inter-dependent module functionalities (except genesis-related functionalities).
  • AppModuleGenesis for inter-dependent genesis-related module functionalities.

The AppModuleBasic interface exists to define independent methods of the module, i.e. those that do not depend on other modules in the application. This allows for the construction of the basic application structure early in the application definition, generally in the init() function of the main application file.

The AppModule interface exists to define inter-dependent module methods. Many modules need to interact with other modules, typically through keepers, which means there is a need for an interface where modules list their keepers and other methods that require a reference to another module's object. AppModule interface also enables the module manager to set the order of execution between module's methods like BeginBlock and EndBlock, which is important in cases where the order of execution between modules matters in the context of the application.

Lastly the interface for genesis functionality AppModuleGenesis is separated out from full module functionality AppModule so that modules which are only used for genesis can take advantage of the Module patterns without having to define many placeholder functions.

# AppModuleBasic

The AppModuleBasic interface defines the independent methods modules need to implement.

Copy // AppModuleBasic is the standard form for basic non-dependant elements of an application module. type AppModuleBasic interface { Name() string RegisterLegacyAminoCodec(*codec.LegacyAmino) RegisterInterfaces(codectypes.InterfaceRegistry) DefaultGenesis(codec.JSONCodec) json.RawMessage ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage) error // client functionality RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux) GetTxCmd() *cobra.Command GetQueryCmd() *cobra.Command }

Let us go through the methods:

  • Name(): Returns the name of the module as a string.
  • RegisterLegacyAminoCodec(*codec.LegacyAmino): Registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore.
  • RegisterInterfaces(codectypes.InterfaceRegistry): Registers a module's interface types and their concrete implementations as proto.Message.
  • DefaultGenesis(codec.JSONCodec): Returns a default GenesisState for the module, marshalled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing.
  • ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage): Used to validate the GenesisState defined by a module, given in its json.RawMessage form. It will usually unmarshall the json before running a custom ValidateGenesis function defined by the module developer.
  • RegisterGRPCGatewayRoutes(client.Context, *runtime.ServeMux): Registers gRPC routes for the module.
  • GetTxCmd(): Returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module.
  • GetQueryCmd(): Return the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module.

All the AppModuleBasic of an application are managed by the BasicManager.

# AppModuleGenesis

The AppModuleGenesis interface is a simple embedding of the AppModuleBasic interface with two added methods.

Copy // AppModuleGenesis is the standard form for an application module genesis functions type AppModuleGenesis interface { AppModuleBasic InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage) []abci.ValidatorUpdate ExportGenesis(sdk.Context, codec.JSONCodec) json.RawMessage }

Let us go through the two added methods:

  • InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage): Initializes the subset of the state managed by the module. It is called at genesis (i.e. when the chain is first started).
  • ExportGenesis(sdk.Context, codec.JSONCodec): Exports the latest subset of the state managed by the module to be used in a new genesis file. ExportGenesis is called for each module when a new chain is started from the state of an existing chain.

It does not have its own manager, and exists separately from AppModule only for modules that exist only to implement genesis functionalities, so that they can be managed without having to implement all of AppModule's methods. If the module is not only used during genesis, InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage) and ExportGenesis(sdk.Context, codec.JSONCodec) will generally be defined as methods of the concrete type implementing the AppModule interface.

# AppModule

The AppModule interface defines the inter-dependent methods that modules need to implement.

Copy // AppModule is the standard form for an application module type AppModule interface { AppModuleGenesis // registers RegisterInvariants(sdk.InvariantRegistry) // Deprecated: use RegisterServices Route() sdk.Route // Deprecated: use RegisterServices QuerierRoute() string // Deprecated: use RegisterServices LegacyQuerierHandler(*codec.LegacyAmino) sdk.Querier // RegisterServices allows a module to register services RegisterServices(Configurator) // ConsensusVersion is a sequence number for state-breaking change of the // module. It should be incremented on each consensus-breaking change // introduced by the module. To avoid wrong/empty versions, the initial version // should be set to 1. ConsensusVersion() uint64 // ABCI BeginBlock(sdk.Context, abci.RequestBeginBlock) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate }

AppModules are managed by the module manager. This interface embeds the AppModuleGenesis interface so that the manager can access all the independent and genesis inter-dependent methods of the module. This means that a concrete type implementing the AppModule interface must either implement all the methods of AppModuleGenesis (and by extension AppModuleBasic), or include a concrete type that does as parameter.

Let us go through the methods of AppModule:

  • RegisterInvariants(sdk.InvariantRegistry): Registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted).
  • Route() (deprecated): Returns the route for messages to be routed to the module by BaseApp.
  • QuerierRoute() (deprecated): Returns the name of the module's query route, for queries to be routes to the module by BaseApp.
  • LegacyQuerierHandler(*codec.LegacyAmino) (deprecated): Returns a querier given the query path, in order to process the query.
  • RegisterServices(Configurator): Allows a module to register services.
  • BeginBlock(sdk.Context, abci.RequestBeginBlock): This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module.
  • EndBlock(sdk.Context, abci.RequestEndBlock): This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the staking module). Implement empty if no logic needs to be triggered at the end of each block for this module.

# Implementing the Application Module Interfaces

Typically, the various application module interfaces are implemented in a file called module.go, located in the module's folder (e.g. ./x/module/module.go).

Almost every module needs to implement the AppModuleBasic and AppModule interfaces. If the module is only used for genesis, it will implement AppModuleGenesis instead of AppModule. The concrete type that implements the interface can add parameters that are required for the implementation of the various methods of the interface. For example, the Route() function often calls a NewMsgServerImpl(k keeper) function defined in keeper/msg_server.go and therefore needs to pass the module's keeper as a parameter.

Copy // example type AppModule struct { AppModuleBasic keeper Keeper }

In the example above, you can see that the AppModule concrete type references an AppModuleBasic, and not an AppModuleGenesis. That is because AppModuleGenesis only needs to be implemented in modules that focus on genesis-related functionalities. In most modules, the concrete AppModule type will have a reference to an AppModuleBasic and implement the two added methods of AppModuleGenesis directly in the AppModule type.

If no parameter is required (which is often the case for AppModuleBasic), just declare an empty concrete type like so:

Copy type AppModuleBasic struct{}

# Module Managers

Module managers are used to manage collections of AppModuleBasic and AppModule.

# BasicManager

The BasicManager is a structure that lists all the AppModuleBasic of an application:

Copy // BasicManager is a collection of AppModuleBasic type BasicManager map[string]AppModuleBasic // NewBasicManager creates a new BasicManager object func NewBasicManager(modules ...AppModuleBasic) BasicManager { moduleMap := make(map[string]AppModuleBasic) for _, module := range modules { moduleMap[module.Name()] = module } return moduleMap }

It implements the following methods:

  • NewBasicManager(modules ...AppModuleBasic): Constructor function. It takes a list of the application's AppModuleBasic and builds a new BasicManager. This function is generally called in the init() function of app.go to quickly initialize the independent elements of the application's modules (click here (opens new window) to see an example).
  • RegisterLegacyAminoCodec(cdc *codec.LegacyAmino): Registers the codec.LegacyAminos of each of the application's AppModuleBasic. This function is usually called early on in the application's construction.
  • RegisterInterfaces(registry codectypes.InterfaceRegistry): Registers interface types and implementations of each of the application's AppModuleBasic.
  • DefaultGenesis(cdc codec.JSONCodec): Provides default genesis information for modules in the application by calling the DefaultGenesis(cdc codec.JSONCodec) function of each module. It is used to construct a default genesis file for the application.
  • ValidateGenesis(cdc codec.JSONCodec, txEncCfg client.TxEncodingConfig, genesis map[string]json.RawMessage): Validates the genesis information modules by calling the ValidateGenesis(codec.JSONCodec, client.TxEncodingConfig, json.RawMessage) function of each module.
  • RegisterGRPCGatewayRoutes(clientCtx client.Context, rtr *runtime.ServeMux): Registers gRPC routes for modules.
  • AddTxCommands(rootTxCmd *cobra.Command): Adds modules' transaction commands to the application's rootTxCommand. This function is usually called function from the main.go function of the application's command-line interface.
  • AddQueryCommands(rootQueryCmd *cobra.Command): Adds modules' query commands to the application's rootQueryCommand. This function is usually called function from the main.go function of the application's command-line interface.

# Manager

The Manager is a structure that holds all the AppModule of an application, and defines the order of execution between several key components of these modules:

Copy // Manager defines a module manager that provides the high level utility for managing and executing // operations for a group of modules type Manager struct { Modules map[string]AppModule OrderInitGenesis []string OrderExportGenesis []string OrderBeginBlockers []string OrderEndBlockers []string OrderMigrations []string }

The module manager is used throughout the application whenever an action on a collection of modules is required. It implements the following methods:

  • NewManager(modules ...AppModule): Constructor function. It takes a list of the application's AppModules and builds a new Manager. It is generally called from the application's main constructor function.

  • SetOrderInitGenesis(moduleNames ...string): Sets the order in which the InitGenesis function of each module will be called when the application is first started. This function is generally called from the application's main constructor function.

    To initialize modules successfully, module dependencies should be considered. For example, the genutil module must occur after staking module so that the pools are properly initialized with tokens from genesis accounts, the genutils module must also occur after auth so that it can access the params from auth, capability module should be initialized before all other modules so that it can initialize any capabilities.

  • SetOrderExportGenesis(moduleNames ...string): Sets the order in which the ExportGenesis function of each module will be called in case of an export. This function is generally called from the application's main constructor function.

  • SetOrderBeginBlockers(moduleNames ...string): Sets the order in which the BeginBlock() function of each module will be called at the beginning of each block. This function is generally called from the application's main constructor function.

  • SetOrderEndBlockers(moduleNames ...string): Sets the order in which the EndBlock() function of each module will be called at the end of each block. This function is generally called from the application's main constructor function.

  • SetOrderMigrations(moduleNames ...string): Sets the order of migrations to be run. If not set then migrations will be run with an order defined in DefaultMigrationsOrder.

  • RegisterInvariants(ir sdk.InvariantRegistry): Registers the invariants of each module.

  • RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino): Registers legacy Msg and querier routes.

  • RegisterServices(cfg Configurator): Registers all module services.

  • InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData map[string]json.RawMessage): Calls the InitGenesis function of each module when the application is first started, in the order defined in OrderInitGenesis. Returns an abci.ResponseInitChain to the underlying consensus engine, which can contain validator updates.

  • ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec): Calls the ExportGenesis function of each module, in the order defined in OrderExportGenesis. The export constructs a genesis file from a previously existing state, and is mainly used when a hard-fork upgrade of the chain is required.

  • BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock): At the beginning of each block, this function is called from BaseApp and, in turn, calls the BeginBlock function of each module, in the order defined in OrderBeginBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns an abci.ResponseBeginBlock which contains the aforementioned events.

  • EndBlock(ctx sdk.Context, req abci.RequestEndBlock): At the end of each block, this function is called from BaseApp and, in turn, calls the EndBlock function of each module, in the order defined in OrderEndBlockers. It creates a child context with an event manager to aggregate events emitted from all modules. The function returns an abci.ResponseEndBlock which contains the aforementioned events, as well as validator set updates (if any).

Here's an example of a concrete integration within an application:

Copy // NOTE: Any module instantiated in the module manager that is later modified // must be passed by reference here. app.mm = module.NewManager( genutil.NewAppModule( app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, encodingConfig.TxConfig, ), auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts), vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), capability.NewAppModule(appCodec, *app.CapabilityKeeper), crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper, nil), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), upgrade.NewAppModule(app.UpgradeKeeper), evidence.NewAppModule(app.EvidenceKeeper), params.NewAppModule(app.ParamsKeeper), authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), ) // During begin block slashing happens after distr.BeginBlocker so that // there is nothing left over in the validator fee pool, so as to keep the // CanWithdrawInvariant invariant. // NOTE: staking module is required if HistoricalEntries param > 0 // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) app.mm.SetOrderBeginBlockers( upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, stakingtypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, authz.ModuleName, feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, vestingtypes.ModuleName, ) app.mm.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are // properly initialized with tokens from genesis accounts. // NOTE: The genutils module must also occur after auth so that it can access the params from auth. // NOTE: Capability module must occur first so that it can initialize any capabilities // so that other modules that want to create or claim capabilities afterwards in InitChain // can do so safely. app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, ) // Uncomment if you want to set a custom migration order here. // app.mm.SetOrderMigrations(custom order) app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino) app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter()) app.mm.RegisterServices(app.configurator)

# Next

Learn more about messages and queries