💰
Megapot
  • Overview
    • About Megapot
    • How to play
    • How to provide liquidity
    • How to refer users
    • Earn as a liquidity provider
    • About the Team
    • Megapoints
  • Deep dive
    • Components
    • System Diagram
    • Smart Contract
    • Provably Fair
  • Developers
    • Start Here
    • React UI Kit (Beta)
      • MegapotProvider
      • Jackpot
      • useJackpot
    • Standalone Integration
      • Getting Started
      • Contract Functions
      • Jackpot Page
      • LP Deposit Page
      • History Page
    • Custom ERC-20 Jackpot
  • Developer Reference
    • Testnet & Mainnet
    • Contract Overview & Functions
    • Megapot API
    • Megapot Examples
      • Refer Tickets
      • Gifting Tickets
    • Brand Kit
  • Appendix
    • VIP Program
    • FAQ
    • About Megapot
  • Terms of Service
  • Privacy Policy
  • Responsible Gaming
Powered by GitBook
On this page
  • Jackpot Page
  • Jackpot Component
  • Buy Tickets
  • Countdown
  • Current Jackpot
  • Last Jackpot
  • Ticket Price
  • Winning Odds
  • Withdraw Winnings
  • API Route
  1. Developers
  2. Standalone Integration

Jackpot Page

PreviousContract FunctionsNextLP Deposit Page

Last updated 1 month ago

This example will guide you through displaying the jackpot's size, ticket price, odds of winning and a countdown to the next drawing along with a section for claiming winnings and the last jackpot results.

This references our Jackpot Demo Integration Github Repo. You can find the code for the demo integration or view the live demo .

Jackpot Page

This is the entry point for the jackpot page.

// /app/jackpot/page.tsx

import { JackpotComponent } from '@/components/jackpot-component';

export default function JackpotPage() {
    return <JackpotComponent />;
}

Jackpot Component

  • This component will display the jackpot's size, ticket price, odds of winning and a countdown to the next drawing.

  • This component will also display a form to purchase tickets.

// /app/components/jackpot-component.tsx
'use client';

import { Card, CardContent } from '@/components/ui/card';
import { getUsersInfo } from '@/lib/contract';
import { useEffect, useState } from 'react';
import { useAccount } from 'wagmi';
import { BuyTickets } from './jackpot-components/buy-tickets';
import { Countdown } from './jackpot-components/countdown';
import { CurrentJackpot } from './jackpot-components/current-jackpot';
import { LastJackpot } from './jackpot-components/last-jackpot';
import { TicketPrice } from './jackpot-components/ticket-price';
import { WinningOdds } from './jackpot-components/winning-odds';
import { WithdrawWinnings } from './jackpot-components/withdraw-winnings';

export function JackpotComponent() {
    const { address, isConnected } = useAccount();

    const [winningsAvailable, setWinningsAvailable] = useState<number>(0);

    useEffect(() => {
        if (!address) return;
        const fetchWinningsAvailable = async () => {
            const usersInfo = await getUsersInfo(address);
            if (!usersInfo) return;
            const winningsAvailable = usersInfo.winningsClaimable;
            setWinningsAvailable(Number(winningsAvailable));
        };
        fetchWinningsAvailable();
    }, [address]);
    return (
        <div className="space-y-6">
            {winningsAvailable > 0 && (
                <WithdrawWinnings winningsAvailable={winningsAvailable} />
            )}

            <CurrentJackpot />

            <Countdown />

            <Card className="bg-white rounded-xl shadow-sm">
                <CardContent className="p-6">
                    <div className="text-center">
                        <TicketPrice />
                        <WinningOdds />
                        {isConnected && address && (
                            <BuyTickets walletAddress={address} />
                        )}
                    </div>
                </CardContent>
            </Card>

            <LastJackpot />
        </div>
    );
}

Buy Tickets

This component will display the current jackpot's size.

// /app/components/jackpot-components/buy-tickets.tsx
import { BaseJackpotAbi } from '@/lib/abi';
import {
    CONTRACT_ADDRESS,
    ERC20_TOKEN_ADDRESS,
    REFERRER_ADDRESS,
} from '@/lib/constants';
import { getTokenAllowance, getTokenBalance } from '@/lib/contract';
import { useEffect, useState } from 'react';
import { parseAbi } from 'viem';
import { useAccount, useWriteContract } from 'wagmi';
import { ConnectButton } from '../connect-button';
import { Button } from '../ui/button';

export function BuyTickets({
    walletAddress,
}: {
    walletAddress: `0x${string}`;
}) {
    const { isConnected } = useAccount();

    const { data, error, isError, isPending, writeContract } =
        useWriteContract();
    const [ticketCount, setTicketCount] = useState<number>(1);
    const [walletFunded, setWalletFunded] = useState<boolean>(false);
    const [allowance, setAllowance] = useState<number>(0);

    const increment = () => setTicketCount((prev) => prev + 1);
    const decrement = () => setTicketCount((prev) => (prev > 1 ? prev - 1 : 1));

    useEffect(() => {
        // Get the balance of the wallet so we can check
        //  if it's funded
        const fetchWalletFunds = async () => {
            try {
                const balance = await getTokenBalance(walletAddress);
                if (balance) {
                    setWalletFunded(balance >= ticketCount * 10 ** 6);
                } else {
                    setWalletFunded(false);
                }
            } catch (error) {
                console.error('Error fetching wallet funded status:', error);
            }
        };

        // Fetch wallet funds every 5 seconds
        const intervalFunds = setInterval(fetchWalletFunds, 5000);
        fetchWalletFunds();

        // Get the allowance of the wallet so we can check
        //  if it's approved to buy tickets
        const fetchAllowance = async () => {
            try {
                const allowance = await getTokenAllowance(walletAddress);
                if (allowance) {
                    setAllowance(allowance);
                } else {
                    setAllowance(0);
                }
            } catch (error) {
                console.error('Error fetching allowance:', error);
            }
        };

        // Fetch allowance every 5 seconds
        const intervalAllowance = setInterval(fetchAllowance, 5000);
        fetchAllowance();

        return () => {
            clearInterval(intervalFunds);
            clearInterval(intervalAllowance);
        };
    }, [walletAddress]);

    const handleApproveToken = async () => {
        try {
            if (!walletAddress) {
                throw new Error('Wallet not connected');
            }

            const approveAbi = [
                'function approve(address spender, uint256 amount) returns (bool)',
            ];

            // Approve the token to be spent by the contract
            return writeContract?.({
                abi: parseAbi(approveAbi),
                address: ERC20_TOKEN_ADDRESS as `0x${string}`,
                functionName: 'approve',
                args: [CONTRACT_ADDRESS, walletAddress],
            });
        } catch (error) {
            console.error('Error approving token:', error);
        }
    };

    const handleBuyTicket = async () => {
        try {
            if (!walletAddress) {
                throw new Error('Wallet not connected');
            }

            const ticketCost = BigInt(1) * BigInt(10 ** 6);
            // This is YOUR wallet to collect referral fees
            const referrerAddress = REFERRER_ADDRESS;

            return writeContract?.({
                abi: BaseJackpotAbi,
                address: CONTRACT_ADDRESS as `0x${string}`,
                functionName: 'purchaseTickets',
                args: [referrerAddress, ticketCost, walletAddress],
            });
        } catch (error) {
            console.error('Error buying ticket:', error);
        }
    };

    return (
        <div className="flex flex-col items-center">
            <div className="flex items-center">
                <button
                    onClick={decrement}
                    className="bg-emerald-500 text-white px-2 hover:bg-emerald-600"
                >
                    -
                </button>
                <input
                    type="number"
                    value={ticketCount}
                    onChange={(e) =>
                        setTicketCount(Math.max(1, Number(e.target.value)))
                    }
                    className="mx-2 w-16 text-center border border-emerald-500 rounded"
                    min="1"
                    style={{ appearance: 'none', MozAppearance: 'textfield' }}
                />
                <button
                    onClick={increment}
                    className="bg-emerald-500 text-white px-2 hover:bg-emerald-600"
                >
                    +
                </button>
            </div>
            {isConnected &&
            walletFunded &&
            allowance >= ticketCount * 10 ** 6 ? (
                <Button
                    onClick={handleBuyTicket}
                    disabled={!isConnected || !walletFunded}
                    className="mt-2 w-full bg-emerald-500 hover:bg-emerald-600 text-white"
                >
                    Buy Ticket
                </Button>
            ) : isConnected &&
              walletFunded &&
              allowance < ticketCount * 10 ** 6 ? (
                <Button
                    onClick={handleApproveToken}
                    disabled={!isConnected}
                    className="mt-2 w-full bg-emerald-500 hover:bg-emerald-600 text-white"
                >
                    Approve USDC Token
                </Button>
            ) : isConnected && !walletFunded ? (
                <div className="mt-2 w-full bg-orange-500 hover:bg-orange-600 text-white">
                    Not enough USDC in wallet
                </div>
            ) : (
                <div className="mt-2 w-full bg-emerald-500 hover:bg-emerald-600 text-white">
                    <ConnectButton />
                </div>
            )}
        </div>
    );
}

Countdown

This component will display a countdown to the next drawing.

// /app/components/jackpot-components/countdown.tsx
import { getTimeRemaining } from '@/lib/contract';
import { useEffect, useState } from 'react';
import { Card, CardContent } from '../ui/card';

export function Countdown() {
    const [timeRemaining, setTimeRemaining] = useState<string | null>(null);

    useEffect(() => {
        // Fetch time remaining
        const fetchTimeRemaining = async () => {
            const timeRemaining = await getTimeRemaining();
            if (timeRemaining) {
                setTimeRemaining(timeRemaining.toString());
            }
        };
        fetchTimeRemaining();

        // Update time remaining every second
        const timer = setInterval(() => {
            setTimeRemaining((prev) => {
                if (!prev) return null;

                const seconds = parseInt(prev, 10);
                if (seconds <= 0) return '0';

                return (seconds - 1).toString();
            });
        }, 1000);

        return () => clearInterval(timer);
    }, []);

    return (
        <Card className="bg-white rounded-xl shadow-sm">
            <CardContent className="p-6">
                <div className="text-center">
                    <h2 className="text-lg font-medium text-gray-500 mb-2">
                        Time Remaining
                    </h2>
                    <p className="text-3xl font-bold">
                        {timeRemaining
                            ? formatTime(parseInt(timeRemaining, 10))
                            : '--:--:--'}
                    </p>
                </div>
            </CardContent>
        </Card>
    );
}

const formatTime = (totalSeconds: number) => {
    if (totalSeconds <= 0) return '00:00:00';

    const hours = Math.floor(totalSeconds / 3600) % 24;
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    const seconds = totalSeconds % 60;

    return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(
        2,
        '0'
    )}:${String(seconds).padStart(2, '0')}`;
};

Current Jackpot

This component will display the current jackpot's size.

import { getJackpotAmount } from '@/lib/contract';
import { useEffect, useState } from 'react';
import { Card, CardContent } from '../ui/card';

export function CurrentJackpot() {
    const [jackpotAmount, setJackpotAmount] = useState<number | undefined>(
        undefined
    );

    useEffect(() => {
        const fetchJackpotAmount = async () => {
            const jackpotAmount = await getJackpotAmount();
            setJackpotAmount(jackpotAmount);
        };
        fetchJackpotAmount();
    }, []);

    return (
        <Card className="bg-white rounded-xl shadow-sm">
            <CardContent className="p-6">
                <div className="text-center">
                    <h2 className="text-lg font-medium text-gray-500 mb-2">
                        Current Jackpot
                    </h2>
                    <p className="text-4xl font-bold text-emerald-500">
                        {jackpotAmount
                            ? `$${jackpotAmount.toFixed(2)}`
                            : 'Loading...'}
                    </p>
                </div>
            </CardContent>
        </Card>
    );
}

Last Jackpot

This component will display the last jackpot's results.

// /app/components/jackpot-components/last-jackpot.tsx

import { getLastJackpotResults } from '@/lib/contract';
import { useEffect, useState } from 'react';
import { zeroAddress } from 'viem';
import { Card, CardContent } from '../ui/card';

interface LastJackpotEvent {
    time: number;
    winner: string;
    winningTicket: number;
    winAmount: number;
    ticketsPurchasedTotalBps: number;
}
export function LastJackpot() {
    const [lastJackpot, setLastJackpot] = useState<
        LastJackpotEvent | undefined
    >(undefined);
    const [isLoading, setIsLoading] = useState(true);
    useEffect(() => {
        const fetchLastJackpot = async () => {
            const lastJackpot = await getLastJackpotResults();
            if (lastJackpot) {
                const lastJackpotEvent = {
                    time: lastJackpot.time,
                    winner: lastJackpot.winner,
                    winningTicket: lastJackpot.winningTicket,
                    winAmount: lastJackpot.winAmount,
                    ticketsPurchasedTotalBps:
                        lastJackpot.ticketsPurchasedTotalBps,
                };
                setLastJackpot(lastJackpotEvent);
                setIsLoading(false);
            }
        };
        fetchLastJackpot();
    }, []);
    return (
        <Card>
            <CardContent>
                <div className="flex flex-col items-center justify-center">
                    <h1 className="text-2xl font-bold">Last Jackpot</h1>
                    {lastJackpot && !isLoading ? (
                        <>
                            <div className="flex justify-between w-full">
                                <p className="text-lg text-emerald-500">
                                    Winner:
                                </p>
                                <p className="text-lg text-emerald-500">
                                    {lastJackpot.winner === zeroAddress
                                        ? 'LPs Won'
                                        : lastJackpot.winner}
                                </p>
                            </div>
                            <div className="flex justify-between w-full">
                                <p className="text-lg text-emerald-500">
                                    Win Amount:
                                </p>
                                <p className="text-lg text-emerald-500">
                                    {(lastJackpot.winAmount / 10 ** 6).toFixed(
                                        2
                                    )}{' '}
                                    USDC
                                </p>
                            </div>
                        </>
                    ) : (
                        <p className="text-lg">Loading...</p>
                    )}
                </div>
            </CardContent>
        </Card>
    );
}

Ticket Price

This component will display the current ticket price.

// /app/components/jackpot-components/ticket-price.tsx

import { getTicketPrice } from '@/lib/contract';
import { useEffect, useState } from 'react';

export function TicketPrice() {
    const [ticketPrice, setTicketPrice] = useState<string | null>(null);

    useEffect(() => {
        const fetchTicketPrice = async () => {
            const price = await getTicketPrice();
            setTicketPrice(price?.toString() || null);
        };
        fetchTicketPrice();
    }, []);

    return (
        <div>
            <h2 className="text-lg font-medium text-gray-500 mb-2">
                Ticket Price
            </h2>
            <p className="text-2xl font-bold mb-4">{ticketPrice} USDC</p>
        </div>
    );
}

Winning Odds

This component will display the odds of winning.

// /app/components/jackpot-components/winning-odds.tsx

import { getJackpotOdds } from '@/lib/contract';
import { useEffect, useState } from 'react';

export function WinningOdds() {
    const [jackpotOdds, setJackpotOdds] = useState<number | null>(null);

    useEffect(() => {
        const fetchJackpotOdds = async () => {
            const odds = await getJackpotOdds();
            setJackpotOdds(odds || null);
        };
        fetchJackpotOdds();
    }, []);

    return (
        <p className="text-sm text-gray-500 mb-4">
            Odds of winning: 1 in {Number(jackpotOdds).toFixed(2)}
        </p>
    );
}

Withdraw Winnings

This component will display the form to withdraw winnings.


import { BaseJackpotAbi } from '@/lib/abi';
import { CONTRACT_ADDRESS } from '@/lib/constants';
import { useWriteContract } from 'wagmi';
import { Button } from '../ui/button';
import { Card, CardContent } from '../ui/card';

export function WithdrawWinnings({
    winningsAvailable,
}: {
    winningsAvailable: number;
}) {
    const { writeContract } = useWriteContract();
    const handleWithdraw = async () => {
        writeContract?.({
            address: CONTRACT_ADDRESS as `0x${string}`,
            abi: BaseJackpotAbi,
            functionName: 'withdrawWinnings',
            args: [],
        });
    };
    return (
        <Card className="bg-white rounded-xl shadow-sm">
            <CardContent className="p-6">
                <div className="flex flex-col items-center justify-center">
                    <h1 className="text-2xl font-bold">Withdraw Winnings</h1>
                    <p className="text-lg">
                        {(winningsAvailable / 10 ** 6).toFixed(2)} USDC
                    </p>
                    <Button
                        onClick={handleWithdraw}
                        className="mt-2 w-full bg-emerald-500 hover:bg-emerald-600 text-white"
                    >
                        Withdraw
                    </Button>
                </div>
            </CardContent>
        </Card>
    );
}

API Route

This API route will fetch the past jackpot results from the contract using BaseScan's API.

// /app/api/past-jackpot.ts

import { CONTRACT_ADDRESS, CONTRACT_START_BLOCK, JACKPOT_RUN_TOPIC } from '@/lib/constants';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
    try {

        const urlParams = new URLSearchParams({
            module: 'logs',
            action: 'getLogs',
            address: CONTRACT_ADDRESS,
            topic0: JACKPOT_RUN_TOPIC,
            apikey: process.env.BASESCAN_API_KEY || '',
            fromBlock: CONTRACT_START_BLOCK.toString(),
        });

        const response = await fetch(
            `https://api.basescan.org/api?${urlParams.toString()}`,
            {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/json',
                },
            }
        );

        const data = await response.json();
        // reverse the data
        const reversedData = data.result.reverse();

        return NextResponse.json(reversedData);
    } catch (error) {
        console.error('Error fetching from BaseScan:', error);
        return NextResponse.json(
            { message: 'Error fetching data' },
            { status: 500 }
        );
    }
}
here
here