<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Kaushik</title>
        <link>https://paragraph.com/@kaushik-2</link>
        <description>undefined</description>
        <lastBuildDate>Sat, 11 Jul 2026 08:06:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Kaushik</title>
            <url>https://storage.googleapis.com/papyrus_images/ceda0ebe20dc37852da25615d02d05506583796e76edf18ce5ca50a9f7923186.png</url>
            <link>https://paragraph.com/@kaushik-2</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[A Deep Dive into Building Your Own Rollup: RollupKit Architecture Explained]]></title>
            <link>https://paragraph.com/@kaushik-2/a-deep-dive-into-building-your-own-rollup-rollupkit-architecture-explained</link>
            <guid>IYQncclqYnwEeaw28AMG</guid>
            <pubDate>Wed, 13 Nov 2024 03:01:34 GMT</pubDate>
            <description><![CDATA[In recent years, rollups have become a popular approach to scaling blockchains. But building a rollup from scratch is no small feat—especially for those new to decentralized development. This is where RollupKit comes in. RollupKit is a simplified version of a Sovereign Rollup that offers developers a sandbox environment to understand the architecture and key components of rollups. In this post, we’ll go over the different parts of the RollupKit architecture, including data availability, state...]]></description>
            <content:encoded><![CDATA[<p>In recent years, rollups have become a popular approach to scaling blockchains. But building a rollup from scratch is no small feat—especially for those new to decentralized development. This is where <strong>RollupKit</strong> comes in. RollupKit is a simplified version of a Sovereign Rollup that offers developers a sandbox environment to understand the architecture and key components of rollups.</p><p>In this post, we’ll go over the different parts of the RollupKit architecture, including data availability, state derivation, the sequencer, and the wallet/frontend interface. Let’s unpack each component and see how they work together to form a lightweight, scalable rollup solution.</p><p>For those interested, the full code for RollupKit is available on GitHub. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/KaushikKC/sovereign_kaushik_chain">Here’s the link to the repository</a>.</p><hr><h3 id="h-what-is-rollupkit" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What is RollupKit?</h3><p>RollupKit is a streamlined version of a <strong>Sovereign Rollup</strong>—a type of rollup that relies on Ethereum only for data availability and consensus, skipping the more complex verification mechanisms like fraud proofs or zero-knowledge proofs.</p><p>Unlike optimistic or zero-knowledge rollups, RollupKit doesn&apos;t have a trust-minimized bridge with Ethereum. Instead, it focuses on providing data availability through Ethereum&apos;s calldata, making it more accessible for developers to experiment with the core mechanics of rollups without getting into the weeds of cryptographic proofs.</p><p>Think of RollupKit as a DIY kit for building your own mini-blockchain that scales using Ethereum but doesn’t rely on it for complex validations. It’s perfect for understanding the building blocks of rollup technology.</p><hr><h2 id="h-key-components-of-the-rollupkit-architecture" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Key Components of the RollupKit Architecture</h2><p>The RollupKit project is composed of three main layers:</p><ol><li><p><strong>Node / Backend</strong>: Manages data availability, state transitions, and database storage.</p></li><li><p><strong>Sequencer</strong>: Collects and posts transactions in batches to the Ethereum network.</p></li><li><p><strong>Wallet / Frontend</strong>: Provides a user interface for interacting with the rollup.</p></li></ol><p>Each component plays a critical role in ensuring that the rollup functions smoothly and efficiently.</p><hr><h2 id="h-node-backend" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Node / Backend</h2><p>The node or backend in BYOR handles several key processes, including data availability, state derivation, and storing the current state. Let&apos;s take a closer look at each of these functionalities.</p><h3 id="h-1-data-availability" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. Data Availability</h3><p>Data availability is crucial for rollups. In BYOR, this is achieved by posting transaction data as calldata to the <code>Inputs</code> contract on Ethereum. This contract’s purpose is to serve as a repository of data accessible by the rollup.</p><p>Imagine the <code>Inputs</code> contract as a public bulletin board. Anyone can post data to it, and anyone can read the data from it. This setup ensures that all participants in the rollup have access to the transaction history. Users or sequencers can send transactions to the contract, and once posted, the contract emits a <code>BatchAppended</code> event, making it easy to track new transactions.</p><p><strong>Example</strong>:</p><pre data-type="codeBlock" text="// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Inputs {
    event BatchAppended(address sender);

    function appendBatch(bytes calldata _data) external {
        require(msg.sender == tx.origin); // Only EOA
        emit BatchAppended(msg.sender);
    }
}
"><code><span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Inputs</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">BatchAppended</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> sender</span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">appendBatch</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>); <span class="hljs-comment">// Only EOA</span>
        <span class="hljs-keyword">emit</span> BatchAppended(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }
}
</code></pre><p>When a user or sequencer calls the <code>appendBatch</code> function, a <code>BatchAppended</code> event is emitted. This event acts as a signal to the backend, telling it that new data is available for processing.</p><h3 id="h-real-world-analogy-the-drop-box" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Real-World Analogy: &quot;The Drop Box&quot;</h3><p>Think of the <code>Inputs</code> contract like a public drop box at a post office. Anyone can drop a letter (data) into it, and everyone knows new letters have been added by the notification (event) on the bulletin board. This ensures transparency and accessibility to all participants, who can then retrieve the data from this shared storage space.</p><h3 id="h-2-state-derivation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. State Derivation</h3><p>State derivation in BYOR involves taking input data (transactions) and updating the rollup&apos;s state. The process includes defining the initial state (genesis state), fetching data, and transitioning between states.</p><h3 id="h-genesis-state" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Genesis State</h3><p>The genesis state is the starting point of the rollup. It’s defined in a JSON file, <code>genesis.json</code>, which maps each address to an initial balance. This setup resembles a pre-funded account system, where each user has an initial balance but no ability to mint new tokens.</p><pre data-type="codeBlock" text="{
    &quot;0x1234...abcd&quot;: 1000,
    &quot;0xabcd...1234&quot;: 500
}
"><code>{
    <span class="hljs-attr">"0x1234...abcd":</span> <span class="hljs-number">1000</span>,
    <span class="hljs-attr">"0xabcd...1234":</span> <span class="hljs-number">500</span>
}
</code></pre><p>In this example, the addresses are pre-funded with tokens as their starting balances. This file represents the state of the rollup at block 0, ensuring all nodes start with the same foundational data.</p><h3 id="h-data-fetching" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Data Fetching</h3><p>The <code>BatchDownloader.ts</code> script continuously fetches new data from the <code>Inputs</code> contract. It listens to <code>BatchAppended</code> events and retrieves transaction data, which is then passed through the <strong>State Transition Function</strong> (STF) to update the rollup’s state.</p><h3 id="h-state-transition-function-stf" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">State Transition Function (STF)</h3><p>The <strong>State Transition Function</strong> is the heart of the rollup. It takes inputs (transactions) and generates a new state based on them. In BYOR, batches are ordered by their appearance on L1 Ethereum; there&apos;s no additional state tracking to define the order, so the STF is simplified.</p><p><strong>Example</strong>:</p><pre data-type="codeBlock" text="function executeTransaction(state, tx) {
    const fromAccount = state[tx.from] || { balance: 0, nonce: 0 };
    const toAccount = state[tx.to] || { balance: 0, nonce: 0 };

    fromAccount.balance -= tx.value;
    toAccount.balance += tx.value;
    fromAccount.nonce += 1;

    state[tx.from] = fromAccount;
    state[tx.to] = toAccount;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeTransaction</span>(<span class="hljs-params">state, <span class="hljs-built_in">tx</span></span>) </span>{
    const fromAccount <span class="hljs-operator">=</span> state[<span class="hljs-built_in">tx</span>.from] <span class="hljs-operator">|</span><span class="hljs-operator">|</span> { balance: <span class="hljs-number">0</span>, nonce: <span class="hljs-number">0</span> };
    const toAccount <span class="hljs-operator">=</span> state[<span class="hljs-built_in">tx</span>.to] <span class="hljs-operator">|</span><span class="hljs-operator">|</span> { balance: <span class="hljs-number">0</span>, nonce: <span class="hljs-number">0</span> };

    fromAccount.<span class="hljs-built_in">balance</span> <span class="hljs-operator">-</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.value;
    toAccount.<span class="hljs-built_in">balance</span> <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.value;
    fromAccount.nonce <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

    state[<span class="hljs-built_in">tx</span>.from] <span class="hljs-operator">=</span> fromAccount;
    state[<span class="hljs-built_in">tx</span>.to] <span class="hljs-operator">=</span> toAccount;
}
</code></pre><h3 id="h-real-world-analogy-account-ledger" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Real-World Analogy: &quot;Account Ledger&quot;</h3><p>Imagine a community ledger where everyone writes down transactions in sequence. Each new entry depends on the previous one, creating a sequential history of all transactions. The STF updates the ledger (state) as new transactions are added.</p><h3 id="h-3-storing-the-state" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Storing the State</h3><p>BYOR uses a database to store account information, such as balances and nonces, for each address. This data is stored in an <code>accounts</code> table, with the following schema:</p><pre data-type="codeBlock" text="CREATE TABLE accounts (
    address TEXT PRIMARY KEY NOT NULL,
    balance INTEGER DEFAULT 0 NOT NULL,
    nonce INTEGER DEFAULT 0 NOT NULL
);
"><code><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> accounts (
    address TEXT <span class="hljs-keyword">PRIMARY</span> KEY <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    balance <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    nonce <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>
);
</code></pre><p>If a new user interacts with the rollup and has no previous entry in the database, they’re assigned a default balance and nonce of zero.</p><hr><h2 id="h-sequencer" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sequencer</h2><p>The sequencer in BYOR is responsible for gathering transactions and submitting them to the <code>Inputs</code> contract in batches. BYOR’s sequencer is divided into two main parts: <code>Mempool.ts</code> and <code>BatchPoster.ts</code>.</p><h3 id="h-mempool" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Mempool</h3><p>The <strong>Mempool</strong> acts as a waiting room for transactions. Here, transactions are organized by fee, ensuring that higher-fee transactions are prioritized when forming a batch. This incentivizes users to pay higher fees for faster processing.</p><h3 id="h-batch-poster" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Batch Poster</h3><p>The <strong>Batch Poster</strong> creates a batch of transactions from the mempool and posts it to the data availability layer (Ethereum). This batch-posting mechanism reduces the number of submissions to Ethereum, saving on gas costs and improving scalability.</p><hr><h2 id="h-client-frontend" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Client / Frontend</h2><p>BYOR includes a simple wallet interface for users to interact with the rollup. Built using Next.js and WalletConnect, the wallet provides an easy way for users to send tokens, view balances, and check transaction statuses.</p><h3 id="h-key-features" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Key Features</h3><ol><li><p><strong>Token Transfers</strong>: Users can send tokens to other addresses within the rollup.</p></li><li><p><strong>Transaction Status</strong>: Users can monitor the status of transactions, whether in the mempool or already posted to Ethereum.</p></li><li><p><strong>Gas Fee Prioritization</strong>: The interface highlights the importance of gas fees, showing users how fees affect transaction processing speed.</p></li></ol><h3 id="h-real-world-analogy-online-banking-app" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Real-World Analogy: &quot;Online Banking App&quot;</h3><p>Think of the BYOR wallet like an online banking app. Users can check their balances, transfer funds to others, and view the status of recent transactions.</p><hr><h2 id="h-example-flow-sending-tokens" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Example Flow: Sending Tokens</h2><ol><li><p><strong>User Initiates Transfer</strong>: Alice decides to send 100 tokens to Bob. She enters Bob’s address and specifies a gas fee.</p></li><li><p><strong>Transaction Enters Mempool</strong>: The transaction is added to the mempool, where it’s sorted by fee. Alice’s higher fee puts her transaction near the top.</p></li><li><p><strong>Batch Poster Submits</strong>: The sequencer posts Alice’s transaction, along with others in the batch, to the <code>Inputs</code> contract on Ethereum.</p></li><li><p><strong>Backend Fetches Batch</strong>: The backend detects the new <code>BatchAppended</code> event and retrieves Alice’s transaction data.</p></li><li><p><strong>State Transition Function Updates</strong>: The STF processes Alice’s transaction, updating her balance and Bob’s balance in the rollup.</p></li><li><p><strong>Database Stores New State</strong>: The updated balances are saved in the <code>accounts</code> table, ensuring the new state is recorded.</p></li></ol><hr><h2 id="h-in-depth-code-highlights" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">In-Depth Code Highlights</h2><h3 id="h-smart-contract" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Smart Contract</h3><p>The <code>Inputs.sol</code> contract serves as the single point of data submission, emitting events that make it easy to query for new transaction batches.</p><pre data-type="codeBlock" text="
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Inputs {
    event BatchAppended(address sender);
    function appendBatch(bytes calldata) external {
        require(msg.sender == tx.origin);
        emit BatchAppended(msg.sender);
    }
}
"><code>
<span class="hljs-comment">// SPDX-License-Identifier: MIT</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.0;</span>

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">Inputs</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">BatchAppended</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> sender</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">appendBatch</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>);
        <span class="hljs-keyword">emit</span> BatchAppended(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>);
    }
}
</code></pre><h3 id="h-database-schema" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Database Schema</h3><p>BYOR uses a simple SQL database schema to store account information and transaction details.</p><p><strong>accounts Table</strong>:</p><pre data-type="codeBlock" text="
CREATE TABLE accounts (
    address TEXT PRIMARY KEY NOT NULL,
    balance INTEGER DEFAULT 0 NOT NULL,
    nonce INTEGER DEFAULT 0 NOT NULL
);
"><code>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> accounts (
    address TEXT <span class="hljs-keyword">PRIMARY</span> KEY <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    balance <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    nonce <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>
);
</code></pre><p><strong>transactions Table</strong>:</p><pre data-type="codeBlock" text="
CREATE TABLE transactions (
    id INTEGER,
    from TEXT NOT NULL,
    to TEXT NOT NULL,
    value INTEGER NOT NULL,
    nonce INTEGER NOT NULL,
    fee INTEGER NOT NULL,
    feeRecipient TEXT NOT NULL,
    l1SubmittedDate INTEGER NOT NULL,
    hash TEXT NOT NULL,
    PRIMARY KEY(from, nonce)
);
"><code>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> transactions (
    id <span class="hljs-type">INTEGER</span>,
    <span class="hljs-keyword">from</span> TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    <span class="hljs-keyword">to</span> TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    <span class="hljs-keyword">value</span> <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    nonce <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    fee <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    feeRecipient TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    l1SubmittedDate <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    hash TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>,
    <span class="hljs-keyword">PRIMARY</span> KEY(<span class="hljs-keyword">from</span>, nonce)
);
</code></pre><h3 id="h-state-transition-function" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">State Transition Function</h3><p>The function <code>executeTransaction</code> in <code>StateUpdater.ts</code> updates balances and nonces for each transaction in the state.</p><pre data-type="codeBlock" text="const DEFAULT_ACCOUNT = { balance: 0, nonce: 0 };

function executeTransaction(state, tx, feeRecipient) {
    const fromAccount = getAccount(state, tx.from, DEFAULT_ACCOUNT);
    const toAccount = getAccount(state, tx.to, DEFAULT_ACCOUNT);

    fromAccount.nonce = tx.nonce;
    fromAccount.balance -= tx.value;
    toAccount.balance += tx.value;

    fromAccount.balance -= tx.fee;
    feeRecipient.balance += tx.fee;
}
"><code>const DEFAULT_ACCOUNT <span class="hljs-operator">=</span> { balance: <span class="hljs-number">0</span>, nonce: <span class="hljs-number">0</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">executeTransaction</span>(<span class="hljs-params">state, <span class="hljs-built_in">tx</span>, feeRecipient</span>) </span>{
    const fromAccount <span class="hljs-operator">=</span> getAccount(state, <span class="hljs-built_in">tx</span>.from, DEFAULT_ACCOUNT);
    const toAccount <span class="hljs-operator">=</span> getAccount(state, <span class="hljs-built_in">tx</span>.to, DEFAULT_ACCOUNT);

    fromAccount.nonce <span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.nonce;
    fromAccount.<span class="hljs-built_in">balance</span> <span class="hljs-operator">-</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.value;
    toAccount.<span class="hljs-built_in">balance</span> <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.value;

    fromAccount.<span class="hljs-built_in">balance</span> <span class="hljs-operator">-</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.fee;
    feeRecipient.<span class="hljs-built_in">balance</span> <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-built_in">tx</span>.fee;
}
</code></pre><h3 id="h-event-fetching" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Event Fetching</h3><p><code>getNewStates</code> fetches new <code>BatchAppended</code> events, extracting calldata, timestamps, and sender details, then packaging this data for further processing.</p><pre data-type="codeBlock" text="function getNewStates() {
    const lastBatchBlock = getLastBatchBlock();
    const events = getLogs(lastBatchBlock);
    const calldata = getCalldata(events);
    const timestamps = getTimestamps(events);
    const posters = getTransactionPosters(events);

    updateLastFetchedBlock(lastBatchBlock);
    return zip(posters, timestamps, calldata);
}
"><code>function getNewStates() {
    const <span class="hljs-attr">lastBatchBlock</span> = getLastBatchBlock()<span class="hljs-comment">;</span>
    const <span class="hljs-attr">events</span> = getLogs(lastBatchBlock)<span class="hljs-comment">;</span>
    const <span class="hljs-attr">calldata</span> = getCalldata(events)<span class="hljs-comment">;</span>
    const <span class="hljs-attr">timestamps</span> = getTimestamps(events)<span class="hljs-comment">;</span>
    const <span class="hljs-attr">posters</span> = getTransactionPosters(events)<span class="hljs-comment">;</span>

    updateLastFetchedBlock(lastBatchBlock)<span class="hljs-comment">;</span>
    return zip(posters, timestamps, calldata)<span class="hljs-comment">;</span>
}
</code></pre><h3 id="h-mempool-fee-sorting" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Mempool Fee Sorting</h3><p>Transactions in the mempool are sorted by fees to prioritize higher-fee transactions.</p><pre data-type="codeBlock" text="function popNHighestFee(txPool, n) {
    txPool.sort((a, b) =&gt; b.fee - a.fee);
    return txPool.splice(0, n);
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">popNHighestFee</span>(<span class="hljs-params">txPool, n</span>) </span>{
    txPool.sort((a, b) <span class="hljs-operator">=</span><span class="hljs-operator">></span> b.fee <span class="hljs-operator">-</span> a.fee);
    <span class="hljs-keyword">return</span> txPool.splice(<span class="hljs-number">0</span>, n);
}
</code></pre><h2 id="h-limitations-and-future-developments" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Limitations and Future Developments</h2><p>While BYOR provides a foundational model for a Sovereign Rollup, it has limitations:</p><ul><li><p><strong>No Fraud Proofs</strong>: Unlike optimistic rollups, BYOR doesn’t include a mechanism to challenge invalid state transitions.</p></li><li><p><strong>Limited to Data Availability</strong>: Ethereum is used only for storing data, without any form of dispute resolution.</p></li></ul><p>Future improvements could involve adding fraud proofs, using zk-rollup techniques, or implementing gas-efficient data storage.</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/fc98fad99cb74d069bfeec6a52f802946e38ca2fcc82389494a9cb2445c7da9f.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Dare to Deploy: Building Your L3 Rollup Chain with Arbitrum and Avail Explained🚀]]></title>
            <link>https://paragraph.com/@kaushik-2/dare-to-deploy-building-your-l3-rollup-chain-with-arbitrum-and-avail-explained</link>
            <guid>PDFFNoOWx5IpXXUfeGqy</guid>
            <pubDate>Mon, 04 Nov 2024 18:20:33 GMT</pubDate>
            <description><![CDATA[Hey there👋, intrepid blockchain builder! If you&apos;ve ever dreamt of setting up your own Layer 3 (L3) rollup chain, it&apos;s time to roll up those sleeves and make it happen. We’re diving into DYOR-inggg (Deploying your own rollup ) an Arbitrum Orbit L3 chain using Avail as the data availability (DA) layer on top of the Arbitrum Sepolia testnet. Don’t worry—I&apos;ll keep this guide straightforward and peppered with some idioms to keep things lively. Let’s jump right in! 🌟Prerequisites: ...]]></description>
            <content:encoded><![CDATA[<p>Hey there👋, intrepid blockchain builder! If you&apos;ve ever dreamt of setting up your own Layer 3 (L3) rollup chain, it&apos;s time to roll up those sleeves and make it happen. We’re diving into DYOR-inggg (Deploying your own rollup ) an Arbitrum Orbit L3 chain using Avail as the data availability (DA) layer on top of the Arbitrum Sepolia testnet. Don’t worry—I&apos;ll keep this guide straightforward and peppered with some idioms to keep things lively. Let’s jump right in! 🌟</p><h3 id="h-prerequisites-setting-the-stage" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Prerequisites: Setting the Stage 🎬</h3><h4 id="h-1-docker" class="text-xl font-header !mt-6 !mb-3 first:!mt-0 first:!mb-0">1. Docker 🐳</h4><p>First things first, you’ll need Docker and Docker Compose installed to run your chain smoothly.</p><ul><li><p>Install Docker: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.docker.com/engine/">docker (opens in a new tab)</a></p></li><li><p>Install Docker Compose: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.docker.com/compose/">docker-compose (opens in a new tab)</a></p></li></ul><p>✅ <strong>Tip</strong>: Make sure they’re up and running before you continue. If not, installing these tools is just a click away!</p><hr><h3 id="h-2-avail-account" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. <strong>Avail Account</strong> 🏦</h3><p>Before diving head-first into deploying contracts, you need an Avail account. Here’s the skinny on how to do that:</p><p><strong>Step 1: Compatible Wallet Setup</strong> 👜 You can use wallets like <strong>SubWallet</strong>, <strong>Talisman</strong>, <strong>PolkadotJS</strong>, <strong>Nova Wallet</strong>, etc. This guide will use <strong>SubWallet</strong> as an example.</p><ul><li><p><strong>Install SubWallet</strong>:Visit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://subwallet.app">SubWallet.app</a> and install the browser extension.Open the extension and select the option to create a new account or import an existing one.</p></li></ul><p><strong>Step 2: Set Up a Password 🔑</strong>SubWallet will prompt you to create a password, which secures access to the wallet extension itself (not the on-chain account).</p><p><strong>Step 3: Secure Your Seed Phrase 📜Don&apos;t lose this!</strong> It’s your key to recover your account if things go sideways.</p><p><strong>Step 4: Configure Avail Networks 🌐</strong></p><ul><li><p>In SubWallet, open the <strong>Networks Menu</strong>.</p></li><li><p>Search for <strong>Avail testnets</strong> and toggle the network on to connect to Avail DA mainnet or Turing testnet.</p></li></ul><p><strong>Step 5: Retrieve Your Avail Address 📬</strong></p><ul><li><p>Go to the SubWallet homepage and click on <strong>Get Address</strong>.</p></li><li><p>Select your network and copy your account address.</p></li></ul><h3 id="h-step-2-fund-your-account-using-avail-faucet" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Step 2: Fund Your Account Using Avail Faucet</strong> 💧</h3><p>Visit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://faucet.avail.tools/">Avail Faucet</a>, select the chain (e.g., Turing), enter your address, complete the CAPTCHA, and get some test AVAIL tokens.</p><hr><h3 id="h-3-get-appid-for-your-rollup-on-avail-da" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. <strong>Get AppID for Your Rollup on Avail DA</strong></h3><p>Obtain an application key (AppID) for your rollup on Avail DA by following these steps:</p><h3 id="h-step-1-check-the-next-available-appid" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Step 1: Check the Next Available AppID</strong> 🆔</h3><ol><li><p>Go to the <strong>Data Availability pallet</strong> in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://explorer.avail.so/#/chainstate">explorer</a>.</p></li><li><p>Select the <strong>nextAppId</strong> method.</p></li><li><p>Click the <strong>+</strong> button to view the next available index/ID for a new AppID.</p></li></ol><h3 id="h-step-2-register-a-new-appid" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Step 2: Register a New AppID</strong> ✍️</h3><ol><li><p>Ensure your Avail DA wallet is connected to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://explorer.avail.so/#/chainstate">explorer</a>.</p></li><li><p>Navigate to the <strong>Extrinsics</strong> section under the Developer tab.</p></li><li><p>Select the <strong>dataAvailability</strong> pallet and choose the <strong>createApplicationKey</strong> method.</p></li><li><p>Enter a unique key for your app.</p><p><strong>Important Note</strong>:</p><ul><li><p><strong>Do Not Change the appID</strong> field for this transaction. Set it to 0, as this denotes chain-level operations.</p></li><li><p>Each transaction on Avail DA has a unique appID.</p></li></ul></li><li><p>Submit the transaction, and authorize it through your wallet.</p></li></ol><h3 id="h-step-3-verify-your-appid" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Step 3: Verify Your AppID</strong> ✅</h3><ol><li><p>Go to the <strong>Chain State</strong> section of the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="">explorer.</a></p></li><li><p>Select the <strong>dataAvailability</strong> pallet and the <strong>appKeys</strong> method.</p></li><li><p>Uncheck the <strong>Include Option</strong> toggle and click <strong>+</strong> to fetch a list of registered AppIDs, confirming your AppID.</p></li></ol><hr><h3 id="h-4-arbitrum-sepolia-testnet-eth" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4. <strong>Arbitrum Sepolia Testnet ETH</strong> ⚡</h3><p>No ETH, no fun! Make sure you have at least 1 ETH on Arbitrum Sepolia:</p><ol><li><p><strong>Get Testnet ETH</strong>: Visit <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/faucets/ethereum-sepolia">Alchemy’s Sepolia faucet</a>.</p></li><li><p><strong>Bridge ETH to Arbitrum Sepolia</strong>: Use the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bridge.arbitrum.io/?destinationChain=arbitrum-sepolia&amp;sourceChain=sepolia">Arbitrum Bridge</a> to transfer it over.</p></li></ol><p>Following these prerequisites will set you up to create and manage an AppID on Avail DA and interact with the Avail network.</p><hr><h3 id="h-step-1-deploy-rollup-contracts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 1: Deploy Rollup Contracts</h3><ol><li><p><strong>Clone the Nitro Contracts Repository</strong>:</p><pre data-type="codeBlock" text="
git clone https://github.com/availproject/nitro-contracts.git
cd nitro-contracts
git checkout v2.1.0-upstream-v2.1.0
yarn install
yarn build
"><code>
git clone https:<span class="hljs-comment">//github.com/availproject/nitro-contracts.git</span>
cd nitro<span class="hljs-operator">-</span>contracts
git checkout v2<span class="hljs-number">.1</span><span class="hljs-number">.0</span><span class="hljs-operator">-</span>upstream<span class="hljs-operator">-</span>v2<span class="hljs-number">.1</span><span class="hljs-number">.0</span>
yarn install
yarn build
</code></pre></li><li><p><strong>Create and Configure the .env File</strong>:</p><ul><li><p>Copy <code>.env.sample</code> to <code>.env</code>.</p></li><li><p>For the <code>ROLLUP_CREATOR_ADDRESS</code> on any other supported chain, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.availproject.org/docs/build-with-avail/Optimium/arbitrum-nitro/nitro-stack#smart-contract-addresses">please find it here</a>.</p></li><li><p>For the <code>ARBISCAN_API_KEY</code> check out <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.arbiscan.io/getting-started/viewing-api-usage-statistics">https://docs.arbiscan.io/getting-started/viewing-api-usage-statistics</a></p></li><li><p>Edit the following entries:</p><pre data-type="codeBlock" text="ROLLUP_CREATOR_ADDRESS=&quot;0xE917553b67f630C3982236B6A1d7844B1021B909&quot;  # For Arbitrum Sepolia
DEVNET_PRIVKEY=&quot;&lt;--Private-key--&gt;&quot;
ARBISCAN_API_KEY = &quot;&quot;
"><code><span class="hljs-attr">ROLLUP_CREATOR_ADDRESS</span>=<span class="hljs-string">"0xE917553b67f630C3982236B6A1d7844B1021B909"</span>  <span class="hljs-comment"># For Arbitrum Sepolia</span>
<span class="hljs-attr">DEVNET_PRIVKEY</span>=<span class="hljs-string">"&#x3C;--Private-key-->"</span>
<span class="hljs-attr">ARBISCAN_API_KEY</span> = <span class="hljs-string">""</span>
</code></pre></li></ul></li><li><p><strong>Edit the Configuration File</strong>:</p><ul><li><p>Copy <code>scripts/config.ts.example</code> to <code>scripts/config.ts</code> and update with your chain’s parameters:</p></li><li><p>In this change only the chainId to the number you wish. then keep</p><p>remaining unchanged.</p><pre data-type="codeBlock" text="config = {
  rollupConfig: {
    confirmPeriodBlocks: ethers.BigNumber.from(&apos;150&apos;),
    extraChallengeTimeBlocks: ethers.BigNumber.from(&apos;0&apos;),
    stakeToken: ethers.constants.AddressZero,
    baseStake: ethers.utils.parseEther(&apos;0.0001&apos;),
    wasmModuleRoot:
      &apos;0x3f3b4da7b5c231e6faf91ff723d235728b05c9074f2ae3cc4b3e54dd5139d34f&apos;,
    owner: &apos;0x1234123412341234123412341234123412341234&apos;,
    loserStakeEscrow: ethers.constants.AddressZero,
    chainId: ethers.BigNumber.from(&apos;20121999&apos;),
    chainConfig:
      &apos;{&quot;chainId&quot;:20121999,&quot;homesteadBlock&quot;:0,&quot;daoForkBlock&quot;:null,&quot;daoForkSupport&quot;:true,&quot;eip150Block&quot;:0,&quot;eip150Hash&quot;:&quot;0x0000000000000000000000000000000000000000000000000000000000000000&quot;,&quot;eip155Block&quot;:0,&quot;eip158Block&quot;:0,&quot;byzantiumBlock&quot;:0,&quot;constantinopleBlock&quot;:0,&quot;petersburgBlock&quot;:0,&quot;istanbulBlock&quot;:0,&quot;muirGlacierBlock&quot;:0,&quot;berlinBlock&quot;:0,&quot;londonBlock&quot;:0,&quot;clique&quot;:{&quot;period&quot;:0,&quot;epoch&quot;:0},&quot;arbitrum&quot;:{&quot;EnableArbOS&quot;:true,&quot;AllowDebugPrecompiles&quot;:false,&quot;DataAvailabilityCommittee&quot;:false,&quot;InitialArbOSVersion&quot;:10,&quot;InitialChainOwner&quot;:&quot;0xd41996ED89bb5BF7dBfB181D8D93E8067446200B&quot;,&quot;GenesisBlockNum&quot;:0}}&apos;,
    genesisBlockNum: ethers.BigNumber.from(&apos;0&apos;),
    sequencerInboxMaxTimeVariation: {
      delayBlocks: ethers.BigNumber.from(&apos;5760&apos;),
      futureBlocks: ethers.BigNumber.from(&apos;12&apos;),
      delaySeconds: ethers.BigNumber.from(&apos;86400&apos;),
      futureSeconds: ethers.BigNumber.from(&apos;3600&apos;),
    },
  },
  validators: [
    &apos;0x1234123412341234123412341234123412341234&apos;,
  ],
  batchPosters: [&apos;0x1234123412341234123412341234123412341234&apos;,],
  batchPosterManager: &apos;0x1234123412341234123412341234123412341234&apos;
},
 
 
"><code>config <span class="hljs-operator">=</span> {
  rollupConfig: {
    confirmPeriodBlocks: ethers.BigNumber.from(<span class="hljs-string">'150'</span>),
    extraChallengeTimeBlocks: ethers.BigNumber.from(<span class="hljs-string">'0'</span>),
    stakeToken: ethers.constants.AddressZero,
    baseStake: ethers.utils.parseEther(<span class="hljs-string">'0.0001'</span>),
    wasmModuleRoot:
      <span class="hljs-string">'0x3f3b4da7b5c231e6faf91ff723d235728b05c9074f2ae3cc4b3e54dd5139d34f'</span>,
    owner: <span class="hljs-string">'0x1234123412341234123412341234123412341234'</span>,
    loserStakeEscrow: ethers.constants.AddressZero,
    chainId: ethers.BigNumber.from(<span class="hljs-string">'20121999'</span>),
    chainConfig:
      <span class="hljs-string">'{"chainId":20121999,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":true,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"berlinBlock":0,"londonBlock":0,"clique":{"period":0,"epoch":0},"arbitrum":{"EnableArbOS":true,"AllowDebugPrecompiles":false,"DataAvailabilityCommittee":false,"InitialArbOSVersion":10,"InitialChainOwner":"0xd41996ED89bb5BF7dBfB181D8D93E8067446200B","GenesisBlockNum":0}}'</span>,
    genesisBlockNum: ethers.BigNumber.from(<span class="hljs-string">'0'</span>),
    sequencerInboxMaxTimeVariation: {
      delayBlocks: ethers.BigNumber.from(<span class="hljs-string">'5760'</span>),
      futureBlocks: ethers.BigNumber.from(<span class="hljs-string">'12'</span>),
      delaySeconds: ethers.BigNumber.from(<span class="hljs-string">'86400'</span>),
      futureSeconds: ethers.BigNumber.from(<span class="hljs-string">'3600'</span>),
    },
  },
  validators: [
    <span class="hljs-string">'0x1234123412341234123412341234123412341234'</span>,
  ],
  batchPosters: [<span class="hljs-string">'0x1234123412341234123412341234123412341234'</span>,],
  batchPosterManager: <span class="hljs-string">'0x1234123412341234123412341234123412341234'</span>
},
 
 
</code></pre></li></ul></li></ol><h3 id="h-step-2-deploy-base-contracts-on-arbitrum-sepolia" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 2: Deploy Base Contracts on Arbitrum Sepolia</h3><p>To deploy the rollup contracts on Arbitrum Sepolia, execute the following commands in sequence:</p><pre data-type="codeBlock" text="
yarn build:forge:yul
yarn run deploy-eth-rollup --network arbSepolia
"><code>
yarn build:forge:yul
yarn run deploy<span class="hljs-operator">-</span>eth<span class="hljs-operator">-</span>rollup <span class="hljs-operator">-</span><span class="hljs-operator">-</span>network arbSepolia
</code></pre><blockquote><p>Heads up: ⚠️ If you see an error like No matches found: &quot;*.sol&quot;, edit the package.json file:</p></blockquote><pre data-type="codeBlock" text="
&quot;build:forge:yul&quot;: &quot;FOUNDRY_PROFILE=yul forge build --skip \&quot;*.sol\&quot;&quot;
"><code>
<span class="hljs-string">"build:forge:yul"</span>: <span class="hljs-string">"FOUNDRY_PROFILE=yul forge build --skip <span class="hljs-subst">\"</span>*.sol<span class="hljs-subst">\"</span>"</span>
</code></pre><p>Re-run the command, and you’re good to go!</p><p>After deployment, note the addresses for the base contracts, as you will need them in the next configuration step.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a6cb7ec051dddfaba0847848ed6870dddf35eb5915174a52131bb7f1e63c0aff.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-step-3-configure-and-start-the-orbit-node" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 3: Configure and Start the Orbit Node</h3><ol><li><p><strong>Clone the Orbit Setup Script Repository</strong></p><p>Begin by cloning the repository for the Orbit setup script:</p><pre data-type="codeBlock" text="git clone https://github.com/OffchainLabs/orbit-setup-script.git
cd orbit-setup-script
"><code>git <span class="hljs-built_in">clone</span> https://github.com/OffchainLabs/orbit-setup-script.git
<span class="hljs-built_in">cd</span> orbit-setup-script
</code></pre></li><li><p><strong>Create and Configure</strong> <code>nodeConfig.json</code></p><p>In the <code>./config</code> directory, create a <code>nodeConfig.json</code> file. Update it with the configuration details, including base contract addresses and network-specific settings:</p><ul><li><p><code>chain.chain-id</code> - Chain ID for your network.</p></li><li><p><code>chain.chain-name</code> - A name of your choice for the chain.</p></li><li><p><code>chain.InitialChainOwner</code> - Builder wallet address.</p></li><li><p><code>bridge</code> and associated fields - Contract addresses obtained in Step 2.</p></li><li><p><code>node.batch-poster.parent-chain-wallet.private-key</code> - Private key for the builder wallet.</p></li><li><p><code>node.staker.parent-chain-wallet.private-key</code> - Private key for the staker wallet (use a different wallet from the builder).</p></li><li><p><code>node.avail.seed</code> - Seed phrase for the Avail wallet you created initially.</p></li><li><p><code>node.avail.app-id</code> - App ID obtained for Avail.</p></li><li><p><code>node.avail.arbSepolia-rpc</code> - RPC URL for Arbitrum Sepolia (obtainable from providers like Alchemy or Infura).</p></li></ul><p>Here’s an example configuration:</p><pre data-type="codeBlock" text="{
  &quot;chain&quot;: {
    &quot;info-json&quot;: &quot;[{\&quot;chain-id\&quot;:&lt;Insert your chain-id&gt;,\&quot;parent-chain-id\&quot;:421614,\&quot;parent-chain-is-arbitrum\&quot;:true,\&quot;chain-name\&quot;:\&quot;&lt;Insert chain name&gt;\&quot;,\&quot;chain-config\&quot;:{\&quot;homesteadBlock\&quot;:0,\&quot;daoForkBlock\&quot;:null,\&quot;daoForkSupport\&quot;:true,\&quot;eip150Block\&quot;:0,\&quot;eip150Hash\&quot;:\&quot;0x0000000000000000000000000000000000000000000000000000000000000000\&quot;,\&quot;eip155Block\&quot;:0,\&quot;eip158Block\&quot;:0,\&quot;byzantiumBlock\&quot;:0,\&quot;constantinopleBlock\&quot;:0,\&quot;petersburgBlock\&quot;:0,\&quot;istanbulBlock\&quot;:0,\&quot;muirGlacierBlock\&quot;:0,\&quot;berlinBlock\&quot;:0,\&quot;londonBlock\&quot;:0,\&quot;clique\&quot;:{\&quot;period\&quot;:0,\&quot;epoch\&quot;:0},\&quot;arbitrum\&quot;:{\&quot;EnableArbOS\&quot;:true,\&quot;AllowDebugPrecompiles\&quot;:false,\&quot;DataAvailabilityCommittee\&quot;:false,\&quot;InitialArbOSVersion\&quot;:11,\&quot;GenesisBlockNum\&quot;:0,\&quot;MaxCodeSize\&quot;:24576,\&quot;MaxInitCodeSize\&quot;:49152,\&quot;InitialChainOwner\&quot;:\&quot;&lt;Insert chain owner address&gt;\&quot;},\&quot;chainId\&quot;:&lt;Insert chain id&gt;},\&quot;rollup\&quot;:{\&quot;bridge\&quot;:\&quot;&lt;Bridge proxy address&gt;\&quot;,\&quot;inbox\&quot;:\&quot;&lt;Inbox address&gt;\&quot;,\&quot;sequencer-inbox\&quot;:\&quot;&lt;Sequencer inbox address&gt;\&quot;,\&quot;rollup\&quot;:\&quot;&lt;Rollup proxy address&gt;\&quot;,\&quot;validator-utils\&quot;:\&quot;&lt;Validator until address&gt;\&quot;,\&quot;validator-wallet-creator\&quot;:\&quot;&lt;Validator wallet creator address&gt;\&quot;,\&quot;deployed-at\&quot;:&lt;Insert deployed-at block number&gt;}}]&quot;,
    &quot;name&quot;: &quot;&lt;Insert chain name&gt;&quot;
  },
  &quot;parent-chain&quot;: {
    &quot;connection&quot;: {
      &quot;url&quot;: &quot;https://sepolia-rollup.arbitrum.io/rpc&quot;
    }
  },
  &quot;http&quot;: {
    &quot;addr&quot;: &quot;0.0.0.0&quot;,
    &quot;port&quot;: 8449,
    &quot;vhosts&quot;: [
      &quot;*&quot;
    ],
    &quot;corsdomain&quot;: [
      &quot;*&quot;
    ],
    &quot;api&quot;: [
      &quot;eth&quot;,
      &quot;net&quot;,
      &quot;web3&quot;,
      &quot;arb&quot;,
      &quot;debug&quot;
    ]
  },
  &quot;node&quot;: {
    &quot;sequencer&quot;: true,
    &quot;delayed-sequencer&quot;: {
      &quot;enable&quot;: true,
      &quot;use-merge-finality&quot;: false,
      &quot;finalize-distance&quot;: 1
    },
    &quot;batch-poster&quot;: {
      &quot;max-size&quot;: 90000,
      &quot;enable&quot;: true,
      &quot;parent-chain-wallet&quot;: {
        &quot;private-key&quot;: &quot;&lt;Insert your private key here&gt;&quot;
      }
    },
    &quot;staker&quot;: {
      &quot;enable&quot;: true,
      &quot;strategy&quot;: &quot;MakeNodes&quot;,
      &quot;parent-chain-wallet&quot;: {
        &quot;private-key&quot;: &quot;&lt;Insert your private key here&gt;&quot;
      }
    },
    &quot;dangerous&quot;: {
      &quot;no-sequencer-coordinator&quot;: true
    },
    &quot;avail&quot;: {
      &quot;enable&quot;: true,
      &quot;seed&quot;: &quot;&lt;Enter your seed phrase here&gt;&quot;,
      &quot;avail-api-url&quot;: &quot;wss://turing-rpc.avail.so/ws&quot;,
      &quot;app-id&quot;: &lt;Enter your avail app id&gt;,
      &quot;timeout&quot;:&quot;100s&quot;,
      &quot;vectorx&quot;: &quot;0xA712dfec48AF3a78419A8FF90fE8f97Ae74680F0&quot;,
      &quot;arbSepolia-rpc&quot;: &quot;&lt;Enter an arbSepolia wss url here&gt;&quot;
    }
  },
  &quot;execution&quot;: {
    &quot;forwarding-target&quot;: &quot;&quot;,
    &quot;sequencer&quot;: {
      &quot;enable&quot;: true,
      &quot;max-tx-data-size&quot;: 85000,
      &quot;max-block-speed&quot;: &quot;250ms&quot;
    },
    &quot;caching&quot;: {
      &quot;archive&quot;: true
    }
  }
}
 
"><code>{
  <span class="hljs-string">"chain"</span>: {
    <span class="hljs-string">"info-json"</span>: <span class="hljs-string">"[{<span class="hljs-subst">\"</span>chain-id<span class="hljs-subst">\"</span>:&#x3C;Insert your chain-id>,<span class="hljs-subst">\"</span>parent-chain-id<span class="hljs-subst">\"</span>:421614,<span class="hljs-subst">\"</span>parent-chain-is-arbitrum<span class="hljs-subst">\"</span>:true,<span class="hljs-subst">\"</span>chain-name<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Insert chain name><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>chain-config<span class="hljs-subst">\"</span>:{<span class="hljs-subst">\"</span>homesteadBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>daoForkBlock<span class="hljs-subst">\"</span>:null,<span class="hljs-subst">\"</span>daoForkSupport<span class="hljs-subst">\"</span>:true,<span class="hljs-subst">\"</span>eip150Block<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>eip150Hash<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>0x0000000000000000000000000000000000000000000000000000000000000000<span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>eip155Block<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>eip158Block<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>byzantiumBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>constantinopleBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>petersburgBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>istanbulBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>muirGlacierBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>berlinBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>londonBlock<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>clique<span class="hljs-subst">\"</span>:{<span class="hljs-subst">\"</span>period<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>epoch<span class="hljs-subst">\"</span>:0},<span class="hljs-subst">\"</span>arbitrum<span class="hljs-subst">\"</span>:{<span class="hljs-subst">\"</span>EnableArbOS<span class="hljs-subst">\"</span>:true,<span class="hljs-subst">\"</span>AllowDebugPrecompiles<span class="hljs-subst">\"</span>:false,<span class="hljs-subst">\"</span>DataAvailabilityCommittee<span class="hljs-subst">\"</span>:false,<span class="hljs-subst">\"</span>InitialArbOSVersion<span class="hljs-subst">\"</span>:11,<span class="hljs-subst">\"</span>GenesisBlockNum<span class="hljs-subst">\"</span>:0,<span class="hljs-subst">\"</span>MaxCodeSize<span class="hljs-subst">\"</span>:24576,<span class="hljs-subst">\"</span>MaxInitCodeSize<span class="hljs-subst">\"</span>:49152,<span class="hljs-subst">\"</span>InitialChainOwner<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Insert chain owner address><span class="hljs-subst">\"</span>},<span class="hljs-subst">\"</span>chainId<span class="hljs-subst">\"</span>:&#x3C;Insert chain id>},<span class="hljs-subst">\"</span>rollup<span class="hljs-subst">\"</span>:{<span class="hljs-subst">\"</span>bridge<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Bridge proxy address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>inbox<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Inbox address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>sequencer-inbox<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Sequencer inbox address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>rollup<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Rollup proxy address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>validator-utils<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Validator until address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>validator-wallet-creator<span class="hljs-subst">\"</span>:<span class="hljs-subst">\"</span>&#x3C;Validator wallet creator address><span class="hljs-subst">\"</span>,<span class="hljs-subst">\"</span>deployed-at<span class="hljs-subst">\"</span>:&#x3C;Insert deployed-at block number>}}]"</span>,
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"&#x3C;Insert chain name>"</span>
  },
  <span class="hljs-string">"parent-chain"</span>: {
    <span class="hljs-string">"connection"</span>: {
      <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://sepolia-rollup.arbitrum.io/rpc"</span>
    }
  },
  <span class="hljs-string">"http"</span>: {
    <span class="hljs-string">"addr"</span>: <span class="hljs-string">"0.0.0.0"</span>,
    <span class="hljs-string">"port"</span>: <span class="hljs-number">8449</span>,
    <span class="hljs-string">"vhosts"</span>: [
      <span class="hljs-string">"*"</span>
    ],
    <span class="hljs-string">"corsdomain"</span>: [
      <span class="hljs-string">"*"</span>
    ],
    <span class="hljs-string">"api"</span>: [
      <span class="hljs-string">"eth"</span>,
      <span class="hljs-string">"net"</span>,
      <span class="hljs-string">"web3"</span>,
      <span class="hljs-string">"arb"</span>,
      <span class="hljs-string">"debug"</span>
    ]
  },
  <span class="hljs-string">"node"</span>: {
    <span class="hljs-string">"sequencer"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"delayed-sequencer"</span>: {
      <span class="hljs-string">"enable"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"use-merge-finality"</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-string">"finalize-distance"</span>: <span class="hljs-number">1</span>
    },
    <span class="hljs-string">"batch-poster"</span>: {
      <span class="hljs-string">"max-size"</span>: <span class="hljs-number">90000</span>,
      <span class="hljs-string">"enable"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"parent-chain-wallet"</span>: {
        <span class="hljs-string">"private-key"</span>: <span class="hljs-string">"&#x3C;Insert your private key here>"</span>
      }
    },
    <span class="hljs-string">"staker"</span>: {
      <span class="hljs-string">"enable"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"strategy"</span>: <span class="hljs-string">"MakeNodes"</span>,
      <span class="hljs-string">"parent-chain-wallet"</span>: {
        <span class="hljs-string">"private-key"</span>: <span class="hljs-string">"&#x3C;Insert your private key here>"</span>
      }
    },
    <span class="hljs-string">"dangerous"</span>: {
      <span class="hljs-string">"no-sequencer-coordinator"</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-string">"avail"</span>: {
      <span class="hljs-string">"enable"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"seed"</span>: <span class="hljs-string">"&#x3C;Enter your seed phrase here>"</span>,
      <span class="hljs-string">"avail-api-url"</span>: <span class="hljs-string">"wss://turing-rpc.avail.so/ws"</span>,
      <span class="hljs-string">"app-id"</span>: <span class="hljs-operator">&#x3C;</span><span class="hljs-type">Enter</span> your avail app id<span class="hljs-operator">></span>,
      <span class="hljs-string">"timeout"</span>:<span class="hljs-string">"100s"</span>,
      <span class="hljs-string">"vectorx"</span>: <span class="hljs-string">"0xA712dfec48AF3a78419A8FF90fE8f97Ae74680F0"</span>,
      <span class="hljs-string">"arbSepolia-rpc"</span>: <span class="hljs-string">"&#x3C;Enter an arbSepolia wss url here>"</span>
    }
  },
  <span class="hljs-string">"execution"</span>: {
    <span class="hljs-string">"forwarding-target"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-string">"sequencer"</span>: {
      <span class="hljs-string">"enable"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-string">"max-tx-data-size"</span>: <span class="hljs-number">85000</span>,
      <span class="hljs-string">"max-block-speed"</span>: <span class="hljs-string">"250ms"</span>
    },
    <span class="hljs-string">"caching"</span>: {
      <span class="hljs-string">"archive"</span>: <span class="hljs-literal">true</span>
    }
  }
}
 
</code></pre><p>Fill in the required values, including private keys, <code>app-id</code>, and any other specified parameters.</p></li></ol><h3 id="h-step-4-configure-orbitsetupscriptconfigjson" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 4: Configure <code>orbitSetupScriptConfig.json</code></h3><p>In <code>./config/orbitSetupScriptConfig.json</code>, enter your chain information as outlined below:</p><ul><li><p><code>networkFeeReceiver</code> - Builder wallet address.</p></li><li><p><code>infrastructureFeeCollector</code> - Builder wallet address.</p></li><li><p><code>staker</code> - Staker wallet address.</p></li><li><p><code>batchPoster</code> - Builder wallet address.</p></li><li><p><code>chainOwner</code> - Builder wallet address.</p></li><li><p><code>chainId</code> - Chain ID for your network.</p></li><li><p><code>chainName</code> - Chain name.</p></li><li><p><code>minL2BaseFee</code> - Set this to <code>100000000</code>.</p></li><li><p><code>parentChainId</code> - Set to <code>421614</code>.</p></li><li><p><code>parent-chain-node-url</code> - <code>https://sepolia-rollup.arbitrum.io/rpc</code>.</p></li></ul><p>Below is an example configuration:</p><pre data-type="codeBlock" text="
{
  &quot;networkFeeReceiver&quot;: &quot;0xd41996ED89bb5BF7dBfB181D8D93E8067446200B&quot;,
  &quot;infrastructureFeeCollector&quot;: &quot;0xd41996ED89bb5BF7dBfB181D8D93E8067446200B&quot;,
  &quot;staker&quot;: &quot;0xDF819f9Fc3c28FEDFb73374C7B60A4f9BCdE6710&quot;,
  &quot;batchPoster&quot;: &quot;0xB0Ad5AE0a78025613F17B7e4644CE5752487B9d6&quot;,
  &quot;chainOwner&quot;: &quot;0xd41996ED89bb5BF7dBfB181D8D93E8067446200B&quot;,
  &quot;chainId&quot;: 99584959107,
  &quot;chainName&quot;: &quot;My Arbitrum L3 Chain&quot;,
  &quot;minL2BaseFee&quot;: 100000000,
  &quot;parentChainId&quot;: 421614,
  &quot;parent-chain-node-url&quot;: &quot;https://sepolia-rollup.arbitrum.io/rpc&quot;,
  &quot;utils&quot;: &quot;0xB11EB62DD2B352886A4530A9106fE427844D515f&quot;,
  &quot;rollup&quot;: &quot;0xd30eCcf27A6f351EfA4fc9D17e7ec20354309aE3&quot;,
  &quot;inbox&quot;: &quot;0x4512e40a1ec8555f9e93E3B6a06af60F13538087&quot;,
  &quot;nativeToken&quot;: &quot;0x0000000000000000000000000000000000000000&quot;,
  &quot;outbox&quot;: &quot;0x2209755fA3470ED1AFFB4407d1e3B1f7dFC13ce9&quot;,
  &quot;rollupEventInbox&quot;: &quot;0x1e58240B2D769de25B4811354819C901317D0894&quot;,
  &quot;challengeManager&quot;: &quot;0xfC5BbC40d24EcD6FcC247EfFDc87E7D074E9B67D&quot;,
  &quot;adminProxy&quot;: &quot;0xf488b25e6736Ed74E8d37EA434892129E4d62E3B&quot;,
  &quot;sequencerInbox&quot;: &quot;0xD15347309854F1290c9a382ea2719AB5462c7719&quot;,
  &quot;bridge&quot;: &quot;0xC83ee8e28B7b258f41aF8ef4279c02f901288029&quot;,
  &quot;upgradeExecutor&quot;: &quot;0x805bB07B88dDA56030eC48644E0C276e2e5E3949&quot;,
  &quot;validatorUtils&quot;: &quot;0xB11EB62DD2B352886A4530A9106fE427844D515f&quot;,
  &quot;validatorWalletCreator&quot;: &quot;0xEb9885B6c0e117D339F47585cC06a2765AaE2E0b&quot;,
  &quot;deployedAtBlockNumber&quot;: 11274529
}
"><code>
<span class="hljs-punctuation">{</span>
  <span class="hljs-attr">"networkFeeReceiver"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xd41996ED89bb5BF7dBfB181D8D93E8067446200B"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"infrastructureFeeCollector"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xd41996ED89bb5BF7dBfB181D8D93E8067446200B"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"staker"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xDF819f9Fc3c28FEDFb73374C7B60A4f9BCdE6710"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"batchPoster"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xB0Ad5AE0a78025613F17B7e4644CE5752487B9d6"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"chainOwner"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xd41996ED89bb5BF7dBfB181D8D93E8067446200B"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"chainId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">99584959107</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"chainName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Arbitrum L3 Chain"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"minL2BaseFee"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100000000</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"parentChainId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">421614</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"parent-chain-node-url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"https://sepolia-rollup.arbitrum.io/rpc"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"utils"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xB11EB62DD2B352886A4530A9106fE427844D515f"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"rollup"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xd30eCcf27A6f351EfA4fc9D17e7ec20354309aE3"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"inbox"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x4512e40a1ec8555f9e93E3B6a06af60F13538087"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"nativeToken"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"outbox"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x2209755fA3470ED1AFFB4407d1e3B1f7dFC13ce9"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"rollupEventInbox"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x1e58240B2D769de25B4811354819C901317D0894"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"challengeManager"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xfC5BbC40d24EcD6FcC247EfFDc87E7D074E9B67D"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"adminProxy"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xf488b25e6736Ed74E8d37EA434892129E4d62E3B"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"sequencerInbox"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xD15347309854F1290c9a382ea2719AB5462c7719"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"bridge"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xC83ee8e28B7b258f41aF8ef4279c02f901288029"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"upgradeExecutor"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0x805bB07B88dDA56030eC48644E0C276e2e5E3949"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"validatorUtils"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xB11EB62DD2B352886A4530A9106fE427844D515f"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"validatorWalletCreator"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"0xEb9885B6c0e117D339F47585cC06a2765AaE2E0b"</span><span class="hljs-punctuation">,</span>
  <span class="hljs-attr">"deployedAtBlockNumber"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">11274529</span>
<span class="hljs-punctuation">}</span>
</code></pre><h3 id="h-step-5-download-the-docker-image" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 5: Download the Docker Image</h3><p>Retrieve the <code>avail-nitro-node</code> Docker image using the command below:</p><pre data-type="codeBlock" text="
docker pull availj/avail-nitro-node:v2.1.0-upstream-v3.1.1
"><code>
docker pull availj<span class="hljs-operator">/</span>avail<span class="hljs-operator">-</span>nitro<span class="hljs-operator">-</span>node:v2<span class="hljs-number">.1</span><span class="hljs-number">.0</span><span class="hljs-operator">-</span>upstream<span class="hljs-operator">-</span>v3<span class="hljs-number">.1</span><span class="hljs-number">.1</span>
</code></pre><h3 id="h-step-6-update-the-docker-compose-file-in-orbit-setup-script" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 6: Update the <code>docker-compose</code> File in <code>orbit-setup-script</code></h3><p>In the <code>docker-compose</code> file, replace the relevant image field:</p><pre data-type="codeBlock" text="
nitro:
  image: availj/avail-nitro-node:v2.1.0-upstream-v3.1.1
  ports:
  ...
"><code>
nitro:
  image: availj<span class="hljs-operator">/</span>avail<span class="hljs-operator">-</span>nitro<span class="hljs-operator">-</span>node:v2<span class="hljs-number">.1</span><span class="hljs-number">.0</span><span class="hljs-operator">-</span>upstream<span class="hljs-operator">-</span>v3<span class="hljs-number">.1</span><span class="hljs-number">.1</span>
  ports:
  ...
</code></pre><h3 id="h-step-7-run-your-chain" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Step 7: Run Your Chain</h3><p>You’re almost there! From the root directory of the <code>orbit-setup-script</code>, run:</p><pre data-type="codeBlock" text="docker-compose up -d
"><code>docker<span class="hljs-operator">-</span>compose up <span class="hljs-operator">-</span>d
</code></pre><p>Visit <code>http://localhost:8449/</code> for RPC access and <code>http://localhost/</code> to check out your BlockScout explorer.</p><h2 id="h-wrapping-up-you-did-it" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping Up: You Did It!</h2><p>That’s it! You&apos;ve built your own L3 rollup chain on Arbitrum Orbit with Avail DA. Give yourself a pat on the back—you’re now part of an elite group of blockchain pioneers. Whether you’re here for innovation, learning, or just a thrilling ride, keep pushing the boundaries. The blockchain world is your oyster! 🚀</p><hr><h3 id="h-appendix-a-checking-your-chain-logs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Appendix A: Checking Your Chain Logs 📝</h3><p>Keeping an eye on your chain’s logs is crucial to ensure everything is running smoothly. Here’s how you can do it with a single command:</p><p>Navigate to the root directory of your Orbit setup script and run:</p><pre data-type="codeBlock" text="docker-compose logs -f nitro
"><code>docker<span class="hljs-operator">-</span>compose logs <span class="hljs-operator">-</span>f nitro
</code></pre><p>This command streams real-time logs, so you can spot any issues or just confirm your chain is running as expected.</p><h3 id="h-appendix-b-depositing-ethnative-tokens" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Appendix B: Depositing ETH/Native Tokens 💸</h3><p>Need to top up your Orbit chain account with ETH or native tokens? Here’s how to do it:</p><ol><li><p>Make sure you’re in the base directory of your setup script.</p></li><li><p>Run the following command, but don’t forget to replace <code>0xYourPrivateKey</code> with your actual private key and <code>&lt;AMOUNT&gt;</code> with the number of tokens you want to send:</p></li></ol><pre data-type="codeBlock" text="PRIVATE_KEY=&quot;0xYourPrivateKey&quot; L2_RPC_URL=&quot;https://sepolia-rollup.arbitrum.io/rpc&quot; L3_RPC_URL=&quot;http://localhost:8449&quot; AMOUNT=&quot;&lt;AMOUNT&gt;&quot; yarn run deposit
"><code>PRIVATE_KEY<span class="hljs-operator">=</span><span class="hljs-string">"0xYourPrivateKey"</span> L2_RPC_URL<span class="hljs-operator">=</span><span class="hljs-string">"https://sepolia-rollup.arbitrum.io/rpc"</span> L3_RPC_URL<span class="hljs-operator">=</span><span class="hljs-string">"http://localhost:8449"</span> AMOUNT<span class="hljs-operator">=</span><span class="hljs-string">"&#x3C;AMOUNT>"</span> yarn run deposit
</code></pre><p><strong>Tip:</strong> Triple-check your private key for typos or unwanted spaces to avoid any hiccups!</p><h3 id="h-bonus-adding-your-orbit-chain-to-metamask" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Bonus: Adding Your Orbit Chain to MetaMask 🦊</h3><p>What’s a new chain without the ability to manage it through MetaMask? Here’s a quick way to add your Orbit chain:</p><ol><li><p>Open MetaMask and navigate to <strong>Settings</strong> &gt; <strong>Networks</strong>.</p></li><li><p>Click <strong>Add Network</strong> and fill in the following details:</p><ul><li><p><strong>Network Name</strong>: My Orbit Chain (or any name you prefer)</p></li><li><p><strong>New RPC URL</strong>: <code>http://localhost:8449</code></p></li><li><p><strong>Chain ID</strong>: 123456 (replace with your chain’s ID if different)</p></li><li><p><strong>Currency Symbol</strong>: ETH</p></li><li><p><strong>Block Explorer URL</strong>: (Leave empty or add if available)</p></li></ul></li><li><p>Hit <strong>Save</strong>, and you’re ready to manage your Orbit chain seamlessly through MetaMask!</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/113ff714f5b367148b0aab9c2b2d67dd05be8ea96d39e875cbec839b01a3b498.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Voilà!</strong> Now you’re equipped to monitor logs, deposit ETH, and use MetaMask with your chain. It&apos;s time to see your L3 in action. 🚀</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/f6f7167013ee3e6ba8f2cba30a9c8b738e6d1393021cf50ac0fb0cfe255d9e08.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How Do Blockchains Ensure All Data Is Available Without Downloading Everything? Exploring Data Availability Sampling (DAS)]]></title>
            <link>https://paragraph.com/@kaushik-2/how-do-blockchains-ensure-all-data-is-available-without-downloading-everything-exploring-data-availability-sampling-das</link>
            <guid>zdTQ8YwZe68mFtMr0Qff</guid>
            <pubDate>Fri, 01 Nov 2024 12:39:01 GMT</pubDate>
            <description><![CDATA[Have you ever wondered how blockchains can scale up without forcing everyone to download every bit of data? How can we be sure that all the necessary information is available without bogging down everyone’s devices? Or how can we prevent dishonest nodes from hiding or tampering with parts of the data without every single node needing to check every single byte? These questions bring us to the concept of Data Availability Sampling (DAS), a crucial tool for blockchain scaling and security. DAS ...]]></description>
            <content:encoded><![CDATA[<p>Have you ever wondered how blockchains can scale up without forcing everyone to download every bit of data? How can we be sure that all the necessary information is available without bogging down everyone’s devices? Or how can we prevent dishonest nodes from hiding or tampering with parts of the data without every single node needing to check every single byte?</p><p>These questions bring us to the concept of <strong>Data Availability Sampling</strong> (DAS), a crucial tool for blockchain scaling and security. DAS lets nodes confirm that all necessary data in a block is present without downloading the entire thing. But how does this magic happen? Let’s dive in and explore DAS through a fun analogy that makes the process easier to understand.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9c93eef14c22e00130832021689c5b95a10c0324cd316d4e6618ea7703cc1de3.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Understanding DAS Through a Story</strong></p><p>Imagine you and your friends are contributing to a huge art mural for a community event. The mural is assembled from individual square tiles that everyone decorates, and once complete, it’s set up in a gallery with a big, locked glass case over it.</p><p>People want to verify that the mural is complete and untampered, but the gallery only allows visitors to look at it through small peepholes in the glass case. These peepholes are placed randomly, so each visitor can only see a few random sections of the mural. You’re confident that if enough people check enough peepholes and verify the tiles they see, you can be sure the entire mural is intact without anyone having to see the entire mural at once. This is the same concept used in Data Availability Sampling.</p><h3 id="h-how-das-works-in-blockchain-terms" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">How DAS Works in Blockchain Terms</h3><p>In the blockchain world, a “block” of data includes all the transactions and information for a specific time period. Traditionally, for security and verification, every node on the network would need to download and store all this data. This approach, however, becomes impractical as blockchains scale up, especially with popular Layer 2 solutions like rollups. This is where DAS shines: it allows nodes to check smaller, random sections of each block to ensure all the data is available.</p><h3 id="h-breaking-it-down-the-mechanics-of-das" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Breaking It Down: The Mechanics of DAS</h3><ol><li><p><strong>Random Sampling</strong>: Instead of downloading an entire block, each node samples small, random portions of the block’s data. In our mural analogy, this is like people peeking through random peepholes to see if everything looks right.</p></li><li><p><strong>Collective Assurance</strong>: If enough nodes (or “visitors” in our analogy) check enough parts of the block and confirm that their pieces are intact, then the network collectively trusts that the block’s data is complete.</p></li><li><p><strong>Catching Dishonest or Missing Data</strong>: What if someone tries to cheat by hiding or tampering with data? In our story, if someone removed or altered parts of the mural, random checks by enough people would likely catch this because some peeks would reveal missing or suspicious areas. Similarly, in DAS, if data is missing, nodes checking random sections will notice it, raising an alert and preventing the block from being trusted.</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c7adf81cc12c423dbb45b1f10a33581fa829a7c622c0834d9b1ec7f3e7b9e783.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Another Everyday Story: Spot Checking at Work</strong></p><p>DA’s sampling reminded me of how I used to check inventory at a previous job. We had so much stock that it was impossible to check every item. Instead, we’d do random spot checks to confirm that everything matched our records. If a few random samples were accurate, we trusted the whole inventory was in good shape.</p><p>This is essentially what DA does for rollups. It’s about verifying just enough data to trust the whole dataset. If nodes can confirm a small portion of the data, the blockchain assumes all the data is there, which allows for scalability and efficiency that I used to think wasn’t even possible in blockchain.</p><h3 id="h-why-das-is-essential-for-scalable-blockchains" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Why DAS is Essential for Scalable Blockchains</h3><p>Blockchains need to be efficient and secure to accommodate growing transaction volumes. DAS is a game-changer because it tackles two big challenges simultaneously:</p><ul><li><p><strong>Efficiency</strong>: By allowing nodes to check only small portions of a block’s data, DAS dramatically reduces the amount of storage and computation each node needs. This means more lightweight and resource-limited devices can participate in the network.</p></li><li><p><strong>Security with Scale</strong>: DAS maintains the network’s security even as the blockchain scales. Instead of every node needing to verify every bit of data, random sampling allows us to trust that all data is available. This lets the network expand without becoming overloaded.</p></li></ul><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/748d495d2f475d0079f805f527204a8e3b36edcdd6ce797cb707e070eaa91606.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Another Real-World Example: The Community Recipe Book</strong></p><p>Consider a community where each member contributes recipes to a giant recipe book, but no one can see the entire book. Instead, randomly selected “reviewers” each receive a few pages to check for authenticity. If all reviewers report that their sections are intact and well-formatted, the community can trust the entire book without anyone seeing it all. If, however, any reviewer finds a missing or incorrect page, they’ll raise a flag, and others can check that specific area.</p><p>In DAS, every node plays the role of the reviewer, checking random parts of the “recipe book” (the data block) and collectively confirming its completeness.</p><h3 id="h-the-technical-side-of-das" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Technical Side of DAS</h3><p>In blockchains, nodes use cryptographic tools to handle the random sampling efficiently. Here’s how it typically works in practice:</p><ol><li><p><strong>Data Splitting and Sampling</strong>: When a block is created, it’s split into smaller pieces. Each node in the network is responsible for checking a few random pieces.</p></li><li><p><strong>Cross-Node Verification</strong>: Because nodes independently check random pieces, a large enough number of nodes performing these random checks can verify the full data’s availability. Even if some nodes are dishonest or malfunctioning, the majority’s consensus will ensure the data’s validity.</p></li><li><p><strong>Error Detection and Recovery</strong>: If missing or corrupted data is found in a sample, the network can respond by either rejecting the block or requiring further checks.</p></li></ol><h3 id="h-the-role-of-das-in-layer-2-solutions-like-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Role of DAS in Layer 2 Solutions Like Rollups</h3><p>Layer 2 solutions, such as rollups, bundle transactions into batches to be processed more efficiently off the main blockchain (Layer 1). These batches are then anchored to Layer 1, maintaining security without overloading the primary chain.</p><p>DAS is essential here because it ensures data availability for these batches without requiring Layer 1 to handle all the data. By relying on DAS, Layer 2 solutions can confirm that the data they send to Layer 1 is intact, scalable, and secure without needing to process everything on-chain.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/82c21f12bf71f29a6223f37dd1a48fada6fcecc43df294645a459210c315a695.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Looking Ahead: DAS for the Future of Blockchain</strong></p><p>As blockchains continue to grow, especially with advancements in Layer 2 technologies, DAS will be a cornerstone of efficient, secure, and scalable blockchain systems. It allows blockchain networks to onboard more users and handle higher transaction volumes without compromising on security or accessibility.</p><p>Through techniques like DAS, we’re building a blockchain future that’s lighter, faster, and still as reliable as ever. This means that as blockchains continue to scale, we can be confident in their data integrity without weighing down every participant with endless storage and computational needs.</p><p>So, the next time you hear about blockchain scalability, remember DAS and the power of sampling. With a little teamwork and a lot of clever design, we’re making sure that even the largest data can be verified, one sample at a time.</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/8ea8c7e1b930c2542722e63c2a683f2a411fd4c48ffc4a451c4337cdf157985d.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[What is Data Availability (DA) and Why is it Important for Rollups?]]></title>
            <link>https://paragraph.com/@kaushik-2/what-is-data-availability-da-and-why-is-it-important-for-rollups</link>
            <guid>2ylFFcyk1WKaWn4AlyL2</guid>
            <pubDate>Wed, 30 Oct 2024 06:35:57 GMT</pubDate>
            <description><![CDATA[When I first heard about "Data Availability" (or DA) in the context of blockchains and rollups, I remember thinking, "Okay, it sounds like something important... but what is it really?" And maybe you’re thinking the same thing right now. Well, let’s dive into DA and why it’s crucial for rollups—using a few everyday examples that might help make things clearer.Starting with a Question: Why Does Data Matter?Imagine you’re part of a group project in school or at work. Everyone has specific tasks...]]></description>
            <content:encoded><![CDATA[<p>When I first heard about &quot;Data Availability&quot; (or DA) in the context of blockchains and rollups, I remember thinking, &quot;Okay, it sounds like something important... but what <em>is</em> it really?&quot; And maybe you’re thinking the same thing right now. Well, let’s dive into DA and why it’s crucial for rollups—using a few everyday examples that might help make things clearer.</p><hr><h3 id="h-starting-with-a-question-why-does-data-matter" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Starting with a Question: Why Does Data Matter?</strong></h3><p>Imagine you’re part of a group project in school or at work. Everyone has specific tasks, but at the end of the day, for the project to make sense, everyone needs to have access to the same information. If someone decides to keep their part of the project to themselves, the whole thing falls apart. You can’t present an incomplete project to your teacher or boss, right?</p><p>In the blockchain world, &quot;data&quot; works a bit like that. Every transaction on a blockchain is part of a bigger project (the full record of transactions) that needs to be accessible to everyone to ensure that the system is working correctly. This is where Data Availability (DA) comes in. It&apos;s about making sure everyone in the system has access to the necessary data to validate transactions and keep everything transparent.</p><hr><h3 id="h-so-whats-the-role-of-da-in-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>So, What’s the Role of DA in Rollups?</strong></h3><p>Okay, but what does this mean for rollups? Well, rollups, as you may know, are a way to scale blockchains by bundling (or “rolling up”) transactions off the main chain. Imagine a theme park where instead of each person going through the entrance gate one by one, you gather groups together to speed things up (we’ve talked about rollups like this before, right?). Now, after rolling up these transactions, you still need to “publish” them somewhere so everyone knows they happened. And this is where DA steps in.</p><p>In short, DA ensures that the data behind those bundled transactions is available for everyone to check if they want to. Without reliable DA, the whole rollup idea wouldn’t work—because how could anyone trust that the transactions in those rollups are valid if they can’t see the data behind them?</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c648384f389cc6cc176d68768c3eea885aeedfbe94d578792200b3b653ac0bae.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-a-real-life-analogy-the-public-notice-board" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>A Real-Life Analogy: The Public Notice Board</strong></h3><p>Think of DA like a public notice board in a town square. Imagine there&apos;s a rule that every group project (our rollup, in this case) needs to be pinned to this board for everyone to see. If someone has doubts about whether the work was done correctly, they can walk up to the board, check the details, and verify it.</p><p>In blockchain terms, DA is that &quot;public board&quot; where transaction data is posted so that anyone can verify it later if needed. It’s a way of saying, “Hey, here’s the proof of what we’ve done, in case anyone wants to double-check.”</p><hr><h3 id="h-why-does-da-matter-so-much" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Does DA Matter So Much?</strong></h3><p>Now, you might be thinking, &quot;But why can’t we just assume things are fine if the rollup says so?&quot; Well, let’s go back to our project analogy.</p><p>Imagine if, one day, someone in the group decided to submit work without sharing their part with anyone else, assuming “Trust me, I did my part.” Now, even if they’re telling the truth, it’s risky to trust without verification. In blockchain, trust is built on transparency, and DA is a key part of that transparency.</p><p>If the data isn’t available for everyone to see, it opens the door to potential fraud. Someone could sneak in false information, and there would be no way to verify it without DA. With DA, however, you have that safety net: everyone can see, check, and verify the data, maintaining trust across the network.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c1d0aa067d56792b91537cde2166999aca3d2521958b7a3a38302f533e8cd280.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-is-da-just-an-off-chain-database-like-aws-or-cloud-storage-how-is-da-different-from-ipfs-and-filecoin" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Is DA Just an Off-Chain Database Like AWS or Cloud Storage? How Is DA Different from IPFS and Filecoin?</strong></h3><p>You’re right that DA solutions have some similarities to off-chain databases or cloud storage, but there are some key differences. Let’s break it down:</p><h3 id="h-data-availability-da-layers-vs-traditional-cloud-storage-aws-google-cloud-etc" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Data Availability (DA) Layers vs. Traditional Cloud Storage (AWS, Google Cloud, etc.)</strong></h3><ul><li><p><strong>Purpose</strong>: DA layers are designed specifically for the needs of blockchains, focusing on making data publicly accessible and verifiable. In contrast, AWS or Google Cloud are general-purpose storage solutions that don’t have built-in transparency or immutability features.</p></li><li><p><strong>Trust Model</strong>: DA layers are usually decentralized, meaning there isn’t a single company or central authority controlling access. They’re designed so that data is verifiable and accessible to all participants on the blockchain. With AWS or Google Cloud, you rely on the cloud provider to manage your data, which isn’t ideal for decentralized applications.</p></li></ul><h3 id="h-how-da-layers-compare-to-decentralized-storage-like-ipfs-and-filecoin" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>How DA Layers Compare to Decentralized Storage like IPFS and Filecoin</strong></h3><ul><li><p><strong>Purpose and Design</strong>: DA layers (like Celestia) are built specifically to store transaction data or proofs that are essential for verifying rollup transactions on the blockchain. They are not meant for storing large files or arbitrary data like documents or videos. Instead, they are optimized to make transaction data available quickly for verification purposes.</p></li><li><p><strong>Data Verification</strong>: In DA layers, the emphasis is on making sure that specific blockchain-related data is always accessible and verifiable. With IPFS or Filecoin, the focus is on storing large files in a decentralized way, where content is referenced by a hash (content addressable). While IPFS makes it possible to find and retrieve data, it doesn’t guarantee that data will be available unless it’s &quot;pinned&quot; or specifically hosted by nodes.</p></li><li><p><strong>Economic Model</strong>: DA layers and IPFS/Filecoin differ in how they incentivize data availability. Filecoin, for example, uses a proof mechanism where miners are incentivized to store and make data available. In contrast, DA layers often integrate more directly with rollup protocols, ensuring that essential blockchain data remains available for validation.</p></li></ul><h3 id="h-to-summarize-da-layers-vs-ipfsfilecoin" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>To Summarize: DA Layers vs. IPFS/Filecoin</strong></h3><ul><li><p><strong>DA Layers</strong>: Designed specifically for blockchain transaction data, to make it available and verifiable. They work closely with rollups to publish transaction data in a way that can be checked by the main blockchain.</p></li><li><p><strong>IPFS/Filecoin</strong>: Primarily designed to store general-purpose files in a decentralized way. Filecoin provides incentives for long-term data storage but doesn’t focus on transaction-level data verification for blockchains.</p></li></ul><hr><h3 id="h-why-not-just-use-ipfs-or-filecoin-as-da" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Not Just Use IPFS or Filecoin as DA?</strong></h3><p>While IPFS and Filecoin are great for decentralized file storage, they don’t meet all the requirements needed for rollup DA:</p><ol><li><p><strong>Immediate Availability</strong>: Rollups need data to be available quickly and reliably so that it can be verified by anyone, anytime. DA solutions for rollups ensure that data is accessible as soon as it’s published.</p></li><li><p><strong>Data Integrity and Verification</strong>: Rollups rely on DA layers to ensure data integrity and that transaction data is available for auditing and dispute resolution if needed. IPFS and Filecoin focus on file storage rather than specific transaction-level verification.</p></li><li><p><strong>Optimized for Transaction Data</strong>: DA layers like Celestia are built specifically for blockchain transaction data, which makes them lightweight and efficient for verifying rollups, whereas IPFS/Filecoin are not optimized for such use cases.</p></li></ol><hr><h3 id="h-different-ways-rollups-handle-data-availability" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Different Ways Rollups Handle Data Availability</strong></h3><p>So, how do different rollups ensure DA? There are a couple of ways, each with its own pros and cons.</p><ol><li><p><strong>On-Chain DA (Layer 1)</strong>: This is the most straightforward way—publish all the data directly on the blockchain. It’s like pinning your project on the main notice board where everyone can see it. It’s reliable, but can be expensive because storing data on-chain is costly.</p></li><li><p><strong>Off-Chain DA Solutions</strong>: Some rollups publish their data off-chain, using specialized DA layers or committees. Imagine a side notice board managed by a trusted group in town, with rules to ensure they keep things transparent. It’s usually cheaper but can introduce some trust assumptions.</p></li><li><p><strong>DA Layers (like Celestia)</strong>: New solutions are being developed that specialize only in DA, like Celestia. These are like dedicated public notice boards only for data availability, designed to store data reliably and make it accessible to anyone who wants to verify it. This could be a game-changer for rollups in terms of scalability and efficiency.</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3ce045b17a0f48eb237cb2fd1a6a0338f23ef5bf95c4666f195cce3bad5de15e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-for-example-celestia" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For example Celestia:</h2><ul><li><p>Data availability is a core scaling bottleneck for crypto applications and is the vast majority of costs that rollups and Layer 2s pay.</p></li><li><p>Data availability is about proving that data was published by allowing anyone to download it for a short period of time.</p></li><li><p>A DA layer is a blockchain that rollups and L2s publish their transaction data to.</p></li><li><p>Celestia’s DA layer eliminates data availability as a core scaling bottleneck, dropping costs for developers by ~95% and enabling them to build fully-onchain apps.</p></li><li><p>This celestia also uses new technology called DAS(data availability sampling ). We will explore more detail about it in my next blogs.</p></li></ul><h2 id="h-why-da-and-rollups-are-the-perfect-team-for-blockchain-scaling" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Why DA and Rollups are the Perfect Team for Blockchain Scaling</strong></h2><p>The beauty of rollups is that they allow blockchains to scale by moving transactions off the main chain while still ensuring security. But without DA, rollups would lose this trustworthiness. DA provides a check to ensure that off-chain transactions are still verifiable. It’s like combining the speed of using shortcuts with the safety of public oversight.</p><hr><h3 id="h-in-simple-terms-da-makes-rollups-trustworthy" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>In Simple Terms: DA Makes Rollups Trustworthy</strong></h3><p>If I were to put it simply: DA is like a safeguard that makes sure all transactions processed by rollups are available for public verification. Without DA, we’d have faster transactions, but we’d lose the transparency that blockchains rely on.</p><p>So, next time you hear about DA and rollups, think about that public notice board in the town square. It’s there to keep things fair, transparent, and trustworthy, allowing us to scale blockchains while preserving the core principle of transparency.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7593d3db74ecd64f563cb70f97c183200ef601acb03e669f1b7533c488c92350.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-final-thoughts-a-must-have-for-scaling" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Final Thoughts: A Must-Have for Scaling</strong></h3><p>Data Availability might seem like a technical detail, but it&apos;s essential for the future of scalable and secure blockchain technology. It’s what makes it possible for rollups to thrive, providing faster, cheaper transactions while keeping the trust that blockchains are known for. DA is not just a feature; it’s a foundational piece of how blockchains can scale effectively.</p><p>So, in the quest for scaling blockchains, remember: DA is like the unsung hero behind the scenes, quietly ensuring that everything we do remains transparent, accessible, and verifiable for everyone involved.</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/d7eb9dd5c28e292cf5ed1f6de8d0fcc1b0ffce2f0d027588507d5937b5197382.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Decentralized Tic Tac Toe Game with Stackr Labs]]></title>
            <link>https://paragraph.com/@kaushik-2/building-a-decentralized-tic-tac-toe-game-with-stackr-labs</link>
            <guid>hzIz2l2Bd5Ssq29gSxYB</guid>
            <pubDate>Sun, 13 Oct 2024 05:13:11 GMT</pubDate>
            <description><![CDATA[Hello, fellow developers! Today, I’m thrilled to guide you through creating a decentralized Tic Tac Toe game using Stackr Labs and Micro Rollups (MRUs). This guide will take you through each step, from setting up your project to deploying your game on the blockchain. Let’s get started!What is Stackr Labs?Stackr Labs gives your dapp a turbo boost! With Micro-Rollups (MRUs), helps developers build fast, scalable apps. Think of it like preparing food at home for a trip—most of the work happens o...]]></description>
            <content:encoded><![CDATA[<p>Hello, fellow developers! Today, I’m thrilled to guide you through creating a decentralized Tic Tac Toe game using <strong>Stackr Labs</strong> and <strong>Micro Rollups (MRUs)</strong>. This guide will take you through each step, from setting up your project to deploying your game on the blockchain. Let’s get started!</p><h2 id="h-what-is-stackr-labs" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">What is Stackr Labs?</h2><p>Stackr Labs gives your dapp a turbo boost! With Micro-Rollups (MRUs), helps developers build fast, scalable apps. Think of it like preparing food at home for a trip—most of the work happens off-chain, ensuring a smoother experience on the blockchain!</p><h2 id="h-table-of-contents" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Table of Contents</h2><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg#prerequisites">Prerequisites</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Setting Up Your Project</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Creating the State Machine</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Implementing the Game Logic</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Frontend Integration</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Testing Your Game</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mirror.xyz/0x5BDf4cE6749d7e93c05897fa23871C280BF59b5b/vE8e9dgoMC5saS5Jq-LS5nif4KVTdAVqu-uvoyLDlmg">Conclusion</a></p></li></ol><h2 id="h-prerequisites" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Prerequisites</h2><p>Before we dive in, make sure you have the following tools installed:</p><ul><li><p><strong>Node.js</strong>: Download and install from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://nodejs.org/">Node.js</a></p></li><li><p><strong>Git</strong>: Download and install from <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://git-scm.com/">Git</a></p></li><li><p><strong>Bun</strong>: Installation guide can be found <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bun.sh/">here</a></p></li><li><p><strong>Stackr CLI</strong>: Install globally using:</p><pre data-type="codeBlock" text="bun install -g @stackr/cli
"><code>bun install <span class="hljs-operator">-</span>g @stackr<span class="hljs-operator">/</span>cli
</code></pre></li></ul><h2 id="h-create-a-new-directory" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Create a New Directory</strong>:</h2><pre data-type="codeBlock" text="mkdir tic-tac-toe
cd tic-tac-toe
"><code>mkdir tic<span class="hljs-operator">-</span>tac<span class="hljs-operator">-</span>toe
cd tic<span class="hljs-operator">-</span>tac<span class="hljs-operator">-</span>toe
</code></pre><h2 id="h-initialize-a-new-stackr-project" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Initialize a New Stackr Project</strong>:</h2><pre data-type="codeBlock" text="bunx @stackr/cli@latest init
"><code>bunx <span class="hljs-meta">@stackr</span>/<span class="hljs-symbol">cli@</span>latest <span class="hljs-keyword">init</span>
</code></pre><h2 id="h-setting-up-your-project" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Setting Up Your Project</h2><p>You can use the following .env values:</p><pre data-type="codeBlock" text="# PRIVATE_KEY is the hex-encoded private key of the Ethereum address that will act as the rollup operator.
PRIVATE_KEY= --YOUR DEVELOPMENT PRIVATE KEY--

# L1_RPC is the RPC endpoint of the L1 chain. Can be found in the network config docs above.
L1_RPC=https://rpc2.sepolia.org/

# VULCAN_RPC is the RPC endpoint of the Vulcan network. Can be found in the network config docs above.
VULCAN_RPC=https://sepolia.vulcan.stf.xyz/

# REGISTRY_CONTRACT is the address of the Stackr registry contract on the L1 chain. Can be found in the network config docs above.
REGISTRY_CONTRACT=0x985bAfb3cAf7F4BD13a21290d1736b93D07A1760

# DATABASE_URI is the connection URI of the SQL database.
DATABASE_URI=./db.sqlite

# NODE_ENV is the current environment (production or development). Relevant for express.js server.
NODE_ENV=development
"><code><span class="hljs-comment"># PRIVATE_KEY is the hex-encoded private key of the Ethereum address that will act as the rollup operator.</span>
<span class="hljs-attr">PRIVATE_KEY</span>= --YOUR DEVELOPMENT PRIVATE KEY--

<span class="hljs-comment"># L1_RPC is the RPC endpoint of the L1 chain. Can be found in the network config docs above.</span>
<span class="hljs-attr">L1_RPC</span>=https://rpc2.sepolia.org/

<span class="hljs-comment"># VULCAN_RPC is the RPC endpoint of the Vulcan network. Can be found in the network config docs above.</span>
<span class="hljs-attr">VULCAN_RPC</span>=https://sepolia.vulcan.stf.xyz/

<span class="hljs-comment"># REGISTRY_CONTRACT is the address of the Stackr registry contract on the L1 chain. Can be found in the network config docs above.</span>
<span class="hljs-attr">REGISTRY_CONTRACT</span>=<span class="hljs-number">0</span>x985bAfb3cAf7F4BD13a21290d1736b93D07A1760

<span class="hljs-comment"># DATABASE_URI is the connection URI of the SQL database.</span>
<span class="hljs-attr">DATABASE_URI</span>=./db.sqlite

<span class="hljs-comment"># NODE_ENV is the current environment (production or development). Relevant for express.js server.</span>
<span class="hljs-attr">NODE_ENV</span>=development
</code></pre><h2 id="h-creating-the-state-machine" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Creating the state machine</h2><p>In this section, we’ll define the game state using Stackr’s state management.</p><h3 id="h-srcstackrstatets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><code>src/stackr/state.ts</code></h3><pre data-type="codeBlock" text="import { BytesLike, solidityPackedKeccak256 } from &quot;ethers&quot;;
import { State } from &quot;@stackr/sdk/machine&quot;;

// Define the structure of a Tic Tac Toe game
interface Game {
  gameId: string;
  owner: string; // Player X&apos;s address
  playerO: string; // Player O&apos;s address
  board: string[]; // 9 cells represented as a string array
  currentPlayer: &quot;X&quot; | &quot;O&quot;;
  winner: &quot;X&quot; | &quot;O&quot; | &quot;Draw&quot; | null;
  startedAt: number;
  endedAt: number | null;
}

// Define the overall application state
export interface AppState {
  games: Record&lt;string, Game&gt;;
}

// Initial state of the application
export const initialState: AppState = {
  games: {},
};

// TicTacToeState class extends the State class from @stackr/sdk
export class TicTacToeState extends State&lt;AppState&gt; {
  constructor(state: AppState) {
    super(state);
  }

  // Generate a root hash of the current state
  // This is used for state verification and integrity checks
  getRootHash(): BytesLike {
    return solidityPackedKeccak256(
      [&quot;string&quot;],
      [JSON.stringify(this.state.games)]
    );
  }
}
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">BytesLike</span>, <span class="hljs-title">solidityPackedKeccak256</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">State</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@stackr/sdk/machine"</span>;

<span class="hljs-comment">// Define the structure of a Tic Tac Toe game</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Game</span> </span>{
  gameId: <span class="hljs-keyword">string</span>;
  owner: <span class="hljs-keyword">string</span>; <span class="hljs-comment">// Player X's address</span>
  playerO: <span class="hljs-keyword">string</span>; <span class="hljs-comment">// Player O's address</span>
  board: <span class="hljs-keyword">string</span>[]; <span class="hljs-comment">// 9 cells represented as a string array</span>
  currentPlayer: <span class="hljs-string">"X"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"O"</span>;
  winner: <span class="hljs-string">"X"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"O"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"Draw"</span> <span class="hljs-operator">|</span> null;
  startedAt: number;
  endedAt: number <span class="hljs-operator">|</span> null;
}

<span class="hljs-comment">// Define the overall application state</span>
export <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">AppState</span> </span>{
  games: Record<span class="hljs-operator">&#x3C;</span><span class="hljs-keyword">string</span>, Game<span class="hljs-operator">></span>;
}

<span class="hljs-comment">// Initial state of the application</span>
export const initialState: AppState <span class="hljs-operator">=</span> {
  games: {},
};

<span class="hljs-comment">// TicTacToeState class extends the State class from @stackr/sdk</span>
export class TicTacToeState extends State<span class="hljs-operator">&#x3C;</span>AppState<span class="hljs-operator">></span> {
  <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">state: AppState</span>) </span>{
    <span class="hljs-built_in">super</span>(state);
  }

  <span class="hljs-comment">// Generate a root hash of the current state</span>
  <span class="hljs-comment">// This is used for state verification and integrity checks</span>
  getRootHash(): BytesLike {
    <span class="hljs-keyword">return</span> solidityPackedKeccak256(
      [<span class="hljs-string">"string"</span>],
      [JSON.stringify(<span class="hljs-built_in">this</span>.state.games)]
    );
  }
}
</code></pre><h3 id="h-explanation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Explanation:</h3><ul><li><p>We define an interface <code>Game</code> to represent the state of the Tic Tac Toe game.</p></li><li><p>The <code>initialState</code> sets up the initial conditions, including an empty board and the starting player.</p></li><li><p>A <code>State</code> instance is created to manage the game state.</p></li></ul><h2 id="h-implementing-the-game-logic" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementing the Game Logic</h2><p>Next, we’ll add the logic to handle player moves and check for wins.</p><h3 id="h-srcstackrmachinets" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><code>src/stackr/machine.ts</code></h3><pre data-type="codeBlock" text="import { StateMachine } from &quot;@stackr/sdk/machine&quot;;
import { TicTacToeState, initialState } from &quot;./state&quot;;
import { transitions } from &quot;./transitions&quot;;

// Define the State Machine for Tic Tac Toe
const machine = new StateMachine({
  id: &quot;tic-tac-toe&quot;, // Unique identifier for this state machine
  stateClass: TicTacToeState, // The state class used by this machine
  initialState, // Initial state
  on: transitions, // Transitions that can be applied to the state
});

export { machine 
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title class_">StateMachine</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stackr/sdk/machine"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">TicTacToeState</span>, initialState } <span class="hljs-keyword">from</span> <span class="hljs-string">"./state"</span>;
<span class="hljs-keyword">import</span> { transitions } <span class="hljs-keyword">from</span> <span class="hljs-string">"./transitions"</span>;

<span class="hljs-comment">// Define the State Machine for Tic Tac Toe</span>
<span class="hljs-keyword">const</span> machine = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StateMachine</span>({
  <span class="hljs-attr">id</span>: <span class="hljs-string">"tic-tac-toe"</span>, <span class="hljs-comment">// Unique identifier for this state machine</span>
  <span class="hljs-attr">stateClass</span>: <span class="hljs-title class_">TicTacToeState</span>, <span class="hljs-comment">// The state class used by this machine</span>
  initialState, <span class="hljs-comment">// Initial state</span>
  <span class="hljs-attr">on</span>: transitions, <span class="hljs-comment">// Transitions that can be applied to the state</span>
});

<span class="hljs-keyword">export</span> { machine 
</code></pre><h3 id="h-explanation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Explanation:</h3><ul><li><p><code>StateMachine</code>: Initializes the state machine with an ID, state class, initial state, and available transitions.</p></li></ul><h2 id="h-implementing-transitions" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Implementing Transitions</h2><p><code>src/stackr/transitions.ts</code></p><p>Define the possible actions/transitions in the game.</p><pre data-type="codeBlock" text="// src/stackr/transitions.ts

import { Transitions, SolidityType } from &quot;@stackr/sdk/machine&quot;;
import { TicTacToeState } from &quot;./state&quot;;
import { hashMessage } from &quot;ethers&quot;;

const winningCombinations: number[][] = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],
  [0, 4, 8],
  [2, 4, 6],
];

// Define the createGame transition
const createGame = TicTacToeState.STF({
  schema: {
    owner: SolidityType.ADDRESS,
    playerO: SolidityType.ADDRESS,
    startedAt: SolidityType.UINT,
  },
  handler: ({ state, inputs, msgSender, block, emit }) =&gt; {
    // Ensure that msgSender matches inputs.owner
    if (msgSender.toLowerCase() !== inputs.owner.toLowerCase()) {
      emit({
        name: &quot;InvalidSigner&quot;,
        value: `${msgSender} is not authorized to create a game.`,
      });
      return state;
    }

    // Generate a unique game ID
    const gameId = hashMessage(
      `${msgSender}::${block.timestamp}::${Object.keys(state.games).length}`
    );

    // Initialize the new game
    state.games[gameId] = {
      gameId,
      owner: inputs.owner,
      playerO: inputs.playerO,
      board: Array(9).fill(&quot;&quot;),
      currentPlayer: &quot;X&quot;,
      winner: null,
      startedAt: inputs.startedAt,
      endedAt: null,
    };

    // Emit an event for game creation
    emit({
      name: &quot;GameCreated&quot;,
      value: gameId,
    });

    return state;
  },
});

// Define the makeMove transition
const makeMove = TicTacToeState.STF({
  schema: {
    gameId: SolidityType.STRING,
    index: SolidityType.UINT,
    player: SolidityType.STRING, // &quot;X&quot; or &quot;O&quot;
  },
  handler: ({ state, inputs, msgSender, block, emit }) =&gt; {
    const { gameId, index, player } = inputs;
    const game = state.games[gameId];

    if (!game) {
      emit({
        name: &quot;GameNotFound&quot;,
        value: gameId,
      });
      return state;
    }

    // Validate player
    if (player !== game.currentPlayer) {
      emit({
        name: &quot;InvalidPlayer&quot;,
        value: `${player} attempted to move.`,
      });
      return state;
    }

    // Validate move
    if (game.board[index] !== &quot;&quot;) {
      emit({
        name: &quot;InvalidMove&quot;,
        value: `${index} is already occupied.`,
      });
      return state;
    }

    // Make the move
    game.board[index] = player;

    // Check for a winner
    let winner: &quot;X&quot; | &quot;O&quot; | &quot;Draw&quot; | null = null;
    for (const combination of winningCombinations) {
      const [a, b, c] = combination;
      if (
        game.board[a] &amp;&amp;
        game.board[a] === game.board[b] &amp;&amp;
        game.board[a] === game.board[c]
      ) {
        winner = game.board[a] as &quot;X&quot; | &quot;O&quot;;
        break;
      }
    }

    // Check for draw
    if (!winner &amp;&amp; game.board.every((cell) =&gt; cell !== &quot;&quot;)) {
      winner = &quot;Draw&quot;;
    }

    if (winner) {
      game.winner = winner;
      game.endedAt = block.timestamp;
      emit({
        name: &quot;GameEnded&quot;,
        value: `${gameId}::${winner}`,
      });
    } else {
      // Switch player
      game.currentPlayer = player === &quot;X&quot; ? &quot;O&quot; : &quot;X&quot;;
      emit({
        name: &quot;MoveMade&quot;,
        value: `${gameId}::${player}::${index}`,
      });
    }

    return state;
  },
});

// Define the resetGame transition
const resetGame = TicTacToeState.STF({
  schema: {
    gameId: SolidityType.STRING,
  },
  handler: ({ state, inputs, msgSender, block, emit }) =&gt; {
    const { gameId } = inputs;
    const game = state.games[gameId];

    if (!game) {
      emit({
        name: &quot;GameNotFound&quot;,
        value: gameId,
      });
      return state;
    }

    // Only the game owner can reset the game
    if (msgSender.toLowerCase() !== game.owner.toLowerCase()) {
      emit({
        name: &quot;UnauthorizedReset&quot;,
        value: `${msgSender} is not authorized to reset the game.`,
      });
      return state;
    }

    // Reset the game
    state.games[gameId] = {
      ...game,
      board: Array(9).fill(&quot;&quot;),
      currentPlayer: &quot;X&quot;,
      winner: null,
      endedAt: null,
    };

    emit({
      name: &quot;GameReset&quot;,
      value: gameId,
    });

    return state;
  },
});

// Export the transitions
export const transitions: Transitions&lt;TicTacToeState&gt; = {
  createGame,
  makeMove,
  resetGame,
};
"><code><span class="hljs-comment">// src/stackr/transitions.ts</span>

<span class="hljs-keyword">import</span> { <span class="hljs-title">Transitions</span>, <span class="hljs-title">SolidityType</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@stackr/sdk/machine"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">TicTacToeState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./state"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">hashMessage</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;

const winningCombinations: number[][] <span class="hljs-operator">=</span> [
  [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>],
  [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>],
  [<span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>],
  [<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>],
  [<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>],
  [<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>],
  [<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>],
  [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>],
];

<span class="hljs-comment">// Define the createGame transition</span>
const createGame <span class="hljs-operator">=</span> TicTacToeState.STF({
  schema: {
    owner: SolidityType.ADDRESS,
    playerO: SolidityType.ADDRESS,
    startedAt: SolidityType.UINT,
  },
  handler: ({ state, inputs, msgSender, <span class="hljs-built_in">block</span>, <span class="hljs-keyword">emit</span> }) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-comment">// Ensure that msgSender matches inputs.owner</span>
    <span class="hljs-keyword">if</span> (msgSender.toLowerCase() <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> inputs.owner.toLowerCase()) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"InvalidSigner"</span>,
        <span class="hljs-built_in">value</span>: `${msgSender} <span class="hljs-keyword">is</span> not authorized to create a game.`,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Generate a unique game ID</span>
    const gameId <span class="hljs-operator">=</span> hashMessage(
      `${msgSender}::${<span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>}::${Object.keys(state.games).<span class="hljs-built_in">length</span>}`
    );

    <span class="hljs-comment">// Initialize the new game</span>
    state.games[gameId] <span class="hljs-operator">=</span> {
      gameId,
      owner: inputs.owner,
      playerO: inputs.playerO,
      board: Array(<span class="hljs-number">9</span>).fill(<span class="hljs-string">""</span>),
      currentPlayer: <span class="hljs-string">"X"</span>,
      winner: null,
      startedAt: inputs.startedAt,
      endedAt: null,
    };

    <span class="hljs-comment">// Emit an event for game creation</span>
    <span class="hljs-keyword">emit</span>({
      name: <span class="hljs-string">"GameCreated"</span>,
      <span class="hljs-built_in">value</span>: gameId,
    });

    <span class="hljs-keyword">return</span> state;
  },
});

<span class="hljs-comment">// Define the makeMove transition</span>
const makeMove <span class="hljs-operator">=</span> TicTacToeState.STF({
  schema: {
    gameId: SolidityType.STRING,
    index: SolidityType.UINT,
    player: SolidityType.STRING, <span class="hljs-comment">// "X" or "O"</span>
  },
  handler: ({ state, inputs, msgSender, <span class="hljs-built_in">block</span>, <span class="hljs-keyword">emit</span> }) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const { gameId, index, player } <span class="hljs-operator">=</span> inputs;
    const game <span class="hljs-operator">=</span> state.games[gameId];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>game) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"GameNotFound"</span>,
        <span class="hljs-built_in">value</span>: gameId,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Validate player</span>
    <span class="hljs-keyword">if</span> (player <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> game.currentPlayer) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"InvalidPlayer"</span>,
        <span class="hljs-built_in">value</span>: `${player} attempted to move.`,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Validate move</span>
    <span class="hljs-keyword">if</span> (game.board[index] <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">""</span>) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"InvalidMove"</span>,
        <span class="hljs-built_in">value</span>: `${index} <span class="hljs-keyword">is</span> already occupied.`,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Make the move</span>
    game.board[index] <span class="hljs-operator">=</span> player;

    <span class="hljs-comment">// Check for a winner</span>
    let winner: <span class="hljs-string">"X"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"O"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"Draw"</span> <span class="hljs-operator">|</span> null <span class="hljs-operator">=</span> null;
    <span class="hljs-keyword">for</span> (const combination of winningCombinations) {
      const [a, b, c] <span class="hljs-operator">=</span> combination;
      <span class="hljs-keyword">if</span> (
        game.board[a] <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
        game.board[a] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> game.board[b] <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
        game.board[a] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> game.board[c]
      ) {
        winner <span class="hljs-operator">=</span> game.board[a] <span class="hljs-keyword">as</span> <span class="hljs-string">"X"</span> <span class="hljs-operator">|</span> <span class="hljs-string">"O"</span>;
        <span class="hljs-keyword">break</span>;
      }
    }

    <span class="hljs-comment">// Check for draw</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>winner <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> game.board.every((cell) <span class="hljs-operator">=</span><span class="hljs-operator">></span> cell <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">""</span>)) {
      winner <span class="hljs-operator">=</span> <span class="hljs-string">"Draw"</span>;
    }

    <span class="hljs-keyword">if</span> (winner) {
      game.winner <span class="hljs-operator">=</span> winner;
      game.endedAt <span class="hljs-operator">=</span> <span class="hljs-built_in">block</span>.<span class="hljs-built_in">timestamp</span>;
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"GameEnded"</span>,
        <span class="hljs-built_in">value</span>: `${gameId}::${winner}`,
      });
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Switch player</span>
      game.currentPlayer <span class="hljs-operator">=</span> player <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"X"</span> ? <span class="hljs-string">"O"</span> : <span class="hljs-string">"X"</span>;
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"MoveMade"</span>,
        <span class="hljs-built_in">value</span>: `${gameId}::${player}::${index}`,
      });
    }

    <span class="hljs-keyword">return</span> state;
  },
});

<span class="hljs-comment">// Define the resetGame transition</span>
const resetGame <span class="hljs-operator">=</span> TicTacToeState.STF({
  schema: {
    gameId: SolidityType.STRING,
  },
  handler: ({ state, inputs, msgSender, <span class="hljs-built_in">block</span>, <span class="hljs-keyword">emit</span> }) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const { gameId } <span class="hljs-operator">=</span> inputs;
    const game <span class="hljs-operator">=</span> state.games[gameId];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>game) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"GameNotFound"</span>,
        <span class="hljs-built_in">value</span>: gameId,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Only the game owner can reset the game</span>
    <span class="hljs-keyword">if</span> (msgSender.toLowerCase() <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> game.owner.toLowerCase()) {
      <span class="hljs-keyword">emit</span>({
        name: <span class="hljs-string">"UnauthorizedReset"</span>,
        <span class="hljs-built_in">value</span>: `${msgSender} <span class="hljs-keyword">is</span> not authorized to reset the game.`,
      });
      <span class="hljs-keyword">return</span> state;
    }

    <span class="hljs-comment">// Reset the game</span>
    state.games[gameId] <span class="hljs-operator">=</span> {
      ...game,
      board: Array(<span class="hljs-number">9</span>).fill(<span class="hljs-string">""</span>),
      currentPlayer: <span class="hljs-string">"X"</span>,
      winner: null,
      endedAt: null,
    };

    <span class="hljs-keyword">emit</span>({
      name: <span class="hljs-string">"GameReset"</span>,
      <span class="hljs-built_in">value</span>: gameId,
    });

    <span class="hljs-keyword">return</span> state;
  },
});

<span class="hljs-comment">// Export the transitions</span>
export const transitions: Transitions<span class="hljs-operator">&#x3C;</span>TicTacToeState<span class="hljs-operator">></span> <span class="hljs-operator">=</span> {
  createGame,
  makeMove,
  resetGame,
};
</code></pre><p><strong>Explanation:</strong></p><ul><li><p><code>createGame</code> <strong>Transition</strong>: Initializes a new Tic Tac Toe game with two players.</p></li><li><p><code>makeMove</code> <strong>Transition</strong>: Processes a player&apos;s move, updates the board, checks for a winner or draw, and switches the current player.</p></li><li><p><code>resetGame</code> <strong>Transition</strong>: Resets an existing game to its initial state.</p></li></ul><h2 id="h-stackr-labs-integration" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Stackr Labs Integration</strong></h2><h3 id="h-srcstackrmruts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><code>src/stackr/mru.ts</code></h3><p>Initialize and configure the Stackr Micro-Rollup (MRU) instance.</p><pre data-type="codeBlock" text="import { MicroRollup } from &quot;@stackr/sdk&quot;;
import { stackrConfig } from &quot;../../stackr.config&quot;;
import { machine } from &quot;./machine&quot;;

// Create a new MRU instance
const mru = await MicroRollup({
  config: stackrConfig, // Configuration for the MRU
  stateMachines: [machine], // State machines used by the MRU
});

export { mru };
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title class_">MicroRollup</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stackr/sdk"</span>;
<span class="hljs-keyword">import</span> { stackrConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../stackr.config"</span>;
<span class="hljs-keyword">import</span> { machine } <span class="hljs-keyword">from</span> <span class="hljs-string">"./machine"</span>;

<span class="hljs-comment">// Create a new MRU instance</span>
<span class="hljs-keyword">const</span> mru = <span class="hljs-keyword">await</span> <span class="hljs-title class_">MicroRollup</span>({
  <span class="hljs-attr">config</span>: stackrConfig, <span class="hljs-comment">// Configuration for the MRU</span>
  <span class="hljs-attr">stateMachines</span>: [machine], <span class="hljs-comment">// State machines used by the MRU</span>
});

<span class="hljs-keyword">export</span> { mru };
</code></pre><p><strong>Explanation:</strong></p><ul><li><p><code>MicroRollup</code>: Represents the Stackr MRU instance.</p></li><li><p><code>stateMachines</code>: Array of state machines the MRU will manage.</p></li></ul><hr><h2 id="h-main-application-logic" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Main Application Logic</strong></h2><h3 id="h-srcindexts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><code>src/index.ts</code></h3><p>Set up the Express server, define API endpoints, and integrate with Stackr.</p><pre data-type="codeBlock" text="import { ActionConfirmationStatus, MicroRollup } from &quot;@stackr/sdk&quot;;
import { machine } from &quot;./stackr/machine&quot;;
import { Playground } from &quot;@stackr/sdk/plugins&quot;;
import express, { Request, Response } from &quot;express&quot;;
import { mru } from &quot;./stackr/mru&quot;;
import path from &quot;path&quot;;
import dotenv from &quot;dotenv&quot;;
import { verifyTypedData } from &quot;ethers&quot;;

dotenv.config();

/**
 * Main function to set up and run the Stackr micro rollup server
 */
const main = async () =&gt; {
  // Initialize the MRU instance
  await mru.init();

  // Initialize the Playground plugin for debugging and testing
  Playground.init(mru);

  // Set up Express server
  const app = express();
  const PORT = process.env.PORT || 3012;

  app.use(express.json());

  // Serve static files from the &quot;public&quot; directory
  app.use(express.static(path.join(__dirname, &quot;public&quot;)));

  // Enable CORS for all routes
  app.use((_req, res, next) =&gt; {
    res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
    res.header(
      &quot;Access-Control-Allow-Headers&quot;,
      &quot;Origin, X-Requested-With, Content-Type, Accept&quot;
    );
    next();
  });

  // Serve the HTML file for the Tic Tac Toe game
  app.get(&quot;/&quot;, (req: Request, res: Response) =&gt; {
    res.sendFile(path.join(__dirname, &quot;public&quot;, &quot;tic_tac_toe.html&quot;));
  });

  /**
   * GET /info - Retrieve information about the MicroRollup instance
   * Returns domain information and schema map for action reducers
   */
  app.get(&quot;/info&quot;, (req: Request, res: Response) =&gt; {
    const schemas = mru.getStfSchemaMap();
    const { name, version, chainId, verifyingContract, salt } =
      mru.config.domain;
    res.send({
      signingInstructions: &quot;signTypedData(domain, schema.types, inputs)&quot;,
      domain: {
        name,
        version,
        chainId,
        verifyingContract,
        salt,
      },
      schemas,
    });
  });

  /**
   * POST /createGame - Submit a createGame action
   */
  app.post(&quot;/createGame&quot;, async (req: Request, res: Response) =&gt; {
    const reducerName = &quot;createGame&quot;;
    const actionReducer = mru.getStfSchemaMap()[reducerName];

    if (!actionReducer) {
      res.status(400).send({ message: &quot;NO_REDUCER_FOR_ACTION&quot; });
      return;
    }

    try {
      const { msgSender, signature, inputs } = req.body;

      // Log received data for debugging
      console.log(`Received createGame action from: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const schemaTypes = {
        createGame: [
          { name: &quot;owner&quot;, type: &quot;address&quot; },
          { name: &quot;playerO&quot;, type: &quot;address&quot; },
          { name: &quot;startedAt&quot;, type: &quot;uint256&quot; },
        ],
        Action: [
          { name: &quot;name&quot;, type: &quot;string&quot; },
          { name: &quot;inputs&quot;, type: &quot;createGame&quot; },
        ],
      };

      const createGameAction = {
        name: &quot;createGame&quot;,
        inputs: inputs,
      };


      const actionParams = {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      // Submit the action to the MicroRollup instance
      const ack = await mru.submitAction(actionParams);

      // Wait for the action to be confirmed (C1 status)
      const { errors, logs } = await ack.waitFor(ActionConfirmationStatus.C1);

      if (errors?.length) {
        console.error(&quot;Action errors:&quot;, errors);
        throw new Error(errors[0].message);
      }

      res.status(201).send({ logs });
    } catch (e: any) {
      console.error(&quot;Error processing action:&quot;, e.message);
      res.status(400).send({ error: e.message });
    }

    return;
  });

  /**
   * POST /makeMove - Submit a makeMove action
   */
  app.post(&quot;/makeMove&quot;, async (req: Request, res: Response) =&gt; {
    const reducerName = &quot;makeMove&quot;;
    const actionReducer = mru.getStfSchemaMap()[reducerName];

    if (!actionReducer) {
      res.status(400).send({ message: &quot;NO_REDUCER_FOR_ACTION&quot; });
      return;
    }

    try {
      const { msgSender, signature, inputs } = req.body;

      // Log received data for debugging
      console.log(`Received makeMove action from: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const actionParams = {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      // Submit the action to the MicroRollup instance
      const ack = await mru.submitAction(actionParams);

      // Wait for the action to be confirmed (C1 status)
      const { errors, logs } = await ack.waitFor(ActionConfirmationStatus.C1);

      if (errors?.length) {
        console.error(&quot;Action errors:&quot;, errors);
        throw new Error(errors[0].message);
      }

      res.status(201).send({ logs });
    } catch (e: any) {
      console.error(&quot;Error processing action:&quot;, e.message);
      res.status(400).send({ error: e.message });
    }

    return;
  });

  /**
   * POST /resetGame - Submit a resetGame action
   */
  app.post(&quot;/resetGame&quot;, async (req: Request, res: Response) =&gt; {
    const reducerName = &quot;resetGame&quot;;
    const actionReducer = mru.getStfSchemaMap()[reducerName];

    if (!actionReducer) {
      res.status(400).send({ message: &quot;NO_REDUCER_FOR_ACTION&quot; });
      return;
    }

    try {
      const { msgSender, signature, inputs } = req.body;

      // Log received data for debugging
      console.log(`Received resetGame action from: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const actionParams = {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      // Submit the action to the MicroRollup instance
      const ack = await mru.submitAction(actionParams);

      // Wait for the action to be confirmed (C1 status)
      const { errors, logs } = await ack.waitFor(ActionConfirmationStatus.C1);

      if (errors?.length) {
        console.error(&quot;Action errors:&quot;, errors);
        throw new Error(errors[0].message);
      }

      res.status(201).send({ logs });
    } catch (e: any) {
      console.error(&quot;Error processing action:&quot;, e.message);
      res.status(400).send({ error: e.message });
    }

    return;
  });

  /**
   * GET /games - Retrieve all games from the state machine
   */
  app.get(&quot;/games&quot;, async (req: Request, res: Response) =&gt; {
    const { games } = machine.state;
    res.json(games);
  });

  /**
   * GET /games/:gameId - Retrieve a specific game by ID
   */
  app.get(&quot;/games/:gameId&quot;, async (req: Request, res: Response) =&gt; {
    const { gameId } = req.params;
    const { games } = machine.state;

    const game = games[gameId];

    if (!game) {
      res.status(404).send({ message: &quot;GAME_NOT_FOUND&quot; });
      return;
    }

    res.json(game);
  });

  // Start the server
  app.listen(PORT, () =&gt; {
    console.log(`Server running on http://localhost:${PORT}`);
  });
};

// Run the main function
main().catch((error) =&gt; {
  console.error(&quot;Error starting the server:&quot;, error);
});
"><code><span class="hljs-keyword">import</span> { <span class="hljs-title">ActionConfirmationStatus</span>, <span class="hljs-title">MicroRollup</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@stackr/sdk"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">machine</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./stackr/machine"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Playground</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"@stackr/sdk/plugins"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">express</span>, { <span class="hljs-title">Request</span>, <span class="hljs-title">Response</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">mru</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./stackr/mru"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">path</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"path"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">dotenv</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"dotenv"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">verifyTypedData</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"ethers"</span>;

dotenv.config();

<span class="hljs-comment">/**
 * Main function to set up and run the Stackr micro rollup server
 */</span>
const main <span class="hljs-operator">=</span> async () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
  <span class="hljs-comment">// Initialize the MRU instance</span>
  await mru.init();

  <span class="hljs-comment">// Initialize the Playground plugin for debugging and testing</span>
  Playground.init(mru);

  <span class="hljs-comment">// Set up Express server</span>
  const app <span class="hljs-operator">=</span> express();
  const PORT <span class="hljs-operator">=</span> process.env.PORT <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-number">3012</span>;

  app.use(express.json());

  <span class="hljs-comment">// Serve static files from the "public" directory</span>
  app.use(express.static(path.join(__dirname, <span class="hljs-string">"public"</span>)));

  <span class="hljs-comment">// Enable CORS for all routes</span>
  app.use((_req, res, next) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    res.header(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>);
    res.header(
      <span class="hljs-string">"Access-Control-Allow-Headers"</span>,
      <span class="hljs-string">"Origin, X-Requested-With, Content-Type, Accept"</span>
    );
    next();
  });

  <span class="hljs-comment">// Serve the HTML file for the Tic Tac Toe game</span>
  app.get(<span class="hljs-string">"/"</span>, (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    res.sendFile(path.join(__dirname, <span class="hljs-string">"public"</span>, <span class="hljs-string">"tic_tac_toe.html"</span>));
  });

  <span class="hljs-comment">/**
   * GET /info - Retrieve information about the MicroRollup instance
   * Returns domain information and schema map for action reducers
   */</span>
  app.get(<span class="hljs-string">"/info"</span>, (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const schemas <span class="hljs-operator">=</span> mru.getStfSchemaMap();
    const { name, version, chainId, verifyingContract, salt } <span class="hljs-operator">=</span>
      mru.config.domain;
    res.<span class="hljs-built_in">send</span>({
      signingInstructions: <span class="hljs-string">"signTypedData(domain, schema.types, inputs)"</span>,
      domain: {
        name,
        version,
        chainId,
        verifyingContract,
        salt,
      },
      schemas,
    });
  });

  <span class="hljs-comment">/**
   * POST /createGame - Submit a createGame action
   */</span>
  app.post(<span class="hljs-string">"/createGame"</span>, async (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const reducerName <span class="hljs-operator">=</span> <span class="hljs-string">"createGame"</span>;
    const actionReducer <span class="hljs-operator">=</span> mru.getStfSchemaMap()[reducerName];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>actionReducer) {
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ message: <span class="hljs-string">"NO_REDUCER_FOR_ACTION"</span> });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
      const { msgSender, signature, inputs } <span class="hljs-operator">=</span> req.body;

      <span class="hljs-comment">// Log received data for debugging</span>
      console.log(`Received createGame action <span class="hljs-keyword">from</span>: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const schemaTypes <span class="hljs-operator">=</span> {
        createGame: [
          { name: <span class="hljs-string">"owner"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
          { name: <span class="hljs-string">"playerO"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
          { name: <span class="hljs-string">"startedAt"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
        ],
        Action: [
          { name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
          { name: <span class="hljs-string">"inputs"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"createGame"</span> },
        ],
      };

      const createGameAction <span class="hljs-operator">=</span> {
        name: <span class="hljs-string">"createGame"</span>,
        inputs: inputs,
      };


      const actionParams <span class="hljs-operator">=</span> {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      <span class="hljs-comment">// Submit the action to the MicroRollup instance</span>
      const ack <span class="hljs-operator">=</span> await mru.submitAction(actionParams);

      <span class="hljs-comment">// Wait for the action to be confirmed (C1 status)</span>
      const { errors, logs } <span class="hljs-operator">=</span> await ack.waitFor(ActionConfirmationStatus.C1);

      <span class="hljs-keyword">if</span> (errors?.<span class="hljs-built_in">length</span>) {
        console.error(<span class="hljs-string">"Action errors:"</span>, errors);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errors[<span class="hljs-number">0</span>].message);
      }

      res.status(<span class="hljs-number">201</span>).<span class="hljs-built_in">send</span>({ logs });
    } <span class="hljs-keyword">catch</span> (e: any) {
      console.error(<span class="hljs-string">"Error processing action:"</span>, e.message);
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ <span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">e</span>.<span class="hljs-title">message</span> })</span>;
    }

    <span class="hljs-keyword">return</span>;
  });

  <span class="hljs-comment">/**
   * POST /makeMove - Submit a makeMove action
   */</span>
  app.post(<span class="hljs-string">"/makeMove"</span>, async (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const reducerName <span class="hljs-operator">=</span> <span class="hljs-string">"makeMove"</span>;
    const actionReducer <span class="hljs-operator">=</span> mru.getStfSchemaMap()[reducerName];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>actionReducer) {
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ message: <span class="hljs-string">"NO_REDUCER_FOR_ACTION"</span> });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
      const { msgSender, signature, inputs } <span class="hljs-operator">=</span> req.body;

      <span class="hljs-comment">// Log received data for debugging</span>
      console.log(`Received makeMove action <span class="hljs-keyword">from</span>: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const actionParams <span class="hljs-operator">=</span> {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      <span class="hljs-comment">// Submit the action to the MicroRollup instance</span>
      const ack <span class="hljs-operator">=</span> await mru.submitAction(actionParams);

      <span class="hljs-comment">// Wait for the action to be confirmed (C1 status)</span>
      const { errors, logs } <span class="hljs-operator">=</span> await ack.waitFor(ActionConfirmationStatus.C1);

      <span class="hljs-keyword">if</span> (errors?.<span class="hljs-built_in">length</span>) {
        console.error(<span class="hljs-string">"Action errors:"</span>, errors);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errors[<span class="hljs-number">0</span>].message);
      }

      res.status(<span class="hljs-number">201</span>).<span class="hljs-built_in">send</span>({ logs });
    } <span class="hljs-keyword">catch</span> (e: any) {
      console.error(<span class="hljs-string">"Error processing action:"</span>, e.message);
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ <span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">e</span>.<span class="hljs-title">message</span> })</span>;
    }

    <span class="hljs-keyword">return</span>;
  });

  <span class="hljs-comment">/**
   * POST /resetGame - Submit a resetGame action
   */</span>
  app.post(<span class="hljs-string">"/resetGame"</span>, async (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const reducerName <span class="hljs-operator">=</span> <span class="hljs-string">"resetGame"</span>;
    const actionReducer <span class="hljs-operator">=</span> mru.getStfSchemaMap()[reducerName];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>actionReducer) {
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ message: <span class="hljs-string">"NO_REDUCER_FOR_ACTION"</span> });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">try</span> {
      const { msgSender, signature, inputs } <span class="hljs-operator">=</span> req.body;

      <span class="hljs-comment">// Log received data for debugging</span>
      console.log(`Received resetGame action <span class="hljs-keyword">from</span>: ${msgSender}`);
      console.log(`Inputs:`, inputs);
      console.log(`Signature: ${signature}`);

      const actionParams <span class="hljs-operator">=</span> {
        name: reducerName,
        inputs,
        signature,
        msgSender,
      };

      <span class="hljs-comment">// Submit the action to the MicroRollup instance</span>
      const ack <span class="hljs-operator">=</span> await mru.submitAction(actionParams);

      <span class="hljs-comment">// Wait for the action to be confirmed (C1 status)</span>
      const { errors, logs } <span class="hljs-operator">=</span> await ack.waitFor(ActionConfirmationStatus.C1);

      <span class="hljs-keyword">if</span> (errors?.<span class="hljs-built_in">length</span>) {
        console.error(<span class="hljs-string">"Action errors:"</span>, errors);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errors[<span class="hljs-number">0</span>].message);
      }

      res.status(<span class="hljs-number">201</span>).<span class="hljs-built_in">send</span>({ logs });
    } <span class="hljs-keyword">catch</span> (e: any) {
      console.error(<span class="hljs-string">"Error processing action:"</span>, e.message);
      res.status(<span class="hljs-number">400</span>).<span class="hljs-built_in">send</span>({ <span class="hljs-function"><span class="hljs-keyword">error</span>: <span class="hljs-title">e</span>.<span class="hljs-title">message</span> })</span>;
    }

    <span class="hljs-keyword">return</span>;
  });

  <span class="hljs-comment">/**
   * GET /games - Retrieve all games from the state machine
   */</span>
  app.get(<span class="hljs-string">"/games"</span>, async (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const { games } <span class="hljs-operator">=</span> machine.state;
    res.json(games);
  });

  <span class="hljs-comment">/**
   * GET /games/:gameId - Retrieve a specific game by ID
   */</span>
  app.get(<span class="hljs-string">"/games/:gameId"</span>, async (req: Request, res: Response) <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    const { gameId } <span class="hljs-operator">=</span> req.params;
    const { games } <span class="hljs-operator">=</span> machine.state;

    const game <span class="hljs-operator">=</span> games[gameId];

    <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>game) {
      res.status(<span class="hljs-number">404</span>).<span class="hljs-built_in">send</span>({ message: <span class="hljs-string">"GAME_NOT_FOUND"</span> });
      <span class="hljs-keyword">return</span>;
    }

    res.json(game);
  });

  <span class="hljs-comment">// Start the server</span>
  app.listen(PORT, () <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    console.log(`Server running on http:<span class="hljs-comment">//localhost:${PORT}`);</span>
  });
};

<span class="hljs-comment">// Run the main function</span>
main().catch((<span class="hljs-function"><span class="hljs-keyword">error</span>) => </span>{
  console.error(<span class="hljs-string">"Error starting the server:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
});
</code></pre><p><strong>Explanation:</strong></p><ul><li><p><code>/info</code> Endpoint: Provides information about the MRU&apos;s domain and schemas.</p></li><li><p><code>/:reducerName</code> Endpoint: Accepts actions to perform state transitions (e.g., <code>createGame</code>, <code>makeMove</code>, <code>resetGame</code>).</p></li><li><p><code>/games</code> &amp; <code>/games/:gameId</code> Endpoints: Retrieve all games or a specific game respectively.</p></li><li><p><strong>CORS</strong>: Enabled for all routes to allow cross-origin requests.</p></li><li><p><strong>Static Files</strong>: Serves the frontend from the <code>public</code> directory.</p></li></ul><h2 id="h-frontend-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Frontend Implementation</strong></h2><h3 id="h-a-create-the-frontend-directory" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>A. Create the Frontend Directory</strong></h3><p>Create a <code>public</code> directory inside your project root to store frontend files.</p><pre data-type="codeBlock" text="mkdir src/public
touch src/public/tic-tac-toe.html
"><code>mkdir src<span class="hljs-operator">/</span><span class="hljs-keyword">public</span>
touch src<span class="hljs-operator">/</span><span class="hljs-keyword">public</span><span class="hljs-operator">/</span>tic<span class="hljs-operator">-</span>tac<span class="hljs-operator">-</span>toe.html
</code></pre><h3 id="h-b-srcpublictic-tac-toehtml" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>B.</strong> <code>src/public/tic-tac-toe.html</code></h3><p>Create the HTML and JavaScript frontend for the Tic Tac Toe game.</p><pre data-type="codeBlock" text="
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;Tic Tac Toe - Stackr Labs&lt;/title&gt;
    &lt;style&gt;
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
        }
        #board {
            display: grid;
            grid-template-columns: repeat(3, 100px);
            grid-gap: 5px;
            justify-content: center;
            margin: 20px auto;
        }
        .cell {
            width: 100px;
            height: 100px;
            border: 2px solid #333;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 48px;
            cursor: pointer;
        }
        #message {
            margin-top: 20px;
            font-size: 24px;
        }
        #controls {
            margin-top: 30px;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            margin: 0 10px;
            cursor: pointer;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h1&gt;Tic Tac Toe&lt;/h1&gt;

&lt;div id=&quot;gameControls&quot;&gt;
    &lt;button id=&quot;createGame&quot;&gt;Create New Game&lt;/button&gt;
&lt;/div&gt;

&lt;div id=&quot;board&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;message&quot;&gt;&lt;/div&gt;

&lt;div id=&quot;controls&quot;&gt;
    &lt;button id=&quot;resetGame&quot;&gt;Reset Game&lt;/button&gt;
&lt;/div&gt;

&lt;script src=&quot;https://cdn.jsdelivr.net/npm/ethers@5.7.0/dist/ethers.umd.min.js&quot;&gt;&lt;/script&gt;
&lt;script&gt;
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    let signer;
    let gameId = null;
    let gameState = null;

    const messageElement = document.getElementById(&quot;message&quot;); // Properly defined

    // Initialize the app
    async function init() {
        try {
            // Request account access if needed
            await provider.send(&quot;eth_requestAccounts&quot;, []);
            signer = provider.getSigner();
            console.log(&quot;Wallet connected:&quot;, await signer.getAddress());

            // Event listeners
            document.getElementById(&quot;createGame&quot;).addEventListener(&quot;click&quot;, createGame);
            document.getElementById(&quot;resetGame&quot;).addEventListener(&quot;click&quot;, resetGame);
        } catch (error) {
            console.error(&quot;Error connecting wallet:&quot;, error);
            messageElement.textContent = &quot;Please connect your wallet.&quot;;
        }
    }

    // Function to create a new game
    async function createGame() {
        try {
            const msgSender = await signer.getAddress(); // Use the connected wallet&apos;s address

            // Define domain and schema types for signing data
            const domain = {
                    name: &quot;Stackr MVP v0&quot;,
                    version: &quot;1&quot;,
                    chainId: 11155111, // Sepolia Testnet
                    verifyingContract: &quot;0x0000000000000000000000000000000000000000&quot;,
                    salt: &quot;0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef&quot;,
                };

            const schemaTypes = {
                CreateGame: [
                    { name: &quot;owner&quot;, type: &quot;address&quot; },
                    { name: &quot;playerO&quot;, type: &quot;address&quot; },
                    { name: &quot;startedAt&quot;, type: &quot;uint256&quot; },
                ],
                Action: [
                    { name: &quot;name&quot;, type: &quot;string&quot; },
                    { name: &quot;inputs&quot;, type: &quot;CreateGame&quot; },
                ],
            };

            const playerO = prompt(&quot;Enter Player O&apos;s Ethereum Address:&quot;);
            if (!playerO || !ethers.utils.isAddress(playerO)) {
                messageElement.textContent = &quot;Invalid Player O address.&quot;;
                return;
            }

            const createGameInputs = {
                owner: msgSender,
                playerO: playerO,
                startedAt: Math.floor(Date.now() / 1000),
            };

            const createGameAction = {
                name: &quot;createGame&quot;,
                inputs: createGameInputs,
            };

            // Sign the data using the connected wallet&apos;s signer
            const signature = await signer._signTypedData(
                domain,
                schemaTypes,
                createGameAction
            );

            // Submit the createGame action to the backend
            const response = await fetch(&quot;/createGame&quot;, { // Ensure backend has &apos;/createGame&apos; route
                method: &quot;POST&quot;,
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: createGameInputs,
                    signature,
                }),
            });

            if (!response.ok) {
                const errorData = await response.json();
                console.error(&quot;Backend Error:&quot;, errorData);
                messageElement.textContent = `Error: ${errorData.error || &quot;Unknown error.&quot;}`;
                return;
            }

            const result = await response.json();
            console.log(&quot;Game created with logs:&quot;, result.logs);

            // Extract gameId from logs
            const gameCreatedLog = result.logs.find(log =&gt; log.name === &quot;GameCreated&quot;);
            if (gameCreatedLog) {
                gameId = gameCreatedLog.value;
                messageElement.textContent = `Game created! Game ID: ${gameId}`;
                fetchGameState();
            } else {
                messageElement.textContent = &quot;Failed to create game.&quot;;
            }
        } catch (error) {
            console.error(&quot;Error creating game:&quot;, error);
            messageElement.textContent = &quot;Error creating game.&quot;;
        }
    }

    // Function to make a move
    async function makeMove(index) {
        if (!gameId) {
            messageElement.textContent = &quot;Please create a game first.&quot;;
            return;
        }

        try {
            const msgSender = await signer.getAddress();

            // Define domain and schema types for signing data
            const domain = {
                    name: &quot;Stackr MVP v0&quot;,
                    version: &quot;1&quot;,
                    chainId: 11155111, // Sepolia Testnet
                    verifyingContract: &quot;0x0000000000000000000000000000000000000000&quot;,
                    salt: &quot;0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef&quot;,
                };

            const schemaTypes = {
                MakeMove: [
                    { name: &quot;gameId&quot;, type: &quot;string&quot; },
                    { name: &quot;index&quot;, type: &quot;uint256&quot; },
                    { name: &quot;player&quot;, type: &quot;string&quot; }, // &quot;X&quot; or &quot;O&quot;
                ],
                Action: [
                    { name: &quot;name&quot;, type: &quot;string&quot; },
                    { name: &quot;inputs&quot;, type: &quot;MakeMove&quot; },
                ],
            };

            const player = gameState.currentPlayer;

            const makeMoveInputs = {
                gameId,
                index,
                player,
            };

            const makeMoveAction = {
                name: &quot;makeMove&quot;,
                inputs: makeMoveInputs,
            };

            // Sign the data using the connected wallet&apos;s signer
            const signature = await signer._signTypedData(
                domain,
                schemaTypes,
                makeMoveAction
            );

            // Submit the makeMove action to the backend
            const response = await fetch(&quot;/makeMove&quot;, { // Ensure backend has &apos;/makeMove&apos; route
                method: &quot;POST&quot;,
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: makeMoveInputs,
                    signature,
                }),
            });

            if (!response.ok) {
                const errorData = await response.json();
                console.error(&quot;Backend Error:&quot;, errorData);
                messageElement.textContent = `Error: ${errorData.error || &quot;Unknown error.&quot;}`;
                return;
            }

            const result = await response.json();
            console.log(&quot;Move made with logs:&quot;, result.logs);

            // Handle logs to update the UI
            const moveMadeLog = result.logs.find(log =&gt; log.name === &quot;MoveMade&quot;);
            const gameEndedLog = result.logs.find(log =&gt; log.name === &quot;GameEnded&quot;);

            if (moveMadeLog) {
                messageElement.textContent = `Move made by ${makeMoveInputs.player}.`;
                fetchGameState();
            } else if (gameEndedLog) {
                const [endedGameId, winner] = gameEndedLog.value.split(&quot;::&quot;);
                messageElement.textContent = winner === &quot;Draw&quot; ? &quot;Game ended in a draw!&quot; : `Player ${winner} wins!`;
                fetchGameState();
            } else {
                messageElement.textContent = &quot;Failed to make move.&quot;;
            }
        } catch (error) {
            console.error(&quot;Error making move:&quot;, error);
            messageElement.textContent = &quot;Error making move.&quot;;
        }
    }

    // Function to reset the game
    async function resetGame() {
        if (!gameId) {
            messageElement.textContent = &quot;No game to reset.&quot;;
            return;
        }

        try {
            const msgSender = await signer.getAddress();

            // Define domain and schema types for signing data
            const domain = {
                    name: &quot;Stackr MVP v0&quot;,
                    version: &quot;1&quot;,
                    chainId: 11155111, // Sepolia Testnet
                    verifyingContract: &quot;0x0000000000000000000000000000000000000000&quot;,
                    salt: &quot;0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef&quot;,
                };
            const schemaTypes = {
                ResetGame: [
                    { name: &quot;gameId&quot;, type: &quot;string&quot; },
                ],
                Action: [
                    { name: &quot;name&quot;, type: &quot;string&quot; },
                    { name: &quot;inputs&quot;, type: &quot;ResetGame&quot; },
                ],
            };

            const resetGameInputs = {
                gameId,
            };

            const resetGameAction = {
                name: &quot;resetGame&quot;,
                inputs: resetGameInputs,
            };

            // Sign the data using the connected wallet&apos;s signer
            const signature = await signer._signTypedData(
                domain,
                schemaTypes,
                resetGameAction
            );

            // Submit the resetGame action to the backend
            const response = await fetch(&quot;/resetGame&quot;, { // Ensure backend has &apos;/resetGame&apos; route
                method: &quot;POST&quot;,
                headers: {
                    &quot;Content-Type&quot;: &quot;application/json&quot;,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: resetGameInputs,
                    signature,
                }),
            });

            if (!response.ok) {
                const errorData = await response.json();
                console.error(&quot;Backend Error:&quot;, errorData);
                messageElement.textContent = `Error: ${errorData.error || &quot;Unknown error.&quot;}`;
                return;
            }

            const result = await response.json();
            console.log(&quot;Game reset with logs:&quot;, result.logs);

            const gameResetLog = result.logs.find(log =&gt; log.name === &quot;GameReset&quot;);

            if (gameResetLog) {
                messageElement.textContent = &quot;Game has been reset!&quot;;
                fetchGameState();
            } else {
                messageElement.textContent = &quot;Failed to reset game.&quot;;
            }
        } catch (error) {
            console.error(&quot;Error resetting game:&quot;, error);
            messageElement.textContent = &quot;Error resetting game.&quot;;
        }
    }

    // Function to fetch the current game state
    async function fetchGameState() {
        try {
            const response = await fetch(`/games/${gameId}`);
            if (!response.ok) {
                const errorData = await response.json();
                console.error(&quot;Backend Error:&quot;, errorData);
                messageElement.textContent = `Error: ${errorData.message || &quot;Failed to fetch game state.&quot;}`;
                return;
            }
            const data = await response.json();
            gameState = data;
            updateBoard();
            if (gameState.winner) {
                messageElement.textContent = gameState.winner === &quot;Draw&quot; ? &quot;Game ended in a draw!&quot; : `Player ${gameState.winner} wins!`;
            } else {
                messageElement.textContent = `Player ${gameState.currentPlayer}&apos;s turn.`;
            }
        } catch (error) {
            console.error(&quot;Error fetching game state:&quot;, error);
            messageElement.textContent = &quot;Error fetching game state.&quot;;
        }
    }

    // Function to update the board UI
    function updateBoard() {
        const boardElement = document.getElementById(&quot;board&quot;);
        boardElement.innerHTML = &quot;&quot;;
        gameState.board.forEach((cell, index) =&gt; {
            const cellDiv = document.createElement(&quot;div&quot;);
            cellDiv.classList.add(&quot;cell&quot;);
            cellDiv.textContent = cell;
            if (cell === &quot;&quot;) {
                cellDiv.addEventListener(&quot;click&quot;, () =&gt; makeMove(index));
            }
            boardElement.appendChild(cellDiv);
        });
    }

    // Initialize the app on page load
    window.onload = init;
&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
"><code>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">!</span>DOCTYPE html<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>html lang<span class="hljs-operator">=</span><span class="hljs-string">"en"</span><span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>head<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>meta charset<span class="hljs-operator">=</span><span class="hljs-string">"UTF-8"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>title<span class="hljs-operator">></span>Tic Tac Toe <span class="hljs-operator">-</span> Stackr Labs<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>title<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>style<span class="hljs-operator">></span>
        body {
            font<span class="hljs-operator">-</span>family: Arial, sans<span class="hljs-operator">-</span>serif;
            text<span class="hljs-operator">-</span>align: center;
            margin<span class="hljs-operator">-</span>top: 50px;
        }
        #board {
            display: grid;
            grid<span class="hljs-operator">-</span>template<span class="hljs-operator">-</span>columns: repeat(<span class="hljs-number">3</span>, 100px);
            grid<span class="hljs-operator">-</span>gap: 5px;
            justify<span class="hljs-operator">-</span>content: center;
            margin: 20px auto;
        }
        .cell {
            width: 100px;
            height: 100px;
            border: 2px solid #<span class="hljs-number">333</span>;
            display: flex;
            align<span class="hljs-operator">-</span>items: center;
            justify<span class="hljs-operator">-</span>content: center;
            font<span class="hljs-operator">-</span>size: 48px;
            cursor: pointer;
        }
        #message {
            margin<span class="hljs-operator">-</span>top: 20px;
            font<span class="hljs-operator">-</span>size: 24px;
        }
        #controls {
            margin<span class="hljs-operator">-</span>top: 30px;
        }
        button {
            padding: 10px 20px;
            font<span class="hljs-operator">-</span>size: 16px;
            margin: <span class="hljs-number">0</span> 10px;
            cursor: pointer;
        }
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>style<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>head<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>body<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>Tic Tac Toe<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"gameControls"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>button id<span class="hljs-operator">=</span><span class="hljs-string">"createGame"</span><span class="hljs-operator">></span>Create New Game<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"board"</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"message"</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>div id<span class="hljs-operator">=</span><span class="hljs-string">"controls"</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span>button id<span class="hljs-operator">=</span><span class="hljs-string">"resetGame"</span><span class="hljs-operator">></span>Reset Game<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>

<span class="hljs-operator">&#x3C;</span>script src<span class="hljs-operator">=</span><span class="hljs-string">"https://cdn.jsdelivr.net/npm/ethers@5.7.0/dist/ethers.umd.min.js"</span><span class="hljs-operator">></span><span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>script<span class="hljs-operator">></span>
<span class="hljs-operator">&#x3C;</span>script<span class="hljs-operator">></span>
    const provider <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ethers.providers.Web3Provider(window.ethereum);
    let signer;
    let gameId <span class="hljs-operator">=</span> null;
    let gameState <span class="hljs-operator">=</span> null;

    const messageElement <span class="hljs-operator">=</span> document.getElementById(<span class="hljs-string">"message"</span>); <span class="hljs-comment">// Properly defined</span>

    <span class="hljs-comment">// Initialize the app</span>
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Request account access if needed</span>
            await provider.<span class="hljs-built_in">send</span>(<span class="hljs-string">"eth_requestAccounts"</span>, []);
            signer <span class="hljs-operator">=</span> provider.getSigner();
            console.log(<span class="hljs-string">"Wallet connected:"</span>, await signer.getAddress());

            <span class="hljs-comment">// Event listeners</span>
            document.getElementById(<span class="hljs-string">"createGame"</span>).addEventListener(<span class="hljs-string">"click"</span>, createGame);
            document.getElementById(<span class="hljs-string">"resetGame"</span>).addEventListener(<span class="hljs-string">"click"</span>, resetGame);
        } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
            console.error(<span class="hljs-string">"Error connecting wallet:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Please connect your wallet."</span>;
        }
    }

    <span class="hljs-comment">// Function to create a new game</span>
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createGame</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            const msgSender <span class="hljs-operator">=</span> await signer.getAddress(); <span class="hljs-comment">// Use the connected wallet's address</span>

            <span class="hljs-comment">// Define domain and schema types for signing data</span>
            const domain <span class="hljs-operator">=</span> {
                    name: <span class="hljs-string">"Stackr MVP v0"</span>,
                    version: <span class="hljs-string">"1"</span>,
                    chainId: <span class="hljs-number">11155111</span>, <span class="hljs-comment">// Sepolia Testnet</span>
                    verifyingContract: <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>,
                    <span class="hljs-built_in">salt</span>: <span class="hljs-string">"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"</span>,
                };

            const schemaTypes <span class="hljs-operator">=</span> {
                CreateGame: [
                    { name: <span class="hljs-string">"owner"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
                    { name: <span class="hljs-string">"playerO"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"address"</span> },
                    { name: <span class="hljs-string">"startedAt"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
                ],
                Action: [
                    { name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
                    { name: <span class="hljs-string">"inputs"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"CreateGame"</span> },
                ],
            };

            const playerO <span class="hljs-operator">=</span> prompt(<span class="hljs-string">"Enter Player O's Ethereum Address:"</span>);
            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>playerO <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-operator">!</span>ethers.utils.isAddress(playerO)) {
                messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Invalid Player O address."</span>;
                <span class="hljs-keyword">return</span>;
            }

            const createGameInputs <span class="hljs-operator">=</span> {
                owner: msgSender,
                playerO: playerO,
                startedAt: Math.floor(Date.now() <span class="hljs-operator">/</span> <span class="hljs-number">1000</span>),
            };

            const createGameAction <span class="hljs-operator">=</span> {
                name: <span class="hljs-string">"createGame"</span>,
                inputs: createGameInputs,
            };

            <span class="hljs-comment">// Sign the data using the connected wallet's signer</span>
            const signature <span class="hljs-operator">=</span> await signer._signTypedData(
                domain,
                schemaTypes,
                createGameAction
            );

            <span class="hljs-comment">// Submit the createGame action to the backend</span>
            const response <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/createGame"</span>, { <span class="hljs-comment">// Ensure backend has '/createGame' route</span>
                method: <span class="hljs-string">"POST"</span>,
                headers: {
                    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: createGameInputs,
                    signature,
                }),
            });

            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
                const errorData <span class="hljs-operator">=</span> await response.json();
                console.error(<span class="hljs-string">"Backend Error:"</span>, errorData);
                messageElement.textContent <span class="hljs-operator">=</span> `<span class="hljs-built_in">Error</span>: ${errorData.error <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Unknown error."</span>}`;
                <span class="hljs-keyword">return</span>;
            }

            const result <span class="hljs-operator">=</span> await response.json();
            console.log(<span class="hljs-string">"Game created with logs:"</span>, result.logs);

            <span class="hljs-comment">// Extract gameId from logs</span>
            const gameCreatedLog <span class="hljs-operator">=</span> result.logs.find(log <span class="hljs-operator">=</span><span class="hljs-operator">></span> log.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"GameCreated"</span>);
            <span class="hljs-keyword">if</span> (gameCreatedLog) {
                gameId <span class="hljs-operator">=</span> gameCreatedLog.<span class="hljs-built_in">value</span>;
                messageElement.textContent <span class="hljs-operator">=</span> `Game created<span class="hljs-operator">!</span> Game ID: ${gameId}`;
                fetchGameState();
            } <span class="hljs-keyword">else</span> {
                messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Failed to create game."</span>;
            }
        } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
            console.error(<span class="hljs-string">"Error creating game:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Error creating game."</span>;
        }
    }

    <span class="hljs-comment">// Function to make a move</span>
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makeMove</span>(<span class="hljs-params">index</span>) </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>gameId) {
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Please create a game first."</span>;
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">try</span> {
            const msgSender <span class="hljs-operator">=</span> await signer.getAddress();

            <span class="hljs-comment">// Define domain and schema types for signing data</span>
            const domain <span class="hljs-operator">=</span> {
                    name: <span class="hljs-string">"Stackr MVP v0"</span>,
                    version: <span class="hljs-string">"1"</span>,
                    chainId: <span class="hljs-number">11155111</span>, <span class="hljs-comment">// Sepolia Testnet</span>
                    verifyingContract: <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>,
                    <span class="hljs-built_in">salt</span>: <span class="hljs-string">"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"</span>,
                };

            const schemaTypes <span class="hljs-operator">=</span> {
                MakeMove: [
                    { name: <span class="hljs-string">"gameId"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
                    { name: <span class="hljs-string">"index"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"uint256"</span> },
                    { name: <span class="hljs-string">"player"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> }, <span class="hljs-comment">// "X" or "O"</span>
                ],
                Action: [
                    { name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
                    { name: <span class="hljs-string">"inputs"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"MakeMove"</span> },
                ],
            };

            const player <span class="hljs-operator">=</span> gameState.currentPlayer;

            const makeMoveInputs <span class="hljs-operator">=</span> {
                gameId,
                index,
                player,
            };

            const makeMoveAction <span class="hljs-operator">=</span> {
                name: <span class="hljs-string">"makeMove"</span>,
                inputs: makeMoveInputs,
            };

            <span class="hljs-comment">// Sign the data using the connected wallet's signer</span>
            const signature <span class="hljs-operator">=</span> await signer._signTypedData(
                domain,
                schemaTypes,
                makeMoveAction
            );

            <span class="hljs-comment">// Submit the makeMove action to the backend</span>
            const response <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/makeMove"</span>, { <span class="hljs-comment">// Ensure backend has '/makeMove' route</span>
                method: <span class="hljs-string">"POST"</span>,
                headers: {
                    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: makeMoveInputs,
                    signature,
                }),
            });

            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
                const errorData <span class="hljs-operator">=</span> await response.json();
                console.error(<span class="hljs-string">"Backend Error:"</span>, errorData);
                messageElement.textContent <span class="hljs-operator">=</span> `<span class="hljs-built_in">Error</span>: ${errorData.error <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Unknown error."</span>}`;
                <span class="hljs-keyword">return</span>;
            }

            const result <span class="hljs-operator">=</span> await response.json();
            console.log(<span class="hljs-string">"Move made with logs:"</span>, result.logs);

            <span class="hljs-comment">// Handle logs to update the UI</span>
            const moveMadeLog <span class="hljs-operator">=</span> result.logs.find(log <span class="hljs-operator">=</span><span class="hljs-operator">></span> log.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"MoveMade"</span>);
            const gameEndedLog <span class="hljs-operator">=</span> result.logs.find(log <span class="hljs-operator">=</span><span class="hljs-operator">></span> log.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"GameEnded"</span>);

            <span class="hljs-keyword">if</span> (moveMadeLog) {
                messageElement.textContent <span class="hljs-operator">=</span> `Move made by ${makeMoveInputs.player}.`;
                fetchGameState();
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (gameEndedLog) {
                const [endedGameId, winner] <span class="hljs-operator">=</span> gameEndedLog.<span class="hljs-built_in">value</span>.split(<span class="hljs-string">"::"</span>);
                messageElement.textContent <span class="hljs-operator">=</span> winner <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"Draw"</span> ? <span class="hljs-string">"Game ended in a draw!"</span> : `Player ${winner} wins<span class="hljs-operator">!</span>`;
                fetchGameState();
            } <span class="hljs-keyword">else</span> {
                messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Failed to make move."</span>;
            }
        } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
            console.error(<span class="hljs-string">"Error making move:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Error making move."</span>;
        }
    }

    <span class="hljs-comment">// Function to reset the game</span>
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resetGame</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>gameId) {
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"No game to reset."</span>;
            <span class="hljs-keyword">return</span>;
        }

        <span class="hljs-keyword">try</span> {
            const msgSender <span class="hljs-operator">=</span> await signer.getAddress();

            <span class="hljs-comment">// Define domain and schema types for signing data</span>
            const domain <span class="hljs-operator">=</span> {
                    name: <span class="hljs-string">"Stackr MVP v0"</span>,
                    version: <span class="hljs-string">"1"</span>,
                    chainId: <span class="hljs-number">11155111</span>, <span class="hljs-comment">// Sepolia Testnet</span>
                    verifyingContract: <span class="hljs-string">"0x0000000000000000000000000000000000000000"</span>,
                    <span class="hljs-built_in">salt</span>: <span class="hljs-string">"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"</span>,
                };
            const schemaTypes <span class="hljs-operator">=</span> {
                ResetGame: [
                    { name: <span class="hljs-string">"gameId"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
                ],
                Action: [
                    { name: <span class="hljs-string">"name"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span> },
                    { name: <span class="hljs-string">"inputs"</span>, <span class="hljs-keyword">type</span>: <span class="hljs-string">"ResetGame"</span> },
                ],
            };

            const resetGameInputs <span class="hljs-operator">=</span> {
                gameId,
            };

            const resetGameAction <span class="hljs-operator">=</span> {
                name: <span class="hljs-string">"resetGame"</span>,
                inputs: resetGameInputs,
            };

            <span class="hljs-comment">// Sign the data using the connected wallet's signer</span>
            const signature <span class="hljs-operator">=</span> await signer._signTypedData(
                domain,
                schemaTypes,
                resetGameAction
            );

            <span class="hljs-comment">// Submit the resetGame action to the backend</span>
            const response <span class="hljs-operator">=</span> await fetch(<span class="hljs-string">"/resetGame"</span>, { <span class="hljs-comment">// Ensure backend has '/resetGame' route</span>
                method: <span class="hljs-string">"POST"</span>,
                headers: {
                    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
                },
                body: JSON.stringify({
                    msgSender,
                    inputs: resetGameInputs,
                    signature,
                }),
            });

            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
                const errorData <span class="hljs-operator">=</span> await response.json();
                console.error(<span class="hljs-string">"Backend Error:"</span>, errorData);
                messageElement.textContent <span class="hljs-operator">=</span> `<span class="hljs-built_in">Error</span>: ${errorData.error <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Unknown error."</span>}`;
                <span class="hljs-keyword">return</span>;
            }

            const result <span class="hljs-operator">=</span> await response.json();
            console.log(<span class="hljs-string">"Game reset with logs:"</span>, result.logs);

            const gameResetLog <span class="hljs-operator">=</span> result.logs.find(log <span class="hljs-operator">=</span><span class="hljs-operator">></span> log.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"GameReset"</span>);

            <span class="hljs-keyword">if</span> (gameResetLog) {
                messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Game has been reset!"</span>;
                fetchGameState();
            } <span class="hljs-keyword">else</span> {
                messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Failed to reset game."</span>;
            }
        } <span class="hljs-keyword">catch</span> (<span class="hljs-function"><span class="hljs-keyword">error</span>) </span>{
            console.error(<span class="hljs-string">"Error resetting game:"</span>, <span class="hljs-function"><span class="hljs-keyword">error</span>)</span>;
            messageElement.textContent <span class="hljs-operator">=</span> <span class="hljs-string">"Error resetting game."</span>;
        }
    }

    <span class="hljs-comment">// Function to fetch the current game state</span>
    async <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchGameState</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">try</span> {
            const response <span class="hljs-operator">=</span> await fetch(`<span class="hljs-operator">/</span>games<span class="hljs-operator">/</span>${gameId}`);
            <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>response.ok) {
                const errorData <span class="hljs-operator">=</span> await response.json();
                console.error(<span class="hljs-string">"Backend Error:"</span>, errorData);
                messageElement.textContent <span class="hljs-operator">=</span> `<span class="hljs-built_in">Error</span>: ${errorData.message <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-string">"Failed to fetch game state."</span>}`;
                <span class="hljs-keyword">return</span>;
            }
            const data <span class="hljs-operator">=</span> await response.json();
            gameState <span class="hljs-operator">=</span> data;
            updateBoard();
            <span class="hljs-keyword">if</span> (gameState.winner) {
                messageElement.textContent <span class="hljs-operator">=</span> gameState.winner <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">"Draw"</span> ? <span class="hljs-string">"Game ended in a draw!"</span> : `Player ${gameState.winner} wins<span class="hljs-operator">!</span>`;
            } <span class="hljs-keyword">else</span> {
                messageElement.textContent <span class="hljs-operator">=</span> `Player ${gameState.currentPlayer}<span class="hljs-string">'s turn.`;
            }
        } catch (error) {
            console.error("Error fetching game state:", error);
            messageElement.textContent = "Error fetching game state.";
        }
    }

    // Function to update the board UI
    function updateBoard() {
        const boardElement = document.getElementById("board");
        boardElement.innerHTML = "";
        gameState.board.forEach((cell, index) => {
            const cellDiv = document.createElement("div");
            cellDiv.classList.add("cell");
            cellDiv.textContent = cell;
            if (cell === "") {
                cellDiv.addEventListener("click", () => makeMove(index));
            }
            boardElement.appendChild(cellDiv);
        });
    }

    // Initialize the app on page load
    window.onload = init;
&#x3C;/script>

&#x3C;/body>
&#x3C;/html>
</span></code></pre><h3 id="h-explanation" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Explanation:</strong></h3><ul><li><p><strong>Connect to MetaMask</strong>: Requests the user to connect their wallet.</p></li><li><p><strong>Deterministic Wallet</strong>: Generates a deterministic wallet based on the user&apos;s signature to avoid signing every transaction.</p></li><li><p><strong>Create Game</strong>: Allows the user to create a new game by specifying Player O&apos;s Ethereum address.</p></li><li><p><strong>Make Move</strong>: Enables players to make moves on the board by clicking on empty cells.</p></li><li><p><strong>Reset Game</strong>: Resets the game to its initial state.</p></li><li><p><strong>Fetch Game State</strong>: Retrieves the current state of the game to update the UI accordingly.</p></li></ul><h2 id="h-testing-your-game" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Testing Your Game</h2><p>After setting up the backend and frontend, you can start your development server:</p><pre data-type="codeBlock" text="bun start
"><code>bun <span class="hljs-keyword">start</span>
</code></pre><p>Open your browser and navigate to <code>http://localhost:3012</code> to see your Tic Tac Toe game in action!</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a5386c2754ebd4a5507fcbb524299e56b46c02a1867da9d2db8cdedf5525783d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-conclusion" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Conclusion</h2><p>Congratulations! 🎉 You’ve successfully built a decentralized Tic Tac Toe game using <strong>Stackr Labs</strong> and Micro Rollups. This project showcases how to manage game state on the blockchain while providing a simple and engaging user interface.</p><p>Feel free to extend the game further, add features like multiplayer support, or improve the UI. Happy coding! If you have any questions or need further assistance, please feel free to contact me. You can connect with me on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/kaushikk1704">X / Twitter</a> or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.linkedin.com/in/kaushik-k-36b871219/">LinkedIn</a>.</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/11f205a8b936a5885c46c25ea5dbbec72f13f6c15ee8ddc0af1857834ca7ed43.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Bridging Between Layer 1 and Rollups: Why It Matters and How It Work]]></title>
            <link>https://paragraph.com/@kaushik-2/bridging-between-layer-1-and-rollups-why-it-matters-and-how-it-work</link>
            <guid>Bv0Nr71QUipmd3j7C6nv</guid>
            <pubDate>Tue, 01 Oct 2024 05:14:39 GMT</pubDate>
            <description><![CDATA[Today, I want to dive into something that I believe is one of the key building blocks for scaling blockchain technology — bridges. Specifically, I want to talk about how Layer 1 (like Ethereum) connects with rollups, and why this connection matters so much for making blockchain usable for everyone. Think of this as a journey — like getting from one island to another. Let’s explore how these “bridges” work and why we need them!Why Do We Need Bridges?So, you might be wondering, why do we even n...]]></description>
            <content:encoded><![CDATA[<figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/042378c4205f78aab489fd39af0e162ab2f056d8c0d5f9510980618a31d35069.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><br>Today, I want to dive into something that I believe is one of the key building blocks for scaling blockchain technology — <strong>bridges</strong>. Specifically, I want to talk about how <strong>Layer 1 (like Ethereum)</strong> connects with <strong>rollups</strong>, and why this connection matters so much for making blockchain usable for everyone.</p><p>Think of this as a journey — like getting from one island to another. Let’s explore how these “bridges” work and why we need them!</p><hr><h3 id="h-why-do-we-need-bridges" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>Why Do We Need Bridges?</strong></h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/92c79e14de82aaffb731978e1f73501a34ddc5992d13a10d65187808e8625164.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>So, you might be wondering, why do we even need these bridges? Imagine this: you have two islands. One is <strong>Island Ethereum (Layer 1)</strong>, and the other is <strong>Island Rollup</strong> (it could be Optimism, Arbitrum, zkSync, etc.). Island Ethereum is the main island, it’s big, popular, and everyone wants to live there. But because it’s so popular, it’s crowded and expensive — you have to pay high fees (like paying for an expensive ferry ride). 😅</p><p>Now, Island Rollup is a new, smaller island. It’s a lot cheaper and has fewer people on it, but it still needs to connect with the main island because that’s where most of the action is. So we build a bridge between Island Ethereum and Island Rollup to make life easier.</p><p>In simple terms, <strong>bridges</strong> let us move money and information between Ethereum and rollups without getting stuck with high fees. They help people save time and cost, allowing us to get the best of both worlds — <strong>security</strong> from Ethereum and <strong>low fees</strong> from rollups.</p><hr><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2f61d2a92af8bc8021c7c26ea1f6895efe2e09cd09acc3fedcd013e5bb8cbcaa.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><br><strong>How Do Bridges Work?</strong></p><p>Okay, so how do these bridges actually work? Let’s break it down step by step:</p><ol><li><p><strong>Locking Your Assets on Ethereum</strong>: Imagine you want to send 1 ETH from Island Ethereum to Island Rollup. The first step is to “lock” that ETH on Ethereum. It’s like putting your money in a safe before taking a copy of it to the other island. This locked ETH stays safe on Layer 1.</p></li><li><p><strong>Creating a Representation on Rollup</strong>: Once your ETH is locked, a “copy” or representation of your ETH is created on the rollup. This isn’t real ETH; it’s more like a receipt that shows you own ETH on Ethereum, but you’re allowed to use it on the rollup. This allows you to start using the cheaper, faster services on Island Rollup.</p></li><li><p><strong>Using and Bringing It Back</strong>: Now you can do things like <strong>make transactions</strong> or <strong>use dApps</strong> on the rollup at a lower cost. And when you’re done, you use the bridge again to move your assets back to Ethereum. The ETH on the rollup gets “burned” (destroyed), and your original ETH on Ethereum gets unlocked, ready for use!</p></li></ol><p>Let’s take an everyday example. If you’ve ever used a <strong>gift card</strong>, you know the card isn’t cash, but it represents money you can spend at a particular store. When you move from Layer 1 to a rollup, the bridge gives you something similar to a gift card to use there. You’re free to spend it however you like, and you can exchange it back into cash when you’re ready.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/90d898fc686fda4f93eadd0ad7036fede8551b405009e0ec426687f801164d1b.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-why-does-bridging-matter-so-much" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Why Does Bridging Matter So Much?</h3><p>Let me tell you why bridging is a game-changer. Here are a few reasons:</p><ol><li><p><strong>Lower Costs for Everyone</strong> 💸 I’ve been there myself — trying to make a simple transaction on Ethereum, and boom! There’s a crazy gas fee that’s just too high. Bridging to a rollup helps cut down these costs, which is a big deal, especially if you’re just trying to send a few dollars worth of crypto or use a dApp without burning a hole in your pocket.</p></li><li><p><strong>Unlocking New Possibilities</strong> 🚀 When you bridge assets to a rollup, suddenly, you have the freedom to interact with many applications without worrying about the cost. Think of playing a blockchain game or trading tokens in DeFi without paying $20 in fees each time. The bridge gives us access to <strong>affordable blockchain experiences</strong> that many users, especially newcomers, really need.</p></li><li><p><strong>Security Backed by Ethereum</strong> 🛡️ Rollups are like lightweight, cheaper versions of Ethereum, but they’re still connected to the main chain. This means that you get the security of Ethereum (which has been tested over many years) while still enjoying the lower costs and faster speeds of a rollup. This connection is what makes bridging so important.</p></li></ol><hr><h3 id="h-types-of-bridges" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Types of Bridges</h3><p>Not all bridges are created equal. You might hear people talking about different types of bridges, so let me quickly explain:</p><ul><li><p><strong>Centralized Bridges</strong>: These are run by a single entity, like a company or a group of people. They’re easier to use but have a higher trust risk. It’s like trusting a bank to hold your money.</p></li><li><p><strong>Decentralized Bridges</strong>: These use smart contracts and are more “trustless.” They rely on code instead of people, and that makes them more secure in theory. It’s like using a locker with an automated lock that only you can open.</p></li></ul><p>For example, the <strong>Arbitrum Bridge</strong> and <strong>Optimism Bridge</strong> are specific to those rollups, while tools like <strong>Hop Protocol</strong> allow you to move assets between different rollups and Ethereum with less friction.</p><hr><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"></h3><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/44fe0fda6a8c7c078a214213a05f9cf1a4222c41ec21db55a7dc37d97cf85b67.webp" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-my-experience-using-bridges" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">My Experience Using Bridges</h3><p>I remember the first time I tried bridging from Ethereum to a rollup. I wanted to try out some new DeFi projects, but the Ethereum fees were just too high for my small transactions. So, I decided to use the <strong>Arbitrum Bridge</strong>. It was surprisingly simple — I just locked my ETH on Ethereum, waited a few minutes, and suddenly I had ETH available on Arbitrum to play around with.</p><p>The experience was like going from paying tolls on a busy highway to cruising down a quiet, free country road. Everything was faster, smoother, and cheaper, and it made me realize how powerful these scaling solutions can be.</p><hr><h3 id="h-security-considerations" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Security Considerations</h3><p>One thing to note, though, is that <strong>bridges do have some risk</strong>. When you lock your assets on Layer 1, you’re trusting the bridge’s smart contract to keep them safe. If the contract has a bug or is hacked, those assets could be at risk. So always use well-known bridges with a good track record, and never bridge more than you’re willing to lose.</p><hr><h3 id="h-final-thoughts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Final Thoughts</h3><p>In the end, bridging between Layer 1 and rollups is a crucial piece of the puzzle for making blockchain scalable and affordable for everyone. Just like building bridges between islands helps people travel and do business more easily, these blockchain bridges are key to creating a world where blockchain isn’t just for the wealthy or tech-savvy — it’s for everyone.</p><p>So if you haven’t tried bridging yet, give it a shot! Whether it’s moving assets to <strong>Arbitrum</strong>, <strong>Optimism</strong>, or <strong>zkSync</strong>, you’ll see firsthand just how powerful these tools can be in making blockchain more accessible.</p><p>Thanks for reading! If you found this helpful, feel free to share it or drop a comment below about your own experience with bridges. Let’s keep exploring and making blockchain technology easier for everyone! 🚀</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
        </item>
        <item>
            <title><![CDATA[Rollups Made Blockchain Simpler]]></title>
            <link>https://paragraph.com/@kaushik-2/rollups-made-blockchain-simpler</link>
            <guid>QCBVeR5LKvQKwkzFgnmV</guid>
            <pubDate>Wed, 25 Sep 2024 14:14:40 GMT</pubDate>
            <description><![CDATA[Blockchain technology, like Ethereum, is amazing. As a developer, I always think that how blockchain works and how the transaction is executed in the backend. When I started learning about the blockchain in 2022, one of the main concepts that came into my learning is layer 2 and rollups. What is L2 ? What is a Rollup ? Is every L2 a Rollup ?To answer all these questions first let us understand what is L2 ?What is L2 (layer 2) ?Layer 2 is another layer below the main blockchain (L1) which is c...]]></description>
            <content:encoded><![CDATA[<p>Blockchain technology, like Ethereum, is amazing. As a developer, I always think that how blockchain works and how the transaction is executed in the backend.</p><p>When I started learning about the blockchain in 2022, one of the main concepts that came into my learning is layer 2 and rollups. What is L2 ? What is a Rollup ? Is every L2 a Rollup ?</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/11295f2a54467124f0035d9a5a32edf10e08b0d1113895f311ac5ee98edb212f.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>To answer all these questions first let us understand what is L2 ?</p><h3 id="h-what-is-l2-layer-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What is L2 (layer 2) ?</h3><p>Layer 2 is another layer below the main blockchain (L1) which is created to process all the transactions more faster and cheaper.</p><p>At the end of this blog, you might have some interesting thoughts about rollups.</p><h3 id="h-what-are-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What Are Rollups?</h3><p>Imagine you’re at a crowded theme park, and there’s a long line for every ride. Wouldn&apos;t it be nice if you could group people together, so instead of 50 individuals waiting in line, you send them in one big group? That&apos;s what rollups do for blockchains. Instead of handling every transaction one by one, rollups <strong>bundle</strong> multiple transactions together, process them off-chain, and then submit a summary of these transactions back to the main blockchain.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/76cbc0b3425c40e236aed6f732a8f41857e285339acc00205f8df29b6bc6b687.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-how-do-rollups-work" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">How Do Rollups Work?</h3><p>There are two important parts of a rollup:</p><ol><li><p><strong>Off-chain processing</strong>: Transactions happen <strong>off-chain</strong>, meaning they are processed outside the main blockchain. This reduces the load on the main chain, making everything faster.</p></li><li><p><strong>On-chain verification</strong>: After transactions are processed off-chain.A rollup submits the compressed data or a proof back to the main blockchain, which acts as the final validator and security layer.</p></li></ol><p>This way, rollups increase transaction speed and reduce costs still maintaining the security of the main blockchain.</p><h3 id="h-types-of-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Types of Rollups</h3><p>There are two main types of rollups: <strong>Optimistic Rollups</strong> and <strong>Zero-Knowledge (ZK) Rollups</strong>. Both improve blockchain scalability, but they do it in different ways.</p><h3 id="h-1-optimistic-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. <strong>Optimistic Rollups</strong></h3><ul><li><p>Optimistic Rollups transactions are initially processed off-chain by a operator. This operator bundles the transactions into a batched block and submits the batched block to the main chain, asserting the validity of the multiple transactions. The principal blockchain then confirms the validity proof and publishes the block, finalizing the transactions.</p></li><li><p>There will be a challenging period for this. If any disputes arise regarding the validity of transactions, The validator will submit a <strong>fraud proof</strong> to challenge the result. The blockchain will then re-check that transaction.</p></li></ul><p><strong>Example</strong>: Arbitrum, Optimism etc</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/9ecb1707c8cfbaf51a902b57b8d0990f298d1121cff2829ee027ddc518f86312.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-2-zero-knowledge-zk-rollups" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. <strong>Zero-Knowledge (ZK) Rollups</strong></h3><ul><li><p><strong>ZK-rollups</strong> uses advanced cryptographic techniques known as <strong>zero-knowledge proofs</strong> to ensure transaction validity without revealing sensitive data. In this model, all transaction data is aggregated off-chain, and only succinct validity proofs are submitted to the main blockchain.</p></li><li><p>This approach provides enhanced privacy and scalability while maintaining the security and privacy of the underlying blockchain.</p></li></ul><p><strong>Example</strong>: zkSync, Starkware.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e3ba47ec5f93fc2bd593fd38707f422e00b56232a274fbcf2522985e81722303.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-why-are-rollups-important" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Why Are Rollups Important?</h3><ol><li><p><strong>Speed and Scalability</strong>: At a crowded theme park, if each person waited individually, lines would move slowly. But grouping 50 people together speeds things up. Similarly, rollups bundle transactions, processing them in one go, which boosts blockchain speed and scalability.</p></li><li><p><strong>Lower Fees</strong>: When the theme park is crowded, the wait is longer and more expensive (like paying extra for a fast pass). Rollups group people (or transactions) into one &quot;group ticket,&quot; reducing the cost, just as rollups lower gas fees on the blockchain.</p></li><li><p><strong>Security</strong>: Even when you group everyone together for the ride, the theme park still checks the group ticket to ensure safety. Similarly, with rollups, the main blockchain (eg Ethereum) verifies that all bundled transactions are correct, providing strong security while being faster and cheaper.</p></li></ol><h2 id="h-summary" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Summary</strong></h2><p>Blockchain Rollups are a Layer 2 solution designed to improve transaction speed with lower gas costs by bundling multiple off-chain transactions**.** This article would have given you a basic understanding of rollups, and their types.</p><p>Follow me for to learn more about rollups!!</p>]]></content:encoded>
            <author>kaushik-2@newsletter.paragraph.com (Kaushik)</author>
        </item>
    </channel>
</rss>