For the complete documentation index, see llms.txt. This page is also available as Markdown.

Sustained Use Gas Discounts

Somnia automatically reduces the base gas price paid by accounts that consistently consume a lot of gas. The more gas you use over a sustained period, the less you pay for each unit of gas.

There is nothing to opt into, claim, stake or sign. Discounts are computed by the network from observed gas usage and applied automatically when your transactions execute.

How the discount is applied

A discount is a percentage off the base gas price only. Any priority fee you pay is added on top, undiscounted:

price charged per gas = base_gas_price * (100 - discount_percent) / 100  +  priority_fee

Discounts never reach 100% - gas is never free.

The discount changes the price you pay per unit of gas. It never changes how many units of gas an operation costs, and it does not raise any gas limit.

How usage is measured

Usage is tracked per sender address - the from of the transaction, which is the account that pays. Gas consumed by contracts you call counts toward your usage, not theirs.

Somnia divides time into epochs. On mainnet and testnet an epoch is 3000 ledger blocks, which is roughly 5 minutes of wall-clock time.

  • Every epoch, the gas used by each account is tracked. An account is only recorded for that epoch if it consumed more than ten million gas units in that epoch, and only the 1000 heaviest gas consumers of the epoch are recorded.

  • Your discount is derived from your total tracked gas across the most recent 16 epochs - a rolling window of roughly 80 minutes. As each epoch passes, the oldest epoch's usage drops out of the window.

  • That rolling total is compared against a table of tiers. Each tier names a total amount of gas and the discount earned by reaching it; you get the discount of the highest tier your rolling total reaches. Below the lowest tier, your discount is zero.

A newly earned discount applies to every transaction you send from the start of the next epoch, so it takes effect at the next 5-minute boundary and reflects roughly your last 80 minutes of activity.

Current tiers

The tier table on mainnet:

Gas units used over the rolling window
Discount

900,000,000

70%

4,000,000,000

90%

At a base gas price of 6 gwei, sustaining those rates costs roughly 100 SOMI and 430 SOMI per day on gas at the undiscounted price.

Tiers are network configuration and may be retuned over time; the authoritative table is always the live one returned by getTiers() (see Reading discounts on chain).

Which transactions accrue usage

Transaction type
Accrues usage
Receives a discount

Ordinary transactions you sign and send

Yes

Yes

Transactions that revert or run out of gas

Yes - you paid for the gas

Yes

Reactivity handler executions

Yes, to the subscription owner

Yes

Reactivity subscription deactivations

No - they consume no chargeable gas

n/a

Protocol-level and zero-gas transactions

No

No

Because a Reactivity subscription owner may be a contract, contract accounts can accrue usage and earn discounts for the handler executions they pay for.

Keeping and losing a discount

Your discount is derived from a rolling window, not from a single epoch, so it is stable across quiet periods. If you stop transacting entirely, your usage ages out gradually and your discount only reaches zero after the full window - 16 epochs, roughly 80 minutes - has passed with no activity.

There is no penalty for bursty traffic. Usage accumulates into the window whenever you are busy, and a spike is credited in full.

What this means for sending transactions

Concretely:

  • Transaction admission is undiscounted. A transaction whose gasPrice is below the current base fee is rejected from the mempool, whatever your discount. Keep quoting eth_gasPrice (or your wallet's estimate) as normal.

  • Balance requirements are undiscounted while pending. The node reserves gasPrice * gas at the full price for in-flight transactions, so you need to be able to cover the undiscounted cost even though you will be charged less.

  • You are charged the discounted price. The saving shows up as a smaller balance deduction, and in effectiveGasPrice on the receipt.

The practical upshot for a dApp is that no change to your submission logic is needed. Fee estimation, gasPrice selection and balance checks all continue to work against the undiscounted base fee; your users simply pay less.

RPC behaviour

No new RPC methods or response fields are involved. Existing fields reflect the discount where - and only where - they describe an actual charge.

Method / field
Reflects the sender's discount?

effectiveGasPrice on a transaction receipt

Yes - the price actually charged. This is the authoritative record.

gasPrice on eth_getTransactionByHash / ...ByBlock...

Yes - note this reports the price charged, which for a discounted sender is lower than the gasPrice you submitted.

debug_traceTransaction, debug_traceBlockByHash, debug_traceBlockByNumber

Yes - replayed with exactly the discount that was charged at the time.

eth_call, eth_estimateGas, eth_createAccessList against latest / pending

Yes - simulated with the from account's current discount.

eth_call or debug_traceCall pinned to a past block

No - historical simulations deliberately run undiscounted.

eth_gasPrice

No - the request has no sender, so it returns the network base price.

eth_feeHistory, block.baseFeePerGas

No - these describe blocks, not senders.

eth_maxPriorityFeePerGas

Unchanged (priority fees are never discounted).

Because receipts record the discount that was applied, a transaction's effectiveGasPrice is reproducible forever, even after the sender's discount has changed or expired.

Reading discounts on chain

Discounts live in the enshrined GasDiscounts contract, at a per-network address. The contract is a UUPS proxy, so the address is stable across upgrades.

Look the address up for the network you are on, rather than hardcoding it, using the somnia_getProtocolParameters method:

The useful read-only views are:

View
Returns

getTiers() → (uint256 gasThreshold, uint256 discountBps)[]

The live tier table. gasThreshold is gas used over the whole rolling window; thresholds are inclusive, and the highest one you reach applies.

getAccountsWithDiscountsForEpoch() → (address[] accounts, uint256[] discountBps, uint256 hotAccountGasThreshold, uint256 maxReportedAccounts)

The discount set active for the current epoch - only accounts earning a non-zero discount, in ascending address order - plus the per-epoch minimum gas and the number of accounts recorded per epoch.

getTrackedAccounts() → (address[] accounts, uint256[] discountBps, uint256[] rollingGasUsed)

Every account with a live usage window, including those not yet earning a discount, with each window's total.

lastReportedEpoch() → uint256

The epoch of the most recent update.

Discount values in these responses are in basis points, where 10000 is 100% - so a 70% discount reads as 7000.

For example, to fetch the tier table and the current discount set:

There is deliberately no per-account lookup function. To find your own discount, read the full set and filter client-side; both arrays are sorted ascending by address, so a binary search is possible. rollingGasUsed from getTrackedAccounts() is each window's total as of the last update, not aged forward to the current epoch.

The simplest way to confirm a discount is being applied to real transactions is to compare effectiveGasPrice on one of your receipts against the block's base gas price.

Events

Event
Emitted

DiscountSetComputed(uint256 indexed epochReceivingDiscounts, uint256 indexed setCounter, uint256 numDiscountedAccounts)

Once per epoch, naming the epoch the new discount set applies to.

HotAccountsReported(uint256 indexed epoch, address[] accounts, uint256[] gasUsedInEpoch)

Once per epoch, with that epoch's tracked usage. Addresses are in the payload, not indexed, so you cannot filter by address.

TiersUpdated(Tier[] newTiers)

When the tier table changes.

ReportingParametersUpdated(uint256 hotAccountGasThreshold, uint256 maxReportedAccounts)

When the per-epoch minimum gas or the number of accounts recorded per epoch changes.

Indexing DiscountSetComputed and TiersUpdated is the cheapest way to keep a dashboard in sync without polling the full set every epoch.

See also

Last updated