Fees in StarkNet Alpha

StarkNet Alpha launched on Mainnet a few weeks ago, without a transaction fee mechanism, i.e., with L1 gas costs effectively subsidized by StarkWare. We present a method to estimate and collect transaction fees in the current StarkNet Alpha version.

As the network evolves, we expect the way fees are evaluated and collected to evolve as well. We present here a first approximation of calculation and method for collecting fees, with the purpose of refining it as StarkNet evolves. Knowing these parameters will help builders to better plan when designing their StarkNet dApp.

Transaction Cost in StarkNet

Generally, a transaction’s cost depends on several factors:

  1. Data - the amount of data sent to L1:
    1.1 State diff information
    1.2 Number of L2 → L1 messages
    1.3 The L1 → L2 messages that were consumed
    These are sent as call data to L1 in different StarkNet L1 transactions.
  2. Cost of computation - this is the count of trace steps, including the OS steps, i.e., system calls.
    It includes:
    1. L1 proof verification
    2. L2 computation cost (SHARP)
  3. Storage cost: how many read/write operations were performed during the transaction execution.
  4. L2 network transmission cost for the transaction: its call data and events emitted.

Current Mechanism

At a high level, the current solution lets users/applications use the sequencer to get an estimate for the fee required to accept the transaction:

A user asks a new service (”estimate gas service”) to estimate the fee required to run the given transaction at the current state. The service estimates and returns the result based on the formula below. This service is exposed by the sequencer. Internally, it will try to estimate the resources consumed by processing the transaction based on the current StarkNet state.

As explained below, the estimation will be a first and somewhat rough approximation of the transaction cost. The fee itself will be specified in ETH - the payment currency - with the gas price provided as a parameter of the estimated service.

At this point, the user can choose whether to accept the estimated fee or mark it up/down. The user then signs the fee she’s willing to pay, as part of the transaction and sends it to the sequencer to be executed and added to a future block.

The Cost Formula

Our first approximation for the fee considers some of the external costs that the service provider (sequencer+prover) pays, like L1 gas for data and computation (items 1 and 2.1 above). The fee calculated may include a markup to compensate for L1 gas cost fluctuations.
At this point, we neglect L2 costs for proof creation, storage operations, and network (items 2.2, 3, 4 above).

The formula, therefore, for calculating the cost, at this phase, will be:

\begin{aligned} gas\_price \cdot &( \\ \sharp(msgs) \cdot &5 \cdot 10^3 \\ + gas\_&per\_byte \cdot ( \\ &\sharp(msgs) \cdot bytes\_per\_msg \\ &+ \sharp(state\_diff\_items) \cdot 2 \cdot 31 ) \\ + gas\_per&\_step \cdot steps_{txn} ) \end{aligned}

where:

  1. gas\_price is the gas price, ETH per gas unit, configured in the service.
    This may be later exposed for querying through an API.
  2. gas\_per\_byte is the gas cost of storing a single byte as call data, currently 16 gas per byte.
  3. \sharp(msgs) is the number of messages sent by the transaction from L2 to L1.
  4. bytes\_per\_msg is the number of bytes of the message. This could of course be different per message, so we may use an average message size for all messages.
    The 5000 extra gas is for updating an internal counter.
  5. \sharp(state\_diff\_items) is the number of state diffs done by this transaction.
    The 2 \cdot 31 factor comes from writing two field elements per storage write, each field element consisting of 31 bytes.
  6. gas\_per\_step is a constant pricing for a single step validation (on L1).
    steps_{txn} is the number of steps for this function’s execution.

:exclamation: The main factor driving the cost is the L1 data component. This includes the state diff and the messages. In other words, the computation cost will be negligible compared to the cost of data posted on L1 (gas\_per\_step << gas\_per\_byte).

The Road Ahead

We see several possible ways this mechanism might change in the future.

First, we expect to make several optimizations that will reduce the cost. Specifically, when several transactions share the same proof, they share some overhead and therefore might cost less to produce and verify. In such a case, some reduction may be applied to the fees estimated for the original transactions.

Introducing volition for data availability (i.e. off-chain data availability) should provide another opportunity for cost savings, and we expect we will be able to factor it into the cost estimation. Storage modifications done for L2-based data may bear different costs.

When the network becomes decentralized, we expect other mechanisms and market designs to apply, the details of which are currently being researched. An example would be to make fee auction possible - users will specify the fee as some linear combination of the different cost components, and allow the sequencer to pick the transaction with the most beneficial fee.

Finally, introducing fee abstraction will allow network participants to specify fees in tokens other than ETH.

22 Likes

Do you have any idea on the size that the markup would need to be to compensate for fluctuations? I was just thinking another option could be to forecast the gas cost at the time the batch will be submitted to L1, this way one might be able to get away with a small markup or none at all.

4 Likes

Is 5000 going to be a minimum gas consumed by every transaction on Starknet?

1 Like

A common pattern in Solidity is to del memory that’s not used anymore to save user’s gas. I guess on StarkNet that’s not the case and so it will be cheaper to have our smart contracts use a deleted flag over zeroing the old values we want to delete?

4 Likes

Thanks for this , a couple of questions

  • Estimate Gas Service: This introduces a point of centralisation to the StarkNet system. Would be it possible to open it up to third party services to observe it?
  • Implementation: I think it would be best to incentivise Wallet providers to handle fee estimation, similar to how it works on Etherum.
  • Currency: What currency would the fee be payable in?
1 Like

I think the 5000 referenced in the original post is just an extra gas cost per L2->L1 message, so it probably won’t apply to starknet tx that aren’t sending messages over the bridge.

5 Likes

Nice post, thanks! My questions:

  • One of the most quoted benefits of StarkNet is the state diff aggregation: only 1 state diff needs to be sent on L1 per batch for the same storage slot no matter how many intermediate changes happened on L2. This batch-wide cost saving is not passed onto users under the current model.

  • How do you determine fee payer? Is it the contract being invoked? What if it’s a contract deployment transaction?

2 Likes

That’s one possibility.
At this point though we’ll start with something simpler that will allow us to get going, and set it manually.

2 Likes

No. The 5000 that appears there is per message sent to L1.
If your transaction doesn’t send messages, this shouldn’t come into the estimate.

2 Likes

We’re still looking into how data will be managed in general, and how we’ll ensure availability.
And the cost of data managed will likely take this into consideration (e.g. if there’s volition on how to manage the data).

I’m not entirely sure I follow your suggestion on the deleted flag. Do you mean to somehow mark a specific storage variable as deleted? or something else?

2 Likes

Right, but note this is for the current Alpha release, which still relies on a central sequencer.

I don’t see a fundamental issue with other providers having a similar functionality; but for the estimation to be good enough, you’d have to rely on the current state.


For now ETH.

1 Like

Indeed, that’s the optimization mentioned as an example of one way to evolve this.


We plan on using some form of account abstraction, so transaction invocation will go through account contracts (“wallet contracts”). The details are still being debated.
As for deployment, this is indeed an open point we’ll need to address.

2 Likes

For example if I store a list of elements I will have two storage variables: one with the number of elements in the list, the other (indexed by index) with the actual elements. If I want to delete the list I just set the length to 0 without zeroing each individual element. In solidity I would del the second mapping (the one with elements) to save some gas to the user.

2 Likes

@fracek
Ok, I see now.
At this point yes, it would probably be cheaper to use such a flag.

1 Like

Units does not match: 5*10^3 seems to be in gas units while it is added to bytes_per_msg which is in bytes.

5 Likes

Correct, editing (need more characters)

2 Likes

Right. Thanks for bringing it up.

1 Like

That’s a good point. The problem is that currently we don’t have full nodes available for anyone to run. The current solution is a way to bridge the gap and is temporary. This is a good point and we should probably mention it explicitly in the post.

The first version of pathfinder (rust full node) is aimed for the end of the month. Many API services will start integrating it once it’s out. This will allow us to change the flow and to estimate gas locally or through the API services, by executing the transaction in the full node.

2 Likes

Well done. Progress everyday.

2 Likes

Great progress! I’d like to second the concerns around gas estimation services since any downtime could really harm the network.

why an average instead of dynamic for each message?

do you mean the number of state updates or the size of the state diff?

does this mean that the current tx ordering is still FIFO? so MEV is going to be a latency game more than anything. Have you looked into proposer-builder separation? do you think that would make any sense in here?

1 Like