Skip to content

Instantly share code, notes, and snippets.

@julienrbrt
Created June 4, 2025 09:21
Show Gist options
  • Save julienrbrt/6f737f275f784c5540dcbd64dfa982a5 to your computer and use it in GitHub Desktop.
Save julienrbrt/6f737f275f784c5540dcbd64dfa982a5 to your computer and use it in GitHub Desktop.
Comparing Cosmos SDK 0.50 to Cosmos SDK v2

Comparing Cosmos SDK 0.50 to Cosmos SDK v2

Introduction

The Cosmos SDK always been a framework for building blockchain applications. It is the core of the app chain vision. As the ecosystem grows, and users are asking for more features and performance, building upon a battle tested software built in 2016, started to show its limit. Binary Builders started to design and build the future of the Cosmos SDK, for setting tomorrow instead of catching up today.

This document compares the current version 0.50 with the upcoming v2, focusing on challenges, motivations, advantages, and design principles.

How things work today

The Cosmos SDK is today a framework that allows to build sovereign blockchains. It leverages the Tendermint Consensus by using CometBFT, helping Cosmos SDK chains to have single slot finality, an advantage compared to Ethereum.

The Cosmos SDK abstracts the internals of a blockchain application, while letting developers implement and customize their blockchain logic with what we call modules.

The system is architectured in a way that developers should only care about their modules, unless they are power user and want to make use of the ABCI functionalities that an ABCI app, that is the an SDK application, can: such as vote extensions, app mempools, custom prepare/process proposal implementation, etc..

Currently, this implementation of the core layer is done with what we call BaseApp. BaseApp implements an ABCI app, handles storage, process transactions, run pre transaction logic (ante handler), allow to define custom ABCI implementations. Additionally, other part of the SDK aren't flexible has well, such as the server package or the store package.

Note: Cosmos SDK 0.50/0.52 and Cosmos SDK v2 sounds more different than it actually is. Cosmos SDK v2 refers to the core layer of the Cosmos SDK. Modules aren't part of what we call Cosmos SDK v2, they are meant to remain unchanged, with the Cosmos SDK version mattering less for them, thanks to cosmossdk.io/core.

Challenges and pain points of current version

Version 0.50 architecture is centered around the BaseApp, a monolithic component that implements core functionalities such as the Application Blockchain Interface (ABCI) for communication with the consensus engine (CometBFT), service routers for message and query routing, and state management.

BaseApp has been created 9 years ago, and with the major refactors between versions (ABCI v1 to v2), and the expected behavior and bugs (that are now just expected behavior), modifying and extended BaseApp has been really complicated. While possible, the monolith of BaseApp impede the possibility of extensions and improvements that can be done on the SDK.

This leads to:

  • Complexity: Numerous dependencies, making it difficult to manage and maintain. For instance, BaseApp includes a multistore for persisting data and handles transaction decoding, routing, state commitment all put under the umbrella of sdk.Context.
  • Maintenance Difficulties: Frequent breaking changes and compatibility issues make updates challenging, and require a whole new Cosmos SDK release.
  • Lack of Flexibility: Adapting to new use cases often requires forking the repository, which is unsustainable for protocol developers, and prohibit the quick rollout of security fixes or major features.

Motivation Behind v2

The Cosmos SDK today has its sets of current users. Those users are asking more and more for more customization in their chains, such a moving away from ABCI, changing the deep behavior of the SDK without having to maintain a long lived fork, customize some components without touching the others.

Additionally, we wanted the SDK to be ready for the future, attract more users. With new blockchain framework appearing, the Cosmos SDK should keep its leaderposition as blockchain framework. This couldn't be done easily and fast enough with the tech debt that contained baseapp and other components of the SDK.

V0.52 helped with the customization of the modules without having to fork the Cosmos SDK by extracting every module in their own versionned package. This was solved as well with the 0-dependency cosmossdk.io/core package for modules: It is important to note that modules aren't part of the core layer and v2 isn't related to modules.

V2 aims to resolve the core issues by:

  • Breaking down the monolithic structure into modular components for better manageability.
  • Enhancing performance through optimized state management and transaction processing.
  • Increasing flexibility to support different consensus engines and custom configurations, reducing the need for forking.

While not changing how modules are built, hence minimally breaking current users of the Cosmos SDK.

Advantages of v2

Taking the learning from BaseApp, v2 is composed of multiple components:

  • server/v2/stf: state transaction function (previously in baseapp)
  • server/v2: server manager (previously server)
  • server/v2/cometbft: consensus server (previously baseapp and server)
  • server/v2/appmanager state management (previously in baseapp)
  • store/v2: rethinking on the storage layer (previously store)
  • iavl/v2: optimizing IAVL with SQLite (perviously iavl v1)
  • runtime/v2: abstracting module wiring and application wiring for better UX (previously runtime and types/module)

Each component brings several improvements compared to the status quo:

STF

STF is aimed to only execute transaction/blocks. It doesn't have knowledge of modules, similar to baseapp. The difference is that compared to baseapp, STF has no knowledge of state or ABCI. It allows a fundamentally simpler system.

Because STF was made small and understandable, with a simple API:

// StateTransitionFunction is an interface for processing transactions and blocks.
type StateTransitionFunction[T transaction.Tx] interface {
	// DeliverBlock executes a block of transactions.
	DeliverBlock(
		ctx context.Context,
		block *server.BlockRequest[T],
		state store.ReaderMap,
	) (blockResult *server.BlockResponse, newState store.WriterMap, err error)

	// ValidateTx validates a transaction.
	ValidateTx(
		ctx context.Context,
		state store.ReaderMap,
		gasLimit uint64,
		tx T,
	) server.TxResult

	// Simulate executes a transaction in simulation mode.
	Simulate(
		ctx context.Context,
		state store.ReaderMap,
		gasLimit uint64,
		tx T,
	) (server.TxResult, store.WriterMap)

	// Query executes a query on the application.
	Query(
		ctx context.Context,
		state store.ReaderMap,
		gasLimit uint64,
		req transaction.Msg,
	) (transaction.Msg, error)

	// DeliverSims provides an interface for state transitions by sims.
	DeliverSims(
		ctx context.Context,
		block *server.BlockRequest[T],
		state store.ReaderMap,
		simsBuilder func(ctx context.Context) iter.Seq[T],
	) (blockResult *server.BlockResponse, newState store.WriterMap, err error)
}

It is easy to replace to extend the capability of the SDK. Think of transaction parallelism, in baseapp this would be fairly more complex (due to sdk.Context), while with STF, the API can stay the same, and a chain, or the SDK maintainer can create an alternative STF with parallel transaction processing. This should remain facultative for chains, as no everyone wants or can have transaction parallelism.

Appmanager

Appmanager is the bridge between the app / modules and STF. To keep STF simple we didn't want STF to have any knowledge of state.

As can be seen below, Appmanager handles genesis and the rest of the API is very similar to STF, just with extra field for state. Appmanager still has no knowledge of ABCI.

type AppManager[T transaction.Tx] interface {
	TransactionFuzzer[T]

	// InitGenesis initializes the genesis state of the application.
	InitGenesis(
		ctx context.Context,
		blockRequest *server.BlockRequest[T],
		initGenesisJSON []byte,
		txDecoder transaction.Codec[T],
	) (*server.BlockResponse, corestore.WriterMap, error)

	// ExportGenesis exports the genesis state of the application.
	ExportGenesis(ctx context.Context, version uint64) ([]byte, error)

	// DeliverBlock executes a block of transactions.
	DeliverBlock(
		ctx context.Context,
		block *server.BlockRequest[T],
	) (*server.BlockResponse, corestore.WriterMap, error)

	// ValidateTx will validate the tx against the latest storage state. This means that
	// only the stateful validation will be run, not the execution portion of the tx.
	// If full execution is needed, Simulate must be used.
	ValidateTx(ctx context.Context, tx T) (server.TxResult, error)

	// Simulate runs validation and execution flow of a Tx.
	Simulate(ctx context.Context, tx T) (server.TxResult, corestore.WriterMap, error)

	// SimulateWithState runs validation and execution flow of a Tx,
	// using the provided state instead of loading the latest state from the underlying database.
	SimulateWithState(ctx context.Context, state corestore.ReaderMap, tx T) (server.TxResult, corestore.WriterMap, error)

	// Query queries the application at the provided version.
	// CONTRACT: Version must always be provided, if 0, get latest
	Query(ctx context.Context, version uint64, request transaction.Msg) (transaction.Msg, error)

	// QueryWithState executes a query with the provided state. This allows to process a query
	// independently of the db state. For example, it can be used to process a query with temporary
	// and uncommitted state
	QueryWithState(ctx context.Context, state corestore.ReaderMap, request transaction.Msg) (transaction.Msg, error)
}

type appManager[T transaction.Tx] struct {
	// Gas limits for validating, querying, and simulating transactions.
	config Config
	// InitGenesis is a function that initializes the application state from a genesis file.
	// It takes a context, a source reader for the genesis file, and a transaction handler function.
	initGenesis InitGenesis
	// ExportGenesis is a function that exports the application state to a genesis file.
	// It takes a context and a version number for the genesis file.
	exportGenesis ExportGenesis
	// The database for storing application data.
	db Store
	// The state transition function for processing transactions.
	stf StateTransitionFunction[T]
}

It was longly discussed if Appmanager shouldn't be merged in runtime, but runtime/v2 role was more about modules wiring, so it was kept split.

Runtime/v2

Runtime v2 is a fork of runtime made to work with the new core layer. Runtime was created to simplify the application and automatize the module wiring with app wiring.

Due to the tech debt, the components for wiring a chain were split accross multiple packages.

Runtime v2 brought together the functionality of runtime v1 and the module manager under one package. As thanks to app wiring, users shouldn't need to know about the wiring internal of the application. Most of it is boilerplate that can be abstracted.

Runtime v2 did add a way to swap / configure STF and other options, in order to allow power users to extensively configure their chain.

Thanks to the core interfaces, runtime has no knowledge of ABCI. Modules can stay unchanged between a 0.52 app and a v2 app thanks to the cosmossdk.io/core package.

Server/v2

Server/v2 was a complete rethought of how the server part of a Cosmos SDK chain is done. Currently, in v0.50, the server is not flexible. Options and extra methods have been added in the recent months to fit the ever demanding use case of new blockchains (Rollups, protocol developer with a wish of enhanced customizability).

Currently, the server package in the SDK handles:

  • Telemetry
  • GRPC
  • GRPC-Gateway / REST
  • Starting the CometBFT node
  • Configs / Flags
  • Commands
  • Start command
Server components

Extending the server isn't possible without forking, or PRing into the SDK. As ever extending the tech debt isn't sustainable, we took a modular approach. Server components were introduced and the mix of servers within the server package has been split.

Each server component is a small server that contains its own config, port, and logic and commands.

This allowed to fundamentally simplify and compartmentalize the different servers of the SDK, while keeping the same functionalities and providing facultative and easily customizable servers

  • server/v2/api/telemtry
  • server/v2/api/grpc
  • server/v2/api/grpc-gateway
  • server/v2/store
  • server/v2/cometbft (explained later)
  • ...

Any protocol developer can extend the servers and create more servers by simply implementing those interfaces:

// ServerComponent is a server component that can be started and stopped.
type ServerComponent[T transaction.Tx] interface {
	// Name returns the name of the server component.
	Name() string

	// Start starts the server component.
	Start(context.Context) error
	// Stop stops the server component.
	// Once Stop has been called on a server component, it may not be reused.
	Stop(context.Context) error
}

// HasStartFlags is a server component that has start flags.
type HasStartFlags interface {
	// StartCmdFlags returns server start flags.
	// Those flags should be prefixed with the server name.
	// They are then merged with the server config in one viper instance.
	StartCmdFlags() *pflag.FlagSet
}

// HasConfig is a server component that has a config.
type HasConfig interface {
	Config() any
}
Main Server

With all those seperate servers, the main server (server/v2), only needed to handle the start/stop of all servers, the start command and the configuration management. This left the main server easier to understand and more maintenable, and kept extending servers easy because a server developer doesn't need to worry about those.

Server/v2/cometbft

One may have noticed that CometBFT wasn't mentioned until now. This is because previously, CometBFT was tangled at the core of the Cosmos SDK, and split accross two packages: BaseApp and Server.

Thanks to the simplification of the core layer, it was discovered, that the application does not need any knowledge of its consensus. The Cosmos SDK is the execution layer, and can only care about execution and storage (via STF and other packages mentioned above)

Server/v2/cometbft is a server component that implements ABCI. It simply takes the appmanager / runtime/v2 (the app), has a start and stop logic, and consensus specific commands. It keeps all functionalities and customization possibilities that baseapp had today (app mempool, custom prepare/process proposal implementation, etc..)

Thanks to this design, extending CometBFT is an easy task as only server/v2/cometbft needs to be updated. It simplify upgrades, features improvements and performance related to CometBFT or the CometBFT server.

Some notable optimization is the delay of event marshalling until the end, keeping execution faster and allow parallelization of event marshalling. Disabling events does not waste resource like it does on baseapp as well.

Additonally, it allows for chain, as every server component is optional, to easily use another consensus engine (think of Gordian or Metric.io), by simply importing another server component. The user is able to use Cosmos SDK modules, such as IBC, staying part of Cosmos.

Server/v2 sum-up

To sum it up again, v2 introduces several improvements, including:

  • Modular Architecture: Separate modules for consensus, state transition function (STF), server, store, and runtime make the SDK easier to extend and maintain.
  • Improved Performance: A new store layer and a more performant IAVL implementation enhance efficiency, especially for high transaction volumes.
  • Greater Flexibility: Support for various consensus engines, not just CometBFT, allows for broader application use cases.
  • Easier Maintainability: Smaller, independent components reduce maintenance overhead, making updates smoother.

Store/v2 & IAVL/v2

Store/v2 is a rework on the store package of the Cosmos SDK, without a CometBFT dependency and a simplication of how state is handled.

It supports multiple storage backend such as IAVL v1, v2 and can be extended to support more.

Check out ADR-65 for more in depth information.

Conclusion

Cosmos SDK v2, did not only bring performance improvements, better maintainability and a simpler core layer of the SDK. It prepared the Cosmos SDK for the future. A future where the Cosmos SDK is not the only blockchain framework, a future where the Cosmos SDK needs to move faster, and can fit or the current and future use cases of blockchain applications. Ready for the next wave of adoption.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment