<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: onyi</title>
    <description>The latest articles on DEV Community by onyi (@fran6).</description>
    <link>https://dev.to/fran6</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3836068%2Ff00a626a-38ec-453c-89d3-440edaf34968.png</url>
      <title>DEV Community: onyi</title>
      <link>https://dev.to/fran6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fran6"/>
    <language>en</language>
    <item>
      <title>From Zero to GenLayer: Connecting Your dApp with Next.js and genlayer-js (Part 3/3)</title>
      <dc:creator>onyi</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:25:37 +0000</pubDate>
      <link>https://dev.to/fran6/from-zero-to-genlayer-connecting-your-dapp-with-nextjs-and-genlayer-js-part-33-4fa8</link>
      <guid>https://dev.to/fran6/from-zero-to-genlayer-connecting-your-dapp-with-nextjs-and-genlayer-js-part-33-4fa8</guid>
      <description>&lt;p&gt;Your contract is deployed. It lives on the GenLayer network, it can create bets, accept them, and use AI to resolve subjective outcomes. You tested it in Studio, watched validators argue about whether a movie was "critically acclaimed," and saw the right answer win. That was satisfying.&lt;/p&gt;

&lt;p&gt;But nobody is going to interact with your contract through a developer console. They need a frontend. So let us build one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;GenLayer provides an official boilerplate that gives you a full-stack project out of the box. Clone it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/genlayerlabs/genlayer-project-boilerplate.git GenBets
&lt;span class="nb"&gt;cd &lt;/span&gt;GenBets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at what you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GenBets/
  contracts/          # Python intelligent contracts
  frontend/           # Next.js 15 app (TypeScript, Tailwind, TanStack Query)
  deploy/             # TypeScript deployment scripts
  test/               # Python integration tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The boilerplate ships with a sample contract called &lt;code&gt;football_bets.py&lt;/code&gt;. We do not need it. Replace it with the &lt;code&gt;genbets.py&lt;/code&gt; contract you wrote in Part 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;your-genbets-contract/genbets.py contracts/genbets.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now set up the frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the environment template and configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;.env&lt;/code&gt; and set the contract address you got from Studio when you deployed in Part 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_GENLAYER_RPC_URL=https://studio.genlayer.com/api
NEXT_PUBLIC_GENLAYER_CHAIN_ID=61999
NEXT_PUBLIC_GENLAYER_CHAIN_NAME=GenLayer Studio
NEXT_PUBLIC_GENLAYER_SYMBOL=GEN
NEXT_PUBLIC_CONTRACT_ADDRESS=0xYourContractAddressFromStudio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line is the important one. Without it, the frontend has no idea which contract to talk to.&lt;/p&gt;

&lt;p&gt;The frontend stack is modern but not exotic: Next.js 15 with the App Router, React 19, TypeScript, Tailwind CSS for styling, TanStack Query for data management, and MetaMask wallet integration through &lt;code&gt;genlayer-js&lt;/code&gt;. If you have built a React app before, nothing here will surprise you. The only new piece is &lt;code&gt;genlayer-js&lt;/code&gt; -- GenLayer's JavaScript SDK for reading from and writing to intelligent contracts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Architecture
&lt;/h2&gt;

&lt;p&gt;Before diving into code, it helps to understand how the pieces connect. The boilerplate separates concerns into four layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contract class&lt;/strong&gt; (&lt;code&gt;lib/contracts/GenBets.ts&lt;/code&gt;) -- A TypeScript wrapper around raw &lt;code&gt;genlayer-js&lt;/code&gt; calls. This is where &lt;code&gt;readContract()&lt;/code&gt; and &lt;code&gt;writeContract()&lt;/code&gt; live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React hooks&lt;/strong&gt; (&lt;code&gt;lib/hooks/useGenBets.ts&lt;/code&gt;) -- Custom hooks that wrap the contract class with TanStack Query for caching, loading states, and automatic refetching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI components&lt;/strong&gt; (&lt;code&gt;components/&lt;/code&gt;) -- The visible interface. Tables, modals, buttons, status badges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallet management&lt;/strong&gt; (&lt;code&gt;lib/genlayer/client.ts&lt;/code&gt; and &lt;code&gt;WalletProvider.tsx&lt;/code&gt;) -- MetaMask connection, network switching, and account management.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data flow for every user action follows the same path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User clicks button
  -&amp;gt; React hook fires
    -&amp;gt; Contract class method called
      -&amp;gt; genlayer-js sends request to GenLayer RPC
        -&amp;gt; GenLayer network processes transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GenLayer client connects to the Studio RPC endpoint (or a local node) and delegates transaction signing to MetaMask. When you call &lt;code&gt;writeContract()&lt;/code&gt;, MetaMask pops up asking the user to confirm. When you call &lt;code&gt;readContract()&lt;/code&gt;, it is a free read -- no signature needed, no popup.&lt;/p&gt;

&lt;p&gt;TanStack Query sits in the middle and handles the tedious parts: caching responses so you do not re-fetch data on every render, automatically refetching when the browser window regains focus, and invalidating stale data after a mutation succeeds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Contract Class
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;GenBets.ts&lt;/code&gt; contract class is the bridge between your TypeScript frontend and your Python contract on GenLayer. Here is how it works.&lt;/p&gt;

&lt;p&gt;The constructor creates a &lt;code&gt;genlayer-js&lt;/code&gt; client configured with the chain, the user's wallet address, and the RPC endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;genlayer-js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;studionet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;genlayer-js/chains&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenBets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;studioUrl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contractAddress&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;studionet&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s2"&gt;`0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;studioUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;studioUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two fundamental operations drive everything:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading state&lt;/strong&gt; uses &lt;code&gt;readContract()&lt;/code&gt;. This calls &lt;code&gt;@gl.public.view&lt;/code&gt; methods on the contract. No gas, no wallet signature. The &lt;code&gt;getBets()&lt;/code&gt; method demonstrates a common pattern -- calling &lt;code&gt;get_bet_count&lt;/code&gt; first, then looping through &lt;code&gt;get_bet(i)&lt;/code&gt; to build an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getBets&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;betCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readContract&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_bet_count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;betCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;betData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readContract&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_bet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;betData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Writing state&lt;/strong&gt; uses &lt;code&gt;writeContract()&lt;/code&gt;. This calls &lt;code&gt;@gl.public.write&lt;/code&gt; methods, which modify the blockchain. These require a wallet signature and cost gas. After sending the transaction, you wait for it to be accepted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createBet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;resolutionPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TransactionReceipt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeContract&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_bet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForTransactionReceipt&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCEPTED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;waitForTransactionReceipt()&lt;/code&gt; call is worth understanding. GenLayer transactions do not go from "pending" to "confirmed" like Ethereum. They move through a multi-stage consensus pipeline: PENDING, PROPOSING, COMMITTING, REVEALING, ACCEPTED, and finally FINALIZED. Each stage corresponds to a phase of the Optimistic Democracy protocol, where validators independently execute the contract, propose results, and vote on whether they agree. The &lt;code&gt;status: "ACCEPTED"&lt;/code&gt; parameter tells the SDK to wait until validators have reached consensus and the transaction is accepted -- though not yet finalized.&lt;/p&gt;

&lt;p&gt;Notice there is no &lt;code&gt;value&lt;/code&gt; parameter in the &lt;code&gt;writeContract()&lt;/code&gt; call. The GenBets contract does not handle token transfers -- it tracks who wins but does not move money. This keeps the contract simple and focused on the AI resolution logic.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;resolveBet()&lt;/code&gt; method follows the same pattern but returns the winner string from the transaction receipt, so the UI can display who won.&lt;/p&gt;




&lt;h2&gt;
  
  
  React Hooks
&lt;/h2&gt;

&lt;p&gt;The hooks layer wraps each contract method with TanStack Query, turning raw async calls into reactive data that components can consume without thinking about loading states or cache invalidation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useBets()&lt;/code&gt; hook fetches all bets and keeps them fresh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useBets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGenBetsContract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([]),&lt;/span&gt;
    &lt;span class="na"&gt;refetchOnWindowFocus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any component that calls &lt;code&gt;useBets()&lt;/code&gt; gets &lt;code&gt;{ data, isLoading, isError }&lt;/code&gt; -- the standard TanStack Query return shape. Multiple components can call the same hook and they will share the cached result rather than making duplicate network requests.&lt;/p&gt;

&lt;p&gt;Mutations follow a different pattern. &lt;code&gt;useCreateBet()&lt;/code&gt; wraps the contract's write method and handles cache invalidation on success:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useCreateBet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGenBetsContract&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isCreating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsCreating&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;mutationFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="nx"&gt;opponent&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setIsCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createBet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="nx"&gt;resolutionPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nf"&gt;setIsCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isCreating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createBet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a bet is created successfully, &lt;code&gt;invalidateQueries({ queryKey: ["bets"] })&lt;/code&gt; tells TanStack Query that the cached bets list is stale. Every component using &lt;code&gt;useBets()&lt;/code&gt; automatically refetches.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useResolveBet()&lt;/code&gt; hook adds one extra piece: it tracks which bet is currently being resolved and stores the winner string returned from the receipt. This lets the UI show a spinner on the correct row and display the winner when it arrives.&lt;/p&gt;

&lt;p&gt;The pattern across all hooks is consistent: create a contract instance from the current wallet context, call the contract method, update local state for loading indicators, invalidate relevant caches on success, and show toast notifications for success or failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The UI Components
&lt;/h2&gt;

&lt;p&gt;With the hooks in place, the components are straightforward React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bets Table
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;GenBetsTable&lt;/code&gt; component renders every bet as a row in a table with columns for description, status, creator, opponent, winner, and actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GenBetsTable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useBets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isConnected&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;acceptBet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isAccepting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;acceptingBetId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAcceptBet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cancelBet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isCancelling&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancellingBetId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCancelBet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolveBet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isResolving&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolvingBetId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useResolveBet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// ... loading and error states ...&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BetRow&lt;/span&gt;
          &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;bet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;currentAddress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;onAccept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleAccept&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;onCancel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCancel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;onResolve&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleResolve&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;isAccepting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isAccepting&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;acceptingBetId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each row conditionally renders action buttons based on the bet's status and the connected wallet. If you are the invited opponent and the bet is open, you see an "Accept" button. If you are the creator, you see "Cancel." If the bet has been accepted by both parties, anyone can click "Resolve" to trigger AI resolution. Status badges use color coding -- yellow for open, blue for accepted, green for resolved, red for cancelled. Resolved bets display the winner ("creator" or "opponent") in the winner column.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Bet Modal
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;CreateGenBetModal&lt;/code&gt; component is a dialog form with four fields: bet description, resolution URL, resolution prompt, and opponent address. It validates inputs client-side (valid URL format, valid address, cannot bet against yourself) before submitting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FormEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;validateForm&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;createBet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolutionPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;opponentAddress&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The form is straightforward — no stake or amount fields since the contract does not handle token transfers. The modal auto-closes on success and resets its form fields, thanks to a &lt;code&gt;useEffect&lt;/code&gt; that watches the mutation's &lt;code&gt;isSuccess&lt;/code&gt; state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navbar
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Navbar&lt;/code&gt; ties everything together with GenBets branding, live statistics (total bets, resolved count), a "Create Bet" button that opens the modal, and the wallet connection panel. The stats update automatically because they derive from the same &lt;code&gt;useBets()&lt;/code&gt; query that the table uses -- no extra network calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  End-to-End Demo
&lt;/h2&gt;

&lt;p&gt;Time to see it all work together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Start the dev server.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; in your browser. You should see the GenBets interface with an empty bets table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Connect your wallet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click the wallet button in the navbar. MetaMask will ask you to connect and may prompt you to add the GenLayer Studio network (chain ID 61999). Accept both prompts. Your address appears in the navbar.&lt;/p&gt;

&lt;p&gt;If you are using GenLayer Studio, it provides pre-funded test accounts. Import one of those private keys into MetaMask so you have GEN tokens to work with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Create a bet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click "Create Bet." Fill in something like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; "The Shawshank Redemption has above 90% on Rotten Tomatoes"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolution URL:&lt;/strong&gt; &lt;code&gt;https://www.rottentomatoes.com/m/shawshank_redemption&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolution Prompt:&lt;/strong&gt; "Check the Tomatometer score on this page. If it is above 90%, the creator wins. If it is 90% or below, the opponent wins."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opponent:&lt;/strong&gt; paste the address of a second test account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click "Create Bet." MetaMask asks you to sign. Confirm. The button shows a spinner while the transaction moves through consensus. After a few seconds, the bet appears in the table with status "Open."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Accept the bet.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Switch MetaMask to the opponent account (the address you entered as the opponent). Refresh the page. You will see the bet with an "Accept" button. Click it, confirm the MetaMask transaction, and the status changes to "Accepted."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Resolve with AI.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click "Resolve." This is where GenLayer does something no other blockchain can do. The transaction triggers your contract's &lt;code&gt;resolve_bet&lt;/code&gt; method, which tells validators to fetch the Rotten Tomatoes page, read the score, and determine the winner using your prompt. Multiple validators do this independently and compare their answers through the Equivalence Principle.&lt;/p&gt;

&lt;p&gt;Resolution takes longer than a simple transfer -- expect 15 to 30 seconds. The validators are independently browsing the web, running LLM reasoning, and reaching consensus. When they finish, the bet status flips to "Resolved" and the winner ("creator" or "opponent") is displayed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Verify.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check the winner field on the resolved bet. It will show either "creator" or "opponent" based on what the AI validators determined from the web data.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Have Learned
&lt;/h2&gt;

&lt;p&gt;Over three posts, we built a complete dApp on GenLayer from scratch. Here is everything we covered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intelligent Contracts.&lt;/strong&gt; GenLayer's smart contracts are written in Python and can natively access the internet and large language models. No oracles, no off-chain workers -- the contract itself browses the web and reasons about what it finds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimistic Democracy.&lt;/strong&gt; GenLayer's consensus mechanism where multiple validators independently execute non-deterministic operations (web fetches, LLM calls) and vote on whether their results agree. This is not proof-of-work or proof-of-stake. It is a consensus protocol designed specifically for AI-powered execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Equivalence Principle.&lt;/strong&gt; The mechanism validators use to compare non-deterministic outputs. Strict equality for exact matches (like our single-word winner output), comparative prompts for "close enough" reasoning, and non-comparative prompts for independent assessment. This is how a blockchain can reach agreement on subjective questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GenLayer Studio.&lt;/strong&gt; The development sandbox where you write, deploy, and test intelligent contracts. It simulates the full validator network locally so you can iterate quickly before deploying to a live network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;genlayer-js.&lt;/strong&gt; The frontend SDK that connects your JavaScript or TypeScript application to GenLayer. &lt;code&gt;readContract()&lt;/code&gt; for views, &lt;code&gt;writeContract()&lt;/code&gt; for mutations, &lt;code&gt;waitForTransactionReceipt()&lt;/code&gt; to track consensus progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The full stack.&lt;/strong&gt; A Python intelligent contract deployed on GenLayer, connected through &lt;code&gt;genlayer-js&lt;/code&gt; to a Next.js frontend, with MetaMask handling wallet management and TanStack Query managing data flow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;GenBets is a working dApp, but it is a starting point, not a finish line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy to testnet.&lt;/strong&gt; GenLayer's public testnet, Asimov, lets you test with real (test) validators and real network conditions. Run &lt;code&gt;genlayer network&lt;/code&gt; and select &lt;code&gt;testnet&lt;/code&gt;, then redeploy your contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extend GenBets.&lt;/strong&gt; The current design tracks who wins but does not handle token transfers. Consider adding staking functionality where both parties lock tokens and the winner receives the pot. Add bet categories, a reputation system for reliable bettors, or time-limited bets that auto-resolve after a deadline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore the ecosystem.&lt;/strong&gt; The GenLayer documentation at &lt;a href="https://docs.genlayer.com" rel="noopener noreferrer"&gt;docs.genlayer.com&lt;/a&gt; covers advanced contract patterns, testing strategies, and deployment workflows. Join the &lt;a href="https://discord.gg/genlayer" rel="noopener noreferrer"&gt;GenLayer Discord&lt;/a&gt; to connect with other builders. Browse the contract examples on &lt;a href="https://github.com/genlayerlabs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; for inspiration.&lt;/p&gt;

&lt;p&gt;You started this series not knowing what an intelligent contract was. Now you have written one, tested it, deployed it, and built a frontend around it. You understand how AI-native consensus works, why the Equivalence Principle matters, and how &lt;code&gt;genlayer-js&lt;/code&gt; connects a web app to a blockchain that can think.&lt;/p&gt;

&lt;p&gt;Welcome to GenLayer. Now go build something intelligent.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ai</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Zero to GenLayer: Writing Your First Intelligent Contract in Python (Part 2/3)</title>
      <dc:creator>onyi</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:19:56 +0000</pubDate>
      <link>https://dev.to/fran6/from-zero-to-genlayer-writing-your-first-intelligent-contract-in-python-part-23-2bbo</link>
      <guid>https://dev.to/fran6/from-zero-to-genlayer-writing-your-first-intelligent-contract-in-python-part-23-2bbo</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;Welcome back. In Part 1 of this series, we covered what GenLayer is — an AI-native blockchain where "Intelligent Contracts" can call LLMs and fetch live web data right from on-chain code. We also got GenLayer Studio up and running so we have a local playground ready to go. If you missed it, go back and read Part 1 first. Now it is time to actually write some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;We are building &lt;strong&gt;GenBets&lt;/strong&gt; — a peer-to-peer betting contract where users can wager on subjective, real-world outcomes. Here is the flow: a creator posts a bet with a description (like "Inception will have above 80% on Rotten Tomatoes") and names an opponent. The opponent accepts the bet. Then anyone can trigger resolution, at which point the AI validators fetch a web page, read the actual data, reason about who won, and record the winner on-chain. No oracles. No manual judging. The AI handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contract Architecture
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code line by line, let us look at the big picture. The GenBets contract has six public methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Decorator&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_bet()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a new bet with a named opponent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;accept_bet()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opponent accepts the bet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cancel_bet()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creator cancels an open bet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resolve_bet()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AI fetches web data, evaluates the outcome, records the winner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_bet()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns all details of a specific bet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_bet_count()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@gl.public.view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns how many bets have been created&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let us talk about those decorators. They tell GenLayer how a method behaves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@gl.public.write&lt;/code&gt;&lt;/strong&gt; — This method modifies the contract's state. When a user calls a write method, the transaction goes through the validator consensus process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@gl.public.view&lt;/code&gt;&lt;/strong&gt; — Read-only. It looks at the contract's data but changes nothing. These calls are free and fast.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contract stores its data using two state variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bets: TreeMap[u256, Bet]&lt;/code&gt;&lt;/strong&gt; — A key-value map where the key is a bet ID (an unsigned 256-bit integer) and the value is a &lt;code&gt;Bet&lt;/code&gt; object. &lt;code&gt;TreeMap&lt;/code&gt; is GenLayer's on-chain storage type, similar to a dictionary that persists between transactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bet_counter: u256&lt;/code&gt;&lt;/strong&gt; — A simple counter that increments every time a bet is created, giving each bet a unique ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each bet is represented by the &lt;code&gt;Bet&lt;/code&gt; dataclass, which holds the creator's address (as a string), the opponent's address (as a string), a human-readable description, the URL to check for resolution, the prompt that tells the AI how to judge, the current status (&lt;code&gt;"open"&lt;/code&gt;, &lt;code&gt;"accepted"&lt;/code&gt;, &lt;code&gt;"cancelled"&lt;/code&gt;, or &lt;code&gt;"resolved"&lt;/code&gt;), and the winner (a string — either &lt;code&gt;"creator"&lt;/code&gt;, &lt;code&gt;"opponent"&lt;/code&gt;, or empty).&lt;/p&gt;

&lt;p&gt;One important runtime value you will see throughout the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gl.message.sender_address&lt;/code&gt;&lt;/strong&gt; — The wallet address of whoever is calling the method. This is how the contract knows &lt;em&gt;who&lt;/em&gt; is making a request. The contract converts it to a string with &lt;code&gt;str()&lt;/code&gt; for comparison and storage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building Step by Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: The Foundation
&lt;/h3&gt;

&lt;p&gt;Let us start with the skeleton — imports, the data model, and the contract class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# { "Depends": "py-genlayer:test" }
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;genlayer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;


&lt;span class="nd"&gt;@allow_storage&lt;/span&gt;
&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenBets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bet_counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;
    &lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TreeMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walking through this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The comment on line 1 is a metadata hint for GenLayer's build system — it tells it which dependencies this contract needs.&lt;/li&gt;
&lt;li&gt;We import &lt;code&gt;json&lt;/code&gt; (standard Python) for potential use in data handling.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;from genlayer import *&lt;/code&gt; gives us everything GenLayer-specific: &lt;code&gt;gl&lt;/code&gt;, &lt;code&gt;u256&lt;/code&gt;, &lt;code&gt;TreeMap&lt;/code&gt;, and the decorators.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;@allow_storage&lt;/code&gt; decorator on the &lt;code&gt;Bet&lt;/code&gt; dataclass tells GenLayer that this class can be stored on-chain. Without it, you cannot put a custom object into a &lt;code&gt;TreeMap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Notice that &lt;code&gt;creator&lt;/code&gt;, &lt;code&gt;opponent&lt;/code&gt;, and &lt;code&gt;winner&lt;/code&gt; are all &lt;code&gt;str&lt;/code&gt; fields, not &lt;code&gt;Address&lt;/code&gt;. We store addresses as plain strings, which simplifies comparisons and storage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GenBets&lt;/code&gt; extends &lt;code&gt;gl.Contract&lt;/code&gt;, which is the base class for all intelligent contracts. The two class-level type annotations (&lt;code&gt;bet_counter&lt;/code&gt; and &lt;code&gt;bets&lt;/code&gt;) declare the contract's persistent storage.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;__init__&lt;/code&gt; method runs once when the contract is first deployed. We initialize the counter to zero. The &lt;code&gt;TreeMap&lt;/code&gt; does not need explicit initialization — it starts empty.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Creating a Bet
&lt;/h3&gt;

&lt;p&gt;Now let us add the ability for someone to create a bet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bet_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method is &lt;code&gt;@gl.public.write&lt;/code&gt; — it modifies the contract's state but does not handle any token transfers.&lt;/p&gt;

&lt;p&gt;We grab the current counter value as the new bet's ID, create a &lt;code&gt;Bet&lt;/code&gt; object with all the details, and store it in the &lt;code&gt;TreeMap&lt;/code&gt;. The &lt;code&gt;creator&lt;/code&gt; field is set by converting &lt;code&gt;gl.message.sender_address&lt;/code&gt; to a string. The &lt;code&gt;opponent&lt;/code&gt; is passed in as a string (the opponent's address). The &lt;code&gt;winner&lt;/code&gt; field starts as an empty string meaning "no winner yet." Finally, we bump the counter for the next bet and return the new bet's ID so the caller knows which bet they just created.&lt;/p&gt;

&lt;p&gt;Notice that the caller provides four arguments (&lt;code&gt;description&lt;/code&gt;, &lt;code&gt;resolution_url&lt;/code&gt;, &lt;code&gt;resolution_prompt&lt;/code&gt;, &lt;code&gt;opponent&lt;/code&gt;), but we also capture one implicit value: &lt;code&gt;gl.message.sender_address&lt;/code&gt; becomes the creator. The caller does not pass this explicitly — it comes from the transaction itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Accepting a Bet
&lt;/h3&gt;

&lt;p&gt;The opponent needs to accept the bet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accept_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bet is not open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cannot accept your own bet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only the invited opponent can accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three validations protect against misuse. The bet must be open (not already accepted, cancelled, or resolved). You cannot accept your own bet. Only the specific opponent the creator named can accept — the contract compares &lt;code&gt;str(gl.message.sender_address)&lt;/code&gt; against the stored opponent string.&lt;/p&gt;

&lt;p&gt;If all checks pass, we update the status to &lt;code&gt;"accepted"&lt;/code&gt; and write the updated bet back to storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Cancelling a Bet
&lt;/h3&gt;

&lt;p&gt;What if no one accepts your bet and you want to withdraw it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can only cancel open bets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only creator can cancel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancelled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only open bets can be cancelled (you cannot cancel a bet someone already accepted — that would be unfair). Only the creator can cancel. If those checks pass, we mark it as cancelled and save the updated bet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: The Magic — Resolving a Bet
&lt;/h3&gt;

&lt;p&gt;This is the heart of the contract and the part that makes GenLayer different from every other blockchain. Let us look at the full method first, then break it down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bet must be in accepted status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;web_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are an impartial judge evaluating a bet.

Bet description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Resolution criteria: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Web page content:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;web_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
End of web page data.

Based on the data above, determine who wins this bet.
You must respond with exactly one word: either &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
Nothing else. Just one word.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eq_principle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strict_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a lot happening here, so let us go piece by piece.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The closure pattern.&lt;/strong&gt; The inner function &lt;code&gt;nondet()&lt;/code&gt; is the non-deterministic part — the code that each validator will run independently. Everything inside this function can vary between validators (because web pages can return slightly different content and LLMs can give slightly different answers). Everything outside it is deterministic and runs the same way on every validator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fetching web data.&lt;/strong&gt; &lt;code&gt;gl.nondet.web.render(bet.resolution_url, mode="text")&lt;/code&gt; tells the validator's GenVM to actually visit the URL and render the page, then return the text content as a string. The &lt;code&gt;mode="text"&lt;/code&gt; parameter means we want the readable text, not raw HTML. This is the "internet-native" part of GenLayer — your contract can read any web page. Note that &lt;code&gt;render()&lt;/code&gt; returns a string directly — there is no need to call &lt;code&gt;.body.decode()&lt;/code&gt; on the result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the LLM prompt.&lt;/strong&gt; We construct a prompt that gives the AI everything it needs: the bet description, the resolution criteria the creator specified, and the actual web page content. We truncate the page content to 3000 characters with &lt;code&gt;web_data[:3000]&lt;/code&gt; to keep the prompt a reasonable size — web pages can be huge, and we only need the relevant data. Crucially, the prompt instructs the LLM to respond with &lt;strong&gt;exactly one word&lt;/strong&gt;: either "creator" or "opponent." Nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calling the LLM.&lt;/strong&gt; &lt;code&gt;gl.nondet.exec_prompt(task)&lt;/code&gt; sends the prompt to the LLM and returns a plain string. There is no &lt;code&gt;response_format="json"&lt;/code&gt; here — we want a simple, single-word answer. The result is normalized with &lt;code&gt;.strip().lower()&lt;/code&gt; to remove whitespace and ensure consistent casing. Then we check if "creator" appears in the result; if so, we return &lt;code&gt;"creator"&lt;/code&gt;, otherwise &lt;code&gt;"opponent"&lt;/code&gt;. This normalization step is critical for consensus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ensuring validator consensus with &lt;code&gt;strict_eq&lt;/code&gt;.&lt;/strong&gt; Here is where it gets interesting. Each validator runs &lt;code&gt;nondet()&lt;/code&gt; independently — they each fetch the web page and query the LLM separately. But for the transaction to be finalized, the validators need to agree on the result. That is what &lt;code&gt;gl.eq_principle.strict_eq(nondet)&lt;/code&gt; does.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;strict_eq&lt;/code&gt; function requires that every validator produces &lt;em&gt;exactly the same string output&lt;/em&gt;. This is why we designed the prompt to return a single word and then normalize the response to one of exactly two possible values: &lt;code&gt;"creator"&lt;/code&gt; or &lt;code&gt;"opponent"&lt;/code&gt;. No matter which LLM a validator uses, no matter how it phrases its reasoning internally, the final output of &lt;code&gt;nondet()&lt;/code&gt; will always be one of these two strings. Since &lt;code&gt;strict_eq&lt;/code&gt; compares the outputs character-by-character, this design guarantees that validators who reach the same conclusion will produce identical output. No JSON serialization tricks needed — just a clean, deterministic single-word result.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recording the winner.&lt;/strong&gt; The &lt;code&gt;winner&lt;/code&gt; variable is a string — either &lt;code&gt;"creator"&lt;/code&gt; or &lt;code&gt;"opponent"&lt;/code&gt;. We store it directly in &lt;code&gt;bet.winner&lt;/code&gt;, update the status to &lt;code&gt;"resolved"&lt;/code&gt;, save the bet, and return the winner string.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: View Methods
&lt;/h3&gt;

&lt;p&gt;Finally, two read-only methods to inspect the contract's state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@gl.public.view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolution_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolution_prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;winner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@gl.public.view&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_bet_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get_bet()&lt;/code&gt; takes a bet ID and returns a dictionary with all the bet's details. Since &lt;code&gt;creator&lt;/code&gt;, &lt;code&gt;opponent&lt;/code&gt;, and &lt;code&gt;winner&lt;/code&gt; are already strings, no conversion is needed — we return them directly. &lt;code&gt;get_bet_count()&lt;/code&gt; simply returns how many bets have been created — useful for iterating through all bets on the frontend.&lt;/p&gt;

&lt;p&gt;These are &lt;code&gt;@gl.public.view&lt;/code&gt; methods, meaning they do not modify any state. They are free to call and return instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Contract
&lt;/h2&gt;

&lt;p&gt;Here is the complete contract in one block — copy and paste this into Studio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# { "Depends": "py-genlayer:test" }
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;genlayer&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;


&lt;span class="nd"&gt;@allow_storage&lt;/span&gt;
&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenBets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bet_counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;
    &lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TreeMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bet_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;accept_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bet is not open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cannot accept your own bet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only the invited opponent can accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cancel_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Can only cancel open bets&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only creator can cancel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancelled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.write&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accepted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bet must be in accepted status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;web_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are an impartial judge evaluating a bet.

Bet description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Resolution criteria: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

Web page content:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;web_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
End of web page data.

Based on the data above, determine who wins this bet.
You must respond with exactly one word: either &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; or &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.
Nothing else. Just one word.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eq_principle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strict_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;winner&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.view&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_bet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bet_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;creator&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;creator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;opponent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;opponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolution_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resolution_prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolution_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;winner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@gl.public.view&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_bet_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;u256&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bet_counter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy and Test in Studio
&lt;/h2&gt;

&lt;p&gt;Let us take this contract for a spin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy the contract.&lt;/strong&gt; Open GenLayer Studio (either the local version at &lt;code&gt;localhost:8080&lt;/code&gt; or the hosted version at &lt;code&gt;studio.genlayer.com&lt;/code&gt;). Paste the complete contract code into the editor and click &lt;strong&gt;Deploy&lt;/strong&gt;. Once it deploys successfully, note the contract address — you will need it later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a bet.&lt;/strong&gt; With your first account selected, call &lt;code&gt;create_bet&lt;/code&gt; with these parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: &lt;code&gt;"Inception will have above 80% on Rotten Tomatoes"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resolution_url&lt;/code&gt;: &lt;code&gt;"https://www.rottentomatoes.com/m/inception"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resolution_prompt&lt;/code&gt;: &lt;code&gt;"Check if the Tomatometer score is above 80%. If yes, creator wins. If no, opponent wins."&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;opponent&lt;/code&gt;: the address of your second Studio account (copy it from the account dropdown)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The transaction should succeed and return &lt;code&gt;0&lt;/code&gt; (the first bet's ID).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accept the bet.&lt;/strong&gt; Switch to your second account in Studio. Call &lt;code&gt;accept_bet&lt;/code&gt; with &lt;code&gt;bet_id&lt;/code&gt; set to &lt;code&gt;0&lt;/code&gt;. You should see the transaction succeed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolve the bet.&lt;/strong&gt; Now call &lt;code&gt;resolve_bet&lt;/code&gt; with &lt;code&gt;bet_id&lt;/code&gt; set to &lt;code&gt;0&lt;/code&gt;. This is where it gets interesting — watch the transaction logs. You will see each validator independently fetch the Rotten Tomatoes page, extract the score, and reason about whether it is above 80%. The validators compare their results, reach consensus, and the winner is recorded. The method returns the winner as a string — either "creator" or "opponent."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspect the result.&lt;/strong&gt; Call &lt;code&gt;get_bet&lt;/code&gt; with &lt;code&gt;bet_id&lt;/code&gt; set to &lt;code&gt;0&lt;/code&gt;. You will see the full bet details including the &lt;code&gt;status&lt;/code&gt; (now &lt;code&gt;"resolved"&lt;/code&gt;) and the &lt;code&gt;winner&lt;/code&gt; (either &lt;code&gt;"creator"&lt;/code&gt; or &lt;code&gt;"opponent"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try the error cases.&lt;/strong&gt; These are worth testing to make sure the validations work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try calling &lt;code&gt;cancel_bet&lt;/code&gt; on the resolved bet — you should get "Can only cancel open bets."&lt;/li&gt;
&lt;li&gt;Switch back to the first account and try calling &lt;code&gt;accept_bet&lt;/code&gt; on your own bet — you should get "Cannot accept your own bet."&lt;/li&gt;
&lt;li&gt;Create a new bet and try accepting it from an account that is not the named opponent — you should get "Only the invited opponent can accept."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These guardrails ensure the contract behaves correctly in every scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 3, we will build a Next.js frontend that connects to our contract using &lt;code&gt;genlayer-js&lt;/code&gt;, letting users create, accept, and resolve bets through a polished web interface. We will cover wallet connection with MetaMask, reading contract state with TanStack Query, and sending transactions — everything you need to turn this contract into a real application users can interact with.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Zero to GenLayer: Why Smart Contracts Need AI (Part 1/3)</title>
      <dc:creator>onyi</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:18:06 +0000</pubDate>
      <link>https://dev.to/fran6/from-zero-to-genlayer-why-smart-contracts-need-ai-part-13-hko</link>
      <guid>https://dev.to/fran6/from-zero-to-genlayer-why-smart-contracts-need-ai-part-13-hko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 1 of a 3-part tutorial series where we build GenBets -- a peer-to-peer betting dApp on GenLayer where friends bet on subjective outcomes and AI validators settle the result.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bet You Can't Settle on a Blockchain
&lt;/h2&gt;

&lt;p&gt;Picture this: you and a friend are arguing about whether the latest Marvel movie is actually good. Not just box-office numbers -- you're debating whether it was &lt;em&gt;well-received&lt;/em&gt;. Critics loved it, audiences were split, and Reddit is a warzone of hot takes. Your friend says it flopped creatively. You say it landed. So you make a bet.&lt;/p&gt;

&lt;p&gt;Now here's the question nobody thinks to ask: &lt;strong&gt;who decides who wins?&lt;/strong&gt; You could ask a mutual friend, but they might be biased. You could check Rotten Tomatoes, but what score counts as "well-received"? 85%? 70%? And what about audience scores versus critic scores? The whole thing is... subjective.&lt;/p&gt;

&lt;p&gt;What if, instead of trusting a single person's judgment, the blockchain itself could read reviews, analyze sentiment, and reason about whether the movie was genuinely well-received -- and what if multiple independent AI models had to agree before anyone won? That is not science fiction. That is what GenLayer does, and by the end of this three-part series, you will have built exactly that.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Smart Contracts Hit a Wall
&lt;/h2&gt;

&lt;p&gt;If you have never touched blockchain before, here is the thirty-second version. A &lt;strong&gt;blockchain&lt;/strong&gt; is a shared ledger that nobody owns but everyone can verify. A &lt;strong&gt;smart contract&lt;/strong&gt; is a program that lives on that ledger -- it runs automatically, it cannot be tampered with, and it does exactly what its code says. Think of it as a vending machine: you put in the right inputs, you get the guaranteed output. No middleman needed.&lt;/p&gt;

&lt;p&gt;That vending machine analogy, though, reveals the limitation. Traditional smart contracts are &lt;strong&gt;deterministic&lt;/strong&gt; -- given the same input, they always produce the same output. They can add numbers, move tokens between accounts, and enforce rules like "only release payment after both parties sign." What they absolutely cannot do is think. They cannot read a news article and form an opinion. They cannot browse the web. They cannot interpret nuance.&lt;/p&gt;

&lt;p&gt;So what happens when a smart contract needs real-world information? The blockchain world solved this with &lt;strong&gt;oracles&lt;/strong&gt; -- services like Chainlink that feed external data into contracts. Need the current price of ETH? An oracle can provide that. Need last night's football scores? An oracle can deliver those too. Oracles are excellent at providing &lt;strong&gt;objective, verifiable facts&lt;/strong&gt;: numbers, timestamps, binary outcomes.&lt;/p&gt;

&lt;p&gt;But go back to our movie bet. No oracle can tell you whether a movie was "well-received." That requires reading multiple sources, weighing different perspectives, and making a judgment call. The same gap appears everywhere in real life: Did a startup's product launch "succeed"? Was a political debate performance "strong"? Did a restaurant "deserve" its Michelin star? These are the kinds of questions humans bet on, argue about, and make agreements around every single day -- and traditional smart contracts simply cannot handle them. There is a gap between what smart contracts can do and what real-world agreements actually need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter GenLayer: The First AI-Native Blockchain
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GenLayer&lt;/strong&gt; closes that gap. It is a blockchain built from the ground up to integrate large language models (LLMs) into the contract execution layer itself. Contracts on GenLayer are not just deterministic code -- they are programs that can reason, browse the internet, and make nuanced judgments, all while maintaining the trustless guarantees that make blockchain valuable in the first place.&lt;/p&gt;

&lt;p&gt;Let's break down the three pillars that make this work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intelligent Contracts: Smart Contracts That Think
&lt;/h3&gt;

&lt;p&gt;On GenLayer, smart contracts are called &lt;strong&gt;Intelligent Contracts&lt;/strong&gt;, and they are written in &lt;strong&gt;Python&lt;/strong&gt;. If you are wondering why Python and not Solidity (the language most blockchains use), the answer is straightforward: Python is the lingua franca of AI and machine learning. When your contracts need to compose prompts, parse LLM outputs, and process web data, Python is the natural choice.&lt;/p&gt;

&lt;p&gt;An Intelligent Contract looks like a regular Python class, but it has access to special functions that no traditional blockchain offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gl.nondet.exec_prompt()&lt;/code&gt;&lt;/strong&gt; -- Sends a prompt to a large language model and gets back a response. Your contract can literally ask an AI a question and use the answer in its logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gl.nondet.web.render()&lt;/code&gt;&lt;/strong&gt; -- Fetches and renders a live web page, returning its content. Your contract can read news articles, check review aggregators, or pull data from any public URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are called &lt;strong&gt;non-deterministic operations&lt;/strong&gt; because, unlike adding two numbers, they might return slightly different results each time. Different LLMs might phrase the same answer differently. A web page might update between requests. GenLayer has a specific mechanism for handling this, which brings us to the next pillar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimistic Democracy: How Validators Agree
&lt;/h3&gt;

&lt;p&gt;If a contract calls an LLM, and different validators are connected to different LLMs (GPT-4, Claude, Gemini, etc.), how do they agree on a result? GenLayer uses a consensus mechanism called &lt;strong&gt;Optimistic Democracy&lt;/strong&gt;. Here is how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;               Transaction Submitted
                       |
                       v
            +---------------------+
            |   Leader Validator  |  &amp;lt;-- Randomly selected
            |  (runs the contract |
            |   and proposes an   |
            |      output)        |
            +---------------------+
                       |
                       v
       +-------------------------------+
       |       Validator Set           |
       |  [V1]   [V2]   [V3]   [V4]   |  &amp;lt;-- Each connected to
       |   |       |       |       |   |      a different LLM
       |   v       v       v       v   |
       | verify  verify  verify  verify|
       |   |       |       |       |   |
       |   v       v       v       v   |
       | vote    vote    vote    vote   |
       +-------------------------------+
                       |
                       v
              Majority Agrees?
              /              \
           Yes                No
            |                  |
            v                  v
     Transaction           Transaction
      Accepted              Rejected
            |
            v
     +--- Appeal Window ---+
     | Anyone can challenge |
     | by posting a bond.   |
     | Validator set doubles|
     | and re-votes.        |
     +----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A randomly selected subset of validators processes each transaction. One validator is chosen as the &lt;strong&gt;leader&lt;/strong&gt; -- it runs the contract and proposes an output. The remaining validators then independently run the same contract and &lt;strong&gt;vote&lt;/strong&gt; on whether the leader's output is acceptable. If a majority agrees, the transaction goes through.&lt;/p&gt;

&lt;p&gt;But there is an important safety net: the &lt;strong&gt;appeal window&lt;/strong&gt;. After a transaction is accepted, anyone who disagrees with the result can challenge it by posting a bond (putting tokens at stake). When a challenge is filed, the validator set doubles in size and the transaction is re-processed. This means bad results can be overturned, but frivolous challenges are discouraged because the challenger loses their bond if the original result holds. It is democracy with skin in the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Equivalence Principle: Agreeing on "Close Enough"
&lt;/h3&gt;

&lt;p&gt;Here is the subtle problem: if you ask GPT-4 and Claude the same question, they will give you correct but differently worded answers. "The movie was well-received" and "Critics and audiences responded positively to the film" mean the same thing, but they are different strings. A naive equality check would reject the transaction every time.&lt;/p&gt;

&lt;p&gt;GenLayer solves this with the &lt;strong&gt;Equivalence Principle&lt;/strong&gt; -- a mechanism that defines what "agreement" means for non-deterministic outputs. There are three variants, and choosing the right one is a key design decision when writing Intelligent Contracts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Strict Equality -- &lt;code&gt;gl.eq_principle.strict_eq()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The simplest form. The leader's output must exactly match what each validator produces. This works when you design the non-deterministic function to produce a single, deterministic output -- for example, returning a single word like "creator" or "opponent" rather than free-form text. If the LLM's answer is normalized to one of a small set of possible values, every validator will produce the same string, and strict equality passes cleanly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: strict equality for normalized single-word output
&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eq_principle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strict_eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Every validator must return exactly the same string
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Comparative -- &lt;code&gt;gl.eq_principle.prompt_comparative()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both the leader and each validator perform the same task independently. Then an LLM compares their outputs to see if they are substantively equivalent. This works well for &lt;strong&gt;quantitative or structured outputs&lt;/strong&gt; where you expect the same conclusion but possibly different wording.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: comparative equivalence
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eq_principle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt_comparative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Are these two summaries making the same factual claims?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nondet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize this data...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Validators do the same work, then compare
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Non-Comparative -- &lt;code&gt;gl.eq_principle.prompt_non_comparative()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Validators do &lt;strong&gt;not&lt;/strong&gt; replicate the leader's work. Instead, they assess the leader's output against a set of criteria. Think of it like a teacher grading an essay: the teacher does not write their own essay first -- they just evaluate whether the submitted one meets the requirements. This is ideal for subjective, qualitative assessments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: non-comparative equivalence
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eq_principle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt_non_comparative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the sentiment of these reviews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;criteria&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The summary should accurately reflect the overall tone&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Which does GenBets use?&lt;/strong&gt; GenBets uses &lt;code&gt;strict_eq&lt;/code&gt; with a clever design: instead of asking the LLM to return complex JSON or free-form text, we ask it to return exactly one word -- either "creator" or "opponent." The code then normalizes the response with &lt;code&gt;.strip().lower()&lt;/code&gt; and checks whether "creator" appears in the result. This means every validator, regardless of which LLM it runs, will produce the same single-word output: either &lt;code&gt;"creator"&lt;/code&gt; or &lt;code&gt;"opponent"&lt;/code&gt;. Since the output is a simple, deterministic string, &lt;code&gt;strict_eq&lt;/code&gt; passes reliably. We will see this pattern in detail in Part 2.&lt;/p&gt;




&lt;h2&gt;
  
  
  Meet GenBets: What We Are Building
&lt;/h2&gt;

&lt;p&gt;Across this three-part series, we are building &lt;strong&gt;GenBets&lt;/strong&gt; -- a peer-to-peer betting platform for subjective outcomes. No bookmakers, no centralized judges, no oracles feeding in predetermined data points. Just two people, a bet, and AI-powered validators that settle it.&lt;/p&gt;

&lt;p&gt;Here is how it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+------------------+       +-------------------+       +--------------------+
|   1. CREATE BET  |       |  2. ACCEPT BET    |       |  3. RESOLVE BET    |
|                  |       |                   |       |                    |
| Creator defines: | ----&amp;gt; | Opponent accepts  | ----&amp;gt; | Anyone triggers    |
| - Description    |       |   the bet         |       |   resolution       |
| - Resolution URL |       |                   |       |                    |
| - Criteria       |       | Bet status moves  |       | Contract fetches   |
| - Opponent addr  |       |   to "accepted"   |       |   data from URL,   |
|                  |       |                   |       |   LLM evaluates    |
| Names opponent   |       |                   |       |   against criteria |
+------------------+       +-------------------+       +--------------------+
                                                               |
                                                               v
                                                  +-------------------------+
                                                  |  4. VALIDATORS VERIFY   |
                                                  |                         |
                                                  | Multiple validators     |
                                                  |   (different LLMs)      |
                                                  |   independently check   |
                                                  |   the result via        |
                                                  |   Optimistic Democracy  |
                                                  +-------------------------+
                                                               |
                                                               v
                                                  +-------------------------+
                                                  |  5. RESULT RECORDED     |
                                                  |                         |
                                                  | Winner ("creator" or    |
                                                  |   "opponent") is stored |
                                                  |   on-chain.             |
                                                  |                         |
                                                  +-------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, you might create a bet: &lt;em&gt;"The new Dune movie will be well-received by critics"&lt;/em&gt;, set the resolution URL to Rotten Tomatoes, define your criteria as &lt;em&gt;"well-received means a critic score above 75% and generally positive consensus"&lt;/em&gt;, and name your friend as the opponent. Your friend accepts the bet. After the movie releases, anyone triggers resolution. The contract fetches the Rotten Tomatoes page, an LLM evaluates the reviews against your criteria, validators verify the judgment, and the winner is recorded on-chain. All transparent, all trustless.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Your Environment
&lt;/h2&gt;

&lt;p&gt;Let's get your local development environment ready so you can hit the ground running in Part 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;You need two things installed on your machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js 18+&lt;/strong&gt; -- Download from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt; if you do not have it. Verify with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Should output v18.x.x or higher&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A modern browser&lt;/strong&gt; -- Chrome, Firefox, or Edge. You will use it to access GenLayer Studio.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Install the GenLayer CLI
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;GenLayer CLI&lt;/strong&gt; is a command-line tool that sets up and manages your local GenLayer development environment. Install it globally with npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; genlayer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initialize Your Local Environment
&lt;/h3&gt;

&lt;p&gt;Once installed, spin up a local GenLayer network with three validators:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;genlayer init &lt;span class="nt"&gt;--numValidators&lt;/span&gt; 3 &lt;span class="nt"&gt;--reset-db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command pulls the necessary Docker containers, configures three validator nodes (each connected to a different LLM), and sets up a local blockchain instance. It may take a few minutes on the first run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open GenLayer Studio
&lt;/h3&gt;

&lt;p&gt;Once initialization completes, open your browser and navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;http://localhost:8080/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Welcome to &lt;strong&gt;GenLayer Studio&lt;/strong&gt; -- your development playground. Take a moment to familiarize yourself with the interface:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contract Editor&lt;/strong&gt; (center panel) -- This is where you write your Intelligent Contracts in Python. It has syntax highlighting and is where we will spend most of our time in Part 2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accounts Panel&lt;/strong&gt; (sidebar) -- You will see several pre-funded test accounts. These come loaded with test tokens so you can deploy contracts and send transactions without any real money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction History&lt;/strong&gt; (bottom panel) -- Every transaction you submit shows up here, including its status, the validator votes, and the final result. This is invaluable for debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will use Studio's built-in web editor for Parts 1 and 2 of this series. In Part 3, when we build the frontend, we will switch to a local project repository with a proper development setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we will write the GenBets Intelligent Contract in Python from scratch -- including the AI-powered resolution logic that reads live web data, prompts an LLM to evaluate subjective criteria, and lets validators reach consensus through the Equivalence Principle. That is where the real magic happens. See you there.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
