Gas and transaction fees
In this section, we will review Starknet Alpha fee mechanism. If you want to skip the motivation and deep dive into the mechanism, you can skip directly to the final formula.
This section focuses on fees paid by an account on L2. For information about messaging fees that are paid on L1, see L1 → L2 message fees.
Introduction
Users can specify the maximum fee that they are willing to pay for a transaction via the max_fee
field.
The only limitation on the sequencer (enforced by the Starknet OS) is that the actual fee charged is bounded by max_fee
, but for now, StarkWare’s sequencer will only charge the fee required to cover the proof cost (potentially less than the max fee).
Presently, the sequencer only takes into account L1 costs involving proof submission. There are two components affecting the L1 footprint of a transaction:

Computational complexity: the heavier the transaction, the larger its portion in the proof verification cost.

Onchain data: L1 calldata cost originating from data availability and L2→L1 messages.
Fee units
The fee is denominated in ETH (this may change in future versions). Each transaction is associated with a gas estimate (explained below), and combining this with the gas price yields the estimated fee.
How much fee is charged? Highlevel overview
Computation
Let’s analyze the correct metric for measuring transaction complexity. For simplicity, we will ignore Cairo’s builtins for the sake of the explanation, and later see how to address them.
Without builtins
Recall that 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 (this is where our “nobuiltins” assumption kicks in! Obviously 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\).
Adding builtins
The Cairo execution trace is separated — and each builtin has its own slot. We have to mind this slot allocation when determining the fee.
Let’s go over a concrete example first. For example, imagine the following trace that the prover will occupy:
(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 will be closed and sent to L1 when any of these components becomes full. It’s important to realize that the division to builtins must be predetermined! We can’t decide on the fly to have proof with 20,000,001 Pedersen and nothing else.
Suppose, for example, that a transaction uses 10,000 Cairo steps and 500 Pedersen hashes. We can squeeze at most 40,000 such transactions into our hypothetical trace (20,000,000/500). Therefore, its gas price will be correlated with 1/40,000 of the cost of submitting proof.
Notice we completely ignored the number of Cairo steps in this transaction performance estimation, as it is not the limiting factor (since 500,000,000/10,000 > 20,000,000/500). With this example in mind, we can now formulate the exact fee associated with L2 computation.
General case
For each transaction, the sequencer calculates a vector CairoResourceUsage
holding:

Number of Cairo steps

Number of applications of each Cairo builtin (e.g., five range checks and two Pedersen hashes)
The sequencer crosses this information with the CairoResourceFeeWeights
vector. For each resource type (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 will charge only according to the limiting factor! Therefore the fee is correlated with:
Where \(k\) here enumerates the Cairo resource components, i.e. the number of steps and builtins used.
The weights are:
Step  Gas cost 

Cairo step 
0.01 gas/step 
Pedersen 
0.32 gas/application 
Poseidon 
0.32 gas/application 
Range check 
0.16 gas/application 
ECDSA 
20.48 gas/application 
Keccak 
20.48 gas/application 
Bitwise 
0.64 gas/application 
EC_OP 
10.24 gas/application 
Onchain data
The onchain data associated with a transaction is composed of three parts

Storage updates

L2→L1 messages

Deployed contracts
Storage updates
Whenever a transaction updates a key at the storage of some contract, the following data reaches L1:

Contract_address

Number of updated keys in that contract

Key to update

New value
Note that only the most recent value reaches L1. That is, 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). 
Consequently, the associated storage update fee for a transaction updating \(n\) unique contracts and \(m\) unique keys is:
where \(c_w\) is the calldata cost (in gas) per 32byte word.
Note that there are many possible improvements to the above pessimistic estimation that will be gradually presented 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 both of them (only the latest value reaches L1). In the future, Starknet may include a refund mechanism for such cases. 
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 fee associated with a single L2→L1 message is:
where \(c_w\) is the calldata cost (in gas) per 32byte word.
Deployed contracts
When a transaction that raises the deploy
syscall is included in a state update, the following data reaches L1:

Contract address

Class hash
Consequently, the fee associated with a single deployment is:
where \(c_w\) is the calldata cost (in gas) per 32byte word.
Overall fee
The fee for a transaction with:

Cairo usage represented by the vector \(v\) (the entries of \(v\) correspond to the number of steps and number of applications per builtin)

\(n\) unique contract updates

\(m\) unique key updates

\(t\) messages with payload sizes \(q_1,...,q_t\)

\(\ell\) contract deployments
is given by:
where \(w\) is the weights vector discussed above and \(c_w\) is the calldata cost (in gas) per 32byte word.