Purpose: Explain how the v0.16.0 ante handler routes Structs gameplay transactions and Cosmos staking transactions through a free-gas path while everything else continues to pay fees in ualpha.
structs module except MsgUpdateParams) are free. No fee in ualpha is required, and they execute against a dedicated 20 M free-gas meter that does not consume the block’s normal gas budget.x/staking messages) are also free, against a 40 M free-staking meter, but capped to one free staking tx per address per block.MsgUpdateParams, governance, bank, distribution, etc.) in a single transaction breaks the free path and the whole tx pays normal fees.--gas auto. Gas is metered and rejected on overflow; “free” only means the gas is not paid for, not that it’s unlimited.CheckTx.Cosmos SDK ante decorators run before the message handler executes. Structs’s custom ante chain (app/ante/ante.go) inserts a GasRouterDecorator early in the chain that checks every incoming transaction:
IsFreeTransaction(msgs) – every message URL starts with /structs.structs.Msg AND none of them is MsgUpdateParams – the gas meter is replaced with a free meter capped at FreeGasCap (default 20_000_000).IsFreeStakingTransaction(msgs) – every message URL is one of the six entries in FreeStakingMessages – the gas meter is replaced with a free meter capped at FreeStakingGasCap (default 40_000_000), and a per-address per-block throttle gates how many of these can land.ConditionalMempoolFeeDecorator + ConditionalFeeDecorator, which behave like the upstream SDK fee decorators.Both ConditionalMempoolFeeDecorator and ConditionalFeeDecorator short-circuit when the context flag IsFreeTx(ctx) is set, so free transactions are never charged a fee even if the user accidentally attaches one.
--gas auto (or --gas-adjustment 1.5) is still required because the SDK simulator must produce a gas estimate; if the estimate exceeds the meter cap (free or paid) the tx is rejected with out of gas. Free does not mean infinite.
Anything in KnownStructsMessages (defined in app/ante/maps.go) qualifies for the free-gas path, except MsgUpdateParams (governance). The list covers, as of v0.16.0:
MsgAddress*, MsgAgreement*, MsgAllocation*, MsgPlayer*, MsgProvider*, MsgReactor*, MsgStruct*, MsgSubstation*, MsgPermission* messages.MsgFleetMove, MsgPlanetExplore, MsgPlanetRaidComplete, MsgPlanetUpdateName.MsgGuildCreate, MsgGuildBank*, MsgGuildMembership*, MsgGuildUpdate* (including the seven new UGC messages: MsgGuildUpdateName/Pfp, MsgPlayerUpdateName/Pfp, MsgPlanetUpdateName, MsgSubstationUpdateName/Pfp).Excluded from the free path:
MsgUpdateParams (/structs.structs.MsgUpdateParams) – this is a governance message and must pay normal fees even though it’s in the Structs module.If you want to see the canonical, complete list, read KnownStructsMessages in .references/structsd/app/ante/maps.go.
The chain treats staking as a first-class gameplay action because reactor staking is what creates and powers a player. To remove that economic friction the following six messages are free, against a separate 40 M meter:
cosmos.staking.v1beta1.MsgDelegatecosmos.staking.v1beta1.MsgUndelegatecosmos.staking.v1beta1.MsgBeginRedelegatecosmos.staking.v1beta1.MsgCancelUnbondingDelegationcosmos.staking.v1beta1.MsgCreateValidatorcosmos.staking.v1beta1.MsgEditValidatorConstraints:
StakingThrottleDecorator enforces one free staking transaction per signer address per block. A second free staking tx in the same block from the same address is rejected.DelegatorAddress for delegations, ValidatorAddress for validator messages).Use this for routine MsgDelegate / MsgUndelegate activity. If you need to send several staking ops in one block, batch them into a single tx (as long as it stays purely staking) – the throttle is on free txs, not on the messages inside the tx.
Anything not classified as free runs through the standard fee path:
bank, distribution, gov, slashing, evidence, IBC, etc.MsgUpdateParams in any combination.These pay fees in ualpha like any other Cosmos SDK chain. Set --fees or --gas-prices accordingly.
Even when a transaction is free, the rest of the ante chain still runs:
TxSizeDecorator) – rejects oversize transactions before any state read.MsgCountDecorator) – caps the number of messages per tx.CheckTxThrottleDecorator) – rate limits how many txs a single address can cram into the mempool per block.StructsDecorator – looks up the player for the signing address, applies the static permission check from PermissionMap for messages that use it, enforces the per-player message cap, and short-circuits same-block lastAction collisions for ChargeMessages.ThrottleDecorator – per-object throttles for proof-of-work messages, fleet move, planet explore, address register.StructActivate, StructAttack, StructBuildInitiate, StructDefenseClear/Set, StructMove, StructStealthActivate/Deactivate) – still require positive charge, so you cannot use the free path to side-step the once-per-block-per-object rules.The DynamicPermissionMessages set (e.g. all UGC messages, address/permission management, guild membership voting flows) skips the ante-level permission check and lets the handler enforce the right permission, since the bits depend on runtime fields. This has nothing to do with fees – those messages are still free as long as the tx stays purely Structs.
--gas auto --gas-adjustment 1.5 as mandatory on every structsd tx structs command. The free meter is tight enough that hand-tuning a low gas value is a footgun.MsgPlayerSend (Structs) and cosmos.bank.v1beta1.MsgSend (bank) in the same tx will pay fees. If you need to do both, send two transactions.account_sequence. One in-flight tx per address at a time still applies. Wait for the previous block (~6 s) before broadcasting the next.StakingThrottleDecorator. Wait a block.FreeGasCap, FreeStakingGasCap on HandlerOptions). Treat them as soft-coded numbers; check structsd query params if you suspect they’ve moved.protocols/transactions.md – Transaction broadcast/verify flowtroubleshooting/transaction-issues.md – Common rejections and fixes