# Interface

### **Prerequisites** <a href="#prerequisites" id="prerequisites"></a>

* [NodeJS](https://nodejs.org/en)
* [Metamask](https://metamask.io/)

In blockchain projects, it's often not practical to directly use ethers.js or web3.js to interact with MetaMask, as it can be cumbersome. To address this and facilitate faster connection and interaction with the blockchain, many excellent third-party libraries have been developed, such as web3-react, web3-modal, rainbow-kit (which is an encapsulation based on wagmi), and wagmi. To quickly build the Coin Pusher dapp, we will use the [Next.js](https://nextjs.org/), [Wagmi](https://wagmi.sh/), [Rainbow-kit](https://www.rainbowkit.com/), and [Viem](https://viem.sh/) technology stack for development.

* **Nextjs**: It is a React framework for building server-rendered and static websites and web applications.
* **Wagmi**: It is a React Hooks library for Ethereum, designed to simplify interactions with the Ethereum blockchain and smart contracts. It supports over 40 out-of-the-box Ethereum features for accounts, wallets, contracts, transactions, signatures, ENS, and more. Wagmi also supports almost all wallets through its official connectors, EIP-6963 support, and extensible API.
* **Rainbow-kit**: It is a React library that provides components to build a Connect Wallet UI with just a few lines of code. It supports many wallets, including Metamask, Rainbow, Coinbase Wallet, WalletConnect, and more. It is also highly customizable and comes with stunning built-in themes.
* **Viem**: It is a low-level TypeScript interface for Ethereum that enables developers to interact with the Ethereum blockchain, including: JSON-RPC API abstraction, smart contract interactions, wallet and signature implementations, encoding/parsing utilities, and more. Wagmi Core is essentially a wrapper for Viem, providing multi-chain capabilities through Wagmi Config and automatic account management through connectors.

**Final effect display**

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FsA7SobYN7h58tdBu9xf6%2F%E6%88%AA%E5%B1%8F2024-11-14%2015.41.43.png?alt=media&#x26;token=c4cbcb94-ce0d-4edc-88e5-2850a824b715" alt=""><figcaption></figcaption></figure>

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2Fd9EtxmuvNRxgLRew9o7a%2F%E6%88%AA%E5%B1%8F2024-11-14%2015.40.39.png?alt=media&#x26;token=07a89d99-5078-454e-bfea-986c63a9e4b3" alt=""><figcaption></figcaption></figure>

### Create a Nextjs project

```sh
mkdir coin_pusher_app
cd coin_pusher_app
npx create-next-app ./
```

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2Fgpvt6JLSujVz9ro1ZEET%2F%E6%88%AA%E5%B1%8F2024-11-08%2014.29.05.png?alt=media&#x26;token=5974052a-55b1-4819-a1e7-27f4f6c6a650" alt=""><figcaption></figcaption></figure>

The default version of React used in the current Next.js project is version 19, but it is not very stable and may have compatibility issues with other dependencies. Therefore, I suggest changing the version of `react` and `react-dom` in `package.json` to `"^18.2.0"`, and then re-running `npm install`.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FBA0jKP8bc4PhZfor3pXv%2F%E6%88%AA%E5%B1%8F2024-11-14%2015.59.56.png?alt=media&#x26;token=c15b5e7a-f507-4fde-ad2a-25c2f52b1454" alt="" width="372"><figcaption></figcaption></figure>

Run the project in development mode.

```sh
npm run dev
```

The image below is the Next.js initialization page.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FrZ7tC6grk1iinZDeUDbV%2F%E6%88%AA%E5%B1%8F2024-11-08%2016.00.02.png?alt=media&#x26;token=c5e0e45b-7ce9-4c1b-90fa-e2c75c02c782" alt=""><figcaption></figcaption></figure>

**Project structure.**

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2Fdjpw4r4PLMruUjaBgydy%2F%E6%88%AA%E5%B1%8F2024-11-08%2016.41.21.png?alt=media&#x26;token=bd346aaa-bbaf-4947-ae6a-6d3b0cd74910" alt="" width="453"><figcaption></figcaption></figure>

This is the project structure when initializing Next.js, but the `contract` folder is newly created, where is store the contract's ABI and contract address. You can copy the ABI and address of the contract you just deployed.

### Install dependencies

Install RainbowKit and its peer dependencies, [wagmi](https://wagmi.sh/), [viem](https://viem.sh/), and [@tanstack/react-query](https://tanstack.com/query/v5).

```sh
npm install @rainbow-me/rainbowkit wagmi viem@2.x @tanstack/react-query react-hot-toast 
```

> **Note**: RainbowKit is a [React](https://reactjs.org/) library.

### Import the RainbowKit configuration

Copy the following code snippet into the `/context/Web3Provider.tsx` file.

```typescript
"use client";

import "@rainbow-me/rainbowkit/styles.css";
import {
  getDefaultConfig,
  RainbowKitProvider,
  lightTheme,
} from "@rainbow-me/rainbowkit";
import { WagmiProvider } from "wagmi";
import { sepolia } from "wagmi/chains";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";

const config = getDefaultConfig({
  appName: "Coin Pusher",
  projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
  chains: [sepolia],
  ssr: true,
});

const queryClient = new QueryClient();

export default function Web3Provider({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider
          theme={lightTheme({
            accentColor: "#FF652B",
            accentColorForeground: "white",
          })}
        >
          {children}
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

```

This is the configuration file for [RainbowKit](https://www.rainbowkit.com/docs/installation). I have separated it and placed it in `/context/Web3Provider.tsx` to make the project structure clearer. Configure the required chains and generate the necessary connectors. You also need to set up a wagmi configuration.

> **Note**: Every dApp that relies on WalletConnect now needs to obtain a `projectId` from [WalletConnect Cloud](https://cloud.walletconnect.com/). This is absolutely free and only takes a few minutes.

Place the project ID in the `.env` folder.

```properties
NEXT_PUBLIC_PROJECT_ID=<YOUR_PROJECT_ID>
```

Import the `Web3Provider` configuration file into `app/layout.tsx`.

```typescript
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <Web3Provider>{children}</Web3Provider>
        <Toaster />
      </body>
    </html>
  );
}
```

The `Toaster` component comes from the `react-hot-toast` library, which is a lightweight, customizable notification library designed for React applications.

### Retrieve the status from contact

Copy the following code snippet into the `hooks/useGameStatus.ts` file.

```typescript
import { useEffect, useState } from "react";
import { useAccount, useReadContracts } from "wagmi";
import { abi } from "@/contract/contract-abi.json";
import { coinPusherAddress } from "@/contract/contract-address.json";
import { Address, formatUnits, zeroAddress } from "viem";

type GameStatus = {
  isCompleted: boolean;
  winner: Address;
  sum: string;
  target: string;
  state: number;
  myDeposit: string;
};

type GameStatusResult = {
  isCompleted: boolean;
  winner: Address;
  sum: bigint;
  target: bigint;
  state: number;
};

const wagmigotchiContract = {
  address: coinPusherAddress as `0x${string}`,
  abi: abi,
} as const;

export const useGameStatus = () => {
  const { address } = useAccount();
  const [gameStatus, setGameStatus] = useState<GameStatus>({
    isCompleted: false,
    winner: zeroAddress,
    sum: "0",
    target: "0",
    state: 0,
    myDeposit: "0",
  });

  const result = useReadContracts({
    contracts: [
      {
        ...wagmigotchiContract,
        functionName: "getGameStatus",
      },
      {
        ...wagmigotchiContract,
        functionName: "depositOf",
        args: [address],
      },
    ],
  });

  const data = result.data as [
    { result: GameStatusResult },
    { result: bigint | undefined }
  ];
  const { isLoading, refetch } = result;

  useEffect(() => {
    if (!data) return;
    setGameStatus({
      isCompleted: data[0].result.isCompleted,
      winner: data[0].result.winner,
      sum: formatUnits(data[0].result.sum, 6),
      target: formatUnits(data[0].result.target, 6),
      state: data[0].result.state,
      myDeposit: formatUnits(data[1].result || BigInt(0), 6),
    });
  }, [data]);

  return {
    gameStatus,
    isLoading,
    refetch,
  };
};
```

This is a custom Hook named `useGameStatus`, which is mainly used to retrieve the status of the Coin Pusher game.

**Explanation:**

* **useAccount**: a Hook for getting current account.
* **gameStatues:** This is a state declaration in a React Hook, using the `useState` Hook, which stores the state of the game.
* **useReadContracts**: `useReadContracts` is a Hook provided by wagmi for querying multiple contract functions at once. We use it to query the `getGameStatus` and `depositOf` functions. `wagmigotchiContract` contains the contract's ABI and address, which are necessary information for calling smart contracts.
* **isLoading and refetch**: `isLoading` and `refetch` are both data returned from the `useReadContracts` hook. `isLoading` is a boolean value indicating whether the data is currently loading, and `refetch` is a function used to fetch the data again.
* **useEffect**: This is a React `useEffect` Hook, used to listen for and handle updates to game status data. When the `data` changes, the callback function within `useEffect` is executed, and within this callback function, the data from `data` is synchronized with `gameStatus`.
* **formatUnits**: `formatUnits` is a utility function in viem (an Ethereum development library), primarily used to convert large on-chain values (usually represented in their smallest units) into a human-readable format.

### Deposit function

Copy the following code snippet into the `hooks/useGameDeposit.ts` file.

```typescript
import { useWaitForTransactionReceipt, useWriteContract } from "wagmi";
import { parseEther } from "viem";

import { coinPusherAddress } from "@/contract/contract-address.json";
import { abi } from "@/contract/contract-abi.json";
import toast from "react-hot-toast";

const wagmigotchiContract = {
  address: coinPusherAddress as `0x${string}`,
  abi: abi,
} as const;

const VALUE_BY_FLAG = ["0.001", "0.005", "0.01"];

export const useGameDeposit = () => {
  const { data: hash, isPending, writeContractAsync } = useWriteContract();

  const { isLoading: isConfirming, isSuccess: isConfirmed } =
    useWaitForTransactionReceipt({
      hash,
    });

  const handleDeposit = async (flag: number) => {
    try {
      const value = VALUE_BY_FLAG[flag];
      await writeContractAsync({
        ...wagmigotchiContract,
        functionName: "deposit",
        args: [flag],
        value: parseEther(value),
      });
      toast.loading("Confirming transaction...", { id: "hash" });
    } catch (error) {
      toast.error("Failed to send transaction");
      console.error(error);
    }
  };

  return {
    handleDeposit,
    isPending,
    isConfirming,
    isConfirmed,
  };
};

```

This is a custom Hook named `useGameDeposit`, which is used to handle the deposit functionality in blockchain games.

**Explanation**:

* **useWriteContract:** `useWriteContract` is a Hook in Wagmi designed for executing write functions on a contract. It returns several properties, from which we extract attributes using destructuring: `data`, `isPending`, and `writeContractAsync`. `data` is the transaction hash returned after sending a transaction. `isPending` is a boolean value indicating whether there is an ongoing write operation (such as a transaction); if `isPending` returns `true`, it means the transaction has not yet been confirmed by the blockchain. `writeContractAsync` is an asynchronous function that allows you to send transactions to a smart contract.
* **useWriteForTransactionReceipt**: `useWaitForTransactionReceipt` is also a hook provided by Wagmi. The role of this hook is to listen for a transaction receipt, ensuring that the transaction has been fully confirmed. `isLoading` is a property from the object returned by `useWaitForTransactionReceipt`, which is renamed to `isConfirming` here. This property is a boolean value indicating whether the transaction is still pending confirmation.
* **handleDeposit**: This function accepts a flag parameter and retrieves the corresponding amount from the `VALUE_BY_FLAG` array. It then uses `writeContractAsync` to call the smart contract's `deposit` function, passing the `flag` as an argument with `args: [flag]`. The `value: parseEther(value)` converts the ETH amount to Wei units for the transaction.

### CSS Style

Copy the following code snippet into the `app/globals.css` file.

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

@layer components {
  .dataPanel {
    @apply relative m-4 flex h-80 w-11/12 p-8 flex-col items-center justify-between gap-4 rounded-[38px] border border-black bg-white sm:m-8 sm:h-96 sm:w-[450px] lg:w-[400px] xl:h-[400px] 2xl:mx-10 2xl:h-[450px] 2xl:w-[500px] 2xl:border-2;
  }

  .dataItem {
    @apply flex justify-between w-full border-b border-black pb-2;
  }

  .depositButton {
    @apply w-full rounded-lg bg-[#FF652B] border 2xl:border-2 border-black p-4 text-white;
  }
}
```

### Coin Pusher Interface

Copy the following code snippet into the `app/page.tsx` file.

In Next.js, `app/page.tsx` is a special file used to define the root page of the application, that is, the UI that is displayed when you visit the root URL of the application (usually `/`).

```tsx
"use client";

import { ConnectButton } from "@rainbow-me/rainbowkit";
import { useGameStatus } from "@/hooks/useGameStatus";
import { zeroAddress } from "viem";
import { useGameDeposit } from "@/hooks/useGameDeposit";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";

export const POOL_STATE = [
  "Initial",
  "Launching",
  "Launched",
  "Completed",
  "Revealing",
  "Revealed",
];

const DEPOSIT_AMOUNTS = [
  { value: "0.001", flag: 0 },
  { value: "0.005", flag: 1 },
  { value: "0.01", flag: 2 },
];

const Home = () => {
  const [activeDepositFlag, setActiveDepositFlag] = useState<number | null>(
    null
  );
  const { gameStatus, isLoading, refetch: refetchGameStatus } = useGameStatus();
  const { handleDeposit, isPending, isConfirmed, isConfirming } =
    useGameDeposit();
  const isDepositing = isPending || isConfirming;

  const handleDepositClick = (flag: number) => {
    setActiveDepositFlag(flag);
    handleDeposit(flag);
  };

  useEffect(() => {
    if (isConfirmed) {
      toast.dismiss("hash");
      toast.success("Transaction confirmed!");
      refetchGameStatus();
      setActiveDepositFlag(null);
    }
  }, [isConfirmed, refetchGameStatus]);

  if (isLoading) {
    return;
  }

  return (
    <div className="flex min-h-svh w-screen items-center bg-white">
      <div className="mx-auto flex flex-col items-center gap-6 max-w-[1440px] px-4 md:px-10">
        {/* "ConnectButton" component from RainbowKit */}
        <ConnectButton />
        <div className="flex flex-col items-center rounded-[38px] border border-black bg-[#FF652B] shadow lg:mb-0 2xl:border-2">
          <div className="dataPanel">
            <p className="2x:mb-10 rounded-full border border-[#FF652B] px-5 py-2 text-[#FF652B] text-lg">
              pool state: {POOL_STATE[gameStatus.state]}
            </p>
            <div className="flex flex-col gap-4 w-full text-lg ">
              <div className="dataItem">
                <p>Pool Winner:</p>
                <p>
                  {gameStatus.winner === zeroAddress
                    ? "No Winner Yet"
                    : gameStatus.winner.slice(0, 6) +
                      "..." +
                      gameStatus.winner.slice(-4)}
                </p>
              </div>
              <div className="dataItem">
                <p>Pool Target:</p>
                <p>{gameStatus.target} ETH</p>
              </div>
              <div className="dataItem">
                <p>Pool Sum:</p>
                <p>{gameStatus.sum} ETH</p>
              </div>
              <div className="dataItem">
                <p>My Deposit:</p>
                <p>{gameStatus.myDeposit} ETH</p>
              </div>
            </div>
          </div>
          <div className="w-full px-4 sm:px-10 flex gap-6 pb-10 2xl:pb-20">
            {DEPOSIT_AMOUNTS.map(({ value, flag }) => (
              <button
                key={flag}
                className={`depositButton ${
                  activeDepositFlag === flag ? "bg-white text-black" : ""
                }`}
                onClick={() => handleDepositClick(flag)}
                disabled={isDepositing}
              >
                {activeDepositFlag === flag && isDepositing ? (
                  <span>Loading...</span>
                ) : (
                  `${value}ETH`
                )}
              </button>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Home;
```

Run `npm run dev`, and then you will be able to view the page locally on port `http://localhost:3000`.

**Explanation**:

This code snippet imports the `ConnectButton` component from RainbowKit, which is primarily responsible for rendering the connect/disconnect wallet button.Then render the `gameStatus` data returned from the `useGameStatus()` hook onto the interface.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FEDjNk0IxsDIQRKWKHLvs%2F%E6%88%AA%E5%B1%8F2024-11-15%2015.40.01.png?alt=media&#x26;token=0faf732a-5973-4b46-a787-b2078a49b31e" alt="" width="375"><figcaption></figcaption></figure>

<pre class="language-typescript"><code class="lang-typescript"><strong>const handleDepositClick = (flag: number) => {
</strong><strong>    setActiveDepositFlag(flag);
</strong>    handleDeposit(flag);
};
</code></pre>

The `handleDepositClick` function receives a `flag` parameter and primarily accomplishes two operations:&#x20;

* `setActiveDepositFlag(flag)` is a state update function used to track the deposit button currently selected by the user.
* `handleDeposit(flag)` is a function obtained from the `useGameDeposit` custom Hook, responsible for handling the actual deposit transaction logic.

```typescript
 useEffect(() => {
    if (isConfirmed) {
      toast.dismiss("hash");
      toast.success("Transaction confirmed!");
      refetchGameStatus();
      setActiveDepositFlag(null);
    }
  }, [isConfirmed, refetchGameStatus]);
```

The purpose of this code is to refetch the game status using `refetchGameStatus` and reset the current active deposit state when the transaction is confirmed (`isConfirmed=true`).

Next, you can try playing the game.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FGwKpVA1AeMDeOK35HPLw%2F%E6%88%AA%E5%B1%8F2024-11-15%2016.09.33.png?alt=media&#x26;token=b3e80448-6a9a-4280-8314-073d472f1368" alt="" width="375"><figcaption></figcaption></figure>

Click the button corresponding to the amount you wish to deposit, and then the button you clicked will turn white and display 'Loading...'.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FtGqMI0QevRCBpaobkSzj%2F%E6%88%AA%E5%B1%8F2024-11-15%2015.25.48.png?alt=media&#x26;token=e1782be2-df5b-433a-8b3b-22d0970d9ab2" alt="" width="188"><figcaption></figcaption></figure>

After a few seconds, the MetaMask wallet will pop up a transaction confirmation window. Click the confirm button to send a deposit transaction.

<figure><img src="https://4239479252-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fc6n88IVs810uKFVbVyUQ%2Fuploads%2FvTIWa5H5SMpMe7FLGLoY%2F%E6%88%AA%E5%B1%8F2024-11-15%2016.04.51.png?alt=media&#x26;token=9d14b4a6-68d8-43d1-a6c3-763810025ac1" alt="" width="375"><figcaption></figcaption></figure>

You can switch between multiple different wallet addresses to send deposit request until the total game amount reaches the encrypted target.

> **Note**: In contract have limited each player to make only one deposit, and making multiple deposits will result in an error.

Congratulations, you have completed the development of the entire dapp.
