How Gas Really Works on Arbitrum and Optimism — Real Deployment Data Tells a Surprising Story
Cross-posted on Dev.to · Medium · Mirror.xyz
The Part Nobody Warned Me About
Before I ran this experiment, I believed the standard pitch: deploy on an L2, pay a fraction of what you'd pay on Ethereum mainnet. Simple.
Then I deployed the same contract — Vault.sol — to three networks using Foundry, and checked the receipts.
Arbitrum Sepolia cost more than Ethereum L1.
Not a little more. About 13× more. An L2, designed to be cheaper than Ethereum, charging more than Ethereum itself. That result is what sent me down the rabbit hole this article documents. By the time I understood why it happened, I understood how L2 gas actually works — not the marketing version, but the real mechanics underneath.
Here's everything I found.
2. L1 vs L2 Gas: What You're Actually Paying For
Most developers think of gas as one number: gas_used × gas_price. That's true on Ethereum mainnet. On L2s, it's more complicated — and that complexity is where most of the confusion lives.
How Optimistic Rollups Work
Arbitrum (Nitro stack) and Optimism/Base (OP Stack) are both optimistic rollups. The architecture is straightforward in principle: transactions execute on the L2 sequencer off-chain, the sequencer periodically compresses batches of those transactions, and posts the compressed data back to Ethereum L1 as calldata (or, post-EIP-4844, as blobs).
This means every L2 transaction carries an invisible L1 liability. The sequencer will eventually pay to post your transaction's data to mainnet. That cost gets passed to you.
The Two-Part Gas Bill
Total L2 Fee = L2 Execution Fee + L1 Data Fee
L2 Execution Fee is what you'd expect: gas_used × L2_gas_price. This covers actual EVM computation — opcodes, storage writes, memory expansion.
L1 Data Fee is the portion most developers never think about. It scales with how much calldata your transaction contributes to the batch that gets posted to L1. The exact formula differs per chain:
- Arbitrum Nitro uses a proprietary ArbOS pricing model with separate gas pools for L1 and L2 components.
-
OP Stack (Optimism, Base) uses an
L1Blockpredeploy at a fixed address that tracks current L1 base fee, updated each block.
Why Calldata Dominates
On Ethereum mainnet, a zero byte of calldata costs 4 gas and a non-zero byte costs 16 gas. Those numbers haven't changed significantly post-merge. Your L2 transaction, when it reaches L1 as part of a compressed batch, is essentially calldata. The more calldata you generate, the higher your L1 data fee.
On mainnet under real load, this L1 data cost gets split across thousands of bundled transactions, making it negligible per user. On a quiet testnet, it doesn't get split across much — and that's exactly where my Arbitrum numbers got weird.
3. The Experiment: Deploying Vault.sol Across Three Networks
I deployed the same contract — Vault.sol — to three different networks using Foundry's deployment scripts. Same bytecode, same constructor arguments, same everything. The only variable was the network.
Networks tested:
- Sepolia (Ethereum L1 testnet)
- Arbitrum Sepolia (L2, Nitro stack)
- Optimism Sepolia (L2, OP Stack)
Deployment Results
| Network | Total Paid (ETH) | Gas Used | Avg Gas Price |
|---|---|---|---|
| Sepolia (L1) | 0.000011552793406581 | 7,470,897 | 0.001546373 Gwei |
| Arbitrum Sepolia (L2) | 0.000149435898978 | 7,470,885 | 0.020004333 Gwei |
| Optimism Sepolia (L2) | 0.00000747275272125 | 7,470,885 | 0.00100025 Gwei |
The first thing that jumps out: gas used was nearly identical across all three chains — within 12 units of each other. The EVM ran the same bytecode with the same computational effort everywhere. Every cost difference comes down to one thing: gas price, not gas consumption.
The second thing that jumps out: Arbitrum cost 13× more than Optimism, and more than L1 itself.
4. The Arbitrum Anomaly — And Why It Actually Makes Sense
This is the result that breaks people's mental model of L2s. Let me explain exactly what happened.
Cause 1: Arbitrum's Minimum Gas Floor
Arbitrum enforces a minimum base fee of approximately 0.02 Gwei, even when the network is completely idle. This is intentional — it's a spam protection mechanism that prevents the testnet from being flooded with near-free transactions.
On a real mainnet under real load, this floor sits well below the market-clearing gas price and simply doesn't matter. On a quiet testnet with almost no competing transactions, the floor becomes the price. In my deployment, Arbitrum Sepolia's gas price was sitting exactly at 0.02 Gwei — the enforced minimum, not a market price.
Optimism Sepolia had no equivalent floor. Its base fee had drifted down to 0.001 Gwei — one-twentieth of Arbitrum's floor — because nothing was driving demand up.
Cause 2: L1 Data Posting Overhead at Testnet Scale
Every L2 sequencer bundles transactions and posts them to L1. On mainnet, a single L1 posting might include thousands of user transactions, so each user pays a tiny fraction of the L1 gas cost. On a testnet, the sequencer still has to run and still has to post batches — but there are far fewer transactions to split that cost across.
In this deployment, Arbitrum's data posting overhead was enough to push total cost past what a direct L1 deployment would've cost. Optimism's sequencer happened to be in a more favorable state at the time of my run, so its economics held.
What This Means in Practice
The Arbitrum anomaly is a testnet artifact, not a mainnet reality. Under real congestion, both L2s deliver dramatically lower fees. Here's what the same 7.4M gas deployment looks like under realistic mainnet conditions:
| Scenario | Network | Est. Gas Price | Estimated Total Cost |
|---|---|---|---|
| High-traffic mainnet | Ethereum L1 | ~30 Gwei | ~0.22 ETH (~$600) |
| L2 under load | Arbitrum / Optimism | ~0.1 Gwei | ~0.001 ETH (~$3) |
That's roughly a 200× cost reduction for identical execution. The promise of L2s is real — you just can't verify it on a quiet testnet.
5. How to Analyze Gas Costs in Tenderly (Before You Deploy)
I didn't have Tenderly set up during this experiment — which I now consider a mistake. Running the same deployments through Tenderly's simulator first would have let me see the L1/L2 cost split before touching real (even testnet) ETH.
Here's how to do it properly.
Setting Up a Simulation
- Go to dashboard.tenderly.co and create a free account.
- Navigate to Simulator → New Simulation.
- Select your target network (Arbitrum Sepolia, Optimism Sepolia, etc.).
- Paste your contract bytecode or ABI and set your constructor arguments.
- Run the simulation.
Reading the Gas Profiler
The Gas Profiler tab is where the real value lives. For any L2 transaction, Tenderly breaks down:
- L2 Execution Gas — what the EVM actually consumed running your code
- L1 Data Fee — the estimated cost of including your transaction in the next L1 batch posting
- Per-opcode breakdown — useful for identifying expensive storage writes or loops inside your contract
For a deployment transaction like Vault.sol, you'll typically see the L1 data fee as a significant fraction of total cost — sometimes more than the execution cost itself. That ratio tells you whether optimization effort should go into reducing bytecode size (cuts L1 data fee) or simplifying constructor logic (cuts L2 execution fee).
The Simulation Workflow That Would Have Saved Me Time
Deploy locally (Anvil) → Simulate on Tenderly → Check L1/L2 split → Adjust → Deploy to testnet
Tenderly simulations are free and run in seconds. There's no reason to be surprised by your testnet costs if you run this step first. The Gas Profiler will surface the Arbitrum floor issue, the data posting overhead, and any contract-level inefficiencies before you commit to a real deployment.
6. What the Numbers Actually Teach You
Gas Used ≠ Gas Cost
This is the most important thing the experiment confirmed. Across Sepolia, Arbitrum Sepolia, and Optimism Sepolia, the EVM consumed between 7,470,885 and 7,470,897 gas — within 12 units. Identical computation. Wildly different costs.
What you pay is determined entirely by the fee market of each individual network, not by how hard your contract makes the processor work.
L2 Costs Have Two Components
Execution fees are what you pay the L2 sequencer. Data availability fees are what the sequencer pays to post your transaction batch to L1. On testnets, the data availability component can behave unexpectedly. On mainnet, it gets amortized across enough transactions that it becomes predictable.
When you're analyzing a deployment failure or an unexpectedly expensive transaction, always ask: which component is high? A Tenderly simulation will tell you immediately.
Testnet Economics Are Not Mainnet Economics
Arbitrum's behavior in this experiment is entirely testnet-specific. The 0.02 Gwei floor was enforced. L1 posting overhead wasn't amortized. Under mainnet conditions with real demand, Arbitrum consistently delivers fees well below L1.
Don't benchmark your mainnet cost estimates against testnet deployments without accounting for these distortions.
Rollup Architecture Details Matter
Arbitrum Nitro and OP Stack are both optimistic rollups, but their sequencer designs, fee floors, and pricing models are different enough to produce meaningfully different costs for the same transaction. For large deployments — anything over a million gas — those differences will show up in your receipts.
7. Key Takeaways and Recommended Tools
When you're deploying contracts across multiple chains:
- Always simulate in Tenderly first to see the L1/L2 cost split before committing to testnet or mainnet.
- Don't trust testnet cost comparisons at face value — check whether the L2 you're testing has a gas floor, and whether the sequencer was actively amortizing costs at the time of your run.
- For mainnet cost estimates, use realistic gas prices (30+ Gwei for L1, 0.05–0.1 Gwei for major L2s) rather than scaling from testnet figures.
Tools worth adding to your workflow:
- Tenderly Gas Profiler — simulate before deploying, see L1/L2 cost breakdown, identify hot spots in your contract. Free tier covers most use cases.
-
Foundry
forge snapshot— captures gas usage snapshots and diffs between code changes. Essential for tracking optimization progress in CI. - L2Beat — tracks the current data posting status, security model, and blob usage of every major L2. Use it to understand why costs might differ between Arbitrum and Optimism on any given day.
-
OP Stack
GasPriceOraclepredeploy — on Optimism and Base, query0x420000000000000000000000000000000000000Ffor currentl1BaseFee()andbaseFeeScalar()to calculate expected L1 data fees before submitting a transaction.
8. Conclusion — And What's Next
The experiment I set out to run was simple: deploy the same contract on three networks and compare costs. What I got was something more useful — a concrete demonstration of why L2 costs behave the way they do, and a clear illustration of how testnet conditions can completely invert the expected cost ordering.
Arbitrum costing more than Ethereum L1 on a quiet testnet isn't a bug. It's the fee floor doing its job in an environment it wasn't really designed for. Understanding that distinction is the difference between trusting your testnet benchmarks and being surprised by your mainnet bill.
The full deployment scripts, Foundry configuration, and raw transaction data are in [my GitHub repo — Anjan-thedestroyer/L2-demo-with-erc4626-vault]. Take the scripts, run them yourself, and see if your numbers match.
Spotted something wrong, or got different numbers when you ran this? Drop a comment or find me on [Twitter — APaudel22202].
Top comments (0)