Gas and Fees
This document describes the default strategies to handle gas and fees within a Cosmos SDK application.
In the Cosmos SDK,
gas is a special unit that is used to track the consumption of resources during execution.
gas is typically consumed whenever read and writes are made to the store, but it can also be consumed if expensive computation needs to be done. It serves two main purposes:
- Make sure blocks are not consuming too many resources and are finalized. This is implemented by default in the Cosmos SDK via the block gas meter.
- Prevent spam and abuse from end-user. To this end,
messageexecution is typically priced, resulting in a
fees = gas * gas-prices).
feesgenerally have to be paid by the sender of the
message. Note that the Cosmos SDK does not enforce
gaspricing by default, as there may be other ways to prevent spam (e.g. bandwidth schemes). Still, most applications implement
feemechanisms to prevent spam by using the
In the Cosmos SDK,
gas is a simple alias for
uint64, and is managed by an object called a gas meter. Gas meters implement the
GasConsumed()returns the amount of gas that was consumed by the gas meter instance.
GasConsumedToLimit()returns the amount of gas that was consumed by gas meter instance, or the limit if it is reached.
GasRemaining()returns the gas left in the GasMeter.
Limit()returns the limit of the gas meter instance.
0if the gas meter is infinite.
ConsumeGas(amount Gas, descriptor string)consumes the amount of
gasprovided. If the
gasoverflows, it panics with the
descriptormessage. If the gas meter is not infinite, it panics if
gasconsumed goes above the limit.
RefundGas()deducts the given amount from the gas consumed. This functionality enables refunding gas to the transaction or block gas pools so that EVM-compatible chains can fully support the go-ethereum StateDB interface.
trueif the amount of gas consumed by the gas meter instance is strictly above the limit,
trueif the amount of gas consumed by the gas meter instance is above or equal to the limit,
The gas meter is generally held in
ctx, and consuming gas is done with the following pattern:
Main Gas Meter
ctx.GasMeter() is the main gas meter of the application. The main gas meter is initialized in
setFinalizeBlockState, and then tracks gas consumption during execution sequences that lead to state-transitions, i.e. those originally triggered by
FinalizeBlock. At the beginning of each transaction execution, the main gas meter must be set to 0 in the
AnteHandler, so that it can track gas consumption per-transaction.
Gas consumption can be done manually, generally by the module developer in the
Msg service, but most of the time it is done automatically whenever there is a read or write to the store. This automatic gas consumption logic is implemented in a special store called
Block Gas Meter
ctx.BlockGasMeter() is the gas meter used to track gas consumption per block and make sure it does not go above a certain limit. A new instance of the
BlockGasMeter is created each time
FinalizeBlock is called. The
BlockGasMeter is finite, and the limit of gas per block is defined in the application's consensus parameters. By default, Cosmos SDK applications use the default consensus parameters provided by CometBFT:
When a new transaction is being processed via
FinalizeBlock, the current value of
BlockGasMeter is checked to see if it is above the limit. If it is, the transaction fails and returned to the consensus engine as a failed transaction. This can happen even with the first transaction in a block, as
FinalizeBlock itself can consume gas. If not, the transaction is processed normally. At the end of
FinalizeBlock, the gas tracked by
ctx.BlockGasMeter() is increased by the amount consumed to process the transaction:
"block gas meter",
AnteHandler is run for every transaction during
FinalizeBlock, before a Protobuf
Msg service method for each
sdk.Msg in the transaction.
The anteHandler is not implemented in the core Cosmos SDK but in a module. That said, most applications today use the default implementation defined in the
auth module. Here is what the
anteHandler is intended to do in a normal Cosmos SDK application:
- Verify that the transactions are of the correct type. Transaction types are defined in the module that implements the
anteHandler, and they follow the transaction interface:
This enables developers to play with various types for the transaction of their application. In the default
auth module, the default transaction type is
- Verify signatures for each
messagecontained in the transaction. Each
messageshould be signed by one or multiple sender(s), and these signatures must be verified in the
CheckTx, verify that the gas prices provided with the transaction is greater than the local
min-gas-prices(as a reminder, gas-prices can be deducted from the following equation:
fees = gas * gas-prices).
min-gas-pricesis a parameter local to each full-node and used during
CheckTxto discard transactions that do not provide a minimum amount of fees. This ensures that the mempool cannot be spammed with garbage transactions.
- Verify that the sender of the transaction has enough funds to cover for the
fees. When the end-user generates a transaction, they must indicate 2 of the 3 following parameters (the third one being implicit):
gas-prices. This signals how much they are willing to pay for nodes to execute their transaction. The provided
gasvalue is stored in a parameter called
GasWantedfor later use.
newCtx.GasMeterto 0, with a limit of
GasWanted. This step is crucial, as it not only makes sure the transaction cannot consume infinite gas, but also that
ctx.GasMeteris reset in-between each transaction (
ctxis set to
anteHandleris run, and the
anteHandleris run each time a transactions executes).
As explained above, the
anteHandler returns a maximum limit of
gas the transaction can consume during execution called
GasWanted. The actual amount consumed in the end is denominated
GasUsed, and we must therefore have
GasUsed =< GasWanted. Both
GasUsed are relayed to the underlying consensus engine when