How to Build a Custom Interface for PoolTogether v4
October 19th, 2023

PoolTogether is a decentralized prize savings protocol with over 50,000 users (poolers) who’ve collectively won over $5,000,000 USDC. The protocol has been live for over four years and has quickly become one of the most popular decentralized finance (DeFi) applications.

Being a decentralized protocol, PoolTogether is open-source and driven forward by its community of poolers. This means that anyone can contribute to the protocol or integrate PoolTogether into their own dapp, smart contract, or project, permissionlessly.

In this guide, we’ll walk you through how to build a custom interface for interacting with the PoolTogether v4 contracts.

You can also see a live demo here, as well as the reference code

TurboETH Demo
TurboETH Demo

Prerequisites

Before we dive into the development process, make sure you have the following prerequisites in place:

  1. Web Development Skills: Familiarity with React and TypeScript are essential for creating the front-end interface.

  2. Cryptocurrency Wallet: Install your favorite wallet, like MetaMask to interact with the blockchain and test out your interface.

  3. Solidity Knowledge (optional): A basic understanding of Solidity, Ethereum’s smart contract programming language, will be beneficial.

Step 1: Set Up Your Development Environment

We’re going to use the TurboETH CLI to jumpstart our project.

To get started run:

Using npm:

npm create turbo-eth@latest

Using pnpm:

pnpm create turbo-eth@latest

This is going to ask us a few questions to initialize our project correctly. You can answer the questions as you like, but be sure to select the PoolTogether V4 integration.

TurboETH CLI
TurboETH CLI

Upon the CLI’s successful completion of the project scaffolding, you can start running it by typing the following commands into your terminal:

cd <project-name>
npm dev or pnpm dev

Step 2: Interacting with the PoolTogether Smart Contracts

TurboETH uses Wagmi CLI to generate react hooks that enables frontend applications to easily interact with smart contracts. This integration exposes 2 hooks to interact with the depositToAndDeletage function of PoolTogether V4:

  1. usePreparePoolTogetherPrizePoolDepositToAndDelegate to prepare the data we’re going to send to PoolTogether.

  2. usePoolTogetherPrizePoolDepositToAndDelegate to call the write method on PoolTogether’s smart contracts.

Note: both hooks are wrappers of wagmi’s usePrepareContractWrite and useContractWrite , so the structure and usage is similar.

To begin, we need to know which chain our user is on. TurboETH has already scaffolded the project with wallet connection via Rainbowkit included, so we just need to grab that information to pass to our usePreparePoolTogetherPrizePoolDepositToAndDelegate hook.

TurboETH already aggregated all the contract addresses and ABIs, so we don’t have to worry about them and we can just grab the address using a helper function:

const poolTogetherAddress = useLoadContractFromChainId(PRIZE_POOL_CONTRACT)

Next, our usePreparePoolTogetherPrizePoolDepositToAndDelegate hook requires three arguments: 1) the PoolTogether contract address 2) the user address 3) the amount the user is depositing.

We already have the PT contract address above, so you’ll only need to retreive the user address and how much they’re going to deposit. To get the user’s address, we can use wagmi’s useAccount hook, and in the next step we’ll create a form for the user to tell use how much they want to deposit.

const { address } = useAccount()

const { config, error } = usePreparePoolTogetherPrizePoolDepositToAndDelegate({
  address: poolTogetherAddress,
  args: [address, depositAmount, address],
  enabled: Boolean(isValidContractCall),
})

Step 3: Design Your Custom Interface

The primary information we need from the user is how much they want to deposit into PoolTogether. For the purpose of this guide, we’re just going to focus on creating a simple form to capture this information, but in your own project you may want to showcase relevant information, like if they’re already deposited, the amount of tokens they have available to deposit, etc.

Forms can be tricky in React, but luckily we’re going to use a few libraries that’ll make our lives much easier. For our purposes, we’re going to use react-hook-form , zod , and shadcnUI components to compose together our form.

a) Let’s start by defining the shape of our form using zod. You can read more about using Zod in the Zod documentation

"use client"

import * as z from "zod"

const formSchema = z.object({
  depositAmount: z.bigint()
})

b) Next we’ll use the useForm hook from react-hook-form to create a form.

"use client"

import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"

const formSchema = z.object({
  depositAmount: z.bigint()
})

export function DepositAmountForm() {
  // 1. Define your form.
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      depositAmount: 0,
    },
  })

	// 2. TODO: Prepare the PoolTogehter Contract

  // 3. Define a submit handler.
  function onSubmit(values: z.infer<typeof formSchema>) {
    // ✅ This will be type-safe and validated.
		// This is where we will write to PoolTogether in the next step
		console.log(values)
  }
}

c) Finally, we can build the form frontend using <Form /> components from shadcnUI.

import { Button } from "@/components/ui/button"
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
} from "@/components/ui/form"

export function DepositAmountForm() {
	// ...

	return (
		<Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
        <FormField
          control={form.control}
          name="username"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Deposit Amount</FormLabel>
              <FormControl>
                <Input placeholder="$20.00" {...field} />
              </FormControl>
              <FormDescription>
                This is how much you will deposit into PoolTogether.
              </FormDescription>
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
	)
}

And now we have a fully accessible form that is type-safe with client-side validation!

Step 4: Tying it All Together

There’s a little bit more trickery that we need to pull off to get the full flow working properly.

Because we need to prepare the configuration for the PoolTogether contract before writing to it, we need to use the watch function from react-hook-form to grab the deposit amount before submitting the form.

// ...
const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      depositAmount: 0,
    },
  })
const depositAmount = form.watch()

Now, we can use the deposit amount from the form to prepare the contract, then pass that configuration to our usePoolTogetherPrizePoolDepositToAndDelegate hook. Once we’ve got all of those ready, we can call the write function in our onSubmit handler.

export function DepositAmountForm() {
  // 1. Define your form.
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      depositAmount: 0,
    },
  })

	// 2. Prepare the PoolTogehter Contract
	const { config, error } = usePreparePoolTogetherPrizePoolDepositToAndDelegate({
	  address: poolTogetherAddress,
	  args: [address, depositAmount, address],
	  enabled: Boolean(isValidContractCall),
	})
	
	const { 
		data,
		isLoading,
		isSuccess,
		write
	} =	usePoolTogetherPrizePoolDepositToAndDelegate(config)

  // 3. Define a submit handler.
  function onSubmit(values: z.infer<typeof formSchema>) {
    // ✅ This will be type-safe and validated.
		write()
  }
}

Conclusion

By following these steps, you can create your own custom interface for PoolTogether v4. In this guide, we have covered only the depositToAndDelegate function, but you can extend it with any other contract from the protocol.

Customizing the interface not allows you to control the end-to-end interaction with the protocol, but it also contributes to the broader PoolTogether community.

Disclaimer: This guide is for educational purposes only. Always exercise caution and conduct thorough testing before deploying any smart contracts or applications on the Ethereum blockchain.

Join The Community

TurboETH has a growing community and you’re invited to join!

For a more complete references to everything mentioned in the article we recommend reading the TurboETH documentation.

Subscribe to ⚡️ TurboETH
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from ⚡️ TurboETH

Skeleton

Skeleton

Skeleton