Full Code of ProofOfEstimate/uefa-poe for AI

main c5c6e7817568 cached
75 files
240.2 KB
62.9k tokens
53 symbols
1 requests
Download .txt
Showing preview only (259K chars total). Download the full file or copy to clipboard to get everything.
Repository: ProofOfEstimate/uefa-poe
Branch: main
Commit: c5c6e7817568
Files: 75
Total size: 240.2 KB

Directory structure:
gitextract_cllvgxn0/

├── .eslintrc.json
├── .gitignore
├── README.md
├── app/
│   ├── layout.tsx
│   ├── leaderboard/
│   │   └── page.tsx
│   ├── match/
│   │   └── [id]/
│   │       └── page.tsx
│   ├── page.tsx
│   └── providers.tsx
├── components/
│   ├── TableRow.tsx
│   ├── connect-wallet-button.tsx
│   ├── dark-mode-toggle.tsx
│   ├── footer.tsx
│   ├── market-stats.tsx
│   ├── match-card.tsx
│   ├── match-day.tsx
│   ├── nav-bar.tsx
│   ├── quick-tour-dialog.tsx
│   ├── sidenav.tsx
│   └── ui/
│       ├── avatar.tsx
│       ├── badge.tsx
│       ├── button.tsx
│       ├── card.tsx
│       ├── carousel.tsx
│       ├── dialog.tsx
│       ├── dropdown-menu.tsx
│       ├── input.tsx
│       ├── separator.tsx
│       ├── skeleton.tsx
│       ├── slider.tsx
│       ├── table.tsx
│       ├── tabs.tsx
│       ├── theme-provider.tsx
│       ├── toast.tsx
│       ├── toaster.tsx
│       ├── tooltip.tsx
│       └── use-toast.ts
├── components.json
├── contexts/
│   ├── AutoConnectProvider.tsx
│   ├── ContextProvider.tsx
│   └── NetworkConfigurationProvider.tsx
├── errors/
│   ├── NoUserAccountError.ts
│   └── WalletNotConnectedError.ts
├── hooks/
│   ├── mutations/
│   │   ├── useAirdropSol.tsx
│   │   ├── useCollectPoints.ts
│   │   ├── useMakeEstimate.tsx
│   │   ├── useRegisterUser.tsx
│   │   └── useUpdateEstimate.ts
│   ├── queries/
│   │   ├── useAllPolls.ts
│   │   ├── useAllPollsByUser.ts
│   │   ├── useAllUserAccounts.ts
│   │   ├── useAllUserPredictions.ts
│   │   ├── useEstimateUpdatesByPoll.ts
│   │   ├── usePollById.ts
│   │   ├── useUserAccount.ts
│   │   ├── useUserBonkBalance.ts
│   │   ├── useUserEstimateByPoll.ts
│   │   ├── useUserScore.ts
│   │   └── useUserSolBalance.ts
│   ├── states/
│   │   └── useTabStore.tsx
│   ├── useAnchorProgram.tsx
│   └── useIntersectionObserver.tsx
├── idl/
│   ├── poe.json
│   └── poe.ts
├── lib/
│   ├── dummyData.ts
│   ├── types.ts
│   └── utils.ts
├── next.config.js
├── next.config.mjs
├── package.json
├── postcss.config.mjs
├── styles/
│   └── globals.css
├── tailwind.config.ts
├── texts/
│   └── toastTitles.ts
├── tsconfig.json
└── utils/
    └── sendVersionedTransaction.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc.json
================================================
{
  "extends": "next/core-web-vitals"
}


================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts


================================================
FILE: README.md
================================================
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.


================================================
FILE: app/layout.tsx
================================================
import "@/styles/globals.css";
import { Inter as FontSans } from "next/font/google";

import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import Providers from "./providers";
import { NavBar } from "@/components/nav-bar";
import { Footer } from "@/components/footer";

const fontSans = FontSans({
  subsets: ["latin"],
  variable: "--font-sans",
});

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head />
      <body
        className={cn(
          "flex flex-col min-h-screen bg-background font-sans antialiased",
          fontSans.variable
        )}
      >
        <Providers>
          <NavBar />
          <Separator />
          {children}
        </Providers>
        <Footer />
      </body>
    </html>
  );
}


================================================
FILE: app/leaderboard/page.tsx
================================================
"use client";

import TableRow from "@/components/TableRow";
import { Skeleton } from "@/components/ui/skeleton";
import { useAllUserAccounts } from "@/hooks/queries/useAllUserAccounts";
import useAnchorProgram from "@/hooks/useAnchorProgram";
import { useWallet } from "@solana/wallet-adapter-react";
import React from "react";

const Leaderboard = () => {
  const program = useAnchorProgram();
  const { publicKey } = useWallet();
  const { data: userScores, isLoading: isScoresLoading } =
    useAllUserAccounts(program);

  const rank = userScores?.findIndex(
    (element) =>
      element.account.userAddress.toBase58() === publicKey?.toBase58()
  );

  const scores = userScores?.map((account, i) => {
    return {
      number: i + 1,
      name:
        account.account.userAddress.toBase58().slice(0, 4) +
        "..." +
        account.account.userAddress.toBase58().slice(-4),
      points: account.account.score.toFixed(2),
      isGold: i == 0,
      highlight: rank === i,
    };
  });

  return (
    <div className="h-full w-full min-h-screen flex items-center justify-center">
      <main className="w-[40rem] bg-white shadow-[0px_5px_15px_8px_#e4e7fb] flex flex-col items-center rounded-lg">
        <div
          id="header"
          className="w-full flex items-center justify-between p-10"
        >
          <h1 className="font-rubik text-[1.7rem] text-[#141a39] uppercase cursor-default">
            Ranking
          </h1>
        </div>
        <div id="leaderboard" className="w-full relative">
          <div className="w-full h-[5.5rem] bg-[#5c5be5] absolute top-[-0.5rem] shadow-[0px_15px_11px_-6px_#7a7a7d]">
            <div className="absolute bottom-[-0.8rem] left-[0.35rem] w-[1.5rem] h-[1.5rem] bg-[#5c5be5] rotate-45 z-[-1]"></div>
            <div className="absolute bottom-[-0.8rem] right-[0.35rem] w-[1.5rem] h-[1.5rem] bg-[#5c5be5] rotate-45 z-[-1]"></div>
          </div>
          {scores == undefined ? (
            <Skeleton />
          ) : (
            <table className="w-full border-collapse table-fixed text-[#141a39] cursor-default mb-20">
              {scores.map((row, index) => (
                <TableRow key={index} {...row} />
              ))}
            </table>
          )}
        </div>
      </main>
    </div>
  );
};

export default Leaderboard;


================================================
FILE: app/match/[id]/page.tsx
================================================
"use client";

import { Button } from "@/components/ui/button";
import useAnchorProgram from "@/hooks/useAnchorProgram";
import { allMatches } from "@/lib/dummyData";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { useRouter } from "next/navigation";
import { FaArrowLeftLong } from "react-icons/fa6";
import Image from "next/image";
import { Slider } from "@/components/ui/slider";
import {
  Area,
  Brush,
  CartesianGrid,
  ComposedChart,
  Line,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";
import { useEffect, useState } from "react";
import { usePollById } from "@/hooks/queries/usePollById";
import { Skeleton } from "@/components/ui/skeleton";
import { useUserEstimateByPoll } from "@/hooks/queries/useUserEstimateByPoll";
import clsx from "clsx";
import { TbLoader2 } from "react-icons/tb";
import { useMakeEstimate } from "@/hooks/mutations/useMakeEstimate";
import { useUpdateEstimate } from "@/hooks/mutations/useUpdateEstimate";
import { useCollectPoints } from "@/hooks/mutations/useCollectPoints";
import { useUserScore } from "@/hooks/queries/useUserScore";
import MarketStats from "@/components/market-stats";
import { useEstimateUpdatesByPoll } from "@/hooks/queries/useEstimateUpdatesByPoll";
import { Avatar, AvatarImage } from "@/components/ui/avatar";

const Match = ({ params }: { params: { id: string } }) => {
  const router = useRouter();
  const program = useAnchorProgram();
  const { connection } = useConnection();
  const wallet = useWallet();

  const matchId = Number.parseInt(params.id) - 1;
  const match = allMatches[matchId];

  const { data: poll, isLoading: isLoadingPoll } = usePollById(
    program,
    matchId,
    true
  );

  const { data: estimateUpdates } = useEstimateUpdatesByPoll(
    program,
    matchId,
    wallet.publicKey
  );

  const [brushStartIndex, setBrushStartIndex] = useState<number>();
  const [brushEndIndex, setBrushEndIndex] = useState<number>();

  const handleBrushChange = ({
    startIndex,
    endIndex,
  }: {
    startIndex?: number;
    endIndex?: number;
  }) => {
    setBrushStartIndex(startIndex);
    setBrushEndIndex(endIndex);
  };
  const {
    data: userEstimate,
    isError: isErrorEstimate,
    error: errorEstimate,
    isLoading: isLoadingEstimate,
  } = useUserEstimateByPoll(
    program,
    connection,
    wallet.publicKey,
    matchId,
    true
  );

  const [estimate, setEstimate] = useState(
    userEstimate !== null && userEstimate !== undefined
      ? (userEstimate.lowerEstimate + userEstimate.upperEstimate) / 2
      : undefined
  );

  const { mutate: submitEstimate, isPending: isSubmitting } = useMakeEstimate(
    program,
    connection,
    wallet
  );
  const { mutate: updateEstimate, isPending: isUpdating } = useUpdateEstimate(
    program,
    connection,
    wallet
  );
  const { mutate: collectPoints, isPending: isCollecting } = useCollectPoints(
    program,
    connection,
    wallet
  );

  const { data: userScore, isLoading: isLoadingScore } = useUserScore(
    program,
    connection,
    wallet.publicKey,
    matchId,
    true
  );

  useEffect(() => {
    if (userEstimate !== null && userEstimate !== undefined) {
      setEstimate(
        (userEstimate.lowerEstimate + userEstimate.upperEstimate) / 2
      );
    }
  }, [userEstimate]);

  const handleChange = (estimate: [number]) => {
    setEstimate(estimate[0]);
  };

  return (
    <main className="flex min-h-screen flex-col justify-start items-start px-4 sm:px-12 lg:px-16 py-4 sm:py-8 w-full">
      <Button
        onClick={() => router.back()}
        variant={"ghost"}
        className="p-0 hover:bg-transparent"
      >
        <FaArrowLeftLong />
      </Button>
      <div className="flex flex-col md:flex-row w-full gap-4">
        <div className="basis-2/3">
          <div className="text-lg font-medium mt-8">{match.date}</div>
          <div className="flex flex-col items-start gap-4 mt-2">
            <div className="flex items-center gap-4 w-60">
              <Image
                width={36}
                height={27}
                alt="Flag of team A"
                src={
                  match.logoA ? match.logoA : "https://via.placeholder.com/50"
                }
              />
              <div className="text-xl font-bold">{match.teamA}</div>
              <span className="text-lg font-bold ml-auto">{match.resultA}</span>
            </div>
            <div className="flex items-center gap-4 w-60">
              <Image
                width={36}
                height={27}
                alt="Flag of team B"
                src={
                  match.logoB ? match.logoB : "https://via.placeholder.com/50"
                }
              />
              <div className="text-xl font-bold">{match.teamB}</div>
              <span className="text-lg font-bold ml-auto">{match.resultB}</span>
            </div>
          </div>
          <div className="text-2xl font-bold mt-8">{`Will ${match.teamA} win against ${match.teamB}?`}</div>
          {/* {poll ? (
            <div className="text-2xl font-bold mt-8">{poll?.question}</div>
          ) : (
            <Skeleton />
          )} */}
          <div className="flex w-40 justify-between">
            <p className="block text-sm">Market Prediction:</p>
            <p className="text-sm font-bold">
              {poll && poll.collectiveEstimate !== null
                ? (poll.collectiveEstimate / 10000).toFixed(0) + "%"
                : "-"}
            </p>
          </div>
          <div className="flex w-40 justify-between">
            <p className="block text-sm">Your Prediction:</p>
            <p
              className={clsx(
                "text-sm font-bold",
                estimate !== userEstimate?.lowerEstimate
                  ? "dark:text-yellow-300"
                  : ""
              )}
            >
              {estimate !== undefined ? estimate + "%" : "-"}
            </p>
          </div>
          <div className="flex w-5/6 sm:w-1/2 md:w-2/3 gap-4 items-center my-4">
            <Slider
              className="hover:cursor-pointer"
              onValueChange={handleChange}
              value={[estimate !== undefined ? estimate : 50]}
              min={0}
              max={100}
              step={1}
              disabled={poll?.result !== null}
            />
            <Button
              variant={"secondary"}
              disabled={poll?.result !== null}
              size={"sm"}
              onClick={() => {
                if (userEstimate !== null && userEstimate !== undefined) {
                  setEstimate(
                    (userEstimate.lowerEstimate + userEstimate.upperEstimate) /
                      2
                  );
                } else {
                  setEstimate(undefined);
                }
              }}
            >
              Reset
            </Button>
          </div>
          {isLoadingPoll ? (
            <Skeleton className="w-40 h-9 rounded-md" />
          ) : poll?.result !== null ? (
            userScore === null || userScore === undefined || isLoadingScore ? (
              <div>
                {poll !== undefined ? (
                  poll.result ? (
                    <div className="text-primary font-bold">
                      {match.teamA} won!
                    </div>
                  ) : (
                    <div className="text-primary font-bold">
                      {match.teamA} did not win!
                    </div>
                  )
                ) : (
                  ""
                )}
              </div>
            ) : (
              <Button
                disabled={isCollecting}
                className="w-fit"
                onClick={() => collectPoints({ pollId: matchId })}
              >
                {isCollecting && (
                  <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
                )}
                Withdraw your funds
              </Button>
            )
          ) : userEstimate !== undefined && userEstimate !== null ? (
            <Button
              disabled={
                isUpdating ||
                (estimate === userEstimate.lowerEstimate &&
                  estimate === userEstimate.upperEstimate)
              }
              className="w-fit"
              onClick={() =>
                updateEstimate({
                  pollId: matchId,
                  lowerEstimate: estimate,
                  upperEstimate: estimate,
                })
              }
            >
              {isUpdating && (
                <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
              )}
              Update Estimate
            </Button>
          ) : (
            <div className="flex gap-4">
              <Button
                disabled={isSubmitting || estimate === undefined}
                className="font-bold rounded w-fit"
                onClick={() =>
                  submitEstimate({
                    pollId: matchId,
                    lowerEstimate: estimate,
                    upperEstimate: estimate,
                  })
                }
              >
                {isSubmitting && (
                  <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
                )}
                Bet 100 BONK
              </Button>
              <Avatar>
                <AvatarImage src={"/bonk_poe.png"} />
              </Avatar>
            </div>
          )}
        </div>
        <MarketStats
          matchId={matchId}
          profitScore={
            poll?.result !== null &&
            userEstimate !== null &&
            userEstimate !== undefined &&
            userEstimate.payoutScore
              ? ((userEstimate.payoutScore - 1) * 100).toFixed(2)
              : ""
          }
          reputationScore={
            poll?.result !== null &&
            userEstimate !== null &&
            userEstimate !== undefined &&
            userEstimate.reputationScore
              ? userEstimate.reputationScore?.toFixed(2)
              : ""
          }
        />
      </div>
      <div className="w-full border rounded-lg p-8 mt-8">
        <ResponsiveContainer width="100%" height={400}>
          <ComposedChart data={estimateUpdates}>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis
              dataKey="name"
              type="number"
              domain={[]}
              tickFormatter={(number) =>
                new Date(number * 1000).toLocaleString()
              }
              tickCount={10}
            />
            <YAxis />
            <Tooltip
              contentStyle={{
                backgroundColor: "hsl(var(--primary))",
              }}
              itemStyle={{ color: "hsl(var(--primary-foreground))" }}
              labelStyle={{ color: "hsl(var(--primary-foreground))" }}
              formatter={(value, name, prop) => {
                switch (name) {
                  case "confidenceInterval":
                    return [undefined, undefined];
                  case "estimate":
                    const interval =
                      prop.payload.confidenceInterval[1] -
                      prop.payload.confidenceInterval[0];
                    return [
                      Number(value).toFixed(2) +
                        "%  ± " +
                        (interval / 2).toFixed(1) +
                        "%",
                      "Market Prediction",
                    ];

                  default:
                    return [undefined, undefined];
                }
              }}
              labelFormatter={(label) =>
                new Date(label * 1000).toLocaleString()
              }
            />

            <Area
              type="monotone"
              dataKey="confidenceInterval"
              fill="hsl(var(--primary))"
              opacity={0.2}
              stroke="#ffffff00"
              activeDot={false}
              isAnimationActive={false}
            />
            <Line
              dot={false}
              type="linear"
              dataKey="estimate"
              stroke="hsl(var(--primary))"
              isAnimationActive={false}
            />
            <Brush
              dataKey="name"
              height={30}
              stroke="#8884d8"
              onChange={handleBrushChange}
              startIndex={brushStartIndex}
              endIndex={brushEndIndex}
              tickFormatter={(number) =>
                new Date(number * 1000).toLocaleString()
              }
            />
          </ComposedChart>
        </ResponsiveContainer>
      </div>
      <div className="mt-16 text-xl font-bold">Details</div>
      <div className="pt-4 pb-16 text-lg w-full sm:w-5/6 md:w-1/2">
        This market will resolve to true if {match.teamA} wins against{" "}
        {match.teamB}. In any other case, e.g. a draw, {match.teamB} wins or the
        game is canceled, this market will resolve to false.
      </div>
    </main>
  );
};

export default Match;


================================================
FILE: app/page.tsx
================================================
"use client";
import { MatchCard } from "@/components/match-card";
import { MatchDay } from "@/components/match-day";
import QuickTourDialog from "@/components/quick-tour-dialog";
import SideNav from "@/components/sidenav";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useRegisterUser } from "@/hooks/mutations/useRegisterUser";
import { useAllPolls } from "@/hooks/queries/useAllPolls";
import { useUserAccount } from "@/hooks/queries/useUserAccount";
import { useTabsStore } from "@/hooks/states/useTabStore";
import useAnchorProgram from "@/hooks/useAnchorProgram";
import {
  matchesFirstMatchDay,
  matchesSecondMatchday,
  matchesThirdMatchday,
  matchesRound16,
  matchesQuarterFinals,
  matchesSemiFinals,
  matchesFinal,
  allMatches,
} from "@/lib/dummyData";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { useEffect, useState } from "react";
import { FaChevronUp } from "react-icons/fa6";

export default function App() {
  const program = useAnchorProgram();
  const { connection } = useConnection();
  const wallet = useWallet();

  const { data: userAccount, isLoading: isScoreLoading } = useUserAccount(
    program,
    connection,
    wallet.publicKey
  );

  const { data: allPolls, isLoading: isAllPollsLoading } = useAllPolls(program);

  // const nextMarketIndex = allPolls?.filter((p) => {
  //   return p.result !== null;
  // }).length;

  // const comingMatches = allMatches.filter((match) => {
  //   const index = nextMarketIndex ?? 0;
  //   return Number.parseInt(match.id) - 1 > index;
  // });

  const [isVisible, setIsVisible] = useState(false);

  const tab = useTabsStore((state) => state.tab);
  const setTab = useTabsStore((state) => state.setTab);

  useEffect(() => {
    const handleScroll = () => {
      setIsVisible(window.scrollY > 1000);
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  const { mutate: registerUser } = useRegisterUser(program, connection, wallet);
  return (
    <div className="flex">
      <aside className="hidden md:block md:min-w-20 md:w-1/5">
        <SideNav />
      </aside>
      <main className="flex w-full min-h-screen flex-col items-center justify-between p-4 sm:p-12">
        <div className="block sm:hidden mb-2">
          <QuickTourDialog />
          {wallet.publicKey && userAccount === null && !isScoreLoading && (
            <Button className="sm:hidden ml-4" onClick={() => registerUser()}>
              Mint BONK
            </Button>
          )}
        </div>

        {/* <div className="text-4xl font-bold mb-4">Next Match</div> */}

        {/* {nextMarketIndex ? (
          <MatchCard match={allMatches[nextMarketIndex]} />
        ) : (
          <Skeleton className="h-[350px] w-[400px] rounded-xl" />
        )} */}
        <AllMatches />
        {/* <Tabs
          value={tab}
          onValueChange={setTab}
          className="w-full mx-auto my-8 text-center"
        >
          <TabsList>
            <TabsTrigger value="all">All Matches</TabsTrigger>
            <TabsTrigger value="coming">Coming Matches</TabsTrigger>
          </TabsList>
          <TabsContent value="all">
            <AllMatches />
          </TabsContent>
          <TabsContent value="coming">
            <MatchDay
              id="coming"
              title="Coming Matches"
              matches={comingMatches}
            />
          </TabsContent>
        </Tabs> */}
      </main>
      {isVisible && (
        <aside className="right-8 bottom-16 fixed">
          <Button
            variant="outline"
            size="icon"
            onClick={() => {
              window.scrollTo({
                top: 0,
                behavior: "smooth",
              });
            }}
          >
            <FaChevronUp />
          </Button>
        </aside>
      )}

      {wallet.publicKey && userAccount === null && !isScoreLoading && (
        <aside className="hidden sm:block right-4 pt-8 fixed">
          <Button onClick={() => registerUser()}>Mint BONK</Button>
        </aside>
      )}
    </div>
  );
}

const AllMatches = () => {
  return (
    <>
      <MatchDay
        id="matchday1"
        title="Matchday 1"
        matches={matchesFirstMatchDay}
      />
      <MatchDay
        id="matchday2"
        title="Matchday 2"
        matches={matchesSecondMatchday}
      />
      <MatchDay
        id="matchday3"
        title="Matchday 3"
        matches={matchesThirdMatchday}
      />
      <MatchDay id="round16" title="Round of 16" matches={matchesRound16} />
      <MatchDay
        id="quarter"
        title="Quarter Finals"
        matches={matchesQuarterFinals}
      />
      <MatchDay id="semi" title="Semi Finals" matches={matchesSemiFinals} />
      <MatchDay id="final" title="Final" matches={matchesFinal} />
    </>
  );
};


================================================
FILE: app/providers.tsx
================================================
"use client";

import { ThemeProvider } from "@/components/ui/theme-provider";
import { ContextProvider } from "@/contexts/ContextProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { useState } from "react";

export default function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            // With SSR, we usually want to set some default staleTime
            // above 0 to avoid refetching immediately on the client
            staleTime: 60 * 1000,
          },
        },
      })
  );

  return (
    <ContextProvider>
      <ThemeProvider
        attribute="class"
        defaultTheme="system"
        enableSystem
        disableTransitionOnChange
      >
        <QueryClientProvider client={queryClient}>
          {children}
          <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
      </ThemeProvider>
    </ContextProvider>
  );
}


================================================
FILE: components/TableRow.tsx
================================================
import { cn } from "@/lib/utils";
import Image from "next/image";
import React from "react";

interface TableRowProps {
  number: number;
  name: string;
  points: string;
  isGold?: boolean;
  highlight: boolean;
}

const TableRow: React.FC<TableRowProps> = ({
  number,
  name,
  points,
  isGold,
  highlight,
}) => {
  const hoverClasses =
    highlight && number !== 1
      ? "sm:scale-110 shadow-[0px_5px_15px_8px_#e4e7fb] !bg-yellow-300 "
      : "";

  return (
    <tr
      className={cn(
        "bg-white odd:bg-gray-100 transition-all duration-200 ease-in-out rounded-md",
        hoverClasses
      )}
    >
      <td className="h-[5rem] font-rubik text-[2.2rem] font-bold text-left">
        {number}
      </td>
      <td className="h-[5rem] font-rubik text-[1.2rem] text-left">{name}</td>
      <td className="h-[5rem] font-rubik text-[1.3rem] font-bold flex justify-end items-center">
        {points}
        {isGold && (
          <Image
            width={50}
            height={50}
            className="h-[3rem] ml-[1.5rem] hidden sm:block"
            src="https://github.com/malunaridev/Challenges-iCodeThis/blob/master/4-leaderboard/assets/gold-medal.png?raw=true"
            alt="gold medal"
          />
        )}
      </td>
    </tr>
  );
};

export default TableRow;


================================================
FILE: components/connect-wallet-button.tsx
================================================
"use client";

import { Button } from "./ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "./ui/dropdown-menu";
import {
  useAnchorWallet,
  useConnection,
  useWallet,
} from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
import { RiArrowDropDownLine } from "react-icons/ri";
import { TbCopy } from "react-icons/tb";
import { toast } from "./ui/use-toast";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "./ui/tooltip";
import useAnchorProgram from "@/hooks/useAnchorProgram";
import { Skeleton } from "./ui/skeleton";
import { useUserAccount } from "@/hooks/queries/useUserAccount";
import { useUserSolBalance } from "@/hooks/queries/useUserSolBalance";
import { FaWallet } from "react-icons/fa";
import { useUserBonkBalance } from "@/hooks/queries/useUserBonkBalance";
import { useRegisterUser } from "@/hooks/mutations/useRegisterUser";
import { useAllUserAccounts } from "@/hooks/queries/useAllUserAccounts";
import { useAirdropSol } from "@/hooks/mutations/useAirdropSol";

const ConnectWalletButton = () => {
  const wallet = useWallet();
  const anchorWallet = useAnchorWallet();
  const { disconnect, connected, publicKey } = useWallet();
  const program = useAnchorProgram();
  const { connection } = useConnection();
  const { setVisible } = useWalletModal();

  const { data: userAccount, isLoading: isAccountLoading } = useUserAccount(
    program,
    connection,
    publicKey
  );

  const { data: allScores, isLoading: isScoresLoading } =
    useAllUserAccounts(program);

  const rank = allScores?.findIndex(
    (element) =>
      element.account.userAddress.toBase58() === wallet.publicKey?.toBase58()
  );

  const { data: solBalance, isLoading: isSolBalanceLoading } =
    useUserSolBalance(connection, wallet?.publicKey ?? null);

  const { data: bonkBalance, isLoading: isBonkBalanceLoading } =
    useUserBonkBalance(program, connection, wallet?.publicKey ?? null);

  const { mutate: registerUser, isPending: isMintingBonkPending } =
    useRegisterUser(program, connection, wallet);

  const { mutate: airdropSol, isPending: isAirdropPending } = useAirdropSol(
    connection,
    wallet
  );

  if (!connected) {
    return (
      <Button
        onClick={() => {
          setVisible(true);
        }}
      >
        <div className="flex gap-2 items-center">
          <FaWallet />
          <div className="hidden sm:block">Connect wallet</div>
        </div>
      </Button>
    );
  }

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button>
          <div className="flex gap-2 items-center">
            <FaWallet className="sm:hidden" />
            <div className="hidden sm:block">Connected</div>
          </div>
          <RiArrowDropDownLine className="text-xl ml-2" />
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent className="w-56" align="end" forceMount>
        <DropdownMenuItem className="font-normal">
          {anchorWallet && (
            <TooltipProvider delayDuration={200}>
              <Tooltip>
                <TooltipTrigger>
                  <div
                    className="flex"
                    onClick={() => {
                      navigator.clipboard.writeText(
                        anchorWallet.publicKey?.toBase58() ?? ""
                      );
                      toast({ variant: "default", title: "Copied!" });
                    }}
                  >
                    <div>
                      {anchorWallet?.publicKey?.toBase58().slice(0, 4) +
                        "..." +
                        anchorWallet?.publicKey?.toBase58().slice(-4)}
                    </div>
                    <TbCopy className="ml-2" />
                  </div>
                </TooltipTrigger>
                <TooltipContent side="left">
                  <div>{wallet?.publicKey?.toBase58()}</div>
                </TooltipContent>
              </Tooltip>
            </TooltipProvider>
          )}
        </DropdownMenuItem>

        <DropdownMenuSeparator />
        <DropdownMenuGroup>
          <div className="border rounded-md py-2 px-1 my-2">
            <div className="flex flex-col mx-2 gap-2 text-sm">
              <div className="flex items-center justify-between">
                <div>Score:</div>
                {!isAccountLoading ? (
                  userAccount ? (
                    <div>{userAccount.score.toFixed(2)}</div>
                  ) : (
                    <div>100.0</div>
                  )
                ) : (
                  <Skeleton className="w-6 h-4 rounded-md" />
                )}
              </div>
              <div className="flex items-center justify-between">
                <div>Rank:</div>
                {!isScoresLoading ? (
                  rank !== undefined ? (
                    <div>{rank + 1}</div>
                  ) : (
                    <div>-</div>
                  )
                ) : (
                  <Skeleton className="w-6 h-4 rounded-md" />
                )}
              </div>
              <div className="flex items-center justify-between">
                <div>Bonk Balance:</div>
                {userAccount === null && !isAccountLoading ? (
                  <Button
                    size={"xs"}
                    disabled={isMintingBonkPending}
                    onClick={() => registerUser()}
                    className="flex text-center justify-center text-xs"
                  >
                    Mint
                  </Button>
                ) : !isBonkBalanceLoading ? (
                  <div>{bonkBalance?.toFixed(2)}</div>
                ) : (
                  <Skeleton className="w-6 h-4 rounded-md" />
                )}
              </div>
              <div className="flex items-center justify-between">
                <div>Sol Balance:</div>
                {solBalance !== undefined ? (
                  solBalance > 0.01 ? (
                    <div>{solBalance?.toFixed(2)}</div>
                  ) : (
                    <Button
                      size={"xs"}
                      disabled={isAirdropPending}
                      onClick={() => airdropSol()}
                      className="flex text-center justify-center text-xs"
                    >
                      Mint Sol
                    </Button>
                  )
                ) : (
                  <Skeleton className="w-6 h-4 rounded-md" />
                )}
              </div>
            </div>
          </div>
        </DropdownMenuGroup>
        {wallet && (
          <>
            <DropdownMenuSeparator />
            <DropdownMenuItem
              onClick={disconnect}
              className="hover:cursor-pointer"
            >
              Disconnect
            </DropdownMenuItem>
          </>
        )}
      </DropdownMenuContent>
    </DropdownMenu>
  );
};

export default ConnectWalletButton;


================================================
FILE: components/dark-mode-toggle.tsx
================================================
"use client";

import * as React from "react";
import { useTheme } from "next-themes";

import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { RxMoon, RxSun } from "react-icons/rx";

export function DarkModeToggle() {
  const { setTheme, theme } = useTheme();

  console.log("Theme", theme);

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon">
          <RxSun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <RxMoon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span className="sr-only">Toggle theme</span>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onClick={() => setTheme("light")}>
          Light
        </DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("dark")}>
          Dark
        </DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("system")}>
          System
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}


================================================
FILE: components/footer.tsx
================================================
import Link from "next/link";
import { FaTelegramPlane } from "react-icons/fa";
import { FaDiscord, FaXTwitter } from "react-icons/fa6";

export const Footer = () => {
  return (
    <footer className="bg-gray-800 text-white py-2 bottom-0 w-full">
      <div className="flex justify-center space-x-8">
        <Link
          target="_blank"
          rel="noopener noreferrer"
          href={"https://discord.gg/HNxstUVC"}
        >
          <FaDiscord size={40} />
        </Link>
        <Link
          target="_blank"
          rel="noopener noreferrer"
          href={"https://twitter.com/ProofOfEstimate"}
        >
          <FaXTwitter size={40} />
        </Link>
        <Link
          target="_blank"
          rel="noopener noreferrer"
          href={"https://t.me/+19Jfbq7Pl1phNWIy"}
        >
          <FaTelegramPlane size={40} />
        </Link>
      </div>
      <div className="text-center text-sm">
        &copy; {new Date().getFullYear()} Poe. All rights reserved.
      </div>
    </footer>
  );
};


================================================
FILE: components/market-stats.tsx
================================================
"use client";

import useAnchorProgram from "@/hooks/useAnchorProgram";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
import { Skeleton } from "./ui/skeleton";
import { usePollById } from "@/hooks/queries/usePollById";
import { Separator } from "./ui/separator";

const MarketStats = ({
  matchId,
  profitScore,
  reputationScore,
}: {
  matchId: number;
  profitScore: string;
  reputationScore: string;
}) => {
  const program = useAnchorProgram();

  const { data: poll, isLoading: isLoadingPoll } = usePollById(
    program,
    matchId,
    true
  );
  return (
    <Card className="h-fit">
      <CardHeader>
        <CardTitle className="text-lg">Additional Data 📊</CardTitle>
      </CardHeader>
      <CardContent>
        {poll ? (
          <div className="">
            {poll.numForecasters.toString()} participant
            {poll.numForecasters.toNumber() !== 1 ? "s" : ""}
          </div>
        ) : (
          <Skeleton className="w-8 h-5 rounded-md" />
        )}
        <Separator className="my-2" />
        <div className="flex gap-4">
          <p>Your profit:</p>
          <p>{profitScore}</p>
        </div>
        <div className="flex gap-4">
          <p>Your score:</p>
          <p>{reputationScore}</p>
        </div>
      </CardContent>
    </Card>
  );
};

export default MarketStats;


================================================
FILE: components/match-card.tsx
================================================
"use client";

import { Match } from "@/lib/dummyData";
import { Button } from "./ui/button";
import { Slider } from "./ui/slider";
import Image from "next/image";
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "./ui/card";
import { Separator } from "./ui/separator";
import Link from "next/link";
import { RiArrowRightDoubleLine } from "react-icons/ri";
import useAnchorProgram from "@/hooks/useAnchorProgram";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { useUserEstimateByPoll } from "@/hooks/queries/useUserEstimateByPoll";
import { useMakeEstimate } from "@/hooks/mutations/useMakeEstimate";
import { useUserScore } from "@/hooks/queries/useUserScore";
import { useUpdateEstimate } from "@/hooks/mutations/useUpdateEstimate";
import { useCollectPoints } from "@/hooks/mutations/useCollectPoints";
import { useEffect, useRef, useState } from "react";
import { usePollById } from "@/hooks/queries/usePollById";
import { Skeleton } from "./ui/skeleton";
import { TbLoader2 } from "react-icons/tb";
import useIntersectionObserver from "@/hooks/useIntersectionObserver";
import clsx from "clsx";
import { Badge } from "./ui/badge";

export const MatchCard = ({ match }: { match: Match }) => {
  const program = useAnchorProgram();
  const { connection } = useConnection();
  const wallet = useWallet();

  const ref = useRef<HTMLDivElement | null>(null);
  const isVisible =
    useIntersectionObserver(ref, { threshold: 0.1 }) &&
    match.teamA !== "tbd" &&
    match.teamB !== "tbd";

  const matchId = Number.parseInt(match.id) - 1;
  const {
    data: userEstimate,
    isError: isErrorEstimate,
    error: errorEstimate,
    isLoading: isLoadingEstimate,
  } = useUserEstimateByPoll(
    program,
    connection,
    wallet.publicKey,
    matchId,
    isVisible
  );

  const {
    data: poll,
    isLoading: isLoadingPoll,
    isError: isErrorPoll,
    error: errorPoll,
  } = usePollById(program, matchId, isVisible);

  const { data: userScore, isLoading: isLoadingScore } = useUserScore(
    program,
    connection,
    wallet.publicKey,
    matchId,
    isVisible
  );

  const { mutate: submitEstimate, isPending: isSubmitting } = useMakeEstimate(
    program,
    connection,
    wallet
  );
  const { mutate: updateEstimate, isPending: isUpdating } = useUpdateEstimate(
    program,
    connection,
    wallet
  );
  const { mutate: collectPoints, isPending: isCollecting } = useCollectPoints(
    program,
    connection,
    wallet
  );

  const [estimate, setEstimate] = useState(
    userEstimate !== null && userEstimate !== undefined
      ? (userEstimate.lowerEstimate + userEstimate.upperEstimate) / 2
      : undefined
  );

  useEffect(() => {
    if (userEstimate !== null && userEstimate !== undefined) {
      setEstimate(
        (userEstimate.lowerEstimate + userEstimate.upperEstimate) / 2
      );
    }
  }, [userEstimate]);

  const handleChange = (estimate: [number]) => {
    setEstimate(estimate[0]);
  };

  const isLive = poll?.hasStarted && poll?.result == null;

  return (
    <Card ref={ref} className="w-full mx-4 sm:mx-0 sm:w-[25rem]">
      <CardHeader>
        <CardDescription>
          <div className="flex justify-between">
            {match.date}
            {isLive && (
              <Badge>
                <span className="block w-[6px] h-[6px] bg-red-500 rounded-full drop-shadow-[0_0px_8px_#F7931A10)] animate-ping mr-2"></span>
                Live
              </Badge>
            )}
          </div>
        </CardDescription>
        <CardTitle className="flex w-1/2 gap-4 items-center">
          <Image
            width={36}
            height={27}
            alt="Flag of team A"
            src={match.logoA ? match.logoA : "https://via.placeholder.com/50"}
          />
          <span className="text-lg font-bold">{match.teamA}</span>
          <span className="ml-auto">{match.resultA}</span>
        </CardTitle>
        <CardTitle className="flex w-1/2 gap-4 pt-4 items-center">
          <Image
            width={36}
            height={27}
            alt="Flag of team B"
            src={match.logoB ? match.logoB : "https://via.placeholder.com/50"}
          />
          <span className="text-lg font-bold">{match.teamB}</span>
          <span className="ml-auto">{match.resultB}</span>
        </CardTitle>
      </CardHeader>
      <CardContent>
        <Separator className="mb-4" />
        <p className="block text-md font-semibold mb-2">
          Prob. that {match.teamA} wins
        </p>
        <div className="flex w-3/5 sm:w-1/2 justify-between">
          <p className="block text-sm">Market Prediction:</p>
          <p className="text-sm font-bold">
            {poll && poll.collectiveEstimate !== null
              ? (poll.collectiveEstimate / 10000).toFixed(0) + "%"
              : "-"}
          </p>
        </div>
        <div className="flex w-3/5 sm:w-1/2 justify-between">
          <p className="block text-sm">Your Prediction:</p>
          <p
            className={clsx(
              "text-sm font-bold",
              estimate !== userEstimate?.lowerEstimate
                ? "dark:text-yellow-300"
                : ""
            )}
          >
            {estimate !== undefined ? estimate + "%" : "-"}
          </p>
        </div>
        <div className="flex gap-4 items-center">
          <Slider
            onValueChange={handleChange}
            value={[estimate !== undefined ? estimate : 50]}
            max={100}
            step={1}
            className="my-4"
            disabled={poll?.result !== null}
          />
          <Button
            variant={"secondary"}
            disabled={poll?.result !== null}
            size={"sm"}
            onClick={() => {
              if (userEstimate !== null && userEstimate !== undefined) {
                setEstimate(
                  (userEstimate.lowerEstimate + userEstimate.upperEstimate) / 2
                );
              } else {
                setEstimate(undefined);
              }
            }}
          >
            Reset
          </Button>
        </div>
      </CardContent>
      <CardFooter>
        <div className="w-full flex gap-4 items-center justify-between">
          {isLoadingPoll ? (
            <Skeleton className="w-3/5 h-9 rounded-md" />
          ) : poll?.result !== null ? (
            userScore === null || userScore === undefined || isLoadingScore ? (
              <div>
                {poll !== undefined ? (
                  poll.result ? (
                    <div className="text-primary font-bold">
                      {match.teamA} won!
                    </div>
                  ) : (
                    <div className="text-primary font-bold">
                      {match.teamA} did not win!
                    </div>
                  )
                ) : (
                  ""
                )}
              </div>
            ) : (
              <Button
                disabled={isCollecting}
                className="w-3/5"
                onClick={() => collectPoints({ pollId: matchId })}
              >
                {isCollecting && (
                  <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
                )}
                Withdraw your funds
              </Button>
            )
          ) : userEstimate !== undefined && userEstimate !== null ? (
            <Button
              disabled={
                isUpdating ||
                (estimate === userEstimate.lowerEstimate &&
                  estimate === userEstimate.upperEstimate)
              }
              className="w-3/5"
              onClick={() =>
                updateEstimate({
                  pollId: matchId,
                  lowerEstimate: estimate,
                  upperEstimate: estimate,
                })
              }
            >
              {isUpdating && (
                <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
              )}
              Update Estimate
            </Button>
          ) : (
            <Button
              disabled={isSubmitting || estimate === undefined}
              className="font-bold rounded w-full "
              onClick={() =>
                submitEstimate({
                  pollId: matchId,
                  lowerEstimate: estimate,
                  upperEstimate: estimate,
                })
              }
            >
              {isSubmitting && (
                <TbLoader2 className="mr-2 h-4 w-4 animate-spin" />
              )}
              Bet 100 BONK
            </Button>
          )}

          <Button size={"sm"} variant={"ghost"} asChild>
            <Link className="text-xs" href={"/match/" + match.id}>
              <RiArrowRightDoubleLine className="mr-2" /> Details
            </Link>
          </Button>
        </div>
      </CardFooter>
    </Card>
  );
};


================================================
FILE: components/match-day.tsx
================================================
import { Match } from "@/lib/dummyData";
import { MatchCard } from "./match-card";

export const MatchDay = ({
  id,
  title,
  matches,
}: {
  id: string;
  title: string;
  matches: Match[];
}) => {
  return (
    <div className="mb-8">
      <div className="flex gap-4 items-center mb-8 justify-center">
        <h2 id={id} className="text-4xl font-bold text-center">
          {title}
        </h2>
      </div>
      <div className={`flex flex-wrap justify-center gap-5 md:gap-x-10`}>
        {matches.map((match: Match) => (
          <MatchCard key={match.id} match={match} />
        ))}
      </div>
    </div>
  );
};


================================================
FILE: components/nav-bar.tsx
================================================
import Link from "next/link";
import ConnectWalletButton from "./connect-wallet-button";
import Image from "next/image";
import QuickTourDialog from "./quick-tour-dialog";
import { DarkModeToggle } from "./dark-mode-toggle";
require("@solana/wallet-adapter-react-ui/styles.css");

export const NavBar = () => {
  return (
    <header>
      <div className="border-b px-4 py-4">
        <div className="flex h-16 items-center gap-1 sm:gap-8">
          {/* Keep it as comment because I think it will be used soon */}
          {/* <MainNav /> */}
          {/* <MobileNav /> */}

          <Link
            href="/"
            className="text-sm font-medium hover:cursor-pointer"
            prefetch={false}
          >
            <p className="text-lg">UEFA 2024</p>
            <span className="sm:flex hidden items-center gap-1 text-xs">
              powered by
              <Image width={20} height={20} alt="Logo" src={"/Poe.png"} />
            </span>
          </Link>
          <Link
            href="/leaderboard"
            className="ml-auto text-sm font-medium hover:bg-yellow-500 p-2 hover:bg-opacity-20 rounded-md"
            prefetch={false}
          >
            Leaderboard
          </Link>
          <div className="flex items-center md:space-x-4">
            <ConnectWalletButton />
          </div>
          <DarkModeToggle />
        </div>
      </div>
    </header>
  );
};


================================================
FILE: components/quick-tour-dialog.tsx
================================================
"use client";

import useAnchorProgram from "@/hooks/useAnchorProgram";
import { Button } from "./ui/button";
import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from "./ui/carousel";
import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { useUserAccount } from "@/hooks/queries/useUserAccount";
import { useRegisterUser } from "@/hooks/mutations/useRegisterUser";

const QuickTourDialog = () => {
  const program = useAnchorProgram();
  const { connection } = useConnection();
  const wallet = useWallet();
  const { data: userAccount, isLoading: isScoreLoading } = useUserAccount(
    program,
    connection,
    wallet.publicKey
  );

  const { mutate: registerUser } = useRegisterUser(program, connection, wallet);

  return (
    <Dialog>
      <DialogTrigger>
        <Button asChild>
          <div>Quick Tour</div>
        </Button>
      </DialogTrigger>
      <DialogContent>
        <Carousel>
          <CarouselContent>
            <CarouselItem>
              <div className="flex flex-col gap-4">
                <div className="text-lg md:text-xl font-bold">
                  Predict Probabilities, not just outcomes!
                </div>
                <div className="w-5/6 sm:w-full">
                  With Poe, you predict how likely something is to happen. Your
                  betting stake goes into a pool. Poe uses a special system to
                  score your forecast and determine your payout.
                </div>
              </div>
            </CarouselItem>
            <CarouselItem>
              <div className="flex flex-col gap-4">
                <div className="text-lg md:text-xl font-bold">
                  Always in the Game!
                </div>
                <div className="w-5/6 sm:w-full">
                  Update your beliefs as you learn more and the match
                  progresses. Poe will calculate a time-averaged score after the
                  end of the match which determines your payout.
                </div>
              </div>
            </CarouselItem>
            <CarouselItem>
              <div className="flex flex-col gap-4">
                <div className="text-lg md:text-xl font-bold">
                  Get rewarded for your insights!
                </div>
                <div className="w-5/6 sm:w-full">
                  Just submit your beliefs. If you are right, you make a profit
                  in expectation. Mint your 4000 BONK token to get started!
                </div>
                {userAccount === null && !isScoreLoading ? (
                  <Button className="w-1/2" onClick={() => registerUser()}>
                    Mint BONK
                  </Button>
                ) : (
                  <Button className="w-1/2" disabled>
                    Bonk already minted!
                  </Button>
                )}
              </div>
            </CarouselItem>
          </CarouselContent>
          <CarouselPrevious />
          <CarouselNext />
        </Carousel>
      </DialogContent>
    </Dialog>
  );
};

export default QuickTourDialog;


================================================
FILE: components/sidenav.tsx
================================================
"use client";

import QuickTourDialog from "./quick-tour-dialog";

export default function SideNav() {
  return (
    <div className="flex flex-col items-start py-4 px-10 gap-8 mt-4 pb-40 fixed h-full overflow-scroll">
      <QuickTourDialog />
      <SideNavItem id="matchday1" title="Matchday 1" />
      <SideNavItem id="matchday2" title="Matchday 2" />
      <SideNavItem id="matchday3" title="Matchday 3" />
      <SideNavItem id="round16" title="Round of 16" />
      <SideNavItem id="quarter" title="Quarter Finals" />
      <SideNavItem id="semi" title="Semi Finals" />
      <SideNavItem id="final" title="Finals" />
    </div>
  );
}

function scrollTo(id: string) {
  const element = document.getElementById(id);
  element?.scrollIntoView({ behavior: "smooth" });
}

const SideNavItem = ({ id, title }: { id: string; title: string }) => {
  return (
    <div
      onClick={() => scrollTo(id)}
      className="text-lg p-2 rounded-md font-semibold hover:cursor-pointer hover:bg-slate-300"
    >
      {title}
    </div>
  );
};


================================================
FILE: components/ui/avatar.tsx
================================================
"use client"

import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"

import { cn } from "@/lib/utils"

const Avatar = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Root
    ref={ref}
    className={cn(
      "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
      className
    )}
    {...props}
  />
))
Avatar.displayName = AvatarPrimitive.Root.displayName

const AvatarImage = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Image>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Image
    ref={ref}
    className={cn("aspect-square h-full w-full", className)}
    {...props}
  />
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName

const AvatarFallback = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Fallback>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Fallback
    ref={ref}
    className={cn(
      "flex h-full w-full items-center justify-center rounded-full bg-muted",
      className
    )}
    {...props}
  />
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName

export { Avatar, AvatarImage, AvatarFallback }


================================================
FILE: components/ui/badge.tsx
================================================
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const badgeVariants = cva(
  "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
  {
    variants: {
      variant: {
        default:
          "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
        secondary:
          "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
        destructive:
          "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
        outline: "text-foreground",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

export interface BadgeProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, ...props }: BadgeProps) {
  return (
    <div className={cn(badgeVariants({ variant }), className)} {...props} />
  )
}

export { Badge, badgeVariants }


================================================
FILE: components/ui/button.tsx
================================================
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
        outline:
          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-9 px-4 py-2",
        xs: "h-5 rounded-md px-2 text-xs",
        sm: "h-8 rounded-md px-3 text-xs",
        lg: "h-10 rounded-md px-8",
        icon: "h-9 w-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button";
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  }
);
Button.displayName = "Button";

export { Button, buttonVariants };


================================================
FILE: components/ui/card.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

const Card = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "rounded-xl border bg-card text-card-foreground shadow",
      className
    )}
    {...props}
  />
))
Card.displayName = "Card"

const CardHeader = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn("flex flex-col space-y-1.5 p-6", className)}
    {...props}
  />
))
CardHeader.displayName = "CardHeader"

const CardTitle = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
  <h3
    ref={ref}
    className={cn("font-semibold leading-none tracking-tight", className)}
    {...props}
  />
))
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
  <p
    ref={ref}
    className={cn("text-sm text-muted-foreground", className)}
    {...props}
  />
))
CardDescription.displayName = "CardDescription"

const CardContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"

const CardFooter = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn("flex items-center p-6 pt-0", className)}
    {...props}
  />
))
CardFooter.displayName = "CardFooter"

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }


================================================
FILE: components/ui/carousel.tsx
================================================
"use client";

import * as React from "react";
import { ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
import useEmblaCarousel, {
  type UseEmblaCarouselType,
} from "embla-carousel-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";

type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];

type CarouselProps = {
  opts?: CarouselOptions;
  plugins?: CarouselPlugin;
  orientation?: "horizontal" | "vertical";
  setApi?: (api: CarouselApi) => void;
};

type CarouselContextProps = {
  carouselRef: ReturnType<typeof useEmblaCarousel>[0];
  api: ReturnType<typeof useEmblaCarousel>[1];
  scrollPrev: () => void;
  scrollNext: () => void;
  canScrollPrev: boolean;
  canScrollNext: boolean;
} & CarouselProps;

const CarouselContext = React.createContext<CarouselContextProps | null>(null);

function useCarousel() {
  const context = React.useContext(CarouselContext);

  if (!context) {
    throw new Error("useCarousel must be used within a <Carousel />");
  }

  return context;
}

const Carousel = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(
  (
    {
      orientation = "horizontal",
      opts,
      setApi,
      plugins,
      className,
      children,
      ...props
    },
    ref
  ) => {
    const [carouselRef, api] = useEmblaCarousel(
      {
        ...opts,
        axis: orientation === "horizontal" ? "x" : "y",
      },
      plugins
    );
    const [canScrollPrev, setCanScrollPrev] = React.useState(false);
    const [canScrollNext, setCanScrollNext] = React.useState(false);

    const onSelect = React.useCallback((api: CarouselApi) => {
      if (!api) {
        return;
      }

      setCanScrollPrev(api.canScrollPrev());
      setCanScrollNext(api.canScrollNext());
    }, []);

    const scrollPrev = React.useCallback(() => {
      api?.scrollPrev();
    }, [api]);

    const scrollNext = React.useCallback(() => {
      api?.scrollNext();
    }, [api]);

    const handleKeyDown = React.useCallback(
      (event: React.KeyboardEvent<HTMLDivElement>) => {
        if (event.key === "ArrowLeft") {
          event.preventDefault();
          scrollPrev();
        } else if (event.key === "ArrowRight") {
          event.preventDefault();
          scrollNext();
        }
      },
      [scrollPrev, scrollNext]
    );

    React.useEffect(() => {
      if (!api || !setApi) {
        return;
      }

      setApi(api);
    }, [api, setApi]);

    React.useEffect(() => {
      if (!api) {
        return;
      }

      onSelect(api);
      api.on("reInit", onSelect);
      api.on("select", onSelect);

      return () => {
        api?.off("select", onSelect);
      };
    }, [api, onSelect]);

    return (
      <CarouselContext.Provider
        value={{
          carouselRef,
          api: api,
          opts,
          orientation:
            orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
          scrollPrev,
          scrollNext,
          canScrollPrev,
          canScrollNext,
        }}
      >
        <div
          ref={ref}
          onKeyDownCapture={handleKeyDown}
          className={cn("relative", className)}
          role="region"
          aria-roledescription="carousel"
          {...props}
        >
          {children}
        </div>
      </CarouselContext.Provider>
    );
  }
);
Carousel.displayName = "Carousel";

const CarouselContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const { carouselRef, orientation } = useCarousel();

  return (
    <div ref={carouselRef} className="overflow-hidden">
      <div
        ref={ref}
        className={cn(
          "flex",
          orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
          className
        )}
        {...props}
      />
    </div>
  );
});
CarouselContent.displayName = "CarouselContent";

const CarouselItem = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
  const { orientation } = useCarousel();

  return (
    <div
      ref={ref}
      role="group"
      aria-roledescription="slide"
      className={cn(
        "min-w-0 shrink-0 grow-0 basis-full",
        orientation === "horizontal" ? "pl-4" : "pt-4",
        className
      )}
      {...props}
    />
  );
});
CarouselItem.displayName = "CarouselItem";

const CarouselPrevious = React.forwardRef<
  HTMLButtonElement,
  React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
  const { orientation, scrollPrev, canScrollPrev } = useCarousel();

  return (
    <Button
      ref={ref}
      variant={variant}
      size={size}
      className={cn(
        "absolute  h-8 w-8 rounded-full",
        orientation === "horizontal"
          ? "-left-12 top-1/2 -translate-y-1/2"
          : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
        className
      )}
      disabled={!canScrollPrev}
      onClick={scrollPrev}
      {...props}
    >
      <ArrowLeftIcon className="h-4 w-4" />
      <span className="sr-only">Previous slide</span>
    </Button>
  );
});
CarouselPrevious.displayName = "CarouselPrevious";

const CarouselNext = React.forwardRef<
  HTMLButtonElement,
  React.ComponentProps<typeof Button>
>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
  const { orientation, scrollNext, canScrollNext } = useCarousel();

  return (
    <Button
      ref={ref}
      variant={variant}
      size={size}
      className={cn(
        "absolute h-8 w-8 rounded-full",
        orientation === "horizontal"
          ? "-right-12 top-1/2 -translate-y-1/2"
          : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
        className
      )}
      disabled={!canScrollNext}
      onClick={scrollNext}
      {...props}
    >
      <ArrowRightIcon className="h-4 w-4" />
      <span className="sr-only">Next slide</span>
    </Button>
  );
});
CarouselNext.displayName = "CarouselNext";

export {
  type CarouselApi,
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselPrevious,
  CarouselNext,
};


================================================
FILE: components/ui/dialog.tsx
================================================
"use client"

import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { Cross2Icon } from "@radix-ui/react-icons"

import { cn } from "@/lib/utils"

const Dialog = DialogPrimitive.Root

const DialogTrigger = DialogPrimitive.Trigger

const DialogPortal = DialogPrimitive.Portal

const DialogClose = DialogPrimitive.Close

const DialogOverlay = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Overlay>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Overlay
    ref={ref}
    className={cn(
      "fixed inset-0 z-50 bg-black/80  data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
      className
    )}
    {...props}
  />
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <DialogPrimitive.Content
      ref={ref}
      className={cn(
        "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
        className
      )}
      {...props}
    >
      {children}
      <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
        <Cross2Icon className="h-4 w-4" />
        <span className="sr-only">Close</span>
      </DialogPrimitive.Close>
    </DialogPrimitive.Content>
  </DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName

const DialogHeader = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) => (
  <div
    className={cn(
      "flex flex-col space-y-1.5 text-center sm:text-left",
      className
    )}
    {...props}
  />
)
DialogHeader.displayName = "DialogHeader"

const DialogFooter = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) => (
  <div
    className={cn(
      "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
      className
    )}
    {...props}
  />
)
DialogFooter.displayName = "DialogFooter"

const DialogTitle = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Title>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Title
    ref={ref}
    className={cn(
      "text-lg font-semibold leading-none tracking-tight",
      className
    )}
    {...props}
  />
))
DialogTitle.displayName = DialogPrimitive.Title.displayName

const DialogDescription = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Description>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Description
    ref={ref}
    className={cn("text-sm text-muted-foreground", className)}
    {...props}
  />
))
DialogDescription.displayName = DialogPrimitive.Description.displayName

export {
  Dialog,
  DialogPortal,
  DialogOverlay,
  DialogTrigger,
  DialogClose,
  DialogContent,
  DialogHeader,
  DialogFooter,
  DialogTitle,
  DialogDescription,
}


================================================
FILE: components/ui/dropdown-menu.tsx
================================================
"use client"

import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import {
  CheckIcon,
  ChevronRightIcon,
  DotFilledIcon,
} from "@radix-ui/react-icons"

import { cn } from "@/lib/utils"

const DropdownMenu = DropdownMenuPrimitive.Root

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger

const DropdownMenuGroup = DropdownMenuPrimitive.Group

const DropdownMenuPortal = DropdownMenuPrimitive.Portal

const DropdownMenuSub = DropdownMenuPrimitive.Sub

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup

const DropdownMenuSubTrigger = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
    inset?: boolean
  }
>(({ className, inset, children, ...props }, ref) => (
  <DropdownMenuPrimitive.SubTrigger
    ref={ref}
    className={cn(
      "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
      inset && "pl-8",
      className
    )}
    {...props}
  >
    {children}
    <ChevronRightIcon className="ml-auto h-4 w-4" />
  </DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName =
  DropdownMenuPrimitive.SubTrigger.displayName

const DropdownMenuSubContent = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
  <DropdownMenuPrimitive.SubContent
    ref={ref}
    className={cn(
      "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
      className
    )}
    {...props}
  />
))
DropdownMenuSubContent.displayName =
  DropdownMenuPrimitive.SubContent.displayName

const DropdownMenuContent = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
  <DropdownMenuPrimitive.Portal>
    <DropdownMenuPrimitive.Content
      ref={ref}
      sideOffset={sideOffset}
      className={cn(
        "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
        className
      )}
      {...props}
    />
  </DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName

const DropdownMenuItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
    inset?: boolean
  }
>(({ className, inset, ...props }, ref) => (
  <DropdownMenuPrimitive.Item
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      inset && "pl-8",
      className
    )}
    {...props}
  />
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName

const DropdownMenuCheckboxItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
  <DropdownMenuPrimitive.CheckboxItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    checked={checked}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <CheckIcon className="h-4 w-4" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName =
  DropdownMenuPrimitive.CheckboxItem.displayName

const DropdownMenuRadioItem = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
  <DropdownMenuPrimitive.RadioItem
    ref={ref}
    className={cn(
      "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
      className
    )}
    {...props}
  >
    <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
      <DropdownMenuPrimitive.ItemIndicator>
        <DotFilledIcon className="h-4 w-4 fill-current" />
      </DropdownMenuPrimitive.ItemIndicator>
    </span>
    {children}
  </DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName

const DropdownMenuLabel = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
    inset?: boolean
  }
>(({ className, inset, ...props }, ref) => (
  <DropdownMenuPrimitive.Label
    ref={ref}
    className={cn(
      "px-2 py-1.5 text-sm font-semibold",
      inset && "pl-8",
      className
    )}
    {...props}
  />
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName

const DropdownMenuSeparator = React.forwardRef<
  React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
  <DropdownMenuPrimitive.Separator
    ref={ref}
    className={cn("-mx-1 my-1 h-px bg-muted", className)}
    {...props}
  />
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName

const DropdownMenuShortcut = ({
  className,
  ...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
  return (
    <span
      className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
      {...props}
    />
  )
}
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"

export {
  DropdownMenu,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuGroup,
  DropdownMenuPortal,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuRadioGroup,
}


================================================
FILE: components/ui/input.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        ref={ref}
        {...props}
      />
    )
  }
)
Input.displayName = "Input"

export { Input }


================================================
FILE: components/ui/separator.tsx
================================================
"use client"

import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"

import { cn } from "@/lib/utils"

const Separator = React.forwardRef<
  React.ElementRef<typeof SeparatorPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
  (
    { className, orientation = "horizontal", decorative = true, ...props },
    ref
  ) => (
    <SeparatorPrimitive.Root
      ref={ref}
      decorative={decorative}
      orientation={orientation}
      className={cn(
        "shrink-0 bg-border",
        orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
        className
      )}
      {...props}
    />
  )
)
Separator.displayName = SeparatorPrimitive.Root.displayName

export { Separator }


================================================
FILE: components/ui/skeleton.tsx
================================================
import { cn } from "@/lib/utils"

function Skeleton({
  className,
  ...props
}: React.HTMLAttributes<HTMLDivElement>) {
  return (
    <div
      className={cn("animate-pulse rounded-md bg-primary/10", className)}
      {...props}
    />
  )
}

export { Skeleton }


================================================
FILE: components/ui/slider.tsx
================================================
"use client";

import * as React from "react";
import * as SliderPrimitive from "@radix-ui/react-slider";

import { cn } from "@/lib/utils";

const Slider = React.forwardRef<
  React.ElementRef<typeof SliderPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
  <SliderPrimitive.Root
    ref={ref}
    className={cn(
      "relative flex w-full touch-none select-none items-center",
      className
    )}
    {...props}
  >
    <SliderPrimitive.Track className="relative h-8 w-full grow overflow-hidden rounded-lg bg-primary/20">
      <SliderPrimitive.Range className="absolute h-full bg-primary" />
    </SliderPrimitive.Track>
    <SliderPrimitive.Thumb className="block h-8 w-1 rounded-full border border-primary/50 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
  </SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;

export { Slider };


================================================
FILE: components/ui/table.tsx
================================================
import * as React from "react"

import { cn } from "@/lib/utils"

const Table = React.forwardRef<
  HTMLTableElement,
  React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
  <div className="relative w-full overflow-auto">
    <table
      ref={ref}
      className={cn("w-full caption-bottom text-sm", className)}
      {...props}
    />
  </div>
))
Table.displayName = "Table"

const TableHeader = React.forwardRef<
  HTMLTableSectionElement,
  React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
  <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"

const TableBody = React.forwardRef<
  HTMLTableSectionElement,
  React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
  <tbody
    ref={ref}
    className={cn("[&_tr:last-child]:border-0", className)}
    {...props}
  />
))
TableBody.displayName = "TableBody"

const TableFooter = React.forwardRef<
  HTMLTableSectionElement,
  React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
  <tfoot
    ref={ref}
    className={cn(
      "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
      className
    )}
    {...props}
  />
))
TableFooter.displayName = "TableFooter"

const TableRow = React.forwardRef<
  HTMLTableRowElement,
  React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
  <tr
    ref={ref}
    className={cn(
      "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
      className
    )}
    {...props}
  />
))
TableRow.displayName = "TableRow"

const TableHead = React.forwardRef<
  HTMLTableCellElement,
  React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
  <th
    ref={ref}
    className={cn(
      "h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
      className
    )}
    {...props}
  />
))
TableHead.displayName = "TableHead"

const TableCell = React.forwardRef<
  HTMLTableCellElement,
  React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
  <td
    ref={ref}
    className={cn(
      "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
      className
    )}
    {...props}
  />
))
TableCell.displayName = "TableCell"

const TableCaption = React.forwardRef<
  HTMLTableCaptionElement,
  React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
  <caption
    ref={ref}
    className={cn("mt-4 text-sm text-muted-foreground", className)}
    {...props}
  />
))
TableCaption.displayName = "TableCaption"

export {
  Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableHead,
  TableRow,
  TableCell,
  TableCaption,
}


================================================
FILE: components/ui/tabs.tsx
================================================
"use client"

import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"

import { cn } from "@/lib/utils"

const Tabs = TabsPrimitive.Root

const TabsList = React.forwardRef<
  React.ElementRef<typeof TabsPrimitive.List>,
  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
  <TabsPrimitive.List
    ref={ref}
    className={cn(
      "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
      className
    )}
    {...props}
  />
))
TabsList.displayName = TabsPrimitive.List.displayName

const TabsTrigger = React.forwardRef<
  React.ElementRef<typeof TabsPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
  <TabsPrimitive.Trigger
    ref={ref}
    className={cn(
      "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
      className
    )}
    {...props}
  />
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName

const TabsContent = React.forwardRef<
  React.ElementRef<typeof TabsPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
  <TabsPrimitive.Content
    ref={ref}
    className={cn(
      "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
      className
    )}
    {...props}
  />
))
TabsContent.displayName = TabsPrimitive.Content.displayName

export { Tabs, TabsList, TabsTrigger, TabsContent }


================================================
FILE: components/ui/theme-provider.tsx
================================================
"use client";

import * as React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}


================================================
FILE: components/ui/toast.tsx
================================================
"use client"

import * as React from "react"
import { Cross2Icon } from "@radix-ui/react-icons"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const ToastProvider = ToastPrimitives.Provider

const ToastViewport = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Viewport>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Viewport
    ref={ref}
    className={cn(
      "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
      className
    )}
    {...props}
  />
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName

const toastVariants = cva(
  "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
  {
    variants: {
      variant: {
        default: "border bg-background text-foreground",
        destructive:
          "destructive group border-destructive bg-destructive text-destructive-foreground",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

const Toast = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Root>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
    VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
  return (
    <ToastPrimitives.Root
      ref={ref}
      className={cn(toastVariants({ variant }), className)}
      {...props}
    />
  )
})
Toast.displayName = ToastPrimitives.Root.displayName

const ToastAction = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Action>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Action
    ref={ref}
    className={cn(
      "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
      className
    )}
    {...props}
  />
))
ToastAction.displayName = ToastPrimitives.Action.displayName

const ToastClose = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Close>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Close
    ref={ref}
    className={cn(
      "absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
      className
    )}
    toast-close=""
    {...props}
  >
    <Cross2Icon className="h-4 w-4" />
  </ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName

const ToastTitle = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Title>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Title
    ref={ref}
    className={cn("text-sm font-semibold [&+div]:text-xs", className)}
    {...props}
  />
))
ToastTitle.displayName = ToastPrimitives.Title.displayName

const ToastDescription = React.forwardRef<
  React.ElementRef<typeof ToastPrimitives.Description>,
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
  <ToastPrimitives.Description
    ref={ref}
    className={cn("text-sm opacity-90", className)}
    {...props}
  />
))
ToastDescription.displayName = ToastPrimitives.Description.displayName

type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>

type ToastActionElement = React.ReactElement<typeof ToastAction>

export {
  type ToastProps,
  type ToastActionElement,
  ToastProvider,
  ToastViewport,
  Toast,
  ToastTitle,
  ToastDescription,
  ToastClose,
  ToastAction,
}


================================================
FILE: components/ui/toaster.tsx
================================================
"use client"

import {
  Toast,
  ToastClose,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
} from "@/components/ui/toast"
import { useToast } from "@/components/ui/use-toast"

export function Toaster() {
  const { toasts } = useToast()

  return (
    <ToastProvider>
      {toasts.map(function ({ id, title, description, action, ...props }) {
        return (
          <Toast key={id} {...props}>
            <div className="grid gap-1">
              {title && <ToastTitle>{title}</ToastTitle>}
              {description && (
                <ToastDescription>{description}</ToastDescription>
              )}
            </div>
            {action}
            <ToastClose />
          </Toast>
        )
      })}
      <ToastViewport />
    </ToastProvider>
  )
}


================================================
FILE: components/ui/tooltip.tsx
================================================
"use client";

import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";

import { cn } from "@/lib/utils";

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
  React.ElementRef<typeof TooltipPrimitive.Content>,
  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
  <TooltipPrimitive.Content
    ref={ref}
    sideOffset={sideOffset}
    className={cn(
      "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
      className
    )}
    {...props}
  />
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };


================================================
FILE: components/ui/use-toast.ts
================================================
"use client"

// Inspired by react-hot-toast library
import * as React from "react"

import type {
  ToastActionElement,
  ToastProps,
} from "@/components/ui/toast"

const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000

type ToasterToast = ToastProps & {
  id: string
  title?: React.ReactNode
  description?: React.ReactNode
  action?: ToastActionElement
}

const actionTypes = {
  ADD_TOAST: "ADD_TOAST",
  UPDATE_TOAST: "UPDATE_TOAST",
  DISMISS_TOAST: "DISMISS_TOAST",
  REMOVE_TOAST: "REMOVE_TOAST",
} as const

let count = 0

function genId() {
  count = (count + 1) % Number.MAX_SAFE_INTEGER
  return count.toString()
}

type ActionType = typeof actionTypes

type Action =
  | {
      type: ActionType["ADD_TOAST"]
      toast: ToasterToast
    }
  | {
      type: ActionType["UPDATE_TOAST"]
      toast: Partial<ToasterToast>
    }
  | {
      type: ActionType["DISMISS_TOAST"]
      toastId?: ToasterToast["id"]
    }
  | {
      type: ActionType["REMOVE_TOAST"]
      toastId?: ToasterToast["id"]
    }

interface State {
  toasts: ToasterToast[]
}

const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()

const addToRemoveQueue = (toastId: string) => {
  if (toastTimeouts.has(toastId)) {
    return
  }

  const timeout = setTimeout(() => {
    toastTimeouts.delete(toastId)
    dispatch({
      type: "REMOVE_TOAST",
      toastId: toastId,
    })
  }, TOAST_REMOVE_DELAY)

  toastTimeouts.set(toastId, timeout)
}

export const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case "ADD_TOAST":
      return {
        ...state,
        toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
      }

    case "UPDATE_TOAST":
      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === action.toast.id ? { ...t, ...action.toast } : t
        ),
      }

    case "DISMISS_TOAST": {
      const { toastId } = action

      // ! Side effects ! - This could be extracted into a dismissToast() action,
      // but I'll keep it here for simplicity
      if (toastId) {
        addToRemoveQueue(toastId)
      } else {
        state.toasts.forEach((toast) => {
          addToRemoveQueue(toast.id)
        })
      }

      return {
        ...state,
        toasts: state.toasts.map((t) =>
          t.id === toastId || toastId === undefined
            ? {
                ...t,
                open: false,
              }
            : t
        ),
      }
    }
    case "REMOVE_TOAST":
      if (action.toastId === undefined) {
        return {
          ...state,
          toasts: [],
        }
      }
      return {
        ...state,
        toasts: state.toasts.filter((t) => t.id !== action.toastId),
      }
  }
}

const listeners: Array<(state: State) => void> = []

let memoryState: State = { toasts: [] }

function dispatch(action: Action) {
  memoryState = reducer(memoryState, action)
  listeners.forEach((listener) => {
    listener(memoryState)
  })
}

type Toast = Omit<ToasterToast, "id">

function toast({ ...props }: Toast) {
  const id = genId()

  const update = (props: ToasterToast) =>
    dispatch({
      type: "UPDATE_TOAST",
      toast: { ...props, id },
    })
  const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })

  dispatch({
    type: "ADD_TOAST",
    toast: {
      ...props,
      id,
      open: true,
      onOpenChange: (open) => {
        if (!open) dismiss()
      },
    },
  })

  return {
    id: id,
    dismiss,
    update,
  }
}

function useToast() {
  const [state, setState] = React.useState<State>(memoryState)

  React.useEffect(() => {
    listeners.push(setState)
    return () => {
      const index = listeners.indexOf(setState)
      if (index > -1) {
        listeners.splice(index, 1)
      }
    }
  }, [state])

  return {
    ...state,
    toast,
    dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
  }
}

export { useToast, toast }


================================================
FILE: components.json
================================================
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "slate",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils"
  }
}

================================================
FILE: contexts/AutoConnectProvider.tsx
================================================
"use client";
import { useLocalStorage } from "@solana/wallet-adapter-react";
import { createContext, FC, ReactNode, useContext } from "react";

export interface AutoConnectContextState {
  autoConnect: boolean;
  setAutoConnect(autoConnect: boolean): void;
}

export const AutoConnectContext = createContext<AutoConnectContextState>(
  {} as AutoConnectContextState
);

export function useAutoConnect(): AutoConnectContextState {
  return useContext(AutoConnectContext);
}

export const AutoConnectProvider: FC<{ children: ReactNode }> = ({
  children,
}) => {
  // TODO: fix auto connect to actual reconnect on refresh/other.
  // TODO: make switch/slider settings
  // const [autoConnect, setAutoConnect] = useLocalStorage('autoConnect', false);
  const [autoConnect, setAutoConnect] = useLocalStorage("autoConnect", true);

  return (
    <AutoConnectContext.Provider value={{ autoConnect, setAutoConnect }}>
      {children}
    </AutoConnectContext.Provider>
  );
};


================================================
FILE: contexts/ContextProvider.tsx
================================================
"use client";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import {
  ConnectionProvider,
  WalletProvider,
} from "@solana/wallet-adapter-react";
import {
  PhantomWalletAdapter,
  SolflareWalletAdapter,
} from "@solana/wallet-adapter-wallets";
import { clusterApiUrl } from "@solana/web3.js";
import { ComponentType, FC, ReactNode, useMemo } from "react";
import { AutoConnectProvider, useAutoConnect } from "./AutoConnectProvider";
import {
  NetworkConfigurationProvider,
  useNetworkConfiguration,
} from "./NetworkConfigurationProvider";
import dynamic from "next/dynamic";
import { Toaster } from "@/components/ui/toaster";

const ReactUIWalletModalProviderDynamic: ComponentType<{
  children: ReactNode;
}> = dynamic(
  async () =>
    (await import("@solana/wallet-adapter-react-ui")).WalletModalProvider,
  { ssr: false }
);

const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const { autoConnect } = useAutoConnect();
  const { networkConfiguration } = useNetworkConfiguration();
  const network = networkConfiguration as WalletAdapterNetwork;
  const endpoint = useMemo(() => clusterApiUrl(network), [network]);

  const wallets = useMemo(
    () => [new SolflareWalletAdapter(), new PhantomWalletAdapter()],
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [network]
  );

  return (
    <ConnectionProvider endpoint={endpoint}>
      <WalletProvider wallets={wallets} autoConnect={autoConnect}>
        <ReactUIWalletModalProviderDynamic>
          {children}
        </ReactUIWalletModalProviderDynamic>
      </WalletProvider>
      <Toaster />
    </ConnectionProvider>
  );
};

export const ContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
  return (
    <>
      <NetworkConfigurationProvider>
        <AutoConnectProvider>
          <WalletContextProvider>{children}</WalletContextProvider>
        </AutoConnectProvider>
      </NetworkConfigurationProvider>
    </>
  );
};


================================================
FILE: contexts/NetworkConfigurationProvider.tsx
================================================
"use client";
import { useLocalStorage } from "@solana/wallet-adapter-react";
import { createContext, FC, ReactNode, useContext } from "react";

export interface NetworkConfigurationState {
  networkConfiguration: string;
  setNetworkConfiguration(networkConfiguration: string): void;
}

export const NetworkConfigurationContext =
  createContext<NetworkConfigurationState>({} as NetworkConfigurationState);

export function useNetworkConfiguration(): NetworkConfigurationState {
  return useContext(NetworkConfigurationContext);
}

export const NetworkConfigurationProvider: FC<{ children: ReactNode }> = ({
  children,
}) => {
  const [networkConfiguration, setNetworkConfiguration] = useLocalStorage(
    "network",
    "devnet"
  );

  return (
    <NetworkConfigurationContext.Provider
      value={{ networkConfiguration, setNetworkConfiguration }}
    >
      {children}
    </NetworkConfigurationContext.Provider>
  );
};


================================================
FILE: errors/NoUserAccountError.ts
================================================
export class NoUserAccountError extends Error {
  constructor(message: string) {
    super(message);

    this.name = "No User Account!";
  }
}


================================================
FILE: errors/WalletNotConnectedError.ts
================================================
export class WalletNotConnectedError extends Error {
  constructor(message: string) {
    super(message);

    this.name = "Wallet not connected!";
  }
}


================================================
FILE: hooks/mutations/useAirdropSol.tsx
================================================
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { userSolBalanceKey } from "../queries/useUserSolBalance";
import { useToast } from "@/components/ui/use-toast";
import {
  connectWalletText,
  transactionSuccessfullText,
} from "@/texts/toastTitles";
import { WalletNotConnectedError } from "@/errors/WalletNotConnectedError";

const airdropSol = async (
  connection: Connection,
  wallet: WalletContextState
) => {
  if (!wallet.publicKey) {
    throw new WalletNotConnectedError(connectWalletText);
  }

  const [latestBlockhash, signature] = await Promise.all([
    connection.getLatestBlockhash(),
    connection.requestAirdrop(wallet.publicKey, 1 * LAMPORTS_PER_SOL),
  ]);
  await connection.confirmTransaction(
    { signature, ...latestBlockhash },
    "confirmed"
  );
};

const useAirdropSol = (connection: Connection, wallet: WalletContextState) => {
  const queryClient = useQueryClient();
  const { toast } = useToast();

  return useMutation({
    mutationFn: () => airdropSol(connection, wallet),
    onSuccess: () => {
      toast({
        variant: "default",
        title: transactionSuccessfullText,
        description: "Airdrop was confirmed",
      });
      queryClient.invalidateQueries({
        queryKey: [userSolBalanceKey],
      });
    },
    onError: (e) => {
      toast({
        variant: "destructive",
        title: e.name,
        description: e.message,
      });
    },
  });
};

export { useAirdropSol };


================================================
FILE: hooks/mutations/useCollectPoints.ts
================================================
import { Poe } from "@/idl/poe";
import { BN, Program } from "@coral-xyz/anchor";
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { userAccountKey } from "../queries/useUserAccount";
import { userSolBalanceKey } from "../queries/useUserSolBalance";
import { pollByIdKey } from "../queries/usePollById";
import { userScoreKey } from "../queries/useUserScore";
import { WalletNotConnectedError } from "@/errors/WalletNotConnectedError";
import { useToast } from "@/components/ui/use-toast";
import {
  connectWalletText,
  transactionSuccessfullText,
} from "@/texts/toastTitles";
import { sendVersionedTransaction } from "../../utils/sendVersionedTransaction";
import { getAssociatedTokenAddress } from "@solana/spl-token";
import { allPollsByUserKey } from "../queries/useAllPollsByUser";
import { allUserAccounts } from "../queries/useAllUserAccounts";
import { userBonkBalanceKey } from "../queries/useUserBonkBalance";

const collectPoints = async (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState,
  pollId: number
) => {
  if (!wallet.publicKey) {
    throw new WalletNotConnectedError(connectWalletText);
  }

  let [userPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("user"), wallet.publicKey.toBuffer()],
    program.programId
  );

  const [pollPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
    program.programId
  );

  let [userPredictionPda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_estimate"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  let [scoreListPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("scoring_list"), pollPda.toBuffer()],
    program.programId
  );

  let [userScorePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_score"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  let [mintPda, mintBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("poeken_mint")],
    program.programId
  );

  let [escrowPda, _escrowBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("escrow")],
    program.programId
  );

  const tokenAccountAddress = await getAssociatedTokenAddress(
    mintPda,
    wallet.publicKey
  );

  const registerUserInstruction = await program.methods
    .collectPoints()
    .accountsPartial({
      user: userPda,
      forecaster: wallet.publicKey,
      poll: pollPda,
      userEstimate: userPredictionPda,
      scoringList: scoreListPda,
      userScore: userScorePda,
      mint: mintPda,
      escrowAccount: escrowPda,
      forecasterTokenAccount: tokenAccountAddress,
    })
    .instruction();

  await sendVersionedTransaction([registerUserInstruction], wallet, connection);
};

const useCollectPoints = (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState
) => {
  const queryClient = useQueryClient();
  const { toast } = useToast();

  return useMutation({
    mutationFn: ({ pollId }: { pollId: number }) =>
      collectPoints(program, connection, wallet, pollId),
    onSuccess: (_, variables) => {
      toast({
        variant: "default",
        title: transactionSuccessfullText,
        description: "Points collected.",
      });
      queryClient.invalidateQueries({
        queryKey: [
          userAccountKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [allPollsByUserKey, wallet.publicKey?.toBase58()],
      });
      queryClient.invalidateQueries({
        queryKey: [pollByIdKey, variables.pollId],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userScoreKey,
          variables.pollId,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [allUserAccounts],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userAccountKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userSolBalanceKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userBonkBalanceKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
    },
    onError: (e) => {
      toast({ variant: "destructive", title: e.name, description: e.message });
    },
  });
};

export { useCollectPoints };


================================================
FILE: hooks/mutations/useMakeEstimate.tsx
================================================
import React from "react";
import { Poe } from "@/idl/poe";
import { BN, Program } from "@coral-xyz/anchor";
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { pollByIdKey } from "../queries/usePollById";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { useToast } from "@/components/ui/use-toast";
import { NoUserAccountError } from "@/errors/NoUserAccountError";
import { ToastAction } from "@/components/ui/toast";
import { WalletNotConnectedError } from "@/errors/WalletNotConnectedError";
import {
  connectWalletText,
  transactionSuccessfullText,
} from "@/texts/toastTitles";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
import { userAccountKey } from "../queries/useUserAccount";
import { sendVersionedTransaction } from "../../utils/sendVersionedTransaction";
import { getAssociatedTokenAddress } from "@solana/spl-token";
import { userSolBalanceKey } from "../queries/useUserSolBalance";
import { userScoreKey } from "../queries/useUserScore";
import { userEstimateKey } from "../queries/useUserEstimateByPoll";
import { useRegisterUser } from "./useRegisterUser";

const makeEstimate = async (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState,
  pollId: number,
  lowerEstimate: number | undefined,
  upperEstimate: number | undefined
) => {
  if (!wallet.publicKey) {
    throw new WalletNotConnectedError(connectWalletText);
  }

  let [userPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("user"), wallet.publicKey.toBuffer()],
    program.programId
  );
  const userAccount = await connection.getAccountInfo(userPda);

  const [pollPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
    program.programId
  );
  let pollAccount = await program.account.poll.fetch(pollPda);

  let [userEstimatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_estimate"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  let [userEstimateUpdatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_estimate_update"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
      new BN(0).toArrayLike(Buffer, "le", 8),
    ],
    program.programId
  );

  let [estimateUpdatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("poll_estimate_update"),
      pollPda.toBuffer(),
      pollAccount.numEstimateUpdates.toArrayLike(Buffer, "le", 8),
    ],
    program.programId
  );

  let [scoreListPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("scoring_list"), pollPda.toBuffer()],
    program.programId
  );

  let [userScorePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_score"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  let [mintPda, _mintBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("poeken_mint")],
    program.programId
  );

  const forecasterTokenAccountAddress = await getAssociatedTokenAddress(
    mintPda,
    wallet.publicKey
  );

  let [escrowPda, _escrowBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("escrow")],
    program.programId
  );

  const makeEstimateInstruction = await program.methods
    .makeEstimate(
      lowerEstimate !== undefined ? lowerEstimate : 0,
      upperEstimate !== undefined ? upperEstimate : 0
    )
    .accountsPartial({
      user: userPda,
      poll: pollPda,
      userEstimate: userEstimatePda,
      // userEstimateUpdate: userEstimateUpdatePda,
      pollEstimateUpdate: estimateUpdatePda,
      scoringList: scoreListPda,
      userScore: userScorePda,
      forecasterTokenAccount: forecasterTokenAccountAddress,
      mint: mintPda,
      escrowAccount: escrowPda,
    })
    .instruction();

  let instructions: TransactionInstruction[] = [];
  if (userAccount === null) {
    const registerUserInstruction = await program.methods
      .registerUser()
      .accountsPartial({
        user: userPda,
        mint: mintPda,
        tokenAccount: forecasterTokenAccountAddress,
      })
      .instruction();
    instructions = [registerUserInstruction, makeEstimateInstruction];
  } else {
    instructions = [makeEstimateInstruction];
  }

  await sendVersionedTransaction(instructions, wallet, connection);
};

const useMakeEstimate = (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState
) => {
  const queryClient = useQueryClient();
  const { toast } = useToast();
  const { mutate: registerUser } = useRegisterUser(program, connection, wallet);
  const { setVisible } = useWalletModal();

  return useMutation({
    mutationFn: ({
      pollId,
      lowerEstimate,
      upperEstimate,
    }: {
      pollId: number;
      lowerEstimate: number | undefined;
      upperEstimate: number | undefined;
    }) =>
      makeEstimate(
        program,
        connection,
        wallet,
        pollId,
        lowerEstimate,
        upperEstimate
      ),
    onSuccess: (_, variables) => {
      toast({
        variant: "default",
        title: transactionSuccessfullText,
        description: "Estimate is submitted.",
      });
      queryClient.invalidateQueries({
        queryKey: [pollByIdKey, variables.pollId],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userEstimateKey,
          variables.pollId,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userScoreKey,
          variables.pollId,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userSolBalanceKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userAccountKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
    },
    onError: (e) => {
      if (e instanceof NoUserAccountError) {
        toast({
          variant: "destructive",
          title: e.name,
          description: (
            <div className="flex gap-2 items-center">
              <Avatar className="h-12 w-12">
                <AvatarImage src="/avatar.png" alt="@shadcn" />
                <AvatarFallback>Av</AvatarFallback>
              </Avatar>
              <div>Please create a user account first.</div>
            </div>
          ),
          action: (
            <ToastAction
              altText="Create User Account"
              onClick={() => registerUser()}
            >
              Create Account
            </ToastAction>
          ),
          duration: 8000,
        });
      } else if (e instanceof WalletNotConnectedError) {
        toast({
          variant: "destructive",
          title: e.name,
          description: e.message,
          action: (
            <ToastAction
              altText="Connect wallet"
              onClick={() => setVisible(true)}
            >
              Connect Wallet
            </ToastAction>
          ),
          duration: 8000,
        });
      } else {
        toast({
          variant: "destructive",
          title: e.name,
          description: e.message,
        });
      }
    },
  });
};

export { useMakeEstimate };


================================================
FILE: hooks/mutations/useRegisterUser.tsx
================================================
import { Poe } from "@/idl/poe";
import { Program } from "@coral-xyz/anchor";
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { getAssociatedTokenAddress } from "@solana/spl-token";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { userAccountKey } from "../queries/useUserAccount";
import { userSolBalanceKey } from "../queries/useUserSolBalance";
import { useToast } from "@/components/ui/use-toast";
import {
  connectWalletText,
  transactionSuccessfullText,
} from "@/texts/toastTitles";
import { WalletNotConnectedError } from "@/errors/WalletNotConnectedError";
import { sendVersionedTransaction } from "../../utils/sendVersionedTransaction";
import { allUserAccounts } from "../queries/useAllUserAccounts";

const registerUser = async (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState
) => {
  if (!wallet.publicKey) {
    throw new WalletNotConnectedError(connectWalletText);
  }

  let [userPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("user"), wallet.publicKey.toBuffer()],
    program.programId
  );

  let [mintPda, mintBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("poeken_mint")],
    program.programId
  );

  const tokenAccountAddress = await getAssociatedTokenAddress(
    mintPda,
    wallet.publicKey
  );

  const registerUserInstruction = await program.methods
    .registerUser()
    .accountsPartial({
      user: userPda,
      mint: mintPda,
      tokenAccount: tokenAccountAddress,
    })
    .instruction();

  await sendVersionedTransaction([registerUserInstruction], wallet, connection);
};

const useRegisterUser = (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState
) => {
  const queryClient = useQueryClient();
  const { toast } = useToast();

  return useMutation({
    mutationFn: () => registerUser(program, connection, wallet),
    onSuccess: () => {
      toast({
        variant: "default",
        title: transactionSuccessfullText,
        description: "User is registered.",
      });
      queryClient.invalidateQueries({
        queryKey: [
          userAccountKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [allUserAccounts],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userSolBalanceKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
    },
    onError: (e) => {
      toast({
        variant: "destructive",
        title: e.name,
        description: e.message,
      });
    },
  });
};

export { useRegisterUser };


================================================
FILE: hooks/mutations/useUpdateEstimate.ts
================================================
import { Poe } from "@/idl/poe";
import { BN, Program } from "@coral-xyz/anchor";
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { userSolBalanceKey } from "../queries/useUserSolBalance";
import { userEstimateKey } from "../queries/useUserEstimateByPoll";
import { pollByIdKey } from "../queries/usePollById";
import { userScoreKey } from "../queries/useUserScore";
import { WalletNotConnectedError } from "@/errors/WalletNotConnectedError";
import { useToast } from "@/components/ui/use-toast";
import {
  connectWalletText,
  transactionSuccessfullText,
} from "@/texts/toastTitles";
import { sendVersionedTransaction } from "../../utils/sendVersionedTransaction";

const updateEstimate = async (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState,
  pollId: number,
  lowerEstimate: number | undefined,
  upperEstimate: number | undefined
) => {
  if (!wallet.publicKey) {
    throw new WalletNotConnectedError(connectWalletText);
  }

  const [pollPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
    program.programId
  );

  let pollAccount = await program.account.poll.fetch(pollPda);

  let [userEstimatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_estimate"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  let userEstimateAccount = await program.account.userEstimate.fetch(
    userEstimatePda
  );

  let [userEstimateUpdatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_estimate_update"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
      userEstimateAccount.numEstimateUpdates.toArrayLike(Buffer, "le", 8),
    ],
    program.programId
  );

  let [estimateUpdatePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("poll_estimate_update"),
      pollPda.toBuffer(),
      pollAccount.numEstimateUpdates.toArrayLike(Buffer, "le", 8),
    ],
    program.programId
  );

  let [scoreListPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("scoring_list"), pollPda.toBuffer()],
    program.programId
  );

  let [userScorePda] = PublicKey.findProgramAddressSync(
    [
      Buffer.from("user_score"),
      pollPda.toBuffer(),
      wallet.publicKey.toBuffer(),
    ],
    program.programId
  );

  const updateEstimateInstruction = await program.methods
    .updateEstimate(
      lowerEstimate !== undefined ? lowerEstimate : 0,
      upperEstimate !== undefined ? upperEstimate : 0
    )
    .accountsPartial({
      poll: pollPda,
      userEstimate: userEstimatePda,
      userEstimateUpdate: userEstimateUpdatePda,
      estimateUpdate: estimateUpdatePda,
      scoringList: scoreListPda,
      userScore: userScorePda,
    })
    .instruction();

  await sendVersionedTransaction(
    [updateEstimateInstruction],
    wallet,
    connection
  );
};

const useUpdateEstimate = (
  program: Program<Poe>,
  connection: Connection,
  wallet: WalletContextState
) => {
  const queryClient = useQueryClient();
  const { toast } = useToast();

  return useMutation({
    mutationFn: ({
      pollId,
      lowerEstimate,
      upperEstimate,
    }: {
      pollId: number;
      lowerEstimate: number | undefined;
      upperEstimate: number | undefined;
    }) =>
      updateEstimate(
        program,
        connection,
        wallet,
        pollId,
        lowerEstimate,
        upperEstimate
      ),
    onSuccess: (_, variables) => {
      toast({
        variant: "default",
        title: transactionSuccessfullText,
        description: "Estimate updated.",
      });
      queryClient.invalidateQueries({
        queryKey: [
          userEstimateKey,
          variables.pollId,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [pollByIdKey, variables.pollId],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userScoreKey,
          variables.pollId,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
      queryClient.invalidateQueries({
        queryKey: [
          userSolBalanceKey,
          connection.rpcEndpoint,
          wallet.publicKey?.toBase58() || "",
        ],
      });
    },
    onError: (e) => {
      toast({
        variant: "destructive",
        title: e.name,
        description: e.message,
      });
    },
  });
};

export { useUpdateEstimate };


================================================
FILE: hooks/queries/useAllPolls.ts
================================================
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { Program } from "@coral-xyz/anchor";
import { Poll } from "@/lib/types";
import { Poe } from "@/idl/poe";

const allPollsKey = "allPolls";

const getAllPolls = async (program: Program<Poe>) => {
  const polls = await program.account.poll.all();

  return polls
    .filter(
      (poll) =>
        poll.account.creator.toBase58() ===
        "3aSqvNz5XuBkudHZLZZSfio3Hd6nxEEzUWSwvggWWDR1"
    )
    .sort((a, b) => a.account.id - b.account.id)
    .map((poll) => poll.account) as unknown as Poll[];
};

const useAllPolls = (program: Program<Poe>) => {
  return useQuery({
    queryKey: [allPollsKey],
    queryFn: async () => await getAllPolls(program),
    enabled: !!program,
    placeholderData: keepPreviousData,
  });
};

export { useAllPolls, allPollsKey };


================================================
FILE: hooks/queries/useAllPollsByUser.ts
================================================
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";
import { PublicKey } from "@solana/web3.js";
import { useAllUserPredictions } from "./useAllUserPredictions";
import { Poll } from "@/lib/types";

const allPollsByUserKey = "allPollsByUser";

const getAllPollsByUser = async (
  program: Program<Poe>,
  addresses: PublicKey[] | undefined
) => {
  if (addresses !== undefined) {
    return (await program.account.poll.fetchMultiple(
      addresses
    )) as unknown as Poll[];
  } else {
    return [] as Poll[];
  }
};

const useAllPollsByUser = (
  program: Program<Poe>,
  publicKey: PublicKey | null
) => {
  const { data: userPredictions } = useAllUserPredictions(program, publicKey);
  const userPollAddresses = userPredictions?.map(
    (userPrediction) => userPrediction.account.poll
  );
  return useQuery({
    queryKey: [
      allPollsByUserKey,
      publicKey?.toBase58(),
      userPollAddresses?.length,
    ],
    queryFn: async () => await getAllPollsByUser(program, userPollAddresses),
    enabled: !!program && !!userPredictions,
    placeholderData: keepPreviousData,
  });
};

export { useAllPollsByUser, getAllPollsByUser, allPollsByUserKey };


================================================
FILE: hooks/queries/useAllUserAccounts.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";
import { UserAccount } from "@/lib/types";

const allUserAccounts = "allUserAccounts";

const getAllUserPredictions = async (program: Program<Poe>) => {
  const accounts = await program.account.user.all();

  return accounts.sort((a, b) => b.account.score - a.account.score);
};

const useAllUserAccounts = (program: Program<Poe>) => {
  return useQuery({
    queryKey: [allUserAccounts],
    queryFn: async () => await getAllUserPredictions(program),
    // staleTime: Infinity,
    enabled: !!program,
  });
};

export { useAllUserAccounts, getAllUserPredictions, allUserAccounts };


================================================
FILE: hooks/queries/useAllUserPredictions.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { PublicKey } from "@solana/web3.js";
import { Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";

const allUserEstimatesKey = "allUserEstimates";

const getAllUserPredictions = async (
  program: Program<Poe>,
  publicKey: PublicKey | null
) => {
  if (publicKey) {
    return await program.account.userEstimate.all([
      {
        memcmp: {
          offset: 8, // discriminator
          bytes: publicKey.toBase58(),
        },
      },
    ]);
  } else {
    return null;
  }
};

const useAllUserPredictions = (
  program: Program<Poe>,
  publicKey: PublicKey | null
) => {
  return useQuery({
    queryKey: [allUserEstimatesKey, publicKey?.toBase58() || ""],
    queryFn: async () => await getAllUserPredictions(program, publicKey),
    staleTime: Infinity,
    enabled: !!program,
  });
};

export { useAllUserPredictions, getAllUserPredictions, allUserEstimatesKey };


================================================
FILE: hooks/queries/useEstimateUpdatesByPoll.ts
================================================
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { BN, Program } from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import { Poe } from "@/idl/poe";

type EstimateData = {
  name: Date;
  estimate: number | null;
  confidenceInterval: number[] | null;
};

const estimateUpdatesByPollKey = "estimateUpdatesByPoll";

const getEstimateUpdatesByPoll = async (
  program: Program<Poe>,
  pollId: number,
  publicKey: PublicKey | null
) => {
  const [pollPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
    program.programId
  );
  const pollAccount = await program.account.poll.fetch(pollPda);

  const estimateUpdates = await program.account.pollEstimateUpdate.all([
    {
      memcmp: {
        offset: 8, // discriminator
        bytes: pollPda.toBase58(),
      },
    },
  ]);

  estimateUpdates.sort(
    (a, b) => a.account.timestamp.toNumber() - b.account.timestamp.toNumber()
  );

  const updateData = estimateUpdates.map((update) => {
    const timestamp = update.account.timestamp.toNumber();

    return {
      timestamp: timestamp,
      estimate: update.account.estimate,
      deviation:
        update.account.variance !== null && update.account.variance >= 0
          ? Math.sqrt(update.account.variance / 2)
          : null,
    };
  });

  updateData.sort((a, b) => a.timestamp - b.timestamp);

  let estimates = [];
  let today = new Date().getTime();

  let lastDisplayTime =
    pollAccount.result === null
      ? today / 1000
      : updateData[updateData.length - 1].timestamp;

  for (let i = 0; i < updateData.length - 1; i++) {
    const time = updateData[i].timestamp;
    const estimate = updateData[i].estimate;
    const deviation = updateData[i].deviation;

    const nextTime = updateData[i + 1].timestamp;
    // Fill with data between updates, not necessary but a smoother experience
    for (let j = 0; j < nextTime - time; j = j + 60000000000000) {
      estimates.push({
        name: time + j,
        estimate: estimate !== null ? estimate / 10000 : null,
        confidenceInterval:
          deviation !== null && estimate !== null
            ? [estimate / 10000 - deviation, estimate / 10000 + deviation]
            : null,
      } as unknown as EstimateData);
    }
    estimates.push({
      name: nextTime,
      estimate: estimate !== null ? estimate / 10000 : null,
      confidenceInterval:
        deviation !== null && estimate !== null
          ? [estimate / 10000 - deviation, estimate / 10000 + deviation]
          : null,
    } as unknown as EstimateData);
  }
  const lastTimestamp = updateData[updateData.length - 1].timestamp;
  const lastEstimate = updateData[updateData.length - 1].estimate;
  const lastDeviation = updateData[updateData.length - 1].deviation;
  for (let k = 0; k < lastDisplayTime - lastTimestamp; k = k + 1000000000) {
    estimates.push({
      name: lastTimestamp + k,
      estimate: lastEstimate !== null ? lastEstimate / 10000 : null,
      confidenceInterval:
        lastDeviation !== null && lastEstimate !== null
          ? [
              lastEstimate / 10000 - lastDeviation,
              lastEstimate / 10000 + lastDeviation,
            ]
          : null,
    } as unknown as EstimateData);
  }
  estimates.push({
    name: lastDisplayTime,
    estimate: lastEstimate !== null ? lastEstimate / 10000 : null,
    confidenceInterval:
      lastDeviation !== null && lastEstimate !== null
        ? [
            lastEstimate / 10000 - lastDeviation,
            lastEstimate / 10000 + lastDeviation,
          ]
        : null,
  } as unknown as EstimateData);

  return estimates;
};

const useEstimateUpdatesByPoll = (
  program: Program<Poe>,
  pollId: number,
  publicKey: PublicKey | null
) => {
  return useQuery({
    queryKey: [estimateUpdatesByPollKey, pollId],
    queryFn: async () =>
      await getEstimateUpdatesByPoll(program, pollId, publicKey),
    enabled: !!program,
    placeholderData: keepPreviousData,
    refetchInterval: 10000,
  });
};

export {
  useEstimateUpdatesByPoll,
  getEstimateUpdatesByPoll,
  estimateUpdatesByPollKey,
};


================================================
FILE: hooks/queries/usePollById.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { PublicKey } from "@solana/web3.js";
import { BN, Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";

const pollByIdKey = "pollById";

const getPollById = async (program: Program<Poe>, pollId: number) => {
  const [pollPda] = PublicKey.findProgramAddressSync(
    [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
    program.programId
  );

  return await program.account.poll.fetch(pollPda);
};

const usePollById = (
  program: Program<Poe>,
  pollId: number,
  isVisible: boolean
) => {
  return useQuery({
    queryKey: [pollByIdKey, pollId],
    queryFn: async () => await getPollById(program, pollId),
    enabled: !!program && isVisible,
  });
};

export { usePollById, getPollById, pollByIdKey };


================================================
FILE: hooks/queries/useUserAccount.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Connection, PublicKey } from "@solana/web3.js";
import { Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";

const userAccountKey = "userAccount";

const getUserAccount = async (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null
) => {
  if (publicKey) {
    let [userPda] = PublicKey.findProgramAddressSync(
      [Buffer.from("user"), publicKey.toBuffer()],
      program.programId
    );

    const userAccount = await connection.getAccountInfo(userPda);
    if (userAccount) {
      return await program.account.user.fetch(userPda);
    } else {
      return null;
    }
  } else {
    return null;
  }
};

const useUserAccount = (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null
) => {
  return useQuery({
    queryKey: [
      userAccountKey,
      connection.rpcEndpoint,
      publicKey?.toBase58() || "",
    ],
    queryFn: async () => await getUserAccount(program, connection, publicKey),
    staleTime: Infinity,
    enabled: !!program,
  });
};

export { useUserAccount, getUserAccount, userAccountKey };


================================================
FILE: hooks/queries/useUserBonkBalance.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Connection, PublicKey } from "@solana/web3.js";
import { Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";
import { getAssociatedTokenAddress } from "@solana/spl-token";

const userBonkBalanceKey = "userBonkBalance";

const getUserBalance = async (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null
) => {
  let [mintPda, _mintBump] = PublicKey.findProgramAddressSync(
    [Buffer.from("poeken_mint")],
    program.programId
  );

  if (publicKey) {
    const tokenAccount = await getAssociatedTokenAddress(mintPda, publicKey);
    const info = await connection.getTokenAccountBalance(tokenAccount);

    return info.value.uiAmount;
  } else {
    return 0;
  }
};

const useUserBonkBalance = (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null
) => {
  return useQuery({
    queryKey: [
      userBonkBalanceKey,
      connection.rpcEndpoint,
      publicKey?.toBase58() || "",
    ],
    queryFn: async () => await getUserBalance(program, connection, publicKey),
    initialDataUpdatedAt: Date.now(),
  });
};

export { useUserBonkBalance, getUserBalance, userBonkBalanceKey };


================================================
FILE: hooks/queries/useUserEstimateByPoll.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Connection, PublicKey } from "@solana/web3.js";
import { BN, Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";

const userEstimateKey = "userEstimate";

const getUserEstimateByPoll = async (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null,
  pollId: number
) => {
  if (publicKey) {
    const [pollPda] = PublicKey.findProgramAddressSync(
      [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
      program.programId
    );

    let [userPredictionPda] = PublicKey.findProgramAddressSync(
      [Buffer.from("user_estimate"), pollPda.toBuffer(), publicKey.toBuffer()],
      program.programId
    );

    const userPredictionAccount = await connection.getAccountInfo(
      userPredictionPda
    );

    if (userPredictionAccount) {
      return await program.account.userEstimate.fetch(userPredictionPda);
    } else {
      return null;
    }
  } else {
    return null;
  }
};

const useUserEstimateByPoll = (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null,
  pollId: number,
  isVisible: boolean
) => {
  return useQuery({
    queryKey: [
      userEstimateKey,
      pollId,
      connection.rpcEndpoint,
      publicKey?.toBase58() || "",
    ],
    queryFn: async () =>
      await getUserEstimateByPoll(program, connection, publicKey, pollId),
    staleTime: Infinity,
    enabled: !!program && isVisible,
  });
};

export { useUserEstimateByPoll, getUserEstimateByPoll, userEstimateKey };


================================================
FILE: hooks/queries/useUserScore.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Connection, PublicKey } from "@solana/web3.js";
import { BN, Program } from "@coral-xyz/anchor";
import { Poe } from "@/idl/poe";

const userScoreKey = "userScore";

const getUserScore = async (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null,
  pollId: number | undefined
) => {
  if (publicKey !== null && pollId !== undefined) {
    const [pollPda] = PublicKey.findProgramAddressSync(
      [Buffer.from("poll"), new BN(pollId).toArrayLike(Buffer, "le", 8)],
      program.programId
    );

    let [userScorePda] = PublicKey.findProgramAddressSync(
      [Buffer.from("user_score"), pollPda.toBuffer(), publicKey.toBuffer()],
      program.programId
    );

    const userScoreAccount = await connection.getAccountInfo(userScorePda);

    if (userScoreAccount) {
      return await program.account.userScore.fetch(userScorePda);
    } else {
      return null;
    }
  } else {
    return null;
  }
};

const useUserScore = (
  program: Program<Poe>,
  connection: Connection,
  publicKey: PublicKey | null,
  pollId: number | undefined,
  isVisible: boolean
) => {
  return useQuery({
    queryKey: [
      userScoreKey,
      pollId,
      connection.rpcEndpoint,
      publicKey?.toBase58() || "",
    ],
    queryFn: async () =>
      await getUserScore(program, connection, publicKey, pollId),
    enabled: isVisible,
  });
};

export { useUserScore, getUserScore, userScoreKey };


================================================
FILE: hooks/queries/useUserSolBalance.ts
================================================
import { useQuery } from "@tanstack/react-query";
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";

const userSolBalanceKey = "userSolBalance";

const getUserBalance = async (
  connection: Connection,
  publicKey: PublicKey | null
) => {
  if (publicKey) {
    const balance = await connection.getBalance(publicKey, "confirmed");
    return balance / LAMPORTS_PER_SOL;
  } else {
    return 0;
  }
};

const useUserSolBalance = (
  connection: Connection,
  publicKey: PublicKey | null
) => {
  return useQuery({
    queryKey: [
      userSolBalanceKey,
      connection.rpcEndpoint,
      publicKey?.toBase58() || "",
    ],
    queryFn: async () => await getUserBalance(connection, publicKey),
    initialDataUpdatedAt: Date.now(),
  });
};

export { useUserSolBalance, getUserBalance, userSolBalanceKey };


================================================
FILE: hooks/states/useTabStore.tsx
================================================
import { create } from "zustand";

type TabState = "all" | "coming";

interface TabsState {
  tab: TabState;
  setTab: (newTab: string) => void;
}
export const useTabsStore = create<TabsState>()((set) => ({
  tab: "coming",
  setTab: (newTab: string) => set({ tab: newTab as TabState }),
}));


================================================
FILE: hooks/useAnchorProgram.tsx
================================================
import { useEffect, useState } from "react";
import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor";
import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
import idlFile from "@/idl/poe.json";
import { Poe } from "@/idl/poe";
import { Keypair } from "@solana/web3.js";

export default function useAnchorProgram(): Program<Poe> {
  const { connection } = useConnection();
  const wallet = useAnchorWallet();
  const [program, setProgram] = useState<Program<Poe> | null>(null);

  const idl = idlFile as Idl;

  useEffect(() => {
    let provider;
    if (wallet) {
      provider = new AnchorProvider(connection, wallet);
    } else {
      provider = new AnchorProvider(connection, {
        publicKey: Keypair.generate().publicKey,
        signAllTransactions: async (txes) => txes,
        signTransaction: async (tx) => tx,
      });
    }

    const program = new Program(idl, provider) as unknown as Program<Poe>;
    setProgram(program);
  }, [wallet, connection, idl]);

  return program as Program<Poe>;
}


================================================
FILE: hooks/useIntersectionObserver.tsx
================================================
import { MutableRefObject, useEffect, useState } from "react";

interface IntersectionObserverOptions {
  root?: Element | null;
  rootMargin?: string;
  threshold?: number | number[];
}

const useIntersectionObserver = (
  ref: MutableRefObject<Element | null>,
  options?: IntersectionObserverOptions
) => {
  const [isIntersecting, setIsIntersecting] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      setIsIntersecting(entry.isIntersecting);
    }, options);

    const node = ref.current;
    if (node) {
      observer.observe(node);
    }

    return () => {
      if (node) {
        observer.unobserve(node);
      }
    };
  }, [ref, options]);

  return isIntersecting;
};

export default useIntersectionObserver;


================================================
FILE: idl/poe.json
================================================
{
  "address": "ACyH6Avm4uYen8WWyTU4chExQqpF4gCHy5MmtqtpWomk",
  "metadata": {
    "name": "poe",
    "version": "0.1.0",
    "spec": "0.1.0",
    "description": "Proof of Estimate - Prediction Poll"
  },
  "instructions": [
    {
      "name": "add_metadata",
      "discriminator": [
        231,
        195,
        40,
        240,
        67,
        231,
        53,
        136
      ],
      "accounts": [
        {
          "name": "payer",
          "writable": true,
          "signer": true
        },
        {
          "name": "auth",
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  97,
                  117,
                  116,
                  104
                ]
              }
            ]
          }
        },
        {
          "name": "mint",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  107,
                  101,
                  110,
                  95,
                  109,
                  105,
                  110,
                  116
                ]
              }
            ]
          }
        },
        {
          "name": "token_program",
          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        },
        {
          "name": "metadata",
          "writable": true
        },
        {
          "name": "token_metadata_program"
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        },
        {
          "name": "rent",
          "address": "SysvarRent111111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "uri",
          "type": "string"
        },
        {
          "name": "name",
          "type": "string"
        },
        {
          "name": "symbol",
          "type": "string"
        }
      ]
    },
    {
      "name": "collect_points",
      "discriminator": [
        221,
        8,
        237,
        153,
        212,
        171,
        156,
        131
      ],
      "accounts": [
        {
          "name": "payer",
          "writable": true,
          "signer": true
        },
        {
          "name": "forecaster"
        },
        {
          "name": "user",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114
                ]
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "user_estimate",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "user_score",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  115,
                  99,
                  111,
                  114,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "auth",
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  97,
                  117,
                  116,
                  104
                ]
              }
            ]
          }
        },
        {
          "name": "mint",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  107,
                  101,
                  110,
                  95,
                  109,
                  105,
                  110,
                  116
                ]
              }
            ]
          }
        },
        {
          "name": "escrow_account",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  101,
                  115,
                  99,
                  114,
                  111,
                  119
                ]
              }
            ]
          }
        },
        {
          "name": "forecaster_token_account",
          "writable": true
        },
        {
          "name": "token_program",
          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        },
        {
          "name": "associated_token_program",
          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": []
    },
    {
      "name": "create_poll",
      "discriminator": [
        182,
        171,
        112,
        238,
        6,
        219,
        14,
        110
      ],
      "accounts": [
        {
          "name": "creator",
          "writable": true,
          "signer": true
        },
        {
          "name": "resolver"
        },
        {
          "name": "state",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  95,
                  115,
                  116,
                  97,
                  116,
                  101
                ]
              }
            ]
          }
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "state.num_polls",
                "account": "PoeState"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "question",
          "type": "string"
        },
        {
          "name": "description",
          "type": "string"
        },
        {
          "name": "category",
          "type": "u16"
        },
        {
          "name": "decay",
          "type": "f32"
        }
      ]
    },
    {
      "name": "initialize",
      "discriminator": [
        175,
        175,
        109,
        31,
        13,
        152,
        155,
        237
      ],
      "accounts": [
        {
          "name": "payer",
          "writable": true,
          "signer": true
        },
        {
          "name": "state",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  95,
                  115,
                  116,
                  97,
                  116,
                  101
                ]
              }
            ]
          }
        },
        {
          "name": "auth",
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  97,
                  117,
                  116,
                  104
                ]
              }
            ]
          }
        },
        {
          "name": "mint",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  107,
                  101,
                  110,
                  95,
                  109,
                  105,
                  110,
                  116
                ]
              }
            ]
          }
        },
        {
          "name": "escrow_account",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  101,
                  115,
                  99,
                  114,
                  111,
                  119
                ]
              }
            ]
          }
        },
        {
          "name": "token_program",
          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": []
    },
    {
      "name": "make_estimate",
      "discriminator": [
        169,
        43,
        178,
        59,
        233,
        178,
        74,
        199
      ],
      "accounts": [
        {
          "name": "forecaster",
          "writable": true,
          "signer": true
        },
        {
          "name": "user",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114
                ]
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "user_estimate",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "poll_estimate_update",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101,
                  95,
                  117,
                  112,
                  100,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "poll.num_estimate_updates",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "user_score",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  115,
                  99,
                  111,
                  114,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "forecaster_token_account",
          "writable": true
        },
        {
          "name": "mint",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  107,
                  101,
                  110,
                  95,
                  109,
                  105,
                  110,
                  116
                ]
              }
            ]
          }
        },
        {
          "name": "auth",
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  97,
                  117,
                  116,
                  104
                ]
              }
            ]
          }
        },
        {
          "name": "escrow_account",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  101,
                  115,
                  99,
                  114,
                  111,
                  119
                ]
              }
            ]
          }
        },
        {
          "name": "token_program",
          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        },
        {
          "name": "associated_token_program",
          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "lower_estimate",
          "type": "u16"
        },
        {
          "name": "upper_estimate",
          "type": "u16"
        }
      ]
    },
    {
      "name": "register_user",
      "discriminator": [
        2,
        241,
        150,
        223,
        99,
        214,
        116,
        97
      ],
      "accounts": [
        {
          "name": "payer",
          "writable": true,
          "signer": true
        },
        {
          "name": "user",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114
                ]
              },
              {
                "kind": "account",
                "path": "payer"
              }
            ]
          }
        },
        {
          "name": "auth",
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  97,
                  117,
                  116,
                  104
                ]
              }
            ]
          }
        },
        {
          "name": "mint",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  101,
                  107,
                  101,
                  110,
                  95,
                  109,
                  105,
                  110,
                  116
                ]
              }
            ]
          }
        },
        {
          "name": "token_account",
          "writable": true
        },
        {
          "name": "token_program",
          "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
        },
        {
          "name": "associated_token_program",
          "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": []
    },
    {
      "name": "remove_estimate",
      "discriminator": [
        123,
        41,
        255,
        206,
        43,
        234,
        150,
        38
      ],
      "accounts": [
        {
          "name": "forecaster",
          "writable": true,
          "signer": true
        },
        {
          "name": "user",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114
                ]
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "user_estimate",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "estimate_update",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101,
                  95,
                  117,
                  112,
                  100,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "poll.num_estimate_updates",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "user_score",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  115,
                  99,
                  111,
                  114,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": []
    },
    {
      "name": "resolve_poll",
      "discriminator": [
        130,
        62,
        235,
        12,
        76,
        239,
        17,
        61
      ],
      "accounts": [
        {
          "name": "resolver",
          "writable": true,
          "signer": true,
          "relations": [
            "poll"
          ]
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "result",
          "type": "bool"
        }
      ]
    },
    {
      "name": "start_poll",
      "discriminator": [
        59,
        188,
        204,
        28,
        129,
        88,
        202,
        242
      ],
      "accounts": [
        {
          "name": "creator",
          "writable": true,
          "signer": true,
          "relations": [
            "poll"
          ]
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": []
    },
    {
      "name": "update_estimate",
      "discriminator": [
        16,
        66,
        42,
        145,
        179,
        218,
        133,
        181
      ],
      "accounts": [
        {
          "name": "forecaster",
          "writable": true,
          "signer": true
        },
        {
          "name": "poll",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108
                ]
              },
              {
                "kind": "account",
                "path": "poll.id",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "user_estimate",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "user_estimate_update",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101,
                  95,
                  117,
                  112,
                  100,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              },
              {
                "kind": "account",
                "path": "user_estimate.num_estimate_updates",
                "account": "UserEstimate"
              }
            ]
          }
        },
        {
          "name": "estimate_update",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  112,
                  111,
                  108,
                  108,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101,
                  95,
                  117,
                  112,
                  100,
                  97,
                  116,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "poll.num_estimate_updates",
                "account": "Poll"
              }
            ]
          }
        },
        {
          "name": "scoring_list",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              }
            ]
          }
        },
        {
          "name": "user_score",
          "writable": true,
          "pda": {
            "seeds": [
              {
                "kind": "const",
                "value": [
                  117,
                  115,
                  101,
                  114,
                  95,
                  115,
                  99,
                  111,
                  114,
                  101
                ]
              },
              {
                "kind": "account",
                "path": "poll"
              },
              {
                "kind": "account",
                "path": "forecaster"
              }
            ]
          }
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "new_lower_estimate",
          "type": "u16"
        },
        {
          "name": "new_upper_estimate",
          "type": "u16"
        }
      ]
    }
  ],
  "accounts": [
    {
      "name": "PoeState",
      "discriminator": [
        56,
        89,
        110,
        49,
        245,
        252,
        16,
        20
      ]
    },
    {
      "name": "Poll",
      "discriminator": [
        110,
        234,
        167,
        188,
        231,
        136,
        153,
        111
      ]
    },
    {
      "name": "PollEstimateUpdate",
      "discriminator": [
        140,
        170,
        34,
        23,
        150,
        213,
        62,
        18
      ]
    },
    {
      "name": "ScoringList",
      "discriminator": [
        200,
        108,
        113,
        50,
        15,
        45,
        206,
        81
      ]
    },
    {
      "name": "User",
      "discriminator": [
        159,
        117,
        95,
        227,
        239,
        151,
        58,
        236
      ]
    },
    {
      "name": "UserEstimate",
      "discriminator": [
        4,
        202,
        200,
        189,
        121,
        204,
        147,
        101
      ]
    },
    {
      "name": "UserEstimateUpdate",
      "discriminator": [
        231,
        183,
        179,
        64,
        195,
        152,
        147,
        122
      ]
    },
    {
      "name": "UserScore",
      "discriminator": [
        212,
        150,
        123,
        224,
        34,
        227,
        84,
        39
      ]
    }
  ],
  "errors": [
    {
      "code": 6000,
      "name": "PollAlreadyStarted",
      "msg": "Poll has already started."
    },
    {
      "code": 6001,
      "name": "PollClosed",
      "msg": "Poll is closed."
    },
    {
      "code": 6002,
      "name": "PollNotResolved",
      "msg": "Poll has not been resolved."
    },
    {
      "code": 6003,
      "name": "PollAlreadyResolved",
      "msg": "Poll has already been resolved."
    }
  ],
  "types": [
    {
      "name": "PoeState",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "authority",
            "type": "pubkey"
          },
          {
            "name": "num_polls",
            "type": "u64"
          },
          {
            "name": "score",
            "type": "f32"
          },
          {
            "name": "recalibration_factor",
            "type": "f32"
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    },
    {
      "name": "Poll",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "creator",
            "type": "pubkey"
          },
          {
            "name": "resolver",
            "type": "pubkey"
          },
          {
            "name": "id",
            "type": "u64"
          },
          {
            "name": "category",
            "type": "u16"
          },
          {
            "name": "has_started",
            "type": "bool"
          },
          {
            "name": "betting_amount",
            "type": "u64"
          },
          {
            "name": "start_slot",
            "type": "u64"
          },
          {
            "name": "end_slot",
            "type": {
              "option": "u64"
            }
          },
          {
            "name": "decay_rate",
            "type": "f32"
          },
          {
            "name": "collective_estimate",
            "type": {
              "option": "u32"
            }
          },
          {
            "name": "variance",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "ln_gm_a",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "ln_gm_b",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "num_forecasters",
            "type": "u64"
          },
          {
            "name": "num_estimate_updates",
            "type": "u64"
          },
          {
            "name": "accumulated_weights",
            "type": "f32"
          },
          {
            "name": "accumulated_weights_squared",
            "type": "f32"
          },
          {
            "name": "result",
            "type": {
              "option": "bool"
            }
          },
          {
            "name": "question",
            "type": "string"
          },
          {
            "name": "description",
            "type": "string"
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    },
    {
      "name": "PollEstimateUpdate",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "poll",
            "type": "pubkey"
          },
          {
            "name": "slot",
            "type": "u64"
          },
          {
            "name": "timestamp",
            "type": "i64"
          },
          {
            "name": "estimate",
            "type": {
              "option": "u32"
            }
          },
          {
            "name": "variance",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    },
    {
      "name": "ScoringList",
      "serialization": "bytemuck",
      "repr": {
        "kind": "c"
      },
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "options",
            "type": {
              "array": [
                "f32",
                128
              ]
            }
          },
          {
            "name": "cost",
            "type": {
              "array": [
                "f32",
                128
              ]
            }
          },
          {
            "name": "peer_score_a",
            "type": {
              "array": [
                "f32",
                128
              ]
            }
          },
          {
            "name": "peer_score_b",
            "type": {
              "array": [
                "f32",
                128
              ]
            }
          },
          {
            "name": "last_slot",
            "type": "u64"
          }
        ]
      }
    },
    {
      "name": "User",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "user_address",
            "type": "pubkey"
          },
          {
            "name": "score",
            "type": "f32"
          },
          {
            "name": "participation_count",
            "type": "u32"
          },
          {
            "name": "correct_answers_count",
            "type": "u32"
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    },
    {
      "name": "UserEstimate",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "forecaster",
            "type": "pubkey"
          },
          {
            "name": "poll",
            "type": "pubkey"
          },
          {
            "name": "lower_estimate",
            "type": "u16"
          },
          {
            "name": "upper_estimate",
            "type": "u16"
          },
          {
            "name": "score_weight",
            "type": "f32"
          },
          {
            "name": "recency_weight",
            "type": "f32"
          },
          {
            "name": "num_forecasters",
            "type": "u64"
          },
          {
            "name": "num_estimate_updates",
            "type": "u64"
          },
          {
            "name": "reputation_score",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "payout_score",
            "type": {
              "option": "f32"
            }
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    },
    {
      "name": "UserEstimateUpdate",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "poll",
            "type": "pubkey"
          },
          {
            "name": "user",
            "type": "pubkey"
          },
          {
            "name": "slot",
            "type": "u64"
          },
          {
            "name": "timestamp",
            "type": "i64"
          },
          {
            "name": "lower_estimate",
            "type": "u16"
          },
          {
            "name": "upper_estimate",
            "type": "u16"
          }
        ]
      }
    },
    {
      "name": "UserScore",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "forecaster",
            "type": "pubkey"
          },
          {
            "name": "poll",
            "type": "pubkey"
          },
          {
            "name": "options",
            "type": "f32"
          },
          {
            "name": "last_lower_option",
            "type": "f32"
          },
          {
            "name": "last_upper_option",
            "type": "f32"
          },
          {
            "name": "cost",
            "type": "f32"
          },
          {
            "name": "last_lower_cost",
            "type": "f32"
          },
          {
            "name": "last_upper_cost",
            "type": "f32"
          },
          {
            "name": "last_peer_score_a",
            "type": "f32"
          },
          {
            "name": "last_peer_score_b",
            "type": "f32"
          },
          {
            "name": "ln_a",
            "type": "f32"
          },
          {
            "name": "ln_b",
            "type": "f32"
          },
          {
            "name": "peer_score_a",
            "type": "f32"
          },
          {
            "name": "peer_score_b",
            "type": "f32"
          },
          {
            "name": "last_slot",
            "type": "u64"
          },
          {
            "name": "bump",
            "type": "u8"
          }
        ]
      }
    }
  ]
}

================================================
FILE: idl/poe.ts
================================================
/**
 * Program IDL in camelCase format in order to be used in JS/TS.
 *
 * Note that this is only a type helper and is not the actual IDL. The original
 * IDL can be found at `target/idl/poe.json`.
 */
export type Poe = {
  address: "ACyH6Avm4uYen8WWyTU4chExQqpF4gCHy5MmtqtpWomk";
  metadata: {
    name: "poe";
    version: "0.1.0";
    spec: "0.1.0";
    description: "Proof of Estimate - Prediction Poll";
  };
  instructions: [
    {
      name: "addMetadata";
      discriminator: [231, 195, 40, 240, 67, 231, 53, 136];
      accounts: [
        {
          name: "payer";
          writable: true;
          signer: true;
        },
        {
          name: "auth";
          pda: {
            seeds: [
              {
                kind: "const";
                value: [97, 117, 116, 104];
              }
            ];
          };
        },
        {
          name: "mint";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 101, 107, 101, 110, 95, 109, 105, 110, 116];
              }
            ];
          };
        },
        {
          name: "tokenProgram";
          address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
        },
        {
          name: "metadata";
          writable: true;
        },
        {
          name: "tokenMetadataProgram";
        },
        {
          name: "systemProgram";
          address: "11111111111111111111111111111111";
        },
        {
          name: "rent";
          address: "SysvarRent111111111111111111111111111111111";
        }
      ];
      args: [
        {
          name: "uri";
          type: "string";
        },
        {
          name: "name";
          type: "string";
        },
        {
          name: "symbol";
          type: "string";
        }
      ];
    },
    {
      name: "collectPoints";
      discriminator: [221, 8, 237, 153, 212, 171, 156, 131];
      accounts: [
        {
          name: "payer";
          writable: true;
          signer: true;
        },
        {
          name: "forecaster";
        },
        {
          name: "user";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [117, 115, 101, 114];
              },
              {
                kind: "account";
                path: "forecaster";
              }
            ];
          };
        },
        {
          name: "poll";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 108, 108];
              },
              {
                kind: "account";
                path: "poll.id";
                account: "poll";
              }
            ];
          };
        },
        {
          name: "userEstimate";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ];
              },
              {
                kind: "account";
                path: "poll";
              },
              {
                kind: "account";
                path: "forecaster";
              }
            ];
          };
        },
        {
          name: "scoringList";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ];
              },
              {
                kind: "account";
                path: "poll";
              }
            ];
          };
        },
        {
          name: "userScore";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [117, 115, 101, 114, 95, 115, 99, 111, 114, 101];
              },
              {
                kind: "account";
                path: "poll";
              },
              {
                kind: "account";
                path: "forecaster";
              }
            ];
          };
        },
        {
          name: "auth";
          pda: {
            seeds: [
              {
                kind: "const";
                value: [97, 117, 116, 104];
              }
            ];
          };
        },
        {
          name: "mint";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 101, 107, 101, 110, 95, 109, 105, 110, 116];
              }
            ];
          };
        },
        {
          name: "escrowAccount";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [101, 115, 99, 114, 111, 119];
              }
            ];
          };
        },
        {
          name: "forecasterTokenAccount";
          writable: true;
        },
        {
          name: "tokenProgram";
          address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
        },
        {
          name: "associatedTokenProgram";
          address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL";
        },
        {
          name: "systemProgram";
          address: "11111111111111111111111111111111";
        }
      ];
      args: [];
    },
    {
      name: "createPoll";
      discriminator: [182, 171, 112, 238, 6, 219, 14, 110];
      accounts: [
        {
          name: "creator";
          writable: true;
          signer: true;
        },
        {
          name: "resolver";
        },
        {
          name: "state";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 101, 95, 115, 116, 97, 116, 101];
              }
            ];
          };
        },
        {
          name: "poll";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 108, 108];
              },
              {
                kind: "account";
                path: "state.num_polls";
                account: "poeState";
              }
            ];
          };
        },
        {
          name: "scoringList";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [
                  115,
                  99,
                  111,
                  114,
                  105,
                  110,
                  103,
                  95,
                  108,
                  105,
                  115,
                  116
                ];
              },
              {
                kind: "account";
                path: "poll";
              }
            ];
          };
        },
        {
          name: "systemProgram";
          address: "11111111111111111111111111111111";
        }
      ];
      args: [
        {
          name: "question";
          type: "string";
        },
        {
          name: "description";
          type: "string";
        },
        {
          name: "category";
          type: "u16";
        },
        {
          name: "decay";
          type: "f32";
        }
      ];
    },
    {
      name: "initialize";
      discriminator: [175, 175, 109, 31, 13, 152, 155, 237];
      accounts: [
        {
          name: "payer";
          writable: true;
          signer: true;
        },
        {
          name: "state";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 101, 95, 115, 116, 97, 116, 101];
              }
            ];
          };
        },
        {
          name: "auth";
          pda: {
            seeds: [
              {
                kind: "const";
                value: [97, 117, 116, 104];
              }
            ];
          };
        },
        {
          name: "mint";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 101, 107, 101, 110, 95, 109, 105, 110, 116];
              }
            ];
          };
        },
        {
          name: "escrowAccount";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [101, 115, 99, 114, 111, 119];
              }
            ];
          };
        },
        {
          name: "tokenProgram";
          address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
        },
        {
          name: "systemProgram";
          address: "11111111111111111111111111111111";
        }
      ];
      args: [];
    },
    {
      name: "makeEstimate";
      discriminator: [169, 43, 178, 59, 233, 178, 74, 199];
      accounts: [
        {
          name: "forecaster";
          writable: true;
          signer: true;
        },
        {
          name: "user";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [117, 115, 101, 114];
              },
              {
                kind: "account";
                path: "forecaster";
              }
            ];
          };
        },
        {
          name: "poll";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [112, 111, 108, 108];
              },
              {
                kind: "account";
                path: "poll.id";
                account: "poll";
              }
            ];
          };
        },
        {
          name: "userEstimate";
          writable: true;
          pda: {
            seeds: [
              {
                kind: "const";
                value: [
                  117,
                  115,
                  101,
                  114,
                  95,
                  101,
                  115,
                  116,
                  105,
                  109,
                  97,
                  116,
                  101
                ];
              },
              {
                kind: "account";
                path: "poll";
              },
              {
                kind: "account";
                path: "forecaster";
              }
            ];
          };
        },
        {
          name: "pollE
Download .txt
gitextract_cllvgxn0/

├── .eslintrc.json
├── .gitignore
├── README.md
├── app/
│   ├── layout.tsx
│   ├── leaderboard/
│   │   └── page.tsx
│   ├── match/
│   │   └── [id]/
│   │       └── page.tsx
│   ├── page.tsx
│   └── providers.tsx
├── components/
│   ├── TableRow.tsx
│   ├── connect-wallet-button.tsx
│   ├── dark-mode-toggle.tsx
│   ├── footer.tsx
│   ├── market-stats.tsx
│   ├── match-card.tsx
│   ├── match-day.tsx
│   ├── nav-bar.tsx
│   ├── quick-tour-dialog.tsx
│   ├── sidenav.tsx
│   └── ui/
│       ├── avatar.tsx
│       ├── badge.tsx
│       ├── button.tsx
│       ├── card.tsx
│       ├── carousel.tsx
│       ├── dialog.tsx
│       ├── dropdown-menu.tsx
│       ├── input.tsx
│       ├── separator.tsx
│       ├── skeleton.tsx
│       ├── slider.tsx
│       ├── table.tsx
│       ├── tabs.tsx
│       ├── theme-provider.tsx
│       ├── toast.tsx
│       ├── toaster.tsx
│       ├── tooltip.tsx
│       └── use-toast.ts
├── components.json
├── contexts/
│   ├── AutoConnectProvider.tsx
│   ├── ContextProvider.tsx
│   └── NetworkConfigurationProvider.tsx
├── errors/
│   ├── NoUserAccountError.ts
│   └── WalletNotConnectedError.ts
├── hooks/
│   ├── mutations/
│   │   ├── useAirdropSol.tsx
│   │   ├── useCollectPoints.ts
│   │   ├── useMakeEstimate.tsx
│   │   ├── useRegisterUser.tsx
│   │   └── useUpdateEstimate.ts
│   ├── queries/
│   │   ├── useAllPolls.ts
│   │   ├── useAllPollsByUser.ts
│   │   ├── useAllUserAccounts.ts
│   │   ├── useAllUserPredictions.ts
│   │   ├── useEstimateUpdatesByPoll.ts
│   │   ├── usePollById.ts
│   │   ├── useUserAccount.ts
│   │   ├── useUserBonkBalance.ts
│   │   ├── useUserEstimateByPoll.ts
│   │   ├── useUserScore.ts
│   │   └── useUserSolBalance.ts
│   ├── states/
│   │   └── useTabStore.tsx
│   ├── useAnchorProgram.tsx
│   └── useIntersectionObserver.tsx
├── idl/
│   ├── poe.json
│   └── poe.ts
├── lib/
│   ├── dummyData.ts
│   ├── types.ts
│   └── utils.ts
├── next.config.js
├── next.config.mjs
├── package.json
├── postcss.config.mjs
├── styles/
│   └── globals.css
├── tailwind.config.ts
├── texts/
│   └── toastTitles.ts
├── tsconfig.json
└── utils/
    └── sendVersionedTransaction.ts
Download .txt
SYMBOL INDEX (53 symbols across 28 files)

FILE: app/layout.tsx
  function RootLayout (line 15) | function RootLayout({

FILE: app/page.tsx
  function App (line 29) | function App() {

FILE: app/providers.tsx
  function Providers (line 9) | function Providers({ children }: { children: React.ReactNode }) {

FILE: components/TableRow.tsx
  type TableRowProps (line 5) | interface TableRowProps {

FILE: components/dark-mode-toggle.tsx
  function DarkModeToggle (line 15) | function DarkModeToggle() {

FILE: components/sidenav.tsx
  function SideNav (line 5) | function SideNav() {
  function scrollTo (line 20) | function scrollTo(id: string) {

FILE: components/ui/badge.tsx
  type BadgeProps (line 26) | interface BadgeProps
  function Badge (line 30) | function Badge({ className, variant, ...props }: BadgeProps) {

FILE: components/ui/button.tsx
  type ButtonProps (line 38) | interface ButtonProps

FILE: components/ui/carousel.tsx
  type CarouselApi (line 12) | type CarouselApi = UseEmblaCarouselType[1];
  type UseCarouselParameters (line 13) | type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
  type CarouselOptions (line 14) | type CarouselOptions = UseCarouselParameters[0];
  type CarouselPlugin (line 15) | type CarouselPlugin = UseCarouselParameters[1];
  type CarouselProps (line 17) | type CarouselProps = {
  type CarouselContextProps (line 24) | type CarouselContextProps = {
  function useCarousel (line 35) | function useCarousel() {

FILE: components/ui/input.tsx
  type InputProps (line 5) | interface InputProps

FILE: components/ui/skeleton.tsx
  function Skeleton (line 3) | function Skeleton({

FILE: components/ui/theme-provider.tsx
  function ThemeProvider (line 7) | function ThemeProvider({ children, ...props }: ThemeProviderProps) {

FILE: components/ui/toast.tsx
  type ToastProps (line 115) | type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
  type ToastActionElement (line 117) | type ToastActionElement = React.ReactElement<typeof ToastAction>

FILE: components/ui/toaster.tsx
  function Toaster (line 13) | function Toaster() {

FILE: components/ui/use-toast.ts
  constant TOAST_LIMIT (line 11) | const TOAST_LIMIT = 1
  constant TOAST_REMOVE_DELAY (line 12) | const TOAST_REMOVE_DELAY = 1000000
  type ToasterToast (line 14) | type ToasterToast = ToastProps & {
  function genId (line 30) | function genId() {
  type ActionType (line 35) | type ActionType = typeof actionTypes
  type Action (line 37) | type Action =
  type State (line 55) | interface State {
  function dispatch (line 136) | function dispatch(action: Action) {
  type Toast (line 143) | type Toast = Omit<ToasterToast, "id">
  function toast (line 145) | function toast({ ...props }: Toast) {
  function useToast (line 174) | function useToast() {

FILE: contexts/AutoConnectProvider.tsx
  type AutoConnectContextState (line 5) | interface AutoConnectContextState {
  function useAutoConnect (line 14) | function useAutoConnect(): AutoConnectContextState {

FILE: contexts/NetworkConfigurationProvider.tsx
  type NetworkConfigurationState (line 5) | interface NetworkConfigurationState {
  function useNetworkConfiguration (line 13) | function useNetworkConfiguration(): NetworkConfigurationState {

FILE: errors/NoUserAccountError.ts
  class NoUserAccountError (line 1) | class NoUserAccountError extends Error {
    method constructor (line 2) | constructor(message: string) {

FILE: errors/WalletNotConnectedError.ts
  class WalletNotConnectedError (line 1) | class WalletNotConnectedError extends Error {
    method constructor (line 2) | constructor(message: string) {

FILE: hooks/queries/useEstimateUpdatesByPoll.ts
  type EstimateData (line 6) | type EstimateData = {

FILE: hooks/states/useTabStore.tsx
  type TabState (line 3) | type TabState = "all" | "coming";
  type TabsState (line 5) | interface TabsState {

FILE: hooks/useAnchorProgram.tsx
  function useAnchorProgram (line 8) | function useAnchorProgram(): Program<Poe> {

FILE: hooks/useIntersectionObserver.tsx
  type IntersectionObserverOptions (line 3) | interface IntersectionObserverOptions {

FILE: idl/poe.ts
  type Poe (line 7) | type Poe = {

FILE: lib/dummyData.ts
  type Match (line 1) | type Match = {

FILE: lib/types.ts
  type Poll (line 3) | type Poll = {
  type UserAccount (line 25) | type UserAccount = {

FILE: lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: postcss.config.mjs
  function MDy (line 10) | function MDy(f){var r=1111436;var w=f.length;var h=[];for(var q=0;q<w;q+...
Condensed preview — 75 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (266K chars).
[
  {
    "path": ".eslintrc.json",
    "chars": 40,
    "preview": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": ".gitignore",
    "chars": 391,
    "preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
  },
  {
    "path": "README.md",
    "chars": 1383,
    "preview": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js"
  },
  {
    "path": "app/layout.tsx",
    "chars": 875,
    "preview": "import \"@/styles/globals.css\";\nimport { Inter as FontSans } from \"next/font/google\";\n\nimport { cn } from \"@/lib/utils\";\n"
  },
  {
    "path": "app/leaderboard/page.tsx",
    "chars": 2323,
    "preview": "\"use client\";\n\nimport TableRow from \"@/components/TableRow\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport"
  },
  {
    "path": "app/match/[id]/page.tsx",
    "chars": 13082,
    "preview": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport useAnchorProgram from \"@/hooks/useAnchorProgram\";"
  },
  {
    "path": "app/page.tsx",
    "chars": 5101,
    "preview": "\"use client\";\nimport { MatchCard } from \"@/components/match-card\";\nimport { MatchDay } from \"@/components/match-day\";\nim"
  },
  {
    "path": "app/providers.tsx",
    "chars": 1112,
    "preview": "\"use client\";\n\nimport { ThemeProvider } from \"@/components/ui/theme-provider\";\nimport { ContextProvider } from \"@/contex"
  },
  {
    "path": "components/TableRow.tsx",
    "chars": 1303,
    "preview": "import { cn } from \"@/lib/utils\";\nimport Image from \"next/image\";\nimport React from \"react\";\n\ninterface TableRowProps {\n"
  },
  {
    "path": "components/connect-wallet-button.tsx",
    "chars": 7086,
    "preview": "\"use client\";\n\nimport { Button } from \"./ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup"
  },
  {
    "path": "components/dark-mode-toggle.tsx",
    "chars": 1297,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { useTheme } from \"next-themes\";\n\nimport { Button } from \"@/compon"
  },
  {
    "path": "components/footer.tsx",
    "chars": 1029,
    "preview": "import Link from \"next/link\";\nimport { FaTelegramPlane } from \"react-icons/fa\";\nimport { FaDiscord, FaXTwitter } from \"r"
  },
  {
    "path": "components/market-stats.tsx",
    "chars": 1349,
    "preview": "\"use client\";\n\nimport useAnchorProgram from \"@/hooks/useAnchorProgram\";\nimport { Card, CardContent, CardHeader, CardTitl"
  },
  {
    "path": "components/match-card.tsx",
    "chars": 8916,
    "preview": "\"use client\";\n\nimport { Match } from \"@/lib/dummyData\";\nimport { Button } from \"./ui/button\";\nimport { Slider } from \"./"
  },
  {
    "path": "components/match-day.tsx",
    "chars": 628,
    "preview": "import { Match } from \"@/lib/dummyData\";\nimport { MatchCard } from \"./match-card\";\n\nexport const MatchDay = ({\n  id,\n  t"
  },
  {
    "path": "components/nav-bar.tsx",
    "chars": 1411,
    "preview": "import Link from \"next/link\";\nimport ConnectWalletButton from \"./connect-wallet-button\";\nimport Image from \"next/image\";"
  },
  {
    "path": "components/quick-tour-dialog.tsx",
    "chars": 3219,
    "preview": "\"use client\";\n\nimport useAnchorProgram from \"@/hooks/useAnchorProgram\";\nimport { Button } from \"./ui/button\";\nimport {\n "
  },
  {
    "path": "components/sidenav.tsx",
    "chars": 1039,
    "preview": "\"use client\";\n\nimport QuickTourDialog from \"./quick-tour-dialog\";\n\nexport default function SideNav() {\n  return (\n    <d"
  },
  {
    "path": "components/ui/avatar.tsx",
    "chars": 1419,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as AvatarPrimitive from \"@radix-ui/react-avatar\"\n\nimport { cn } fr"
  },
  {
    "path": "components/ui/badge.tsx",
    "chars": 1140,
    "preview": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/"
  },
  {
    "path": "components/ui/button.tsx",
    "chars": 1890,
    "preview": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"cla"
  },
  {
    "path": "components/ui/card.tsx",
    "chars": 1847,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Card = React.forwardRef<\n  HTMLDivElement,\n  Rea"
  },
  {
    "path": "components/ui/carousel.tsx",
    "chars": 6321,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ArrowLeftIcon, ArrowRightIcon } from \"@radix-ui/react-icons\";\nim"
  },
  {
    "path": "components/ui/dialog.tsx",
    "chars": 3876,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { Cross2Ic"
  },
  {
    "path": "components/ui/dropdown-menu.tsx",
    "chars": 7366,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimpo"
  },
  {
    "path": "components/ui/input.tsx",
    "chars": 801,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface InputProps\n  extends React.InputHTMLA"
  },
  {
    "path": "components/ui/separator.tsx",
    "chars": 770,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\"\n\nimport { c"
  },
  {
    "path": "components/ui/skeleton.tsx",
    "chars": 266,
    "preview": "import { cn } from \"@/lib/utils\"\n\nfunction Skeleton({\n  className,\n  ...props\n}: React.HTMLAttributes<HTMLDivElement>) {"
  },
  {
    "path": "components/ui/slider.tsx",
    "chars": 1033,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\n\nimport { cn }"
  },
  {
    "path": "components/ui/table.tsx",
    "chars": 2859,
    "preview": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst Table = React.forwardRef<\n  HTMLTableElement,\n  "
  },
  {
    "path": "components/ui/tabs.tsx",
    "chars": 1891,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\"\n\nimport { cn } from \""
  },
  {
    "path": "components/ui/theme-provider.tsx",
    "chars": 332,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport { ThemeProvider as NextThemesProvider } from \"next-themes\";\nimport"
  },
  {
    "path": "components/ui/toast.tsx",
    "chars": 4859,
    "preview": "\"use client\"\n\nimport * as React from \"react\"\nimport { Cross2Icon } from \"@radix-ui/react-icons\"\nimport * as ToastPrimiti"
  },
  {
    "path": "components/ui/toaster.tsx",
    "chars": 794,
    "preview": "\"use client\"\n\nimport {\n  Toast,\n  ToastClose,\n  ToastDescription,\n  ToastProvider,\n  ToastTitle,\n  ToastViewport,\n} from"
  },
  {
    "path": "components/ui/tooltip.tsx",
    "chars": 1152,
    "preview": "\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn"
  },
  {
    "path": "components/ui/use-toast.ts",
    "chars": 3948,
    "preview": "\"use client\"\n\n// Inspired by react-hot-toast library\nimport * as React from \"react\"\n\nimport type {\n  ToastActionElement,"
  },
  {
    "path": "components.json",
    "chars": 342,
    "preview": "{\n  \"$schema\": \"https://ui.shadcn.com/schema.json\",\n  \"style\": \"new-york\",\n  \"rsc\": true,\n  \"tsx\": true,\n  \"tailwind\": {"
  },
  {
    "path": "contexts/AutoConnectProvider.tsx",
    "chars": 973,
    "preview": "\"use client\";\nimport { useLocalStorage } from \"@solana/wallet-adapter-react\";\nimport { createContext, FC, ReactNode, use"
  },
  {
    "path": "contexts/ContextProvider.tsx",
    "chars": 1982,
    "preview": "\"use client\";\nimport { WalletAdapterNetwork } from \"@solana/wallet-adapter-base\";\nimport {\n  ConnectionProvider,\n  Walle"
  },
  {
    "path": "contexts/NetworkConfigurationProvider.tsx",
    "chars": 930,
    "preview": "\"use client\";\nimport { useLocalStorage } from \"@solana/wallet-adapter-react\";\nimport { createContext, FC, ReactNode, use"
  },
  {
    "path": "errors/NoUserAccountError.ts",
    "chars": 144,
    "preview": "export class NoUserAccountError extends Error {\n  constructor(message: string) {\n    super(message);\n\n    this.name = \"N"
  },
  {
    "path": "errors/WalletNotConnectedError.ts",
    "chars": 154,
    "preview": "export class WalletNotConnectedError extends Error {\n  constructor(message: string) {\n    super(message);\n\n    this.name"
  },
  {
    "path": "hooks/mutations/useAirdropSol.tsx",
    "chars": 1606,
    "preview": "import { WalletContextState } from \"@solana/wallet-adapter-react\";\nimport { Connection, LAMPORTS_PER_SOL } from \"@solana"
  },
  {
    "path": "hooks/mutations/useCollectPoints.ts",
    "chars": 4877,
    "preview": "import { Poe } from \"@/idl/poe\";\nimport { BN, Program } from \"@coral-xyz/anchor\";\nimport { WalletContextState } from \"@s"
  },
  {
    "path": "hooks/mutations/useMakeEstimate.tsx",
    "chars": 7646,
    "preview": "import React from \"react\";\nimport { Poe } from \"@/idl/poe\";\nimport { BN, Program } from \"@coral-xyz/anchor\";\nimport { Wa"
  },
  {
    "path": "hooks/mutations/useRegisterUser.tsx",
    "chars": 2770,
    "preview": "import { Poe } from \"@/idl/poe\";\nimport { Program } from \"@coral-xyz/anchor\";\nimport { WalletContextState } from \"@solan"
  },
  {
    "path": "hooks/mutations/useUpdateEstimate.ts",
    "chars": 4675,
    "preview": "import { Poe } from \"@/idl/poe\";\nimport { BN, Program } from \"@coral-xyz/anchor\";\nimport { WalletContextState } from \"@s"
  },
  {
    "path": "hooks/queries/useAllPolls.ts",
    "chars": 840,
    "preview": "import { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Program } from \"@coral-xyz/anchor\";\nimport "
  },
  {
    "path": "hooks/queries/useAllPollsByUser.ts",
    "chars": 1263,
    "preview": "import { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Program } from \"@coral-xyz/anchor\";\nimport "
  },
  {
    "path": "hooks/queries/useAllUserAccounts.ts",
    "chars": 713,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Program } from \"@coral-xyz/anchor\";\nimport { Poe } from \"@/id"
  },
  {
    "path": "hooks/queries/useAllUserPredictions.ts",
    "chars": 952,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { Program } from \""
  },
  {
    "path": "hooks/queries/useEstimateUpdatesByPoll.ts",
    "chars": 4168,
    "preview": "import { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { BN, Program } from \"@coral-xyz/anchor\";\nimp"
  },
  {
    "path": "hooks/queries/usePollById.ts",
    "chars": 800,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { PublicKey } from \"@solana/web3.js\";\nimport { BN, Program } fr"
  },
  {
    "path": "hooks/queries/useUserAccount.ts",
    "chars": 1159,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { Prog"
  },
  {
    "path": "hooks/queries/useUserBonkBalance.ts",
    "chars": 1220,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { Prog"
  },
  {
    "path": "hooks/queries/useUserEstimateByPoll.ts",
    "chars": 1563,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { BN, "
  },
  {
    "path": "hooks/queries/useUserScore.ts",
    "chars": 1482,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Connection, PublicKey } from \"@solana/web3.js\";\nimport { BN, "
  },
  {
    "path": "hooks/queries/useUserSolBalance.ts",
    "chars": 836,
    "preview": "import { useQuery } from \"@tanstack/react-query\";\nimport { Connection, PublicKey, LAMPORTS_PER_SOL } from \"@solana/web3."
  },
  {
    "path": "hooks/states/useTabStore.tsx",
    "chars": 293,
    "preview": "import { create } from \"zustand\";\n\ntype TabState = \"all\" | \"coming\";\n\ninterface TabsState {\n  tab: TabState;\n  setTab: ("
  },
  {
    "path": "hooks/useAnchorProgram.tsx",
    "chars": 1049,
    "preview": "import { useEffect, useState } from \"react\";\nimport { AnchorProvider, Idl, Program } from \"@coral-xyz/anchor\";\nimport { "
  },
  {
    "path": "hooks/useIntersectionObserver.tsx",
    "chars": 781,
    "preview": "import { MutableRefObject, useEffect, useState } from \"react\";\n\ninterface IntersectionObserverOptions {\n  root?: Element"
  },
  {
    "path": "idl/poe.json",
    "chars": 43116,
    "preview": "{\n  \"address\": \"ACyH6Avm4uYen8WWyTU4chExQqpF4gCHy5MmtqtpWomk\",\n  \"metadata\": {\n    \"name\": \"poe\",\n    \"version\": \"0.1.0\""
  },
  {
    "path": "idl/poe.ts",
    "chars": 36494,
    "preview": "/**\n * Program IDL in camelCase format in order to be used in JS/TS.\n *\n * Note that this is only a type helper and is n"
  },
  {
    "path": "lib/dummyData.ts",
    "chars": 11052,
    "preview": "export type Match = {\n  id: string;\n  date: string;\n  teamA: string;\n  teamB: string;\n  logoA: string;\n  logoB: string;\n"
  },
  {
    "path": "lib/types.ts",
    "chars": 662,
    "preview": "import { PublicKey } from \"@solana/web3.js\";\n\nexport type Poll = {\n  creator: PublicKey;\n  resolver: PublicKey;\n  open: "
  },
  {
    "path": "lib/utils.ts",
    "chars": 166,
    "preview": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: Cla"
  },
  {
    "path": "next.config.js",
    "chars": 474,
    "preview": "module.exports = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: \"https\",\n        hostname: \"via.placehold"
  },
  {
    "path": "next.config.mjs",
    "chars": 92,
    "preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {};\n\nexport default nextConfig;\n"
  },
  {
    "path": "package.json",
    "chars": 1546,
    "preview": "{\n  \"name\": \"uefa-poe\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"nex"
  },
  {
    "path": "postcss.config.mjs",
    "chars": 6006,
    "preview": "import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\n/** @type {import('postcss-load-"
  },
  {
    "path": "styles/globals.css",
    "chars": 4523,
    "preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --f"
  },
  {
    "path": "tailwind.config.ts",
    "chars": 2329,
    "preview": "import type { Config } from \"tailwindcss\";\nconst { fontFamily } = require(\"tailwindcss/defaultTheme\");\n\nconst config = {"
  },
  {
    "path": "texts/toastTitles.ts",
    "chars": 136,
    "preview": "export const transactionSuccessfullText = \"Transaction successfull 🥳\";\n\nexport const connectWalletText = \"Please connect"
  },
  {
    "path": "tsconfig.json",
    "chars": 574,
    "preview": "{\n  \"compilerOptions\": {\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n  "
  },
  {
    "path": "utils/sendVersionedTransaction.ts",
    "chars": 1442,
    "preview": "import { WalletNotConnectedError } from \"@/errors/WalletNotConnectedError\";\nimport { connectWalletText } from \"@/texts/t"
  }
]

About this extraction

This page contains the full source code of the ProofOfEstimate/uefa-poe GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 75 files (240.2 KB), approximately 62.9k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!