Earn Page is a ready-made component that allows users to earn rewards by connecting their wallets and interacting with DeFi partners.

Overview

The EarnPage component uses provided APIs to fetch partners and display them. You need to provide user so that the component can fetch the user’s join status. If the user has not joined, it prompts them to sign up for Turtle. If no user is provided, it will prompt a login modal.

Installation

If you haven’t already, install the dependencies using the following guide:

Get Started

Get Started with the Typescript SDK

Required Setup

Important: The EarnPage component requires several providers to be configured in your application. Make sure to wrap your component with all the required providers as shown in the examples below.

The EarnPage component depends on the following providers in any React application:

  1. An EVM library provider - We suggest WagmiProvider (from wagmi) and provide an adapter for it, but you can use any EVM library with a custom adapter
  2. QueryClientProvider (from @tanstack/react-query) - Required for data fetching
  3. A wallet connection provider - We suggest RainbowKitProvider (from @rainbow-me/rainbowkit) but you can use any wallet connection solution
  4. TurtleProvider (from @turtledev/react) - Required for theming (this is the only one we provide)

All these providers use client-side hooks and contexts. If you’re using Next.js, your component must be wrapped in a Client Component using the "use client" directive. For other React frameworks, this directive is not needed.

Already have these providers configured? If you already have an EVM library (like Wagmi), React Query, and a wallet connection solution set up in your application, you can skip to the Basic Usage section.

Getting Your Project ID

You need to obtain your own projectId from Reown (formerly WalletConnect):

  1. Visit https://cloud.reown.com/
  2. Create an account or sign in
  3. Create a new project
  4. Copy your Project ID

Remember to use your own projectId. Each application must use its own projectId from Reown.

Basic Usage

If you already have the required providers configured in your application, you can use the EarnPage component directly:

MyEarnPage.tsx
import { ConnectButton, useConnectModal } from "@rainbow-me/rainbowkit";
import { EarnPage, TurtleLogo, useWagmiAdapter } from "@turtledev/react";
import { useAccount } from "wagmi";

export function MyEarnPage(): React.ReactElement {
  const { address } = useAccount();
  const { openConnectModal } = useConnectModal();
  const adapter = useWagmiAdapter();

  return (
    <EarnPage
      referral="YOUR_REFERRAL_CODE" // You can get one at https://app.turtle.club/
      user={address}
      header={{
        logo: <TurtleLogo fill="hsl(117, 85%, 69%)" className="w-10 h-10" />,
        text: "Turtle Earn",
        extra: <ConnectButton />,
      }}
      openConnectionModal={openConnectModal ?? (() => {})}
      {...adapter}
    />
  );
}

This component must be rendered within the required providers (an EVM library provider like WagmiProvider, QueryClientProvider, a wallet connection provider like RainbowKitProvider, and TurtleProvider). If you haven’t set these up yet, see the Complete Integration Example below.

Complete Integration Example

Step 1: Create the Provider Wrapper

widget.tsx
"use client";
import {
  ConnectButton,
  getDefaultConfig,
  RainbowKitProvider,
  useConnectModal,
} from "@rainbow-me/rainbowkit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
  defaultThemeConfig,
  EarnPage,
  TurtleLogo,
  TurtleProvider,
  useWagmiAdapter,
} from "@turtledev/react";
import { useAccount, WagmiProvider } from "wagmi";

// Create these outside the component to avoid recreating on every render
const config = getDefaultConfig({
  appName: "Turtle Widget",
  projectId: "YOUR_PROJECT_ID", // 🔹 Get yours at https://cloud.reown.com
  chains: [
    {
      id: 1,
      name: "Ethereum",
      rpcUrls: {
        default: {
          http: ["https://eth.llamarpc.com"],
        },
      },
    },
  ],
});

const queryClient = new QueryClient();

export function EarnPageProviders({ children }: { children: React.ReactNode }) {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider>
          <TurtleProvider
            themeConfig={{
              ...defaultThemeConfig,
              theme: "dark",
            }}
          >
            {children}
          </TurtleProvider>
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

export function MyEarnPage() {
  const { address } = useAccount();
  const { openConnectModal } = useConnectModal();
  const adapter = useWagmiAdapter();

  return (
    <EarnPage
      referral="YOUR_REFERRAL_CODE"
      user={address}
      header={{
        logo: <TurtleLogo fill="hsl(117, 85%, 69%)" className="w-10 h-10" />,
        text: "Turtle Earn",
        extra: <ConnectButton />,
      }}
      openConnectionModal={openConnectModal ?? (() => {})}
      {...adapter}
    />
  );
}

Step 2: Use in Next.js Page

app/widget/page.tsx
import "@rainbow-me/rainbowkit/styles.css";
import { EarnPageProviders, MyEarnPage } from "@/widget";

export default function WidgetPage() {
  return (
    <EarnPageProviders>
      <MyEarnPage />
    </EarnPageProviders>
  );
}

Advanced Wagmi Configuration

For production applications or multi-chain support, you can use a more comprehensive Wagmi configuration. Here’s the configuration we use at Turtle Club as reference:

Adapter

We provide an adapter for Wagmi, since it’s a popular React-based library for interacting with the blockchain. We use Wagmi at Turtle.club, so we recommend it, but you can use any EVM library you prefer.

Using a different EVM library? You can implement your own adapter by providing the required functions: signMessage, sendTransaction, changeNetwork, and network. The EarnPage component is library-agnostic and only requires these functions to work.

MyEarnPage.tsx
import { useWagmiAdapter } from "@turtledev/react";

// Contains `signMessage`, `sendTransaction`, `changeNetwork`, and `network`
const adapter = useWagmiAdapter();

Props

user
string

User’s wallet address (undefined if not connected)

referral
string
required

Referral code

campaignId
string

Optional campaign identifier for tracking specific campaigns

network
Network
required

Network to use (extends number)

header
object

Header configuration object

openConnectionModal
function
required

Function to open the wallet connection modal

changeNetwork
function
required

Function to change the network: (network: Network) => Promise<void>

signMessage
function
required

Function to sign the message for wallet authentication: (message: string) => Promise<string>

sendTransaction
function
required

Function to send a transaction: (transaction: Transaction<Network>) => Promise<string>

startSigning
function

Optional callback triggered when signing process starts

onError
function

Optional error handler: (error: Error) => void

onSuccess
function

Optional success callback

Types

Transaction Interface

types.ts
import type { Address, Hex } from "viem";

export interface Transaction<Network> {
  from: Address;
  to: Address;
  data: Hex;
  chainId: Network;
  value?: bigint;
}

EarnPageProps Interface

types.ts
export interface EarnPageProps<Network extends number> {
  user: string | undefined;
  referral: string;
  campaignId?: string;
  network: Network;
  header?: {
    logo?: React.ReactNode;
    text?: React.ReactNode;
    extra?: React.ReactNode;
  };
  openConnectionModal: () => void;
  changeNetwork: (network: Network) => Promise<void>;
  signMessage: (message: string) => Promise<string>;
  sendTransaction: (transaction: Transaction<Network>) => Promise<string>;
  startSigning?: () => void;
  onError?: (error: Error) => void;
  onSuccess?: () => void;
}

Theming

Using the TurtleProvider

The EarnPage component must be wrapped in a TurtleProvider to apply theming. We recommend wrapping somewhere in your app, like in App.tsx, but you can wrap it in any component.

ThemedEarnPage.tsx
import { TurtleProvider, EarnPage, defaultThemeConfig } from "@turtledev/react";

function ThemedEarnPage() {
  // ...

  return (
    <TurtleProvider
      themeConfig={{
        ...defaultThemeConfig,
        theme: "dark", // or 'light'
      }}
    >
      <EarnPage
        referral="TURTLE"
        user={address}
        network={1} // Ethereum mainnet
        header={{
          text: "Turtle Earn",
        }}
        openConnectionModal={openConnectModal}
        changeNetwork={changeNetwork}
        signMessage={signMessage}
        sendTransaction={sendTransaction}
      />
    </TurtleProvider>
  );
}

Theme Configuration

The themeConfig prop accepts a TurtleThemeConfig object with the following structure:

types.ts
export interface TurtleThemeConfig {
  // Theme selection
  theme: "light" | "dark";

  // Shared variables (apply to both themes)
  shared: {
    borderRadius: string;
    gap: string;
    padding: string;
    fontFamily: string;
    fontSize: string;
    fontWeight: string;
  };

  // Theme-specific colors
  light: ThemeColors;
  dark: ThemeColors;
}

export interface ThemeColors {
  // Background colors
  bgPrimary: string;
  bgSecondary: string;
  bgAccent: string;
  bgTranslucent: string;

  // Text colors
  textPrimary: string;
  textSecondary: string;

  // Border color
  borderColor: string;

  // Button colors
  buttonBgColor: string;
  buttonTextColor: string;

  // Error color
  errorColor: string;
}

Default Theme

The default theme configuration is as follows:

default.ts
{
  theme: "dark",
  shared: {
    borderRadius: "0.5rem",
    gap: "0.75rem",
    padding: "1rem",
    fontFamily: "Inter, system-ui, -apple-system, sans-serif",
    fontSize: "1rem",
    fontWeight: "400",
  },
  light: {
    bgPrimary: "rgb(245, 245, 245)",
    bgSecondary: "rgba(220, 220, 220)",
    bgAccent: "rgba(200, 200, 200)",
    bgTranslucent: "hsl(0 0% 10% / 0.25)",
    borderColor: "rgb(220, 220, 225)",
    textPrimary: "rgba(10, 10, 10)",
    textSecondary: "rgba(30, 30, 30)",
    buttonBgColor: "hsl(117, 85%, 69%)",
    buttonTextColor: "rgb(10, 10, 10)",
    errorColor: "rgb(247, 23, 53)",
  },
  dark: {
    bgPrimary: "rgb(32, 32, 34)",
    bgSecondary: "rgba(20, 20, 20)",
    bgAccent: "rgba(60, 60, 60)",
    bgTranslucent: "hsl(0 0% 90% / 0.25)",
    borderColor: "rgb(53, 53, 59)",
    textPrimary: "rgba(255, 255, 255)",
    textSecondary: "rgba(225, 225, 225)",
    buttonBgColor: "hsl(117, 85%, 69%)",
    buttonTextColor: "rgb(10, 10, 10)",
    errorColor: "rgb(246, 56, 85)",
  },
}

How it Works

  1. The EarnPage component fetches available partners and deals
  2. It checks if the user has joined Turtle using the provided wallet address
  3. If not joined, it prompts the user to sign a message to join
  4. Once joined, users can access partner deals with boosted rewards
  5. The component handles all states: loading, empty results, and listing deals

By providing the necessary authentication functions, the EarnPage component handles the entire flow from wallet connection to deal redemption.

Common Integration Issues