Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fee calculation formula #1092

Merged
merged 13 commits into from
Feb 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,40 @@ This section describes fees that are paid on L2 starting in Starknet 0.13.0. For

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 for a transaction:
The following formula describes the overall fee, stem:[F], for a transaction:

// OLD
// [stem]
// ++++
// F = \text{gas_price}\cdot\left(\max_k v_k w_k + 0.9\cdot\text{calldata_cost}\cdot\left(2(n+m) + 3t + \sum\limits_{i=1}^t q_i +3\ell\right)\right)
// F = \text{gas_price}\cdot\left(\max_k v_k w_k + 0.9\cdot\text{calldata_cost}\cdot\left(2(n+m) + 3t + \sum\limits_{i=1}^t q_i +\ell\right)\right)
// ++++

[stem]
++++
F = \text{gas_price}\cdot\left(\max_k v_k w_k + 0.9\cdot\text{calldata_cost}\cdot\left((n+2m-1) + 3t + \sum\limits_{i=1}^t q_i +3\ell\right)+ t\cdot\text{storage_write_cost} + n \cdot 200 + 272\right)
\begin{align}
F = \text{gas_price}\cdot&\Bigg(\max_k v_k w_k + \\
& + \; \text{da_calldata_cost}\left(2n+2(m-1) + \ell + 3t + \sum\limits_{i=1}^t q_i\right)\\
& - \; \text{contract_update_discount}\cdot n - 240 \\
& + \; \text{message_calldata_cost}\cdot\left(3t + \sum\limits_{i=1}^t q_i\right) \\
& + \; \text{storage_write_cost}\cdot t\Bigg)
\end{align}
++++

where:

* stem:[$v$] is a vector that represents resource usage, where each of its entries, stem:[$v_k$], correspond to different resource types: Cairo steps and number of applications of each builtin.
* stem:[$v$] is a vector that represents resource usage, where each of its entries, stem:[$v_k$], corresponds to different resource types: Cairo steps and number of applications of each builtin.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked two folks to take a look at this, and one of them didn't understand what k is. The other person explained that k is a number that corresponds to one of the resources listed in Table 1, so k is a value 1-8, (or 0-7?). Which means that the table should have a column for resource identifier k.

+
For more information see xref:#calculation_of_computation_costs[Calculation of computation costs].
* stem:[$n$] is xref:#storage_updates[the number of unique contracts updated].
* stem:[$m$] is the number of values updated, not counting multiple updates for the same key.
* stem:[$n$] is xref:#storage_updates[the number of unique contracts updated]. Note that this also takes into account contract deployment, even if the storage of the newly deployed contract was untouched, i.e. stem:[$n\ge\ell$].
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$m$] is the number of values updated, not counting multiple updates for the same key. Note that stem:[$m\ge 1$] always holds since the sequencer balance is always updated (this is not charged for).
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$t$] is the number of L2->L1 messages sent, where the corresponding payload sizes are denoted by stem:[$q_1,...,q_t$].
* stem:[$\ell$] is the number of contract deployments.
* stem:[$0.9$] is a 10% cost reduction that considers that the sequencer does not incur additional costs for repeated updates to the same storage slot within a single block.
* stem:[$\ell$] is the number of contracts whose class was changed. This can happen on deployment or on applying the `replace_class` syscall.
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$w$] is the xref:#calculation_of_computation_costs[`CairoResourceFeeWeights`] vector.
* stem:[$\text{calldata_cost}$] is 612 gas per 32-byte word (~100 gas is charged for on-chain hashing the happens for every sent word).
* stem:[$\text{da_calldata_cost}$] is 551 gas per 32-byte word. This cost is derived as follows: 512 gas per 32-byte word for calldata, ~100gas for on-chain hashing that happens for every sent word, and 10% discount because the sequencer does not incur additional costs for repeated updates to the same storage slot within a single block, resulting in 551gas.
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$\text{message_calldata_cost}$] is 512 gas per 32-byte word. For more details, see xref:#l_2→l_1_messages[].
* stem:[$240$] is the gas discount for updating the sender's balance, for the derivation of this number see xref:#storage_updates[].
* stem:[$\text{contract_update_discount}$] is 312 gas, for the derivation of this discount see xref:#storage_updates[].
* stem:[$\text{storage_write_cost}$] is 20,000 gas, the cost of allocating a new storage cell on Ethereum.
* stem:[200] and stem:[272] are gas costs associated with storage updates. For more information, see xref:#storage_updates[].

== When is the fee charged?

Expand Down Expand Up @@ -174,34 +181,28 @@ The following formula describes the storage update fee for a transaction:

[stem]
++++
\underbrace{\textit{gas_price}(\text{calldata_cost} \cdot n + 200 \cdot n)}_{\text{contract addresses + new nonce and number of storage updates
\underbrace{\textit{gas_price}\left(\text{da_calldata_cost} \cdot 2n - \text{contract_update_discount}\cdot n\right)}_{\text{contract addresses + new nonce and number of storage updates
}} \\

+ \\

\underbrace{\textit{gas_price} \cdot (\text{calldata_cost}(2m-1)+272)}_{\text{storage updates}}
\underbrace{\textit{gas_price} \cdot \left(\text{da_calldata_cost}(2(m-1))-240\right)}_{\text{storage updates}}
++++

where:

* stem:[$n$] is the number of unique contracts updated.
* stem:[$m$] is the number of unique values updated. Be aware that the account balance is always updated, because the account needs to pay a fee. A discount is applied to the account balance update to consider that the value of the account balance most likely does not use the full 32 bytes allotted to it.
* stem:[$\text{calldata_cost}$] is 512 gas per 32-byte word.
* stem:[$200$] is the cost of the onchain storage update's meta information and nonce, in gas. This value accounts for the fact that out of 32 bytes in the meta information, only the following six bytes are non-zero:
* stem:[$n$] is xref:#storage_updates[the number of unique contracts updated]. Note that this also takes into account contract deployment, even if the storage of the newly deployed contract was untouched, i.e. stem:[$n\ge\ell$].
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$m$] is the number of values updated, not counting multiple updates for the same key. Note that stem:[$m\ge 1$] always holds since the sequencer balance is always updated (this is not charged for).
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[\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 stem:[$2n$] words caused by updating contracts, stem:[$n$] words are short, including at most 6 non-zero bytes:
+
--
** three bytes for the nonce
** two bytes for storage updates
** two bytes for the number of storage updates
** one byte for the class information flag
--
+
The cost of a non-zero byte is 16 gas, and the cost of a byte of zeros is 4 gas. So 6 non-zero bytes @ 16 gas per byte + 26 bytes of zeros @ 4 gas per byte results in 200 gas.

* stem:[$\text{272}$] is the cost of updating the balance, in gas. This value assumes a balance of at most 12 non-zero bytes, which is enough for 1.2 billion ETH or STRK.
+
The cost of a non-zero byte is 16 gas, and the cost of a byte of zeros is 4 gas. So 12 non-zero bytes @ 16 gas per byte + 20 bytes of zeros @ 4 gas per byte results in 272 gas.


Taking into account that zero bytes only cost stem:[$4$] gas, the cost difference between a full 32-byte word (all none-zero) and a word with only six non-zero bytes is stem:[$32\cdot16-(6\cdot16+26\cdot4)=312$].
stoobie marked this conversation as resolved.
Show resolved Hide resolved
* stem:[$240$] is the gas discount for updating the sender's balance, and is derived by assuming the balance requires at most 12 non-zero bytes (which is enough for 1.2B ETH or STRK), resulting in the following discount: stem:[512-(20*4+12*16)=240$].
stoobie marked this conversation as resolved.
Show resolved Hide resolved

[NOTE]
====
Expand All @@ -220,14 +221,16 @@ When a transaction that raises the `send_message_to_l1` syscall is included in a
* Payload size
* Payload (list of field elements)

Consequently, the fee associated with a single L2→L1 message is:
Consequently, the gas costs associated with a single L2→L1 message is:
stoobie marked this conversation as resolved.
Show resolved Hide resolved

[stem]
++++
\textit{gas_price}\cdot \text{calldata_cost}\cdot(3+\text{payload_size})
\text{storage_write_cost} + \left(\text{da_calldata_cost} + \text{message_calldata_cost}\right)\cdot\left(3+\text{payload_size}\right)
++++

where stem:[$\text{calldata_cost}$] is 512 gas per 32-byte word.
Where stem:[$\text{da_calldata_cost}$] and stem:[$\text{message_calldata_cost}$] are 551 and 512 gas correspondingly.
stoobie marked this conversation as resolved.
Show resolved Hide resolved
The reason messages appear twice in the fee formula (and pay both in stem:[$\text{da_calldata_cost}$] and stem:[$\text{message_calldata_cost}$]),
stoobie marked this conversation as resolved.
Show resolved Hide resolved
is that they are sent to Ethereum twice: once to the STARK verifier contract (where the additional hashing cost is incured) and once when it is sent to the Starknet Core contract as part of the state update transaction.
stoobie marked this conversation as resolved.
Show resolved Hide resolved

[#deployed_contracts]
=== Onchain data: Deployed contracts
Expand All @@ -238,15 +241,7 @@ When a transaction that raises the `deploy` syscall is included in a state updat
* number of storage updates and the new nonce
* class hash

The following formula describes the fee for a single deployment:

[stem]
++++
\textit{gas_price}\cdot 3 \cdot \text{calldata_cost}
++++

where stem:[$\text{calldata_cost}$] is 512 gas per 32-byte word.

For more information on data saved on L1, see xref:architecture_and_concepts:Network_Architecture/on-chain-data.adoc[Data availability].
The first two elements are counted in the number of unique modified contracts which we denote by stem:[$n$] throughout this page (i.e. stem:[$n\ge\ell$]), so the only addition to the cost is coming from publishing the class hash,
stoobie marked this conversation as resolved.
Show resolved Hide resolved
which adds 551 gas (see stem:[$\text{calldata_cost}$] above).
stoobie marked this conversation as resolved.
Show resolved Hide resolved


Loading