Skip to main content
Version: v0.52

Cosmos SDK Upgrade Guide: v0.50 → v0.52

Below is a high-level guide on how to upgrade your chain from Cosmos SDK v0.50 to v0.52. This tutorial will walk you through each major change, why it matters, and how to update your code base. Please note that every chain is different, and you may not need to apply every step below.

Note: v0.52 relies on Comet v1


Table of Contents

  1. Prerequisites
  2. Important Changes Overview
  3. Step-by-Step Upgrade Guide
    1. Remove x/params
    2. Update root.go / Client Wiring
    3. Update app.go / Server Wiring
      1. Module Manager
      2. gRPC-Web
    4. Enable (or Skip) gRPC-Web Support
    5. Remove GasConsumptionDecorator and IncreaseSequenceDecorator
    6. Unordered Transactions
    7. Handle Sign Mode Textual
    8. Update Depinject app_config.go / app.yml if Applicable
    9. Protobuf Changes
    10. Refactor Module Imports to cosmossdk.io/x/...
    11. Adopt Core API Interfaces
    12. Simulation Changes
    13. Depinject: Update Usage of Invoke, Provide, Registerappconfig
    14. Genesis Interfaces Updated
    15. Migration to Collections
    16. Vesting Accounts Removal in x/auth
    17. Introducing x/accounts AccountNumber
    18. TX Decoder Setup for x/accounts
    19. Remove x/crisis
    20. Add x/protocolpool if Using x/distribution
    21. Proposal Handler Changes in x/gov
    22. Mint Function Updates in x/mint
    23. Add x/validate
    24. Remove AppmoduleBasic
  4. Preparing the Upgrade Handler (On-Chain)
  5. Testing & Verifying the Upgrade
  6. Conclusion

Prerequisites

  • You have already upgraded your chain to Cosmos SDK v0.50 (as recommended in the official docs, all migrations up to v0.50 should be complete).
  • Your chain code is managed in a repository (e.g., Git) and you are comfortable creating feature branches and pushing upgrades.
  • You have pinned down a plan or block height at which you want to upgrade (via x/upgrade or manual chain restart).
  • You have adequate test coverage and a test network (testnet/local environment) to validate the upgrade before rolling out to production.

Important Changes Overview

  1. x/params module removed – Must migrate any usage of x/params to new module-managed parameters.
  2. Simplified module manager – The “basic module manager” is removed; use module.Manager directly.
  3. Server refactor – The server/v2 updates remove embedded gRPC-Web and unify startup commands.
  4. Unordered Transactions – New feature that allows the chain to process transactions without a strict sequence.
  5. gRPC-Webgrpc-web is no longer embedded; set up a reverse proxy like Envoy if you need it.
  6. x/crisis removal – The crisis module is completely removed.
  7. x/validate – A new module for default ante/post handlers if you want to rely on runtime.
  8. x/accounts – Replaces vesting accounts and handles account numbering.
  9. Protobuf changes – Updated location for Any, removal of cosmossdk.io/api/tendermint.
  10. All modules except auth split off – Modules now import from cosmossdk.io/x/....
  11. Genesis & ADR-070 – Constructors now take context.Context and do not pass around a codec.
  12. Migration to Collections – Some modules adopt collections.ErrNotFound; check for usage changes.
  13. Other refactors – AnteHandlers, textual sign modes, plus smaller naming changes in the simulation framework.

Step-by-Step Upgrade Guide

Below, you can follow along as though you maintain an application called MyApp. Adapt these steps for your own chain.

Remove x/params

The Cosmos SDK removed x/params. In 0.52, parameters must live in their respective modules (module-managed params).

  • Remove all references to x/params from your code:
  • Delete any keepers, references, or param-store usage referencing paramsKeeper.
  • For each module using x/params, replace it with the new approach of storing parameters via that module’s param structure or via cosmossdk.io/core/appmodule.
  • If you have custom modules using params, see the migration reference for how to adapt to module-managed parameters.

Update root.go / Client Wiring

The client package no longer relies on a global bech32 config and uses address codecs and address prefixes via client.Context. This is inline with moving away from the concept of globals in the software.

In cmd/myappd/root.go (or root_di.go if using depinject), ensure you set the address and validator prefixes:

clientCtx = clientCtx.
+ WithAddressCodec(addressCodec).
+ WithValidatorAddressCodec(validatorAddressCodec).
+ WithConsensusAddressCodec(consensusAddressCodec).
+ WithAddressPrefix("cosmos").
+ WithValidatorPrefix("cosmosvaloper")

Additionally, the start command changed slightly:

- server.AddCommands(rootCmd, newApp, func(startCmd *cobra.Command) {})
+ server.AddCommands(rootCmd, newApp, server.StartCmdOptions[servertypes.Application]{})

Update app.go / Server Wiring

Module Manager

What changed • The “basic module manager” is removed. You should directly use module.Manager.

Action • Remove references to the BasicModuleManager or “basic module manager” in your app.go. • Instead, invoke RegisterLegacyAminoCodec and RegisterInterfaces on app.ModuleManager.

-app.BasicModuleManager = module.NewBasicManagerFromManager(...)
-app.BasicModuleManager.RegisterLegacyAminoCodec(legacyAmino)
-app.BasicModuleManager.RegisterInterfaces(interfaceRegistry)
+app.ModuleManager.RegisterLegacyAminoCodec(legacyAmino)
+app.ModuleManager.RegisterInterfaces(interfaceRegistry)

gRPC-Web

The embedding of gRPC-Web was removed. if you would like to continue using gRPC-Web you will need to use Envoy

Enable (or Skip) gRPC-Web Support

If you need gRPC-Web, follow the sample envoy.yaml config in the changelog snippet. Otherwise, you can skip this step.

Remove GasConsumptionDecorator and IncreaseSequenceDecorator

The GasDecorator and Sequence decorator were merged into the SigVerificationDecorator. This change reduces the gas consumed by transactions.

Unordered Transactions

v0.52 introduces unordered transactions, this feature allows transcations to not need sequence numbers and has its own replay protection system.

  • Important: This is optional.

Depinject / servertypes.AppOptions: Supply the appOpts to enable it automatically:

depinject.Supply(
appOpts,
logger,
)

Legacy wiring: If you do want unordered transactions but are not using depinject, see the “Step-by-step wiring” block from the changelog:

1.  Create and start unorderedtx.NewManager in your App constructor.
2. Add ante.NewUnorderedTxDecorator(...) near the start of your AnteHandler chain.
3. Register a Snapshotter if using snapshots.
4. Call app.UnorderedTxManager.OnNewBlock() in your PreBlocker().
5. Close the manager in app.Close().

Handle Sign Mode Textual

With the split of x/auth/tx/config in two (x/auth/tx/config as depinject module for txconfig and tx options) and x/validate, sign mode textual is no more automatically configured when using runtime (it was previously the case). For the same instructions than for legacy app wiring to enable sign mode textual (see in v0.50 UPGRADING documentation).

Update Depinject app_config.go / app.yml if Applicable

With the introduction of environment in modules, depinject automatically creates the environment for all modules. Learn more about environment here . Given the fields of environment, this means runtime creates a kv store service for all modules by default. It can happen that some modules do not have a store necessary (such as x/auth/tx for instance). In this case, the store creation should be skipped in app_config.go:

  • In your app_config.go (or app.yml), if you have modules like x/auth/tx that do not need a KV store, skip them:
InitGenesis: []string{
"..."
},
+ // SkipStoreKeys is an optional list of store keys to skip when constructing the
+ // module's keeper. This is useful when a module does not have a store key.
+ SkipStoreKeys: []string{
+ "tx",
+ },

Protobuf Changes

The cosmossdk.io/api/tendermint package has been removed as CometBFT now publishes its protos to buf.build/tendermint and buf.build/cometbft

The codectypes.Any has moved to github.com/cosmos/gogoproto/types/any. Module developers need to update the buf.gen.gogo.yaml configuration files by adjusting the corresponding opt option to Mgoogle/protobuf/any.proto=github.com/cosmos/gogoproto/types/any for directly mapping theAny type to its new location:

version: v1
plugins:
- name: gocosmos
out: ..
- opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types,Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm
+ opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/gogoproto/types/any,Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm
- name: grpc-gateway
out: ..
opt: logtostderr=true,allow_colon_final_segments=true

  • Replace any usage of AnyUnpacker and UnpackInterfacesMessage from cosmos/cosmos-sdk/codec/types with the equivalents in github.com/cosmos/gogoproto/types/any.

Refactor Module Imports to cosmossdk.io/x/

All modules except auth have been split into their own go.mod and are imported via cosmossdk.io/x/<mod>.

  • Replace import paths from github.com/cosmos/cosmos-sdk/x/{moduleName} to cosmossdk.io/x/{moduleName}.
  • Example:
- import govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
+ import govtypes "cosmossdk.io/x/gov/types"

Adopt Core API Interfaces

Core API has been introduced for modules since v0.47. With the deprecation of sdk.Context, we strongly recommend to use the cosmossdk.io/core/appmodule interfaces for the modules. This will allow the modules to work out of the box with server/v2 and baseapp, as well as limit their dependencies on the SDK.

Additionally, the appmodule.Environment struct is introduced to fetch different services from the application. This should be used as an alternative to using sdk.UnwrapContext(ctx) to fetch the services. It needs to be passed into a module at instantiation (or depinject will inject the correct environment).

Example:

app.CircuitKeeper = circuitkeeper.NewKeeper(runtime.NewEnvironment(runtime.NewKVStoreService(keys[circuittypes.StoreKey]), logger.With(log.ModuleKey, "x/circuit")), appCodec, authtypes.NewModuleAddress(govtypes.ModuleName).String(), app.AuthKeeper.AddressCodec())

Interface Registry

  • Replace codec.InterfaceRegistry usage in the HasRegisterInterfaces function with cosmossdk.io/core/registry.InterfaceRegistrar. Same for Amino with cosmossdk.io/core/registry.AminoRegistrar.
  • Example:
-func (AppModule) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
+func (AppModule) RegisterInterfaces(registry registry.InterfaceRegistrar) {

Amino Codec Registry

  • Replace codec.LegacyAmino usage in the HasAminoCodec function with cosmossdk.io/core/registry.AminoRegistrar.
  • Example:
-func (AppModule) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+func (AppModule) RegisterLegacyAminoCodec(registrar registry.AminoRegistrar) {

Simulation Changes

  • MsgSimulatorFn now returns an error and no longer has sdk.Context; it instead passes in an address.Codec.
    • Example:
-type MsgSimulatorFn func(r *rand.Rand, ctx sdk.Context, accs []Account) sdk.Msg
+type MsgSimulatorFn func(r *rand.Rand, accs []Account, cdc address.Codec) (sdk.Msg, error)

The interface HasProposalMsgs has been renamed to HasLegacyProposalMsgs, as we've introduced a new simulation framework, simpler and easier to use, named simsx.

Depinject: Update Usage of Invoke, Provide, Register → appconfig

Previously cosmossdk.io/core/appconfig held functions Invoke, Provide and Register were moved to cosmossdk.io/depinject/appconfig. All modules using dependency injection must update their imports.

import {
- "cosmossdk.io/core/appconfig"
+ "cosmossdk.io/depinject/appconfig"
}

Genesis Interfaces Updated

The genesis interfaces in x/{module_name}/module.go were modified to avoid Cosmos SDK dependencies.

Update your modules’ InitGenesis / ExportGenesis to:

- func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) {
+ func (am AppModule) InitGenesis(ctx context.Context, data json.RawMessage) error {
// ...
}
- func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+ func (am AppModule) ExportGenesis(ctx context.Context) (json.RawMessage, error) {
// ...
}

Migration to Collections

Modules within the Cosmos SDK were migrated to use collections. It is not required to migrate your own modules, but it is recommended to use collections for future modules.

Note: When migrating to collections, the previous logic for not found was returning nil with no error. When migrating collections will return collections.ErrNotFound.

Checking for the errors should follow:

if err != nil {
if err == collections.ErrNotFound {
// handle not found
}
// handle other errors
}
  • If your module or code interacts with updated modules, watch for errors like collections.ErrNotFound.

Vesting Accounts Removal in x/auth

The vesting account message and CLI are removed. Existing vesting accounts remain, but you cannot create new ones.

In order to create new vesting accounts you need to add the accounts module to your chain. We provide vesting acccounts by default which have simlar logic to the previous vesting accounts. If you would like custom vesting accounts, they can be implemented in x/accounts.

Introducing x/accounts AccountNumber

x/accounts uses its own account numbering (global) instead of x/auth. The auth module will now use the x/accounts account numbering.

  • In your upgrade handler, if you have existing accounts, you must migrate them:
import authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"

func MyUpgradeLogic(ctx sdk.Context) error {
err := authkeeper.MigrateAccountNumberUnsafe(ctx, &app.AuthKeeper)
if err != nil {
return err
}
return nil
}

TX Decoder Setup for x/accounts

In order to support x/accounts, you need to initialize a TxDecoder.

In app.go:

import (
txdecode "cosmossdk.io/x/tx/decode"
)

func NewApp(...) *App {
// ...
txDecoder, err := txdecode.NewDecoder(txdecode.Options{
SigningContext: signingCtx,
ProtoCodec: appCodec,
})
if err != nil {
panic(err)
}
// ...
}

Remove x/crisis

The x/crisis module was removed entirely and should removed from your application.

  • Remove code references to x/crisis from your application.
        storeUpgrades := store.StoreUpgrades{
Deleted: []string{"crisis"}, // The SDK discontinued the crisis module in v0.52.0
}

Add x/protocolpool if Using x/distribution

The community pool was moved from distribution to a new module called protocolpool. This new module allows teams and the community to create funding opportunities fot the development team and the community.

  • Add protocolpool keeper + store key in your app.go.
  • In your upgrade handler, ensure you add a store for protocolpooltypes.ModuleName. For example:
storeUpgrades := storetypes.StoreUpgrades{
Added: []string{
protocolpooltypes.ModuleName,
},
}

Proposal Handler Changes in x/gov

The proposal handler was updated away from sdk.Context to context.Context. This change is a prerequisite for upgrading to v2. Stay tuned for a separate document explaining how to migrate to v2.

- func ProposalHandler(_ sdk.Context, c Content) error {
+ func ProposalHandler(_ context.Context, c Content) error {

Mint Function Updates in x/mint

The custom mint function allows teams to customize the minting logic for their chain.

  • If you want a custom inflation function or rely on x/mint, set the keeper’s mint function explicitly if you do not use the default:
mintKeeper.SetMintFn(keeper.DefaultMintFn(
types.DefaultInflationCalculationFn,
stakingKeeper,
mintKeeper,
))

Add x/validate

x/validate is a module for antehandlers. If you are using runtime/depinject or v2 this module is required. Skip this step or define your own custom ante/post handlers, see x/validate documentation for more details.

Remove AppmoduleBasic

In 0.52, there is a single entry point for a module. The appmodule is the single entry point. In order to make the necessary changes the appmodulebasic struct in the module.go should be merged with appmodule.

// AppModuleBasic defines the basic application module used by the bank module.
- type AppModuleBasic struct {
- cdc codec.Codec
- ac address.Codec
- }

// AppModule implements an application module for the bank module.
type AppModule struct {
- AppModuleBasic
+ cdc codec.Codec

keeper keeper.Keeper
accountKeeper types.AccountKeeper

// legacySubspace is used solely for migration of x/params managed parameters
legacySubspace exported.Subspace
}

// Name returns the bank module's name.
- func (AppModuleBasic) Name() string { return types.ModuleName }
+ func (AppModule) Name() string { return types.ModuleName }

// RegisterLegacyAminoCodec registers the bank module's types on the LegacyAmino codec.
- func (AppModuleBasic) RegisterLegacyAminoCodec(registrar registry.AminoRegistrar) {
+ func (AppModule) RegisterLegacyAminoCodec(registrar registry.AminoRegistrar) {
types.RegisterLegacyAminoCodec(cdc)
}

// DefaultGenesis returns default genesis state as raw bytes for the bank
// module.
- func (AppModuleBasic) DefaultGenesis() json.RawMessage {
+ func (am AppModule) DefaultGenesis() json.RawMessage {
return am.cdc.MustMarshalJSON(types.DefaultGenesisState())
}

// ValidateGenesis performs genesis state validation for the bank module.
- func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
+ func (am AppModule) ValidateGenesis(bz json.RawMessage) error {
var data types.GenesisState
- if err := cdc.UnmarshalJSON(bz, &data); err != nil {
+ if err := am.cdc.UnmarshalJSON(bz, &data); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
}

return data.Validate()
}

// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the bank module.
- func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
+ func (AppModule) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
panic(err)
}
}

// GetTxCmd returns the root tx command for the bank module.
- func (ab AppModuleBasic) GetTxCmd() *cobra.Command {
+ func (ab AppModule) GetTxCmd() *cobra.Command {
return cli.NewTxCmd(ab.ac)
}

// RegisterInterfaces registers interfaces and implementations of the bank module.
- func (AppModuleBasic) RegisterInterfaces(registry.InterfaceRegistrar) {
+ func (AppModule) RegisterInterfaces(registry.InterfaceRegistrar) {
types.RegisterInterfaces(registry)

// Register legacy interfaces for migration scripts.
v1bank.RegisterInterfaces(registry)
}

Preparing the Upgrade Handler (On-Chain)

If your chain uses x/upgrade to do in-place upgrades:

1.  Create a new upgrade name (e.g. v0.52).
2. Register the upgrade handler in app.go:
```go
func (app *MyApp) RegisterUpgradeHandlers() {
app.UpgradeKeeper.SetUpgradeHandler(
"v0.52",
func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) {
// Run any store migrations needed (e.g. add "protocolpool" store)
storeUpgrades := storetypes.StoreUpgrades{
Added: []string{protocolpooltypes.ModuleName},
}
// ...
// Then run the module migrations
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), vm)
},
)
}
```
3. Set the upgrade in your governance proposal or simply do a manual upgrade (depending on your chain’s process).
4. On reaching the plan’s height, your node will run the above logic, adding store keys, migrating accounts, etc.

If your chain is small or you do not rely on x/upgrade, you can do a chain halt and manual restart with the new binary after updating your genesis (if needed).

Testing & Verifying the Upgrade

  1. Local test

    • Spin up a local one-node testnet.
    • Import or create some sample data.
    • Execute the upgrade at a known block height.
    • Validate that the chain restarts and that the new features (like x/accounts or x/protocolpool) function correctly.
  2. Integration test

    • If you have a continuous integration (CI) environment, update your go.mod and run all tests.
  3. Public testnet

    • Run a short-lived testnet with the new binary, replicate production environment if possible.

Conclusion

Upgrading from Cosmos SDK v0.50 to v0.52 involves a number of structural and conceptual changes. The major points are: • Removing x/params for new parameter management, • Refactoring module imports to cosmossdk.io/x/..., • Adopting the new environment approach in modules and code, • Handling new or removed modules like x/crisis (removed), x/protocolpool (added), and x/accounts, • Adjusting any custom logic around vesting, ante handlers, simulation, and Protobuf interfaces.

Once completed, your chain will align with the latest design of the Cosmos SDK and be ready to take advantage of new features like unordered transactions, improved parameter management, and a more flexible environment-based architecture.

Good luck with your upgrade! If you run into any issues, check the official Cosmos SDK release notes or ask for help in the Cosmos SDK community channels.