Skip to content

Layer 2 Scaling: Rollups, Sidechains and State Channels

DodaTech Updated 2026-06-22 8 min read

In this tutorial, you'll learn Ethereum Layer 2 scaling solutions including optimistic rollups, zk-rollups, sidechains, and state channels that enable faster and cheaper transactions. Why it matters: Ethereum mainnet processes only 15-30 transactions per second, but L2 solutions scale to thousands of TPS while inheriting Ethereum's security, making decentralized applications viable for mass adoption. By the end, you'll understand how each L2 solution works.

Layer 2 (L2) refers to protocols built on top of Ethereum (Layer 1) that process transactions off-chain while using L1 for security and final settlement, dramatically increasing throughput and reducing costs.

Understanding the Scaling Problem

Ethereum's base layer is intentionally slow to maintain decentralization and security. L2 solutions trade different properties for throughput.

Solution TPS Finality Security Model Trust Assumption
Ethereum L1 15-30 ~15 min Full consensus None
Optimistic Rollup 2,000-4,000 7 days Fraud proofs Honest validator
zk-Rollup 5,000-20,000 Minutes Validity proofs Cryptography
Sidechain 1,000-10,000 Seconds Separate consensus Validator set
State Channels 1,000,000+ Instant Multi-sig Counterparty
flowchart TD
  subgraph "Layer 1 - Ethereum"
    L1[Ethereum Mainnet
15 TPS, 12s blocks] end subgraph "Layer 2 - Scaling Solutions" OR[Optimistic Rollup
Arbitrum, Optimism] ZR[zk-Rollup
zkSync, StarkNet] SC[Sidechain
Polygon, Gnosis] CH[State Channels
Raiden, Connext] end subgraph "Execution" OR -->|Batch txs, post to L1| L1 ZR -->|Post validity proofs| L1 SC -->|Periodic checkpoints| L1 CH -->|Final settlement| L1 end subgraph "Benefits" OR --> D1[Low fees, EVM compatible] ZR --> D2[Lowest fees, fast finality] SC --> D3[Fast, standalone security] CH --> D4[Instant, unlimited TPS] end

Optimistic Rollups

Optimistic rollups assume transactions are valid by default and only run computation when challenged.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Simplified optimistic rollup bridge contract
contract OptimisticBridge {
    struct Transaction {
        address sender;
        address recipient;
        uint256 amount;
        uint256 nonce;
    }
    
    struct Batch {
        bytes32 root;
        uint256 timestamp;
        bool finalized;
    }
    
    mapping(uint256 => Batch) public batches;
    uint256 public challengePeriod = 7 days;
    uint256 public batchCount;
    
    event BatchSubmitted(uint256 indexed batchId, bytes32 root);
    event Challenge(uint256 indexed batchId, uint256 indexed txIndex);
    
    function submitBatch(bytes32 _root) external {
        batchCount++;
        batches[batchCount] = Batch(_root, block.timestamp, false);
        emit BatchSubmitted(batchCount, _root);
    }
    
    function finalizeBatch(uint256 _batchId) external {
        Batch storage batch = batches[_batchId];
        require(block.timestamp >= batch.timestamp + challengePeriod, "Challenge period active");
        require(!batch.finalized, "Already finalized");
        
        batch.finalized = true;
    }
    
    function challengeTransaction(uint256 _batchId, Transaction calldata _tx, bytes[] calldata _proof) external {
        // Verify Merkle proof and check if transaction is invalid
        // If valid challenge, reward challenger and revert batch
    }
}

Expected behavior: Operators submit transaction batches to L1. Anyone can challenge fraudulent transactions during a 7-day window. After the challenge period, the batch is finalized.

zk-Rollups

Zero-knowledge rollups generate cryptographic proofs that verify all transactions in a batch are valid.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Simplified zk-rollup verifier interface
interface IVerifier {
    function verifyProof(
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[] memory input
    ) external view returns (bool);
}

contract ZKRollup {
    IVerifier public verifier;
    mapping(uint256 => bytes32) public stateRoots;
    uint256 public stateId;
    
    event StateUpdated(uint256 indexed stateId, bytes32 newRoot);
    
    constructor(address _verifier) {
        verifier = IVerifier(_verifier);
    }
    
    function updateState(
        bytes32 _newRoot,
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c
    ) external {
        uint256[] memory input = new uint256[](1);
        input[0] = uint256(_newRoot);
        
        require(
            verifier.verifyProof(a, b, c, input),
            "Invalid proof"
        );
        
        stateId++;
        stateRoots[stateId] = _newRoot;
        emit StateUpdated(stateId, _newRoot);
    }
}

Expected behavior: Operators generate a zk-SNARK proof that the new state root is valid given the previous state and pending transactions. The proof is verified on-chain in constant time, regardless of batch size.

flowchart LR
  A[Users send
transactions] --> B[Sequencer] B --> C[Off-chain execution
& state update] C --> D[Generate
validity proof] D --> E[Submit proof +
state root to L1] E --> F[L1 verifies proof
in milliseconds] F --> G[State finalized
with L1 security] B --> H[Transaction data
posted to L1] H --> I[Data availability]

Sidechains

Sidechains are independent blockchains with their own consensus, bridged to Ethereum.

const { ethers } = require("ethers");

// Simulating a Polygon sidechain bridge interaction
async function bridgeToSidechain() {
  const mainnetProvider = new ethers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_KEY");
  const polygonProvider = new ethers.JsonRpcProvider("https://polygon-rpc.com");
  
  // Lock tokens on mainnet
  const tokenAddress = "0x...";
  const bridgeAddress = "0x...";
  
  const token = new ethers.Contract(tokenAddress, [
    "function approve(address spender, uint256 amount) returns (bool)",
    "function balanceOf(address) view returns (uint256)]
  ], mainnetProvider);
  
  const bridge = new ethers.Contract(bridgeAddress, [
    "function deposit(uint256 amount) external",
    "function withdraw(uint256 amount) external",
    "function getBalance(address) view returns (uint256)]
  ], mainnetProvider);
  
  // Check balance
  const balance = await token.balanceOf(process.env.USER_ADDRESS);
  console.log("Mainnet balance:", ethers.formatEther(balance));
  
  // Bridge takes ~20-30 minutes for Polygon
  console.log("Bridging to Polygon...");
  console.log("Expected time: 20-30 minutes");
  console.log("Cost: ~0.01 ETH on mainnet + minimal on Polygon");
  
  // Once bridged, interact on Polygon
  const maticBalance = await polygonProvider.getBalance(process.env.USER_ADDRESS);
  console.log("Polygon MATIC balance:", ethers.formatEther(maticBalance));
}

// Expected output:
// Mainnet balance: 10.0
// Bridging to Polygon...
// Expected time: 20-30 minutes
// Cost: ~0.01 ETH on mainnet + minimal on Polygon
// Polygon MATIC balance: 100.0

State Channels

State channels allow participants to transact off-chain instantly, settling only the final state on-chain.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract SimpleStateChannel {
    struct Channel {
        address alice;
        address bob;
        uint256 aliceBalance;
        uint256 bobBalance;
        uint256 nonce;
        uint256 timeout;
        bool closed;
    }
    
    mapping(bytes32 => Channel) public channels;
    
    event ChannelOpened(bytes32 indexed channelId, address alice, address bob);
    event ChannelClosed(bytes32 indexed channelId, uint256 aliceBalance, uint256 bobBalance);
    
    function openChannel(address _counterparty) external payable returns (bytes32) {
        require(msg.value > 0, "Must deposit ETH");
        
        bytes32 channelId = keccak256(abi.encodePacked(msg.sender, _counterparty, block.timestamp));
        
        channels[channelId] = Channel({
            alice: msg.sender,
            bob: _counterparty,
            aliceBalance: msg.value,
            bobBalance: 0,
            nonce: 0,
            timeout: block.timestamp + 1 days,
            closed: false
        });
        
        emit ChannelOpened(channelId, msg.sender, _counterparty);
        return channelId;
    }
    
    function closeChannel(bytes32 _channelId, uint256 _aliceBalance, uint256 _bobBalance, bytes memory _signature) external {
        Channel storage channel = channels[_channelId];
        require(!channel.closed, "Already closed");
        
        bytes32 message = keccak256(abi.encodePacked(_channelId, _aliceBalance, _bobBalance, channel.nonce));
        
        // Verify both parties signed
        address signer = recoverSigner(message, _signature);
        require(signer == channel.alice || signer == channel.bob, "Invalid signature");
        
        channel.closed = true;
        
        payable(channel.alice).transfer(_aliceBalance);
        payable(channel.bob).transfer(_bobBalance);
        
        emit ChannelClosed(_channelId, _aliceBalance, _bobBalance);
    }
}

Expected behavior: Two parties deposit ETH into a channel. They exchange signed state updates off-chain instantly with no gas. When done, the final state is submitted to L1, distributing funds.

Comparing L2 Solutions

Factor Optimistic Rollup zk-Rollup Sidechain State Channel
Withdrawal delay 7 days Minutes Minutes Instant
EVM compatible Yes Limited Yes No
Gas cost $0.01-0.10 $0.001-0.01 $0.001-0.01 ~$0
Security L1 (fraud proofs) L1 (math proofs) Separate Multi-sig
Best for General dApps Payments, DEX Gaming Micro-payments

Common Errors and Misunderstandings

1. Confusing Sidechains with L2 Rollups

Sidechains have their own security model independent of Ethereum. Rollups inherit Ethereum's security. A sidechain validator collusion can steal funds. Rollups cannot.

2. Forgetting 7-Day Withdrawal Delay

Optimistic rollups require a 7-day challenge period for withdrawals. Users bridging back to L1 must wait. Liquidity providers on L2 offer faster exits for a fee.

3. Assuming All L2s Are EVM Compatible

zk-Rollups like zkSync Era and StarkNet have limited EVM compatibility. Optimistic rollups like Arbitrum and Optimism have near-perfect EVM compatibility.

4. Underestimating Data Availability Costs

Rollups must post transaction data to L1. Calldata is expensive. EIP-4844 (proto-danksharding) will significantly reduce these costs.

5. Security Trade-offs in Bridges

Bridges between L2s or between L1 and sidechains are the most attacked components. Bridge hacks account for over $2 billion in losses.

Practice Questions

  1. What is the main difference between optimistic and zk-rollups? Optimistic rollups assume validity (with fraud proofs) and require a challenge period. zk-rollups provide cryptographic validity proofs, enabling instant finality without waiting periods.

  2. Why do optimistic rollups have a 7-day withdrawal delay? To allow time for validators to challenge potentially fraudulent transactions. If a fraud proof is submitted within 7 days, the batch can be reverted.

  3. How does a sidechain differ from a rollup? A sidechain is an independent blockchain with its own validators and consensus mechanism. A rollup posts data to L1 and inherits L1 security. Sidechains have weaker security guarantees.

  4. What is the maximum TPS of state channels? Theoretically unlimited since transactions happen off-chain without global consensus. Practical limits are network latency and channel participant bandwidth.

  5. What EIP improves rollup cost efficiency? EIP-4844 (proto-danksharding) introduces blob-carrying transactions, providing cheaper data availability for rollups by separating L1 execution from data storage.

Challenge

Build a simple optimistic rollup implementation in Solidity with batch submission, Merkle tree state management, fraud proof challenge mechanism, and a 7-day withdrawal delay. Write tests that prove fraudulent transactions are caught within the challenge period.

Real-World Task

Create a dApp that deploys the same ERC-20 token to Ethereum Sepolia, Arbitrum Sepolia, and Polygon Mumbai, then implements a bridge UI using React and ethers.js that allows users to deposit tokens on L1 and receive wrapped tokens on L2 using official bridge contracts from each platform.

Frequently Asked Questions

Which L2 solution should I use for my dApp?

Choose optimistic rollups (Arbitrum, Optimism) for full EVM compatibility and existing tooling. Choose zk-rollups (zkSync, StarkNet) for lower fees and faster finality if your contract can be adapted. Use sidechains (Polygon) for gaming or high-frequency apps where fast block times matter more than maximum security.

Can I move assets between L2s?

Direct L2-to-L2 transfers are not native. Use bridging protocols (Hop, Across, Stargate) or move back to L1 and then to the other L2. Each bridge has different security assumptions and fees.

Will L2 solutions make Ethereum L1 obsolete?

No. L1 provides security, data availability, and final settlement for all L2s. L1 and L2 are complementary: L1 is the settlement layer, L2s are execution layers. Both are essential for scaling Ethereum.

Next Steps

After understanding L2 scaling, explore IPFS for decentralized storage, then dive into oracle solutions for connecting your L2 dApp to real-world data.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro