Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7a178aa
Create lifecycle.mdx
evanorti Feb 26, 2026
a295e60
Update lifecycle.mdx
evanorti Feb 26, 2026
d2ed14d
Update lifecycle.mdx
evanorti Feb 26, 2026
e207b0d
Update lifecycle.mdx
evanorti Feb 26, 2026
20aa360
Update lifecycle.mdx
evanorti Feb 26, 2026
377c95a
Update lifecycle.mdx
evanorti Feb 27, 2026
04e6c6f
Update lifecycle.mdx
evanorti Feb 27, 2026
1a7277b
Create modules.mdx
evanorti Feb 27, 2026
4b27c06
Update modules.mdx
evanorti Feb 27, 2026
6558c8a
Update modules.mdx
evanorti Feb 27, 2026
44eb881
Update modules.mdx
evanorti Mar 2, 2026
e8c144a
Update modules.mdx
evanorti Mar 2, 2026
87e2eb1
Create store.mdx
evanorti Mar 2, 2026
cea0e10
Update modules.mdx
evanorti Mar 2, 2026
4c7e41e
Update lifecycle.mdx
evanorti Mar 2, 2026
a4ee5e6
Update store.mdx
evanorti Mar 2, 2026
7f23fcf
Update store.mdx
evanorti Mar 3, 2026
4712727
Update lifecycle.mdx
evanorti Mar 3, 2026
6ef2c5b
Update modules.mdx
evanorti Mar 3, 2026
786e6eb
Update modules.mdx
evanorti Mar 3, 2026
5bd5197
Encoding page (#232)
evanorti Mar 5, 2026
ee13b9b
Create baseapp.mdx
evanorti Mar 5, 2026
eb2fb16
Create context-gas-and-events.mdx
evanorti Mar 5, 2026
4a60d73
Create sdk.mdx
evanorti Mar 5, 2026
ca6f94a
Update baseapp.mdx
evanorti Mar 5, 2026
81599f4
Update context-gas-and-events.mdx
evanorti Mar 5, 2026
d176bdf
Update sdk.mdx
evanorti Mar 5, 2026
c849b02
Update context-gas-and-events.mdx
evanorti Mar 5, 2026
978525b
Rename SDK doc and add concrete code references
evanorti Mar 5, 2026
bc69143
Update baseapp.mdx
evanorti Mar 6, 2026
89b8a8b
Update baseapp.mdx
evanorti Mar 6, 2026
600dfff
Update baseapp.mdx
evanorti Mar 6, 2026
4db92d0
Update baseapp.mdx
evanorti Mar 6, 2026
f447399
Update modules.mdx
evanorti Mar 6, 2026
80c7895
Update encoding.mdx
evanorti Mar 6, 2026
e4387b7
Update store.mdx
evanorti Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,4 @@ When updating documentation:
- Navigation structure must be updated in `docs.json` when adding new pages
- Interactive RPC documentation is generated from the source `methods.mdx` file
- Test findings in `tests/README.md` track documentation accuracy against implementation
- Use relative imports for snippets and components (e.g., `/snippets/icons.mdx`)
- Use relative imports for snippets and components (e.g., `/snippets/icons.mdx`)
214 changes: 214 additions & 0 deletions sdk/v0.53/learn/concepts/baseapp.mdx

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions sdk/v0.53/learn/concepts/context-gas-and-events.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: Execution Context, Gas, and Events
---

In the previous section, you learned how data is serialized and why every validator must encode state identically. This page covers the runtime environment that modules execute within: the context object that carries block metadata and state access, the gas system that limits computation, and the event system that allows modules to emit observable signals.

## What is `sdk.Context`

Every message handler, keeper method, and block hook in the Cosmos SDK receives an `sdk.Context`. It is the execution environment for a single unit of work — a transaction, a query, or a block hook — and carries everything that code needs to read state, emit events, and consume gas.

The `Context` struct is defined in `types/context.go`: {/* cosmos/cosmos-sdk types/context.go:41 — type Context struct { ... } */}

```go
type Context struct {
ms storetypes.MultiStore
chainID string
gasMeter storetypes.GasMeter
blockGasMeter storetypes.GasMeter
eventManager EventManagerI
// ... additional fields
}
```

Context is a value type, not a pointer. It is passed by value and mutated through `With*` methods that return a new copy. This means a module can safely derive a sub-context (for example, with a different gas meter) without affecting the caller's context.

### Block metadata

Context exposes read-only access to the current block's metadata:

- `ctx.BlockHeight()` returns the current block number. {/* cosmos/cosmos-sdk types/context.go:91 — func (c Context) BlockHeight() int64 { return c.header.Height } */}
- `ctx.BlockTime()` returns the block's timestamp. {/* cosmos/cosmos-sdk types/context.go:92 — func (c Context) BlockTime() time.Time { return c.header.Time } */}
- `ctx.ChainID()` returns the chain identifier string. {/* cosmos/cosmos-sdk types/context.go:93 — func (c Context) ChainID() string { return c.chainID } */}
- `ctx.Logger()` returns a structured logger scoped to the current execution context. Modules use this for operational logging (e.g., logging an upgrade activation or an unexpected state) without affecting consensus. {/* cosmos/cosmos-sdk types/context.go:95 — func (c Context) Logger() log.Logger { return c.logger } */}

These values are populated by BaseApp from the block header provided by CometBFT before any block logic runs. Modules read them to implement time-dependent logic (for example, checking whether a vesting period has elapsed) or to tag events with the block height.

`ctx.IsCheckTx()` returns true when the context is being used for mempool validation rather than block execution. {/* cosmos/cosmos-sdk types/context.go:99 — func (c Context) IsCheckTx() bool { return c.checkTx } */} Modules that have logic specific to mempool pre-screening — such as skipping expensive computation during `CheckTx` — can branch on this flag.

### Context and state access

State is accessed through context. The context holds a reference to the multistore, and each keeper opens its own store through the context:

```go
func (k Keeper) GetCount(ctx context.Context) (uint64, error) {
return k.counter.Get(ctx)
}
```

The keeper does not hold a direct reference to the live multistore; it opens its module's store from the context on each call. This is why context must be passed to every keeper method — it is the gateway to the current block's state, the gas meter, and the event manager for that execution unit.

## Gas metering

### What gas measures

Gas is a unit of computation. In the Cosmos SDK, gas accounts for both computation and state access. Every store read, store write, and iterator step costs gas. Complex computations — such as signature verification in the AnteHandler — also cost gas.

The gas system exists to prevent abuse. Without a gas limit, a single transaction could exhaust a node's resources with an unbounded computation or an unindexed state scan.

### Gas limit and the transaction gas meter

Every transaction specifies a gas limit in its `auth_info.fee.gas_limit` field. {/* cosmos/cosmos-sdk proto/cosmos/tx/v1beta1/tx.proto:235 — uint64 gas_limit = 2; — the gas_limit field in the Fee message of AuthInfo */} When BaseApp begins executing a transaction, it creates a `GasMeter` initialized with that limit and attaches it to the context. {/* cosmos/cosmos-sdk types/context.go:244 — func (c Context) WithGasMeter(meter storetypes.GasMeter) Context — used by BaseApp to attach the transaction gas meter to context */}

The `GasMeter` interface provides two key methods: {/* cosmos/cosmos-sdk store/types/gas.go:41 — type GasMeter interface { GasConsumed() Gas; ConsumeGas(amount Gas, descriptor string); ... } */}

```go
type GasMeter interface {
GasConsumed() Gas
ConsumeGas(amount Gas, descriptor string)
// ...
}
```

`GasConsumed` returns the total gas used so far in the current execution unit. {/* cosmos/cosmos-sdk store/types/gas.go:42 — GasConsumed() Gas — returns total gas consumed so far */} `ConsumeGas` adds to the running total and panics with `ErrorOutOfGas` if consumption exceeds the limit. {/* cosmos/cosmos-sdk store/types/gas.go:46 — ConsumeGas(amount Gas, descriptor string) — adds amount to consumed; panics with ErrorOutOfGas if limit exceeded */} {/* cosmos/cosmos-sdk store/types/gas.go:30 — type ErrorOutOfGas struct { Descriptor string } — the error type panicked when gas is exhausted */}

### How gas is consumed

Gas is consumed automatically at the store layer. Every read and write through the `GasKVStore` wrapper charges gas before delegating to the underlying store:

- A `Get` (store read) charges a flat read cost plus a per-byte cost for the key and value. {/* cosmos/cosmos-sdk store/gaskv/store.go:66 — gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, ...) on every Get call */}
- A `Set` (store write) charges a flat write cost plus a per-byte cost for the key and value. {/* cosmos/cosmos-sdk store/gaskv/store.go:80 — gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, ...) on every Set call */}

Modules do not need to manually track gas for ordinary state access — the store layer handles it automatically. Modules call `ctx.GasMeter().ConsumeGas(...)` directly only for computation costs that are not captured by store operations (for example, a module that performs a cryptographic operation outside the store).

### When gas runs out

If gas is exhausted during execution, `ConsumeGas` panics with `ErrorOutOfGas`. {/* cosmos/cosmos-sdk store/types/gas.go:116 — basicGasMeter.ConsumeGas panics with ErrorOutOfGas{descriptor} when g.consumed > g.limit */} BaseApp recovers from this panic, discards all state changes from the transaction, and returns an error to the user. Fees may still be charged for the gas consumed up to the point of failure, but none of the transaction's state changes are committed.

### Block gas limit

In addition to the per-transaction gas meter, there is a block-level gas meter that tracks total gas consumed by all transactions in a block. {/* cosmos/cosmos-sdk types/context.go:54 — blockGasMeter storetypes.GasMeter field in Context struct tracks block-level gas */} The block gas limit prevents a single block from consuming unbounded computation. If a transaction would cause the block's gas total to exceed the limit, it is excluded from the block.

## Events

### What events are

Events are observable signals emitted during transaction and block execution. A module emits events to describe what happened: tokens were transferred, a validator was slashed, a governance proposal passed. Events carry structured key-value data alongside a type string.

Events are not part of consensus state. They are not stored in the KVStore, do not affect the app hash, and are not required for deterministic execution. Instead, they are collected by BaseApp and included in the block result, where indexers, explorers, and relayers consume them.

### EventManager

Modules emit events through the `EventManager`, which is attached to the context: {/* cosmos/cosmos-sdk types/context.go:104 — func (c Context) EventManager() *EventManager { return c.eventManager } */}

The `EventManager` is created fresh for each transaction and collects all events emitted during that execution. {/* cosmos/cosmos-sdk types/events.go:24 — type EventManager struct { events Events } — holds the slice of events accumulated during execution */} {/* cosmos/cosmos-sdk types/events.go:28 — func NewEventManager() *EventManager — creates an EventManager with an empty events slice */}

### Emitting events

Modules emit events using `EmitEvent` or `EmitTypedEvent`:

```go
// emit an untyped event
ctx.EventManager().EmitEvent(sdk.NewEvent(
"increment",
sdk.NewAttribute("new_count", strconv.FormatUint(newCount, 10)),
))
```

`EmitEvent` appends a raw key-value event to the manager's accumulated list. {/* cosmos/cosmos-sdk types/events.go:35 — func (em *EventManager) EmitEvent(event Event) — appends the event to em.events */}

For events backed by protobuf message types, `EmitTypedEvent` serializes the message's fields into event attributes automatically: {/* cosmos/cosmos-sdk types/events.go:57 — func (em *EventManager) EmitTypedEvent(tev proto.Message) error — serializes proto message into an Event with attributes derived from its fields */}

```go
ctx.EventManager().EmitTypedEvent(&types.EventCounterIncremented{
NewCount: newCount,
})
```

Using `EmitTypedEvent` is the modern approach. It provides type safety and makes the event schema explicit through proto definitions, allowing clients to deserialize events back into typed structs.

### Block events and transaction events

Events emitted during `BeginBlock` or `EndBlock` hooks are **block events** — they describe things that happened at the block level (inflation minted, validator updates applied). Events emitted inside a message handler are **transaction events** — they describe what a specific transaction did.

Both types are included in the `FinalizeBlock` response that CometBFT returns to the network, but they are reported separately so clients can distinguish block-level activity from per-transaction activity.

### Who consumes events

Events are consumed outside the node:

- **Block explorers** index events to show users what happened in a transaction (which tokens moved, which validator was slashed, which proposal passed).
- **Relayers** (IBC) subscribe to specific event types to detect packet sends and acknowledgments.
- **Indexers and off-chain services** build queryable databases of chain activity from event streams. Events can also be queried via the node's REST API and WebSocket endpoint.
- **Wallets and UIs** display event data to users as transaction receipts.

Events are included in the block result that CometBFT returns after each block. They are not replayed or reprocessed; once a block is finalized, its events are fixed.

## Putting it together

During transaction execution, context, gas, and events work together as the runtime layer:

```
BaseApp creates Context for the transaction
AnteHandler runs
→ signature verification, fee deduction, gas meter initialized
Message handler runs
→ each store read/write consumes gas via GasKVStore
→ module logic emits events via EventManager
If gas exhausted → panic → state reverted, fees charged for gas consumed
If execution succeeds → state changes committed, events returned in block result
```

The context carries the gas meter and event manager into every keeper call. Gas is consumed transparently at the store layer. Events accumulate and are returned as part of the block result once execution completes.

The next section explains how an SDK application is structured as a codebase: where modules live, what goes in `app/`, and how all the pieces are assembled.
Loading