Skip to content
All essays
CraftMarch 27, 202614 min

Layer 2 Blockchain Solutions: Scaling Ethereum

Master Layer 2 solutions and scale blockchain applications efficiently

Ü
Ümit Uz
Mobile & Full Stack Developer

Layer 2 solutions scale blockchains by processing transactions off-chain and settling periodically on the main chain.

What are Layer 2 Solutions?

L2s reduce load on Ethereum mainnet by:

  • Processing transactions off-chain
  • Batching multiple transactions
  • Settling final results on-chain
  • Reducing gas fees significantly

Types of Layer 2s

Optimistic Rollups

solidity
// Optimistic Rollup fraud proof
contract FraudProof {
    mapping(bytes32 => bool) public disputedBatches;
    
    function disputeBatch(
        bytes32 batchRoot,
        bytes calldata oldState,
        bytes calldata newState,
        bytes calldata transaction
    ) external {
        // Verify old state matches committed batch
        require(verifyState(batchRoot, oldState), "Invalid old state");
        
        // Execute transaction locally
        bytes memory computedState = executeTransaction(oldState, transaction);
        
        // Check if computed state matches claimed new state
        if (keccak256(computedState) != keccak256(newState)) {
            // Fraud proven!
            disputedBatches[batchRoot] = true;
            emit FraudProven(batchRoot, msg.sender);
        }
    }
}

ZK-Rollups

typescript
// ZK-Rollup proof generation
import { proving } from 'snarkjs';

async function generateZKProof(
    transactions: Transaction[],
    oldStateRoot: string,
    newStateRoot: string
): Promise<string> {
    // Create circuit input
    const input = {
        oldStateRoot,
        newStateRoot,
        transactions: transactions.map(tx => ({
            from: tx.from,
            to: tx.to,
            amount: tx.amount
        }))
    };
    
    // Generate zero-knowledge proof
    const { proof, publicSignals } = await proving.fullProve(
        input,
        'transaction.wasm',
        'transaction.zkey'
    );
    
    // Verify proof
    const vKey = await fetch('verification_key.json').then(r => r.json());
    const verified = await verifying.verifyProof(
        vKey,
        publicSignals,
        proof
    );
    
    if (!verified) {
        throw new Error('Proof verification failed');
    }
    
    return JSON.stringify(proof);
}

Implementation with zkSync

typescript
// zkSync integration
import { Wallet, Provider } from 'zksync-web3';
import { ethers } from 'ethers';

const zkSyncProvider = new Provider('https://zksync2-testnet.zksync.dev');
const wallet = new Wallet('PRIVATE_KEY', zkSyncProvider);

async function deposit() {
    // Deposit ETH to zkSync
    const depositHandle = await wallet.deposit({
        token: utils.ETH_ADDRESS,
        amount: ethers.utils.parseEther('1.0')
    });
    
    // Wait for confirmation
    await depositHandle.wait();
}

async function transfer() {
    // Transfer on L2
    const transferTx = await wallet.transfer({
        to: 'RECIPIENT_ADDRESS',
        amount: ethers.utils.parseEther('0.1')
    });
    
    await transferTx.wait();
}

async function withdraw() {
    // Withdraw from zkSync
    const withdrawTx = await wallet.withdraw({
        token: utils.ETH_ADDRESS,
        amount: ethers.utils.parseEther('0.5')
    });
    
    await withdrawTx.wait();
}

State Channels

typescript
// State channel implementation
class StateChannel {
    private participants: string[];
    private state: any;
    private stateCount: number = 0;
    private signatures: Map<number, string[]> = new Map();
    
    constructor(participants: string[], initialState: any) {
        this.participants = participants;
        this.state = initialState;
    }
    
    updateState(updater: (state: any) => any) {
        // Apply state update
        this.state = updater(this.state);
        this.stateCount++;
        
        // Reset signatures for new state
        this.signatures.delete(this.stateCount);
    }
    
    sign(participant: string, privateKey: string) {
        // Sign state
        const stateHash = this.hashState();
        const signature = web3.eth.accounts.sign(stateHash, privateKey);
        
        if (!this.signatures.has(this.stateCount)) {
            this.signatures.set(this.stateCount, []);
        }
        
        this.signatures.get(this.stateCount)!.push(signature.signature);
    }
    
    isValidState(): boolean {
        const signatures = this.signatures.get(this.stateCount);
        return signatures && signatures.length === this.participants.length;
    }
    
    hashState(): string {
        return web3.utils.soliditySha3(
            { type: 'uint256', value: this.stateCount },
            { type: 'uint256', value: web3.utils.toWei(this.state.balance, 'ether') }
        )!;
    }
}

Best Practices

  1. 1Start with Optimistic Rollups for general apps
  2. 2Use ZK-Rollups for privacy-focused apps
  3. 3Consider state channels for high-frequency transactions
  4. 4Monitor bridge liquidity
  5. 5Implement proper fraud monitoring

Conclusion

Layer 2 solutions dramatically reduce costs and increase throughput. Choose the right solution based on your application needs.

Enjoyed this essay? Share it.

Related essays

Next essay
WebAssembly Security: Safe WASM Development