Building Cosmos SDK Modules
This guide covers the essential concepts, patterns, and best practices for building custom modules in the Cosmos SDK. Learn how to structure your module, implement keepers, handle messages, and integrate with the module lifecycle.Module Structure
A well-structured module follows the standard Cosmos SDK layout:Core Module Interfaces
Cosmos SDK modules implement theAppModule interface from the core/appmodule package. This interface serves as the foundation for all module functionality.
AppModule Interface
The baseAppModule interface is a tag interface that identifies a struct as an app module:
Extension Interfaces
Modules extend functionality by implementing additional interfaces:HasServices
Register gRPC services for queries and message handling:HasGenesis
Handle genesis state initialization and export:Block Lifecycle Hooks
Modules can hook into different phases of block processing:Keeper Patterns
The keeper is the core component of a module, managing state and business logic. Keepers follow strict access control patterns to ensure security and modularity.Basic Keeper Structure
Keeper Interface Pattern
Define minimal interfaces for keeper dependencies to reduce coupling:Real-World Example: Bank Keeper
The bank keeper demonstrates tiered access control:reference
- Keeper interface hierarchy: ViewKeeper → SendKeeper → BaseKeeper
- Restricted permissions:
WithMintCoinsRestrictionlimits minting capabilities - Module accounts: Special handling for module-owned accounts
- Send restrictions: Composable restrictions using
SendRestrictionFn
Message Handlers
Message handlers process user transactions and update module state. They implement the gRPC service defined in your protobuf files.Message Server Implementation
Real-World Example: Bank Send Message
The bank module’s Send handler demonstrates proper validation and error handling:reference
- Address validation and parsing
- Coin validation (IsValid, IsAllPositive)
- Send restrictions checking
- Blocked address validation
- Telemetry for monitoring
Query Services
Query services provide read-only access to module state:Module Lifecycle
Modules can hook into various stages of block processing to execute custom logic.PreBlock
Runs before BeginBlock. Can modify consensus parameters:BeginBlock
Executes logic at the beginning of each block, before transactions:- Historical data tracking (staking module)
- Time-based state transitions
- Inflation minting (mint module)
- Epoch transitions (epochs module)
EndBlock
Executes logic at the end of each block, after all transactions:- Validator set updates (staking module)
- Proposal tallying (governance module)
- Queue processing (unbonding, redelegations)
- Distribution of rewards
Real-World Example: Staking EndBlock
The staking module’s EndBlock handles validator set changes:State Management
Collections (Modern Approach)
The modern approach uses the collections package for type-safe state management:Store Keys and Prefixes
Define your store keys intypes/keys.go:
Best Practices
1. Keeper Design
- Minimal interfaces: Only expose what’s necessary to other modules
- Validation: Perform thorough validation in message handlers
- Atomicity: Ensure state changes are atomic within a transaction
- Events: Emit events for all state changes for indexing and monitoring
2. Error Handling
3. Events
Emit structured events for all significant state changes:4. Genesis State
Handle genesis initialization and export:5. Testing
Write comprehensive tests for your keeper and message handlers:Module Registration
Implement the AppModule interface inmodule.go:
Resources
- Cosmos SDK Documentation
- Module Tutorial
- Bank Module Source - Reference implementation
- Staking Module Source - Complex lifecycle example
- Collections Package - Modern state management
Next Steps
- Module Examples - Browse existing module implementations
- Testing Modules - Testing strategies and tools
- Module Simulation - Simulation testing for modules