# How to Integrate the FootballBets Contract with a Next.js Frontend

> Integrate the FootballBets contract with Next.js. Deploy to GenLayer, expose the address, and use typed React hooks with genlayer-js for seamless frontend integration.

- Repository: [GenLayer Labs/genlayer-project-boilerplate](https://github.com/genlayerlabs/genlayer-project-boilerplate)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Integrate the FootballBets smart contract with a Next.js frontend by deploying the Python contract to GenLayer, exposing the address via `NEXT_PUBLIC_CONTRACT_ADDRESS`, and consuming it through strongly-typed React hooks that wrap the `genlayer-js` client.**

The `genlayerlabs/genlayer-project-boilerplate` repository provides a complete reference implementation demonstrating how to bridge GenLayer's Python smart contracts with modern React applications. This guide walks through the exact architecture used to connect the `FootballBets` contract to a Next.js 19 interface, covering everything from environment configuration to transaction management.

## Deploy the Contract

Before integrating the frontend, you must deploy the `FootballBets` contract to the GenLayer network. Run the deployment command from the project root:

```bash
genlayer deploy

```

After deployment completes, copy the emitted contract address. You will need this value to configure the frontend environment.

## Configure Environment Variables

Expose the deployed contract address to the Next.js application by adding it to `frontend/.env.example` (and your actual `.env.local`):

```bash
NEXT_PUBLIC_CONTRACT_ADDRESS=0x1234567890abcdef...

```

The frontend reads this variable through `getContractAddress()` defined in [`frontend/lib/genlayer/client.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/genlayer/client.ts). This helper validates that the address is configured and returns it for client initialization.

## Create the GenLayer Client Wrapper

The file [`frontend/lib/contracts/FootballBets.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/contracts/FootballBets.ts) provides a TypeScript abstraction over the raw `genlayer-js` client. The constructor accepts three parameters: the contract address, the user’s wallet address, and an optional Studio URL. It initializes the client using `createClient()` with the `studionet` chain configuration:

```typescript
// frontend/lib/contracts/FootballBets.ts
import { createClient, studionet } from 'genlayer-js';

export class FootballBets {
  private client;
  
  constructor(
    contractAddress: string, 
    account: `0x${string}`, 
    studioUrl?: string
  ) {
    this.client = createClient({
      chain: studionet,
      account,
      endpoint: studioUrl
    });
  }
  
  // Data conversion methods handle GenLayer Map → plain arrays
  async getBets() {
    const result = await this.client.readContract({
      address: this.contractAddress,
      functionName: 'get_bets'
    });
    return Array.from(result.values()); // Convert Map to array
  }
  
  async createBet(params: { gameDate: string; team1: string; team2: string; predictedWinner: string }) {
    // Implementation details below
  }
}

```

## Build React Query Hooks

All contract interactions flow through [`frontend/lib/hooks/useFootballBets.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/hooks/useFootballBets.ts). This file exports `useFootballBetsContract()` to manage the client instance, alongside specialized hooks for reading and writing data.

### Instantiate the Contract Client

The `useFootballBetsContract()` hook creates a singleton `FootballBets` instance whenever the wallet address or configured contract address changes. It validates that `NEXT_PUBLIC_CONTRACT_ADDRESS` is set and displays a toast notification if configuration is missing.

### Reading Contract State

The hooks `useBets()`, `usePlayerPoints()`, and `useLeaderboard()` wrap the contract’s view methods (`get_bets`, `get_player_points`, and `get_points`). They utilize React Query to keep the UI synchronized and automatically refetch after mutations invalidate the cache:

- **`useBets()`** → Calls `contract.getBets()` returning active betting markets
- **`usePlayerPoints()`** → Calls `contract.getPlayerPoints(address)` for individual scores  
- **`useLeaderboard()`** → Calls `contract.getLeaderboard()` for global rankings

### Executing Write Operations

For state-changing operations, `useCreateBet()` and `useResolveBet()` handle the full transaction lifecycle:

1. Estimate gas fees using `estimateWriteFeePreset`
2. Send the transaction via `client.writeContract`
3. Wait for the transaction receipt
4. Invalidate relevant React Query caches to trigger UI updates

Both hooks accept the necessary parameters (bet details for creation, bet ID for resolution) and return loading states for UI feedback.

## Implement Frontend Components

With the hooks in place, UI components remain purely presentational. Here are the implementation patterns used in the boilerplate:

### Display the Leaderboard

```tsx
// frontend/components/Leaderboard.tsx
import { useLeaderboard } from "@/lib/hooks/useFootballBets";

export default function Leaderboard() {
  const { data: leaderboard = [], isLoading } = useLeaderboard();

  if (isLoading) return <div>Loading…</div>;

  return (
    <ul>
      {leaderboard.map(({ address, points }) => (
        <li key={address}>
          {address.slice(0, 6)}… – {points} pts
        </li>
      ))}
    </ul>
  );
}

```

### Create a New Bet

```tsx
// frontend/components/CreateBetModal.tsx
import { useCreateBet } from "@/lib/hooks/useFootballBets";

export default function NewBetForm() {
  const { createBet, isLoading } = useCreateBet();

  const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const form = e.target as HTMLFormElement;
    const gameDate = form.gameDate.value;
    const team1 = form.team1.value;
    const team2 = form.team2.value;
    const predictedWinner = form.predictedWinner.value;

    await createBet({ gameDate, team1, team2, predictedWinner });
  };

  return (
    <form onSubmit={onSubmit}>
      {/* inputs for gameDate, team1, team2, predictedWinner */}
      <button type="submit" disabled={isLoading}>
        {isLoading ? "Creating…" : "Create Bet"}
      </button>
    </form>
  );
}

```

### Resolve an Existing Bet

```tsx
// frontend/components/BetsTable.tsx
import { useResolveBet } from "@/lib/hooks/useFootballBets";

export function ResolveButton({ betId }: { betId: string }) {
  const { resolveBet, isLoading } = useResolveBet();

  return (
    <button onClick={() => resolveBet(betId)} disabled={isLoading}>
      {isLoading ? "Resolving…" : "Resolve"}
    </button>
  );
}

```

## Summary

- **Deploy** the [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) contract using `genlayer deploy` to obtain the contract address
- **Configure** `NEXT_PUBLIC_CONTRACT_ADDRESS` in `frontend/.env.example` so `getContractAddress()` can read it
- **Wrap** the `genlayer-js` client in [`frontend/lib/contracts/FootballBets.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/contracts/FootballBets.ts) to handle type conversions and method calls
- **Hook** into the contract with `useFootballBetsContract()` and specialized React Query hooks for reads (`useBets`, `useLeaderboard`) and writes (`useCreateBet`, `useResolveBet`)
- **Render** using presentational components that consume the hooks, maintaining separation between blockchain logic and UI

## Frequently Asked Questions

### What is the FootballBets contract?

The **FootballBets contract** is a Python-based smart contract located at [`contracts/football_bets.py`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/contracts/football_bets.py) in the repository. It implements on-chain betting logic including creating bets, resolving markets, and tracking player points on the GenLayer network.

### How do I handle the contract address in Next.js?

Store the deployed address in the `NEXT_PUBLIC_CONTRACT_ADDRESS` environment variable. The [`frontend/lib/genlayer/client.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/frontend/lib/genlayer/client.ts) file provides `getContractAddress()` to safely retrieve this value at runtime, ensuring the application fails gracefully with a toast notification if the variable is undefined.

### How are transactions handled in the frontend?

Write operations follow a three-step pattern implemented in `useCreateBet()` and `useResolveBet()`: first calling `estimateWriteFeePreset` to calculate gas costs, then executing `client.writeContract` to submit the transaction, and finally waiting for the receipt before invalidating React Query caches to refresh the UI state.

### What dependencies are required for this integration?

The integration requires `genlayer-js` for blockchain connectivity, React Query (`@tanstack/react-query`) for server-state management, and a Next.js 19 environment with TypeScript. The [`FootballBets.ts`](https://github.com/genlayerlabs/genlayer-project-boilerplate/blob/main/FootballBets.ts) wrapper handles all data structure conversions between GenLayer's native Map types and JavaScript arrays.