Gas and transaction fees
Contributors
This section describes fees that are paid on L2 starting in Starknet 0.13.0. For information about messaging fees that are paid on L1, see L1 → L2 message fees.
Overall transaction fee
Starting with Starknet v0.13.1, Starknet distinguishes between blocks whose state diffs are sent to L1 as calldata and blocks whose state diffs are sent to L1 as blobs. The l1_da_mode
property in the Starknet block header contains this information. The cost of computation remains the same on both options, but the cost related to data availability differs.
Overall transaction fee with blobs
This section shows the formula for determining a transaction’s fee. The following sections describe how this formula was derived.
The following formula describes the overall fee, \(F\), for a transaction:
where:

\(v\) is a vector that represents resource usage, where each of its entries, \(v_k\), corresponds to different resource types: Cairo steps and number of applications of each builtin.
For more information see Calculation of computation costs.

\(w\) is the
CairoResourceFeeWeights
vector. 
\(n\) is the number of unique contracts updated, which also includes changes to classes of existing contracts and contract deployments, even if the storage of the newly deployed contract is untouched. In other words, \(n\ge\ell\). Notice that \(n\ge 1\) always holds, because the fee token contract is always updated, which does not incur any fee.

\(m\) is the number of values updated, not counting multiple updates for the same key. Notice that \(m\ge 1\) always holds, because the sequencer’s balance is always updated, which does not incur any fee.

\(t\) is the number of L2→L1 messages sent, where the corresponding payload sizes are denoted by \(q_1,...,q_t\).

\(\ell\) is the number of contracts whose class was changed, which happens on contract deployment and when applying the
replace_class
syscall. 
\(D\) is 1 if the transaction is of type
DECLARE
and 0 otherwise. Declare transactions need to post on L1 the new class hash and compiled class hash which are added to the state. 
L2→L1 messaging constants:

\(\text{message_calldata_cost}\) is 1124 gas per 32byte word.

\(\text{l1_log_data_cost}\) is 256 gas.

\(\text{l1_storage_write_cost}\) is the cost of writing to a new storage slot on Ethereum, which is 20,000 gas.

\(\text{log_message_to_l1_cost}\) is 1637 gas.
For more information, see Onchain data: L2→L1 messages.


\(\text{l2_payload_costs}\) is the gas cost of data sent over L2. This includes calldata, code, and event emission. For more details see L2 payloads: calldata, events, and code.

\(\text{felt_size_in_bytes}\) is 32, which is the number of bytes required to encode a single STARK field element.
Overall transaction fee with calldata
This section shows the formula for determining a transaction’s fee. The following sections describe how this formula was derived.
The following formula describes the overall fee, \(F\), for a transaction:
where:

The following constants are defined in the same manner as in the blobbased formula:

\(v, w, n, m, t, \ell, D\)

\(\text{message_calldata_cost}, \; \text{l1_log_data_cost}, \; \text{log_message_to_l1_cost}, \; \text{l1_storage_write_cost}\)

\(\text{l2_payload_costs}\)


\(\text{da_calldata_cost}\) is 551 gas per 32byte word. This cost is derived as follows:

512 gas per 32byte word for calldata.

~100 gas for onchain hashing that happens for every sent word.

a 10% discount, because the sequencer does not incur additional costs for repeated updates to the same storage slot within a single block.


\(240\) is the gas discount for updating the sender’s balance, for the derivation of this number see Onchain data: Storage updates.

\(\text{contract_update_discount}\) is 312 gas, for the derivation of this discount see Onchain data: Storage updates.
When is the fee charged?
The fee is charged atomically with the transaction execution on L2. The Starknet OS injects a transfer of the feerelated ERC20, with an amount equal to the fee paid, the sender equal to the transaction submitter, and the sequencer as a receiver.
Transaction Fee limits
v3 transactions
With v3 transactions, users specify the max amount and max price for each resource. At the time of writing, the only available resource is L1 gas. In the future, we will introduce L2 gas which will be used to price L2 work (as opposed to only charging for the proof verification in L1 gas, which is what happens today).
Deprecated transactions (version < 3)
With older transaction versions, users specify the maximum fee that they are willing to pay for a transaction.
The only limitation on the sequencer, which is enforced by the Starknet OS, is that the actual fee charged is bounded by max_fee
. While not enforced in the proof, the Starknet sequencer usually charges less than max_fee
, as it charges in accordance with the above fee formula.
What do we price
At the time of writing, the following components are contributing to the transaction fee:

Computational complexity: The marginal cost of verifying the transaction on L1, measured in L1 gas.

Onchain data: The cost of posting the state diffs induced by the transaction to L1 (for more details, see data availability). This is measured in L1 gas or L1 data gas, depending on whether or not the L2 block in which the transaction was included uses calldata or blobs.

L2→L1 messages: Messages sent to L1 are eventually sent to the Starknet core contract as L1 calldata by the sequencer; therefore L2 transaction that send L2→L1 messages incur an additional L1 gas cost.

L2 calldata, events and code: From Starknet 0.13.1 onwards, there is a perbyte (or per felt) price for L2 payloads. For more details, see L2 payloads: calldata, events, and code.
Fee units
Each transaction is associated with an estimate of the amount of gas used. Combining this estimate with the price of gas yields the estimated fee.
For transactions prior to v3, the fee is denominated in WEI. For transactions v3 and later, the fee is denominated in STRK.
Fee calculation
Calculation of gas prices
Every 60 seconds, Starknet samples the base price of gas and data gas on L1.
The price of gas on Starknet is set to the average of the last 60 gas price samples, plus 1 gwei.
The price of data gas on Starknet is set to the average of the last 60 data gas price samples. The data gas price on Ethereum is derived from the value of excess_blob_gas
. For more information, see EIP4844.
Computation without builtins
Let’s analyze the correct metric for measuring transaction complexity. For simplicity, we will ignore Cairo’s builtins, and address them later.
A Cairo program execution yields an execution trace. When proving a Starknet block, we aggregate all the transactions appearing in that block to the execution trace.
Starknet’s prover generates proofs for execution traces, up to some maximal length \(L\), derived from the specs of the proving machine and the desired proof latency.
Tracking the execution trace length associated with each transaction is simple. Each assertion over field elements, such as verifying addition/multiplication over the field, requires the same, constant number of trace cells, which is where our "nobuiltins" assumption kicks in: Pedersen occupies more trace cells than addition. Therefore, in a world without builtins, the fee of the transaction \(tx\) is correlated with \(\text{TraceCells}[tx]/L\).
Computation with builtins
In the Cairo execution trace each builtin has its own slot, which is important to consider when determining the fee.
For example, consider that the prover can process a trace with the following limits:
up to 500,000,000 Cairo Steps  up to 20,000,000 Pedersen hashes  up to 4,000,000 signature verifications  up to 10,000,000 range checks 

The proof is closed and sent to L1 when any of these slots is filled.
Suppose that a transaction uses 10,000 Cairo steps and 500 Pedersen hashes. At most 40,000 such transactions can fit into the hypothetical trace (20,000,000/500). Therefore, its gas price correlates with 1/40,000 of the cost of submitting proof.
Notice that this estimate ignores the number of Cairo steps, as it is not the limiting factor, since 500,000,000/10,000 > 20,000,000/500.
With this example in mind, it is possible to formulate the exact fee associated with L2 computation.
The allocation of resources among builtin operations must be predetermined; it is not possible to decide, postexecution, to include only 20,000,001 Pedersen hashes without additional components. This safeguards fairness and prevents manipulation, ensuring integrity in proof generation and fee determination. 
Calculation of computation costs
For each transaction, the sequencer calculates a vector, CairoResourceUsage
, that contains the following:

The number of Cairo steps.

The number of applications of each Cairo builtin. For example, five range checks and two Pedersen hashes.
The sequencer crosses this information with the CairoResourceFeeWeights
vector. For each resource type, either a Cairo step or a specific builtin application, CairoResourceFeeWeights
has an entry that specifies the relative gas cost of that component in the proof.
Going back to the above example, if the cost of submitting a proof with 20,000,000 Pedersen hashes is roughly 5m gas, then the weight of the Pedersen builtin is 0.25 gas per application (5,000,000/20,000,000). The sequencer has a predefined weights vector, in accordance with the proof parameters.
The sequencer charges only according to the limiting factor. Therefore the fee is correlated with:
where \(k\) enumerates the Cairo resource components, that is the number of Cairo steps and builtins used.
The weights are listed in the table Amount of gas used per Cairo step or per each time a Cairo builtin is applied.
Step or builtin  Gas cost 

Cairo step 
0.0025 gas/step 
Pedersen 
0.08 gas/application 
Poseidon 
0.08 gas/application 
Range check 
0.04 gas/application 
ECDSA 
5.12 gas/application 
Keccak 
5.12 gas/application 
Bitwise 
0.16 gas/application 
EC_OP 
2.56 gas/application 
Onchain data components
The onchain data associated with a transaction is composed of three parts

Storage updates

L2→L1 messages

Deployed contracts

Declared classes (only relevant for
DECLARE
transactions, and adds two additional words)
Onchain data: Storage updates
Whenever a transaction updates some value in the storage of some contract, the following data is sent to L1:

two 32byte words per contract

two 32byte words for every updated storage value
For information on the exact data and its construction, see Data availability.
Only the most recent value reaches L1. So the transaction’s fee only depends on the number of unique storage updates. If the same storage cell is updated multiple times within the transaction, the fee remains that of a single update. 
The following formula describes the storage update fee for a transaction:
where:

\(n\) is the number of unique contracts updated, which also includes changes to classes of existing contracts and contract deployments, even if the storage of the newly deployed contract is untouched. In other words, \(n\ge\ell\). Notice that \(n\ge 1\) always holds, because the fee token contract is always updated at the end of each transaction, in order to update the sequencer’s and the sender’s balances. The fee token contract update is not taken into account when computing the fee.

\(m\) is the number of values updated, not counting multiple updates for the same key. Notice that \(m\ge 1\) always holds, because the sequencer’s balance is updated at the end of each transaction. The sequencer’s balance update is not taken into account when computing the fee.

\(\text{contract_update_discount}\) is 312 gas, which is discounted for every updated contract. This discount is a result of the fact that out of the \(2n\) words caused by updating contracts, \(n\) words are short, including at most 6 nonzero bytes:

three bytes for the nonce

two bytes for the number of storage updates

one byte for the class information flag
Taking into account that zero bytes only cost 4 gas, the cost difference between a full 32byte word, which does not contain zeros, and a word with only six nonzero bytes is \(32\cdot16(6\cdot16+26\cdot4)=312\).


\(240\) is the gas discount for updating the sender’s balance, and is derived by assuming the balance requires at most 12 nonzero bytes, which is enough for 1.2B ETH or STRK, resulting in the following discount: \(512(20\cdot4+12\cdot16)=240\).
Improvements to the above pessimistic estimation might be gradually implemented in future versions of Starknet. For example, if different transactions within the same block update the same storage cell, there is no need to charge for both transactions, because only the last value reaches L1. In the future, Starknet might include a refund mechanism for such cases. 
Onchain data: L2→L1 messages
When a transaction that raises the send_message_to_l1
syscall is included in a state update, the following data reaches L1:

L2 sender address

L1 destination address

Payload size

Payload (list of field elements)
Consequently, the gas cost associated with a single L2→L1 message is:
Where:

\(\text{message_calldata_cost}\) is 1124 gas. This is the sum of the 512 gas paid to the core contract on submitting the state update, and 612 gas paid for the submitting of the same word to the verifier contract (which incurs ~100 additional gas for hashing). That is, messages are sent to Ethereum twice.

\(\text{log_message_to_l1_cost}\) is 1637 gas. This is the fixed cost involved in emitting a
LogMessageToL1
event. This event has two topics and a data array, which adds two data words to the event, resulting in a total of \(375+2\cdot 375+2\cdot 256\) gas (log opcode cost, topic cost, and two data words cost). 
\(\text{l1_log_data_cost}\) is 256 gas, which is paid for every payload element during the emission of the
LogMessageToL1
event. 
\(\text{l1_storage_write_cost}\) is 20,000 gas per message which is paid in order to store the message hash on the Starknet core contract. This recording of the message is what later enables the intended L1 contract to consume the message.
Onchain data: Deployed contracts
When a transaction that raises the deploy
syscall is included in a state update, the following data reaches L1:

contract address

number of storage updates and the new nonce

class hash
The first two elements are counted in the number of unique modified contracts, denoted by \(n\) throughout this page. So the only additional word comes from publishing the class hash, which adds 551 gas. For more information, see \(\text{da_calldata_cost}\) in the final formula.
L2 payloads: calldata, events, and code
As of Starknet v0.13.1 onwards, L2 data is taken into account during pricing. This includes:

calldata: this includes transaction calldata (in the case of
INVOKE
transactions orL1_HANDLER
), constructor calldata (in the case ofDEPLOY_ACCOUNT
transactions), and signatures 
events: data and keys of emitted events

ABI: classes abi in
DECLARE
transactions (relevant only forDECLARE
transactions of version ≥ 2) 
CASM bytecode (for all available
DECLARE
transactions, where in version ≥ 2 this refers to the compiled class) 
Sierra bytecode (relevant only for
DECLARE
transactions of version ≥ 2)
The pricing of the above components in terms of L1 gas is given by the following table:
Resource  Gas cost 

Event key 
0.256 gas/felt 
Event data 
0.128 gas/felt 
Calldata 
0.128 gas/felt 
CASM bytecode 
1 gas/felt 
Sierra bytecode 
1 gas/felt 
ABI 
0.032 gas/character 